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: 21 окт. 2015 г. 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/ui.h> 23 #include <string.h> 24 #include <core/debug.h> 25 #include <core/alloc.h> 26 #include <core/calc/Expression.h> 27 #include <core/calc/Variables.h> 28 #include <errno.h> 29 30 #include <ui/XMLHandler.h> 31 32 namespace lsp 33 { 34 class ui_recording_handler: public XMLPlaybackNode 35 { 36 private: 37 ui_recording_handler & operator = (const ui_recording_handler &src); 38 39 protected: 40 ui_builder *pBuilder; 41 42 public: ui_recording_handler(ui_builder * bld,XMLNode * handler)43 explicit ui_recording_handler(ui_builder *bld, XMLNode *handler): 44 XMLPlaybackNode(handler) 45 { 46 pBuilder = bld; 47 } 48 ~ui_recording_handler()49 virtual ~ui_recording_handler() 50 { 51 pBuilder = NULL; 52 } 53 }; 54 55 class ui_for_handler: public ui_recording_handler 56 { 57 private: 58 LSPString *pID; 59 ssize_t nFirst; 60 ssize_t nLast; 61 ssize_t nStep; 62 63 protected: iterate(ssize_t value)64 status_t iterate(ssize_t value) 65 { 66 status_t res; 67 if ((res = pBuilder->vars()->set_int(pID, value)) != STATUS_OK) 68 return res; 69 return playback(); 70 } 71 72 public: ui_for_handler(ui_builder * bld,XMLNode * handler)73 explicit ui_for_handler(ui_builder *bld, XMLNode *handler) : ui_recording_handler(bld, handler) 74 { 75 pID = NULL; 76 nFirst = 0; 77 nLast = 0; 78 nStep = 1; 79 } 80 init(const LSPString * const * atts)81 virtual status_t init(const LSPString * const *atts) 82 { 83 bool increment_set = false; 84 status_t res; 85 86 for ( ; *atts != NULL; atts += 2) 87 { 88 const LSPString *name = atts[0]; 89 const LSPString *value = atts[1]; 90 91 if ((name == NULL) || (value == NULL)) 92 continue; 93 94 if (name->equals_ascii("id")) 95 { 96 if (pID != NULL) 97 return STATUS_CORRUPTED; 98 LSPString tmp; 99 if ((res = pBuilder->eval_string(&tmp, value)) != STATUS_OK) 100 return res; 101 if ((pID = tmp.release()) == NULL) 102 return STATUS_NO_MEM; 103 } 104 else if (name->equals_ascii("first")) 105 { 106 if ((res = pBuilder->eval_int(&nFirst, value)) != STATUS_OK) 107 return res; 108 } 109 else if (name->equals_ascii("last")) 110 { 111 if ((res = pBuilder->eval_int(&nLast, value)) != STATUS_OK) 112 return res; 113 } 114 else if (name->equals_ascii("step")) 115 { 116 if ((res = pBuilder->eval_int(&nStep, value)) != STATUS_OK) 117 return res; 118 increment_set = true; 119 } 120 else 121 { 122 lsp_error("Unknown attribute: %s", name->get_utf8()); 123 return STATUS_CORRUPTED; 124 } 125 } 126 127 // Compute increment 128 if (!increment_set) 129 nStep = (nFirst <= nLast) ? 1 : -1; 130 131 return STATUS_OK; 132 } 133 ~ui_for_handler()134 virtual ~ui_for_handler() 135 { 136 if (pID != NULL) 137 { 138 delete pID; 139 pID = NULL; 140 } 141 } 142 143 public: execute()144 virtual status_t execute() 145 { 146 status_t res; 147 if (pID == NULL) 148 return STATUS_OK; 149 150 // Create new scope 151 if ((res = pBuilder->push_scope()) != STATUS_OK) 152 return res; 153 154 // Perform a loop 155 if (nFirst <= nLast) 156 { 157 for (ssize_t value = nFirst; value <= nLast; value += nStep) 158 if ((res = iterate(value)) != STATUS_OK) 159 break; 160 } 161 else 162 { 163 for (ssize_t value = nFirst; value >= nLast; value += nStep) 164 if ((res = iterate(value)) != STATUS_OK) 165 break; 166 } 167 168 // Pop scope and return 169 return (res == STATUS_OK) ? pBuilder->pop_scope() : res; 170 } 171 }; 172 173 class ui_attribute_handler: public ui_recording_handler 174 { 175 private: 176 cvector<LSPString> vAtts; 177 size_t nLevel; 178 ssize_t nRecursion; 179 180 public: ui_attribute_handler(ui_builder * bld,XMLNode * handler)181 explicit ui_attribute_handler(ui_builder *bld, XMLNode *handler) : ui_recording_handler(bld, handler) 182 { 183 nLevel = 0; 184 nRecursion = 0; 185 } 186 ~ui_attribute_handler()187 virtual ~ui_attribute_handler() 188 { 189 for (size_t i=0, n=vAtts.size(); i<n; ++i) 190 { 191 LSPString *s = vAtts.at(i); 192 if (s != NULL) 193 delete s; 194 } 195 vAtts.flush(); 196 } 197 198 public: init(const LSPString * const * atts)199 virtual status_t init(const LSPString * const *atts) 200 { 201 status_t res; 202 203 // Generate list of appended properties 204 for ( ; *atts != NULL; atts += 2) 205 { 206 const LSPString *name = atts[0]; 207 const LSPString *value = atts[1]; 208 209 if ((name == NULL) || (value == NULL)) 210 continue; 211 212 if ((*atts)->equals_ascii("ui:recursion")) 213 { 214 if ((res = pBuilder->eval_int(&nRecursion, value)) != STATUS_OK) 215 return res; 216 } 217 218 // Process name 219 LSPString *xname = name->clone(); 220 if (xname == NULL) 221 return STATUS_NO_MEM; 222 else if (!vAtts.add(xname)) 223 { 224 delete xname; 225 return STATUS_NO_MEM; 226 } 227 228 // Process value 229 LSPString *xattr = new LSPString(); 230 if (xattr == NULL) 231 return STATUS_NO_MEM; 232 else if (!vAtts.add(xattr)) 233 { 234 delete xattr; 235 return STATUS_NO_MEM; 236 } 237 238 // Evaluate string 239 if ((res = pBuilder->eval_string(xattr, value)) != STATUS_OK) 240 return res; 241 } 242 243 return STATUS_OK; 244 } 245 playback_start_element(xml::IXMLHandler * handler,const LSPString * name,const LSPString * const * atts)246 virtual status_t playback_start_element(xml::IXMLHandler *handler, const LSPString *name, const LSPString * const *atts) 247 { 248 size_t level = nLevel++; 249 250 // Skip parameter substitution for control tags 251 if (name->starts_with_ascii("ui:")) 252 return ui_recording_handler::playback_start_element(handler, name, atts); 253 254 cvector<LSPString> tmp; 255 256 // Need to override attributes? 257 if ((nRecursion < 0) || (level <= size_t(nRecursion))) 258 { 259 // Copy attributes 260 for (size_t i=0; atts[i] != NULL; ++i) 261 if (!tmp.add(const_cast<LSPString *>(atts[i]))) 262 return STATUS_NO_MEM; 263 264 // Append unexisting attributes 265 LSPString **vatts = vAtts.get_array(); 266 for (size_t i=0, n=vAtts.size(); i<n; i += 2) 267 { 268 LSPString *name = vatts[i]; 269 LSPString *value = vatts[i+1]; 270 271 // Check for duplicate 272 for (size_t j=0; atts[j] != NULL; j+=2) 273 if (atts[j]->equals(name)) 274 { 275 name = NULL; 276 break; 277 } 278 279 // Append property if it does not exist 280 if (name == NULL) 281 continue; 282 283 if (!tmp.add(name)) 284 return STATUS_NO_MEM; 285 if (!tmp.add(value)) 286 return STATUS_NO_MEM; 287 } 288 289 // Append argument terminator 290 if (!tmp.add(NULL)) 291 return STATUS_NO_MEM; 292 293 // Override properties with our own list 294 atts = tmp.get_array(); 295 } 296 return ui_recording_handler::playback_start_element(handler, name, atts); 297 } 298 playback_end_element(xml::IXMLHandler * handler,const LSPString * name)299 virtual status_t playback_end_element(xml::IXMLHandler *handler, const LSPString *name) 300 { 301 --nLevel; 302 return ui_recording_handler::playback_end_element(handler, name); 303 } 304 }; 305 306 class ui_set_handler: public XMLNode 307 { 308 private: 309 ui_set_handler & operator = (const ui_set_handler &src); 310 311 protected: 312 ui_builder *pBuilder; 313 314 public: ui_set_handler(ui_builder * bld)315 explicit ui_set_handler(ui_builder *bld) 316 { 317 pBuilder = bld; 318 } 319 ~ui_set_handler()320 virtual ~ui_set_handler() 321 { 322 pBuilder = NULL; 323 } 324 325 public: init(const LSPString * const * atts)326 virtual status_t init(const LSPString * const *atts) 327 { 328 status_t res; 329 size_t flags = 0; 330 LSPString v_name; 331 calc::value_t v_value; 332 calc::init_value(&v_value); 333 334 for ( ; *atts != NULL; atts += 2) 335 { 336 const LSPString *name = atts[0]; 337 const LSPString *value = atts[1]; 338 339 if ((name == NULL) || (value == NULL)) 340 continue; 341 342 if (name->equals_ascii("id")) 343 { 344 if ((res = pBuilder->eval_string(&v_name, value)) != STATUS_OK) 345 return res; 346 flags |= 1; 347 } 348 else if (name->equals_ascii("value")) 349 { 350 if ((res = pBuilder->evaluate(&v_value, value)) != STATUS_OK) 351 return res; 352 flags |= 2; 353 } 354 else 355 { 356 lsp_error("Unknown attribute: %s", name->get_utf8()); 357 return STATUS_CORRUPTED; 358 } 359 } 360 361 if (flags != 3) 362 { 363 lsp_error("Not all attributes are set"); 364 return STATUS_CORRUPTED; 365 } 366 367 // Set variable and destroy value 368 res = pBuilder->vars()->set(&v_name, &v_value); 369 calc::destroy_value(&v_value); 370 return res; 371 } 372 }; 373 374 class ui_if_handler: public XMLNode 375 { 376 private: 377 ui_if_handler & operator = (const ui_if_handler &src); 378 379 protected: 380 ui_builder *pBuilder; 381 XMLNode *pHandler; 382 bool bPass; 383 384 public: ui_if_handler(ui_builder * bld,XMLNode * handler)385 explicit ui_if_handler(ui_builder *bld, XMLNode *handler) 386 { 387 pBuilder = bld; 388 pHandler = handler; 389 bPass = true; 390 } 391 ~ui_if_handler()392 virtual ~ui_if_handler() 393 { 394 pBuilder = NULL; 395 pHandler = NULL; 396 } 397 398 public: init(const LSPString * const * atts)399 virtual status_t init(const LSPString * const *atts) 400 { 401 status_t res; 402 bool valid = false; 403 404 for ( ; *atts != NULL; atts += 2) 405 { 406 const LSPString *name = atts[0]; 407 const LSPString *value = atts[1]; 408 409 if ((name == NULL) || (value == NULL)) 410 continue; 411 412 if (name->equals_ascii("test")) 413 { 414 if ((res = pBuilder->eval_bool(&bPass, value)) != STATUS_OK) 415 return res; 416 valid = true; 417 } 418 else 419 { 420 lsp_error("Unknown attribute: %s", name->get_utf8()); 421 return STATUS_CORRUPTED; 422 } 423 } 424 425 if (!valid) 426 { 427 lsp_error("Not all attributes are set"); 428 return STATUS_CORRUPTED; 429 } 430 431 return STATUS_OK; 432 } 433 start_element(XMLNode ** child,const LSPString * name,const LSPString * const * atts)434 virtual status_t start_element(XMLNode **child, const LSPString *name, const LSPString * const *atts) 435 { 436 return (bPass) ? pHandler->start_element(child, name, atts) : STATUS_OK; 437 } 438 end_element(const LSPString * name)439 virtual status_t end_element(const LSPString *name) 440 { 441 return (bPass) ? pHandler->end_element(name) : STATUS_OK; 442 } 443 completed(XMLNode * child)444 virtual status_t completed(XMLNode *child) 445 { 446 return (bPass) ? pHandler->completed(child) : STATUS_OK; 447 } 448 quit()449 virtual status_t quit() 450 { 451 return (bPass) ? pHandler->quit() : STATUS_OK; 452 } 453 enter()454 virtual status_t enter() 455 { 456 return (bPass) ? pHandler->enter() : STATUS_OK; 457 } 458 }; 459 460 class ui_widget_handler: public XMLNode 461 { 462 private: 463 ui_widget_handler & operator = (const ui_widget_handler *); 464 465 private: 466 ui_builder *pBuilder; 467 CtlWidget *pWidget; 468 ui_widget_handler *pChild; 469 ui_recording_handler *pSpecial; 470 XMLNode *pOther; 471 472 public: ui_widget_handler(ui_builder * bld,CtlWidget * widget)473 explicit ui_widget_handler(ui_builder *bld, CtlWidget *widget) 474 { 475 pBuilder = bld; 476 pWidget = widget; 477 pChild = NULL; 478 pSpecial = NULL; 479 pOther = NULL; 480 } 481 ~ui_widget_handler()482 virtual ~ui_widget_handler() 483 { 484 if (pChild != NULL) 485 pChild = NULL; 486 } 487 488 public: enter()489 virtual status_t enter() 490 { 491 pWidget->begin(); 492 return STATUS_OK; 493 } 494 start_element(XMLNode ** child,const LSPString * name,const LSPString * const * atts)495 virtual status_t start_element(XMLNode **child, const LSPString *name, const LSPString * const *atts) 496 { 497 status_t res; 498 499 // Check for special conditions 500 if (name->starts_with_ascii("ui:")) 501 { 502 // Build special handler 503 if (name->equals_ascii("ui:for")) 504 { 505 if ((pSpecial = new ui_for_handler(pBuilder, this)) == NULL) 506 return STATUS_NO_MEM; 507 if ((res = pSpecial->init(atts)) != STATUS_OK) 508 return res; 509 *child = pSpecial; 510 } 511 else if (name->equals_ascii("ui:attributes")) 512 { 513 if ((pSpecial = new ui_attribute_handler(pBuilder, this)) == NULL) 514 return STATUS_NO_MEM; 515 if ((res = pSpecial->init(atts)) != STATUS_OK) 516 return res; 517 *child = pSpecial; 518 } 519 else if (name->equals_ascii("ui:set")) 520 { 521 ui_set_handler *h = new ui_set_handler(pBuilder); 522 if (h == NULL) 523 return STATUS_NO_MEM; 524 if ((res = h->init(atts)) != STATUS_OK) 525 return res; 526 *child = pOther = h; 527 } 528 else if (name->equals_ascii("ui:if")) 529 { 530 ui_if_handler *h = new ui_if_handler(pBuilder, this); 531 if (h == NULL) 532 return STATUS_NO_MEM; 533 if ((res = h->init(atts)) != STATUS_OK) 534 return res; 535 *child = pOther = h; 536 } 537 else 538 res = STATUS_CORRUPTED; 539 540 return res; 541 } 542 543 // Get UI 544 plugin_ui *ui = pBuilder->get_ui(); 545 546 // Create widget 547 CtlWidget *widget = ui->create_widget(name->get_utf8()); 548 if (widget == NULL) 549 return STATUS_OK; // No handler 550 widget->init(); 551 552 // Initialize pWidget parameters 553 for ( ; *atts != NULL; atts += 2) 554 { 555 LSPString aname, avalue; 556 if ((res = pBuilder->eval_string(&aname, atts[0])) != STATUS_OK) 557 return res; 558 if ((res = pBuilder->eval_string(&avalue, atts[1])) != STATUS_OK) 559 return res; 560 561 // Set widget attribute 562 widget->set(aname.get_utf8(), avalue.get_utf8()); 563 } 564 565 // Create handler 566 *child = pChild = new ui_widget_handler(pBuilder, widget); 567 return (pChild != NULL) ? STATUS_OK : STATUS_NO_MEM; 568 } 569 quit()570 virtual status_t quit() 571 { 572 pWidget->end(); 573 return STATUS_OK; 574 } 575 completed(XMLNode * child)576 virtual status_t completed(XMLNode *child) 577 { 578 status_t res = STATUS_OK; 579 if ((child == pChild) && (pChild != NULL)) 580 { 581 if ((pWidget != NULL) && (pChild->pWidget != NULL)) 582 { 583 CtlWidget *w = pChild->pWidget; 584 if (w != NULL) 585 res = pWidget->add(w); 586 } 587 588 // Remove child 589 delete pChild; 590 pChild = NULL; 591 } 592 else if ((child == pSpecial) && (pSpecial != NULL)) 593 { 594 ui_recording_handler * special = pSpecial; 595 pSpecial = NULL; 596 597 res = special->execute(); 598 delete special; 599 } 600 if ((child == pOther) && (pOther != NULL)) 601 { 602 delete pOther; 603 pOther = NULL; 604 } 605 606 return res; 607 } 608 }; 609 610 class ui_root_handler: public XMLNode 611 { 612 private: 613 ui_root_handler & operator = (const ui_root_handler &); 614 615 private: 616 ui_builder *pBuilder; 617 ui_widget_handler *pChild; 618 619 public: ui_root_handler(ui_builder * bld)620 explicit ui_root_handler(ui_builder *bld) 621 { 622 pBuilder = bld; 623 pChild = NULL; 624 } 625 ~ui_root_handler()626 virtual ~ui_root_handler() 627 { 628 if (pChild != NULL) 629 { 630 delete pChild; 631 pChild = NULL; 632 } 633 } 634 635 public: start_element(XMLNode ** child,const LSPString * name,const LSPString * const * atts)636 virtual status_t start_element(XMLNode **child, const LSPString *name, const LSPString * const *atts) 637 { 638 status_t res; 639 640 // Check that root tag is valid 641 const char *root_tag = widget_ctl(WC_PLUGIN); 642 if (!name->equals_ascii(root_tag)) 643 { 644 lsp_error("expected root element <%s>", root_tag); 645 return STATUS_CORRUPTED; 646 } 647 648 // Get UI 649 plugin_ui *ui = pBuilder->get_ui(); 650 651 // Create widget 652 CtlWidget *widget = ui->create_widget(name->get_utf8()); 653 if (widget == NULL) 654 return STATUS_OK; // No handler 655 widget->init(); 656 657 // Initialize widget parameters 658 for ( ; *atts != NULL; atts += 2) 659 { 660 LSPString aname, avalue; 661 if ((res = pBuilder->eval_string(&aname, atts[0])) != STATUS_OK) 662 return res; 663 if ((res = pBuilder->eval_string(&avalue, atts[1])) != STATUS_OK) 664 return res; 665 666 // Set widget attribute 667 widget->set(aname.get_utf8(), avalue.get_utf8()); 668 } 669 670 // Create handler 671 *child = pChild = new ui_widget_handler(pBuilder, widget); 672 return (pChild != NULL) ? STATUS_OK : STATUS_NO_MEM; 673 } 674 }; 675 676 //------------------------------------------------------------------------- 677 // UI Builder implementation ui_builder(plugin_ui * ui)678 ui_builder::ui_builder(plugin_ui *ui) 679 { 680 CtlPortResolver *r = new CtlPortResolver(); 681 r->init(ui); 682 683 pUI = ui; 684 pPlugin = r; 685 vRoot.set_resolver(r); 686 } 687 ~ui_builder()688 ui_builder::~ui_builder() 689 { 690 for (size_t i=0, n=vStack.size(); i<n; ++i) 691 { 692 calc::Resolver *r = vStack.at(i); 693 if (r != NULL) 694 delete r; 695 } 696 697 vRoot.set_resolver(NULL); 698 vStack.flush(); 699 if (pPlugin != NULL) 700 delete pPlugin; 701 } 702 703 // Evaluate evaluate(calc::value_t * value,const LSPString * expr)704 status_t ui_builder::evaluate(calc::value_t *value, const LSPString *expr) 705 { 706 status_t res; 707 calc::Expression e; 708 709 // Parse expression 710 if ((res = e.parse(expr, calc::Expression::FLAG_STRING)) != STATUS_OK) 711 { 712 lsp_error("Could not parse expression: %s", expr->get_utf8()); 713 return res; 714 } 715 e.set_resolver(vars()); 716 717 // Evaluate expression 718 res = e.evaluate(value); 719 if (res != STATUS_OK) 720 lsp_error("Could not evaluate expression: %s", expr->get_utf8()); 721 722 return res; 723 } 724 push_scope()725 status_t ui_builder::push_scope() 726 { 727 // Create variables 728 calc::Variables *v = new calc::Variables(); 729 if (v == NULL) 730 return STATUS_NO_MEM; 731 732 // Bind resolver, push to stack and quit 733 v->set_resolver(vars()); 734 if (!vStack.push(v)) 735 { 736 delete v; 737 return STATUS_NO_MEM; 738 } 739 740 return STATUS_OK; 741 } 742 pop_scope()743 status_t ui_builder::pop_scope() 744 { 745 calc::Variables *r = NULL; 746 if (!vStack.pop(&r)) 747 return STATUS_BAD_STATE; 748 if (r != NULL) 749 delete r; 750 return STATUS_OK; 751 } 752 eval_string(LSPString * value,const LSPString * expr)753 status_t ui_builder::eval_string(LSPString *value, const LSPString *expr) 754 { 755 calc::value_t v; 756 757 init_value(&v); 758 status_t res = evaluate(&v, expr); 759 if (res != STATUS_OK) 760 return res; 761 762 if ((res = calc::cast_string(&v)) == STATUS_OK) 763 { 764 if (v.type == calc::VT_STRING) 765 value->swap(v.v_str); 766 else 767 { 768 lsp_error("Evaluation error: bad return type of expression %s", expr->get_utf8()); 769 res = STATUS_BAD_TYPE; 770 } 771 } 772 destroy_value(&v); 773 return res; 774 } 775 eval_bool(bool * value,const LSPString * expr)776 status_t ui_builder::eval_bool(bool *value, const LSPString *expr) 777 { 778 calc::value_t v; 779 init_value(&v); 780 781 status_t res = evaluate(&v, expr); 782 if (res != STATUS_OK) 783 return res; 784 785 if ((res = calc::cast_bool(&v)) == STATUS_OK) 786 { 787 if (v.type == calc::VT_BOOL) 788 *value = v.v_bool; 789 else 790 { 791 lsp_error("Evaluation error: bad return type of expression %s", expr->get_utf8()); 792 res = STATUS_BAD_TYPE; 793 } 794 } 795 destroy_value(&v); 796 return res; 797 } 798 eval_int(ssize_t * value,const LSPString * expr)799 status_t ui_builder::eval_int(ssize_t *value, const LSPString *expr) 800 { 801 LSPString tmp; 802 status_t res = eval_string(&tmp, expr); 803 if (res != STATUS_OK) 804 return res; 805 806 // Parse string as integer value 807 errno = 0; 808 char *eptr = NULL; 809 const char *p = tmp.get_utf8(); 810 long v = ::strtol(p, &eptr, 10); 811 if ((errno != 0) || (eptr == NULL) || (*eptr != '\0')) 812 { 813 lsp_error("Evaluation error: bad return type of expression %s", expr->get_utf8()); 814 return STATUS_INVALID_VALUE; 815 } 816 817 // Store value 818 *value = v; 819 return STATUS_OK; 820 } 821 build(const LSPString * path)822 status_t ui_builder::build(const LSPString *path) 823 { 824 ui_root_handler root(this); 825 XMLHandler handler; 826 return handler.parse(path, &root); 827 } 828 build(const char * path)829 status_t ui_builder::build(const char *path) 830 { 831 ui_root_handler root(this); 832 XMLHandler handler; 833 return handler.parse(path, &root); 834 } 835 } 836