| 812 | } |
| 813 | |
| 814 | void ModuleWidget::cloneAction(bool cloneCables) { |
| 815 | // history::ComplexAction |
| 816 | history::ComplexAction* h = new history::ComplexAction; |
| 817 | h->name = string::translate("ModuleWidget.history.duplicateModule"); |
| 818 | |
| 819 | // Save patch store in this module so we can copy it below |
| 820 | APP->engine->prepareSaveModule(module); |
| 821 | |
| 822 | // JSON serialization is the obvious way to do this |
| 823 | json_t* moduleJ = toJson(); |
| 824 | DEFER({ |
| 825 | json_decref(moduleJ); |
| 826 | }); |
| 827 | engine::Module::jsonStripIds(moduleJ); |
| 828 | |
| 829 | // Clone Module |
| 830 | INFO("Creating module %s", model->getFullName().c_str()); |
| 831 | engine::Module* clonedModule = model->createModule(); |
| 832 | |
| 833 | // Set ID here so we can copy module storage dir |
| 834 | clonedModule->id = random::u64() % (1ull << 53); |
| 835 | system::copy(module->getPatchStorageDirectory(), clonedModule->getPatchStorageDirectory()); |
| 836 | |
| 837 | // This doesn't need a lock (via Engine::moduleFromJson()) because the Module is not added to the Engine yet. |
| 838 | try { |
| 839 | clonedModule->fromJson(moduleJ); |
| 840 | } |
| 841 | catch (Exception& e) { |
| 842 | WARN("%s", e.what()); |
| 843 | } |
| 844 | APP->engine->addModule(clonedModule); |
| 845 | |
| 846 | // Clone ModuleWidget |
| 847 | INFO("Creating module widget %s", model->getFullName().c_str()); |
| 848 | ModuleWidget* clonedModuleWidget = model->createModuleWidget(clonedModule); |
| 849 | APP->scene->rack->updateModuleOldPositions(); |
| 850 | APP->scene->rack->addModule(clonedModuleWidget); |
| 851 | // Place module to the right of `this` module, by forcing it to 1 HP to the right. |
| 852 | math::Vec clonedPos = box.pos; |
| 853 | clonedPos.x += clonedModuleWidget->box.getWidth(); |
| 854 | if (settings::squeezeModules) |
| 855 | APP->scene->rack->squeezeModulePos(clonedModuleWidget, clonedPos); |
| 856 | else |
| 857 | APP->scene->rack->setModulePosNearest(clonedModuleWidget, clonedPos); |
| 858 | h->push(APP->scene->rack->getModuleDragAction()); |
| 859 | APP->scene->rack->updateExpanders(); |
| 860 | |
| 861 | // history::ModuleAdd |
| 862 | history::ModuleAdd* hma = new history::ModuleAdd; |
| 863 | hma->setModule(clonedModuleWidget); |
| 864 | h->push(hma); |
| 865 | |
| 866 | if (cloneCables) { |
| 867 | // Clone cables attached to input/output ports |
| 868 | for (PortWidget* pw : getPorts()) { |
| 869 | for (CableWidget* cw : APP->scene->rack->getCompleteCablesOnPort(pw)) { |
| 870 | // Skip input ports self-patched to this module's outputs, to avoid double-cloning them. |
| 871 | if (pw->type == engine::Port::OUTPUT && cw->cable->inputModule == module) |
no test coverage detected