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: 1 окт. 2019 г.
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 namespace lsp
25 {
26     namespace tk
27     {
~IStyleListener()28         IStyleListener::~IStyleListener()
29         {
30         }
31 
notify(ui_atom_t property)32         void IStyleListener::notify(ui_atom_t property)
33         {
34         }
35 
LSPStyle()36         LSPStyle::LSPStyle()
37         {
38             nLock       = 0;
39             bDelayed    = false;
40         }
41 
~LSPStyle()42         LSPStyle::~LSPStyle()
43         {
44             do_destroy();
45         }
46 
init()47         status_t LSPStyle::init()
48         {
49             return STATUS_OK;
50         }
51 
destroy()52         void LSPStyle::destroy()
53         {
54             do_destroy();
55         }
56 
do_destroy()57         void LSPStyle::do_destroy()
58         {
59             // Unlock all pending transactions
60             nLock   = 0;
61             delayed_notify();
62 
63             // Unlink from parents and remove all children
64             for (size_t i=0, n=vParents.size(); i<n; ++i)
65             {
66                 LSPStyle *parent = vParents.at(i);
67                 if (parent != NULL)
68                     parent->vChildren.remove(this);
69             }
70 
71             // Unlink from children and remove all children
72             for (size_t i=0, n=vChildren.size(); i<n; ++i)
73             {
74                 LSPStyle *child = vChildren.at(i);
75                 if (child != NULL)
76                 {
77                     child->vParents.remove(this);
78                     child->sync();
79                 }
80             }
81             vChildren.flush();
82 
83             // Synchronize state with listeners and remove them all
84             sync();
85             vListeners.flush();
86 
87             // Destroy stored properties
88             for (size_t i=0, n=vProperties.size(); i<n; ++i)
89                 undef_property(vProperties.at(i));
90             vProperties.flush();
91         }
92 
undef_property(property_t * property)93         void LSPStyle::undef_property(property_t *property)
94         {
95             if (property == NULL)
96                 return;
97 
98             switch (property->type)
99             {
100                 case PT_STRING:
101                     if (property->v.sValue != NULL)
102                         ::free(property->v.sValue);
103                     break;
104                 default:
105                     break;
106             }
107 
108             property->type = PT_UNKNOWN;
109         }
110 
copy_property(property_t * dst,const property_t * src)111         status_t LSPStyle::copy_property(property_t *dst, const property_t *src)
112         {
113             // Check type of property
114             if (src->type != dst->type)
115                 return STATUS_OK;
116 
117             // Update contents
118             switch (src->type)
119             {
120                 case PT_INT:
121                     if (dst->v.iValue != src->v.iValue)
122                         ++dst->changes;
123                     dst->v.iValue   = src->v.iValue;
124                     break;
125                 case PT_FLOAT:
126                     if (dst->v.fValue != src->v.fValue)
127                         ++dst->changes;
128                     dst->v.fValue   = src->v.fValue;
129                     break;
130                 case PT_BOOL:
131                     if (dst->v.bValue != src->v.bValue)
132                         ++dst->changes;
133                     dst->v.bValue   = src->v.bValue;
134                     break;
135                 case PT_STRING:
136                 {
137                     // Value does match?
138                     if (::strcmp(dst->v.sValue, src->v.sValue) == 0)
139                         break;
140 
141                     // Update value
142                     char *tmp = ::strdup(src->v.sValue);
143                     if (tmp == NULL)
144                         return STATUS_NO_MEM;
145                     ::free(dst->v.sValue);
146                     dst->v.sValue = tmp;
147                     ++dst->changes;
148                     break;
149                 }
150             }
151 
152             return STATUS_OK;
153         }
154 
create_property(ui_atom_t id,const property_t * src)155         LSPStyle::property_t *LSPStyle::create_property(ui_atom_t id, const property_t *src)
156         {
157             // Allocate property
158             property_t *dst = vProperties.add();
159             if (dst == NULL)
160                 return NULL;
161 
162             // Init contents
163             switch (src->type)
164             {
165                 case PT_INT:    dst->v.iValue   = src->v.iValue;    break;
166                 case PT_FLOAT:  dst->v.fValue   = src->v.fValue;    break;
167                 case PT_BOOL:   dst->v.bValue   = src->v.bValue;    break;
168                 case PT_STRING:
169                 {
170                     // Update value
171                     if ((dst->v.sValue = ::strdup(src->v.sValue)) == NULL)
172                     {
173                         vProperties.remove(dst);
174                         return NULL;
175                     }
176                     break;
177                 }
178                 default:
179                     return NULL;
180             }
181 
182             dst->id         = id;
183             dst->refs       = 0;
184             dst->type       = src->type;
185             dst->changes    = 0;
186             dst->flags      = F_DEFAULT;
187             dst->owner      = this;
188 
189             return dst;
190         }
191 
create_property(ui_atom_t id,ui_property_type_t type)192         LSPStyle::property_t *LSPStyle::create_property(ui_atom_t id, ui_property_type_t type)
193         {
194             // Allocate property
195             property_t *dst = vProperties.add();
196             if (dst == NULL)
197                 return NULL;
198 
199             // Init contents
200             switch (type)
201             {
202                 case PT_INT:    dst->v.iValue = 0;      break;
203                 case PT_FLOAT:  dst->v.fValue = 0.0;    break;
204                 case PT_BOOL:   dst->v.bValue = 0;      break;
205                 case PT_STRING:
206                     if ((dst->v.sValue = ::strdup("")) == NULL)
207                     {
208                         vProperties.remove(dst);
209                         return NULL;
210                     }
211                     break;
212                 default:
213                     return NULL;
214             }
215 
216             dst->id         = id;
217             dst->refs       = 0;
218             dst->type       = type;
219             dst->changes    = 0;
220             dst->flags      = F_DEFAULT;
221             dst->owner      = this;
222 
223             return dst;
224         }
225 
sync_property(property_t * p)226         status_t LSPStyle::sync_property(property_t *p)
227         {
228             if (!(p->flags & F_DEFAULT))
229                 return STATUS_OK;
230 
231             property_t *parent  = get_parent_property(p->id);
232             size_t changes      = p->changes;
233             status_t res        = (parent != NULL) ? copy_property(p, parent) : set_property_default(p);
234             if ((res == STATUS_OK) && (changes != p->changes))
235             {
236                 notify_listeners(p);
237                 notify_children(p);
238             }
239             return res;
240         }
241 
set_property_default(property_t * p)242         status_t LSPStyle::set_property_default(property_t *p)
243         {
244             switch (p->type)
245             {
246                 case PT_INT:
247                     if (p->v.iValue == 0)
248                         return STATUS_OK;
249                     p->v.iValue = 0;
250                     break;
251                 case PT_FLOAT:
252                     if (p->v.fValue == 0)
253                         return STATUS_OK;
254                     p->v.fValue = 0;
255                     break;
256                 case PT_BOOL:
257                     if (p->v.bValue == false)
258                         return STATUS_OK;
259                     p->v.bValue = false;
260                     break;
261                 case PT_STRING:
262                 {
263                     char *tmp = ::strdup("");
264                     if (tmp == NULL)
265                         return STATUS_NO_MEM;
266                     ::free(p->v.sValue);
267                     p->v.sValue = tmp;
268                     break;
269                 }
270                 default:
271                     return STATUS_BAD_TYPE;
272             }
273 
274             p->flags   |= F_DEFAULT;
275             ++p->changes;
276             return STATUS_OK;
277         }
278 
sync()279         void LSPStyle::sync()
280         {
281             // For each property: copy value from parent and notify children and listeners for changes
282             property_t *vp = vProperties.get_array();
283             for (size_t i=0, n=vProperties.size(); i < n; ++i)
284                 sync_property(&vp[i]);
285 
286             // Call all children for sync()
287             for (size_t i=0, n=vChildren.size(); i<n; ++i)
288             {
289                 LSPStyle *child = vChildren.at(i);
290                 if (child != NULL)
291                     child->sync();
292             }
293         }
294 
delayed_notify()295         void LSPStyle::delayed_notify()
296         {
297             size_t notified;
298 
299             if (bDelayed)
300                 return;
301 
302             bDelayed = true; // Disallow delayed notify because it is already active
303             do
304             {
305                 notified = 0;
306                 for (size_t i=0, n=vProperties.size(); i < n; ++i)
307                 {
308                     property_t *prop = vProperties.at(i);
309 
310                     // Notify if notification is pending
311                     if (prop->flags & F_NTF_LISTENERS)
312                     {
313                         prop->flags &= ~F_NTF_LISTENERS;
314                         notify_listeners(prop);
315                         ++notified;
316                     }
317                     if (prop->flags & F_NTF_CHILDREN)
318                     {
319                         prop->flags &= ~F_NTF_CHILDREN;
320                         notify_children(prop);
321                         ++notified;
322                     }
323                 }
324             } while (notified > 0);
325             bDelayed = false;
326         }
327 
notify_change(property_t * prop)328         void LSPStyle::notify_change(property_t *prop)
329         {
330             // Find the matching property (if present)
331             property_t *p = get_property(prop->id);
332 
333             // Property not found?
334             if (p == NULL)
335             {
336                 notify_children(prop); // Just bypass event to children
337                 return;
338             }
339             else if (!(p->flags & F_DEFAULT)) // Not default property? Ignore the event
340                 return;
341 
342             // Get parent Property
343             property_t *parent = get_parent_property(prop->id);
344             if (parent != NULL)
345             {
346                 // Parent property has been changed?
347                 size_t change = p->changes;
348                 status_t res = copy_property(p, parent);
349                 if ((res == STATUS_OK) && (change == p->changes))
350                     return;
351             }
352             else
353             {
354                 // Copy property value and notify listener and children only if property has changed
355                 size_t change = p->changes;
356                 status_t res = copy_property(p, prop);
357                 if ((res == STATUS_OK) && (change == p->changes))
358                     return;
359             }
360 
361             // Notify children and listeners about property change
362             notify_listeners(p);
363             notify_children(p);
364         }
365 
notify_children(property_t * prop)366         void LSPStyle::notify_children(property_t *prop)
367         {
368             // In transaction, just set notification flag instead of issuing notification procedure
369             if ((nLock > 0) && (prop->owner == this))
370             {
371                 prop->flags    |= F_NTF_CHILDREN;
372                 return;
373             }
374 
375             // Notify all children about property change
376             for (size_t i=0, n=vChildren.size(); i<n; ++i)
377             {
378                 LSPStyle *child = vChildren.at(i);
379                 if (child != NULL)
380                     child->notify_change(prop);
381             }
382         }
383 
notify_listeners(property_t * prop)384         void LSPStyle::notify_listeners(property_t *prop)
385         {
386             // In transaction, just set notification flag instead of issuing notification procedure
387             if ((nLock > 0) && (prop->owner == this))
388             {
389                 prop->flags    |= F_NTF_LISTENERS;
390                 return;
391             }
392 
393             // Notify all listeners about property change
394             ui_atom_t id = prop->id;
395             for (size_t i=0, n=vListeners.size(); i<n; ++i)
396             {
397                 listener_t *lst = vListeners.at(i);
398                 if ((lst != NULL) && (lst->nId == id))
399                     lst->pListener->notify(id);
400             }
401         }
402 
add_child(LSPStyle * child,ssize_t idx)403         status_t LSPStyle::add_child(LSPStyle *child, ssize_t idx)
404         {
405             // Check arguments
406             if (child == NULL)
407                 return STATUS_BAD_ARGUMENTS;
408             if (idx < 0)
409                 idx = vChildren.size();
410             else if (size_t(idx) > vChildren.size())
411                 return STATUS_INVALID_VALUE;
412 
413             // Check
414             if (vChildren.index_of(child) >= 0)
415                 return STATUS_ALREADY_EXISTS;
416             if ((child == this) || (child->has_child(this, true)))
417                 return STATUS_BAD_HIERARCHY;
418 
419             // Make bindings
420             if (!vChildren.insert(child, idx))
421                 return STATUS_NO_MEM;
422             if (!child->vParents.add(this))
423             {
424                 vChildren.remove(child);
425                 return STATUS_NO_MEM;
426             }
427 
428             // Synchronize state
429             child->sync();
430 
431             return STATUS_OK;
432         }
433 
add_parent(LSPStyle * parent,ssize_t idx)434         status_t LSPStyle::add_parent(LSPStyle *parent, ssize_t idx)
435         {
436             // Check arguments
437             if (parent == NULL)
438                 return STATUS_BAD_ARGUMENTS;
439             if (idx < 0)
440                 idx = vParents.size();
441             else if (size_t(idx) > vParents.size())
442                 return STATUS_INVALID_VALUE;
443 
444             // Check
445             if (vParents.index_of(parent) >= 0)
446                 return STATUS_ALREADY_EXISTS;
447             if ((parent == this) || (this->has_child(parent, true)))
448                 return STATUS_BAD_HIERARCHY;
449 
450             // Make bindings
451             if (!vParents.insert(parent, idx))
452                 return STATUS_NO_MEM;
453             if (!parent->vChildren.add(this))
454             {
455                 vParents.remove(parent);
456                 return STATUS_NO_MEM;
457             }
458 
459             // Synchronize state
460             sync();
461 
462             return STATUS_OK;
463         }
464 
remove_child(LSPStyle * child)465         status_t LSPStyle::remove_child(LSPStyle *child)
466         {
467             if (child == NULL)
468                 return STATUS_BAD_ARGUMENTS;
469 
470             if (!vChildren.remove(child, true))
471                 return STATUS_NOT_FOUND;
472 
473             child->vParents.remove(this);
474             child->sync();
475 
476             return STATUS_OK;
477         }
478 
remove_parent(LSPStyle * parent)479         status_t LSPStyle::remove_parent(LSPStyle *parent)
480         {
481             if (parent == NULL)
482                 return STATUS_BAD_ARGUMENTS;
483 
484             if (!vParents.remove(parent))
485                 return STATUS_NOT_FOUND;
486 
487             parent->vChildren.remove(this);
488             sync();
489 
490             return STATUS_OK;
491         }
492 
has_child(LSPStyle * child,bool recursive)493         bool LSPStyle::has_child(LSPStyle *child, bool recursive)
494         {
495             if ((child == NULL) || (child == this))
496                 return false;
497 
498             // First, lookup self children
499             if (vChildren.index_of(child) >= 0)
500                 return true;
501             else if (!recursive)
502                 return false;
503 
504             // Second, lookup recursive
505             for (size_t i=0, n=vChildren.size(); i<n; ++i)
506             {
507                 LSPStyle *s = vChildren.at(i);
508                 if ((s != NULL) && (s->has_child(child, recursive)))
509                     return true;
510             }
511 
512             return false;
513         }
514 
has_parent(LSPStyle * parent,bool recursive)515         bool LSPStyle::has_parent(LSPStyle *parent, bool recursive)
516         {
517             if ((parent == NULL) || (parent == this))
518                 return false;
519 
520             // First, lookup self children
521             if (vParents.index_of(parent) >= 0)
522                 return true;
523             else if (!recursive)
524                 return false;
525 
526             // Second, lookup recursively parents
527             for (size_t i=0, n=vParents.size(); i<n; ++i)
528             {
529                 LSPStyle *s = vParents.at(i);
530                 if ((s != NULL) && (s->has_parent(parent, recursive)))
531                     return true;
532             }
533 
534             return false;
535         }
536 
is_bound(ui_atom_t id,IStyleListener * listener) const537         bool LSPStyle::is_bound(ui_atom_t id, IStyleListener *listener) const
538         {
539             const listener_t *pv = vListeners.get_array();
540             for (size_t i=0, n=vListeners.size(); i<n; ++i)
541             {
542                 const listener_t *p = &pv[i];
543                 if ((p->nId == id) && (p->pListener == listener))
544                     return true;
545             }
546             return false;
547         }
548 
bind(ui_atom_t id,ui_property_type_t type,IStyleListener * listener)549         status_t LSPStyle::bind(ui_atom_t id, ui_property_type_t type, IStyleListener *listener)
550         {
551             property_t *p = get_property(id);
552             listener_t *lst = NULL;
553 
554             // Property has been found?
555             if (p == NULL)
556             {
557                 // Lookup parent property
558                 property_t *parent = get_parent_property(id);
559 
560                 // Create property
561                 p = (parent != NULL) ? create_property(id, parent) : create_property(id, type);
562                 if (p == NULL)
563                     return STATUS_NO_MEM;
564 
565                 // Allocate listener binding
566                 lst = vListeners.add();
567                 if (listener == NULL)
568                 {
569                     undef_property(p);
570                     vProperties.remove(p);
571                     return STATUS_NO_MEM;
572                 }
573             }
574             else
575             {
576                 // Check that not already bound
577                 if (is_bound(id, listener))
578                     return STATUS_ALREADY_BOUND;
579 
580                 // Just allocate listener binding
581                 lst = vListeners.add();
582                 if (listener == NULL)
583                     return STATUS_NO_MEM;
584             }
585 
586             // Save listener to allocated binding
587             lst->nId        = p->id;
588             lst->pListener  = listener;
589             ++p->refs;
590 
591             notify_listeners(p);
592             notify_children(p);
593 
594             return STATUS_OK;
595         }
596 
unbind(ui_atom_t id,IStyleListener * listener)597         status_t LSPStyle::unbind(ui_atom_t id, IStyleListener *listener)
598         {
599             // Find listener binding
600             listener_t *lst = NULL;
601             listener_t *pv = vListeners.get_array();
602             for (size_t i=0, n=vListeners.size(); i<n; ++i)
603             {
604                 listener_t *p = &pv[i];
605                 if ((p->nId == id) && (p->pListener == listener))
606                 {
607                     lst = p;
608                     break;
609                 }
610             }
611 
612             if (lst == NULL)
613                 return STATUS_NOT_BOUND;
614 
615             // Get property
616             property_t *p = get_property(id);
617             if (p == NULL)
618                 return STATUS_CORRUPTED; // This actually should not ever happen
619 
620             // Decrement number of references
621             if ((--p->refs) <= 0)
622             {
623                 undef_property(p);
624                 property_t *parent = get_parent_property(p->id);
625                 notify_children((parent != NULL) ? parent : p);
626                 vProperties.remove(p);
627             }
628 
629             // Remove listener binding
630             vListeners.remove(lst);
631 
632             return STATUS_OK;
633         }
634 
get_property(ui_atom_t id)635         LSPStyle::property_t *LSPStyle::get_property(ui_atom_t id)
636         {
637             for (size_t i=0, n=vProperties.size(); i<n; ++i)
638             {
639                 property_t *p   = vProperties.at(i);
640                 if ((p != NULL) && (p->id == id))
641                     return p;
642             }
643             return NULL;
644         }
645 
get_parent_property(ui_atom_t id)646         LSPStyle::property_t *LSPStyle::get_parent_property(ui_atom_t id)
647         {
648             // Lookup parents in reverse order
649             for (ssize_t i=vParents.size() - 1; i >= 0; --i)
650             {
651                 LSPStyle *curr = vParents.at(i);
652                 if (curr == NULL)
653                     continue;
654 
655                 // Try to fetch property first
656                 property_t *p = curr->get_property(id);
657                 if (p == NULL) // Property not found?
658                     p = curr->get_parent_property(id); // Search parents recursively
659                 if (p != NULL)
660                     return p;
661             }
662 
663             return NULL;
664         }
665 
get_property_recursive(ui_atom_t id)666         LSPStyle::property_t *LSPStyle::get_property_recursive(ui_atom_t id)
667         {
668             property_t *p = get_property(id);
669             return (p != NULL) ? p : get_parent_property(id);
670         }
671 
begin()672         void LSPStyle::begin()
673         {
674             ++nLock;
675         }
676 
end()677         void LSPStyle::end()
678         {
679             if (nLock == 0)
680                 return;
681             if (!(--nLock)) // last end() ?
682                 delayed_notify();
683         }
684 
get_int(ui_atom_t id,ssize_t * dst) const685         status_t LSPStyle::get_int(ui_atom_t id, ssize_t *dst) const
686         {
687             const property_t *prop = get_property_recursive(id);
688             if (prop == NULL)
689             {
690                 *dst = 0;
691                 return STATUS_OK;
692             }
693             else if (prop->type != PT_INT)
694                 return STATUS_BAD_TYPE;
695             if (dst != NULL)
696                 *dst = prop->v.iValue;
697             return STATUS_OK;
698         }
699 
get_float(ui_atom_t id,float * dst) const700         status_t LSPStyle::get_float(ui_atom_t id, float *dst) const
701         {
702             const property_t *prop = get_property_recursive(id);
703             if (prop == NULL)
704             {
705                 *dst = 0.0f;
706                 return STATUS_OK;
707             }
708             else if (prop->type != PT_FLOAT)
709                 return STATUS_BAD_TYPE;
710             if (dst != NULL)
711                 *dst = prop->v.fValue;
712             return STATUS_OK;
713         }
714 
get_bool(ui_atom_t id,bool * dst) const715         status_t LSPStyle::get_bool(ui_atom_t id, bool *dst) const
716         {
717             const property_t *prop = get_property_recursive(id);
718             if (prop == NULL)
719             {
720                 *dst = false;
721                 return STATUS_OK;
722             }
723             else if (prop->type != PT_BOOL)
724                 return STATUS_BAD_TYPE;
725             if (dst != NULL)
726                 *dst = prop->v.bValue;
727             return STATUS_OK;
728         }
729 
get_string(ui_atom_t id,LSPString * dst) const730         status_t LSPStyle::get_string(ui_atom_t id, LSPString *dst) const
731         {
732             const property_t *prop = get_property_recursive(id);
733             if (prop == NULL)
734             {
735                 if (dst != NULL)
736                     dst->truncate();
737                 return STATUS_OK;
738             }
739             else if (prop->type != PT_STRING)
740                 return STATUS_BAD_TYPE;
741 
742             if (dst == NULL)
743                 return STATUS_OK;
744             return (dst->set_utf8(prop->v.sValue)) ? STATUS_OK : STATUS_NO_MEM;
745         }
746 
get_string(ui_atom_t id,const char ** dst) const747         status_t LSPStyle::get_string(ui_atom_t id, const char **dst) const
748         {
749             const property_t *prop = get_property_recursive(id);
750             if (prop == NULL)
751             {
752                 if (dst != NULL)
753                     *dst = "";
754                 return STATUS_OK;
755             }
756             else if (prop->type != PT_STRING)
757                 return STATUS_BAD_TYPE;
758 
759             if (dst != NULL)
760                 *dst = prop->v.sValue;
761             return STATUS_OK;
762         }
763 
is_default(ui_atom_t id) const764         bool LSPStyle::is_default(ui_atom_t id) const
765         {
766             const property_t *prop = get_property_recursive(id);
767             return (prop != NULL) ? (prop->flags & F_DEFAULT) : false;
768         }
769 
exists(ui_atom_t id) const770         bool LSPStyle::exists(ui_atom_t id) const
771         {
772             const property_t *prop = get_property_recursive(id);
773             return (prop != NULL);
774         }
775 
get_type(ui_atom_t id) const776         ssize_t LSPStyle::get_type(ui_atom_t id) const
777         {
778             const property_t *prop = get_property_recursive(id);
779             return (prop != NULL) ? prop->type : PT_UNKNOWN;
780         }
781 
set_property(ui_atom_t id,property_t * src)782         status_t LSPStyle::set_property(ui_atom_t id, property_t *src)
783         {
784             status_t res = STATUS_OK;
785             property_t *p = get_property(id);
786             if (p == NULL)
787             {
788                 // Allocate new property
789                 p = create_property(id, src);
790                 if (p == NULL)
791                     return STATUS_NO_MEM;
792                 p->flags   &= ~F_DEFAULT;
793                 notify_listeners(p);
794                 notify_children(p);
795             }
796             else
797             {
798                 // Notify only if value has changed
799                 size_t change = p->changes;
800                 res         = copy_property(p, src);
801 
802                 if (res == STATUS_OK)
803                 {
804                     p->flags   &= ~F_DEFAULT;
805                     if (change != p->changes)
806                     {
807                         notify_listeners(p);
808                         notify_children(p);
809                     }
810                 }
811             }
812 
813             return res;
814         }
815 
set_int(ui_atom_t id,ssize_t value)816         status_t LSPStyle::set_int(ui_atom_t id, ssize_t value)
817         {
818             property_t tmp;
819             tmp.type        = PT_INT;
820             tmp.v.iValue    = value;
821             return set_property(id, &tmp);
822         }
823 
set_float(ui_atom_t id,float value)824         status_t LSPStyle::set_float(ui_atom_t id, float value)
825         {
826             property_t tmp;
827             tmp.type        = PT_FLOAT;
828             tmp.v.fValue    = value;
829             return set_property(id, &tmp);
830         }
831 
set_bool(ui_atom_t id,bool value)832         status_t LSPStyle::set_bool(ui_atom_t id, bool value)
833         {
834             property_t tmp;
835             tmp.type        = PT_BOOL;
836             tmp.v.bValue    = value;
837             return set_property(id, &tmp);
838         }
839 
set_string(ui_atom_t id,const LSPString * value)840         status_t LSPStyle::set_string(ui_atom_t id, const LSPString *value)
841         {
842             if (value == NULL)
843                 return STATUS_BAD_ARGUMENTS;
844 
845             property_t tmp;
846             tmp.type        = PT_STRING;
847             tmp.v.sValue    = const_cast<char *>(value->get_utf8());
848             return set_property(id, &tmp);
849         }
850 
set_string(ui_atom_t id,const char * value)851         status_t LSPStyle::set_string(ui_atom_t id, const char *value)
852         {
853             if (value == NULL)
854                 return STATUS_BAD_ARGUMENTS;
855 
856             property_t tmp;
857             tmp.type        = PT_STRING;
858             tmp.v.sValue    = const_cast<char *>(value);
859             return set_property(id, &tmp);
860         }
861 
set_default(ui_atom_t id)862         status_t LSPStyle::set_default(ui_atom_t id)
863         {
864             property_t *p = get_property(id);
865             if (p == NULL)
866                 return STATUS_NOT_FOUND;
867             else if (p->flags & F_DEFAULT)
868                 return STATUS_OK;
869 
870             // Initialize property with default value
871             p->flags   |= F_DEFAULT;
872             return sync_property(p);
873         }
874 
875     } /* namespace tk */
876 } /* namespace lsp */
877