| 844 | } |
| 845 | |
| 846 | void ModuleWidget::cloneAction(bool cloneCables) { |
| 847 | // history::ComplexAction |
| 848 | history::ComplexAction* h = new history::ComplexAction; |
| 849 | h->name = "duplicate module"; |
| 850 | |
| 851 | // Save patch store in this module so we can copy it below |
| 852 | APP->engine->prepareSaveModule(module); |
| 853 | |
| 854 | // JSON serialization is the obvious way to do this |
| 855 | json_t* moduleJ = toJson(); |
| 856 | DEFER({ |
| 857 | json_decref(moduleJ); |
| 858 | }); |
| 859 | engine::Module::jsonStripIds(moduleJ); |
| 860 | |
| 861 | // Clone Module |
| 862 | INFO("Creating module %s", model->getFullName().c_str()); |
| 863 | engine::Module* clonedModule = model->createModule(); |
| 864 | |
| 865 | // Set ID here so we can copy module storage dir |
| 866 | clonedModule->id = random::u64() % (1ull << 53); |
| 867 | system::copy(module->getPatchStorageDirectory(), clonedModule->getPatchStorageDirectory()); |
| 868 | |
| 869 | // This doesn't need a lock (via Engine::moduleFromJson()) because the Module is not added to the Engine yet. |
| 870 | try { |
| 871 | clonedModule->fromJson(moduleJ); |
| 872 | } |
| 873 | catch (Exception& e) { |
| 874 | WARN("%s", e.what()); |
| 875 | } |
| 876 | APP->engine->addModule(clonedModule); |
| 877 | |
| 878 | // Clone ModuleWidget |
| 879 | INFO("Creating module widget %s", model->getFullName().c_str()); |
| 880 | ModuleWidget* clonedModuleWidget = model->createModuleWidget(clonedModule); |
| 881 | APP->scene->rack->updateModuleOldPositions(); |
| 882 | APP->scene->rack->addModule(clonedModuleWidget); |
| 883 | // Place module to the right of `this` module, by forcing it to 1 HP to the right. |
| 884 | math::Vec clonedPos = box.pos; |
| 885 | clonedPos.x += clonedModuleWidget->box.getWidth(); |
| 886 | if (settings::squeezeModules) |
| 887 | APP->scene->rack->squeezeModulePos(clonedModuleWidget, clonedPos); |
| 888 | else |
| 889 | APP->scene->rack->setModulePosNearest(clonedModuleWidget, clonedPos); |
| 890 | h->push(APP->scene->rack->getModuleDragAction()); |
| 891 | APP->scene->rack->updateExpanders(); |
| 892 | |
| 893 | // history::ModuleAdd |
| 894 | history::ModuleAdd* hma = new history::ModuleAdd; |
| 895 | hma->setModule(clonedModuleWidget); |
| 896 | h->push(hma); |
| 897 | |
| 898 | if (cloneCables) { |
| 899 | // Clone cables attached to input ports |
| 900 | for (PortWidget* pw : getInputs()) { |
| 901 | for (CableWidget* cw : APP->scene->rack->getCompleteCablesOnPort(pw)) { |
| 902 | // Create cable attached to cloned ModuleWidget's input |
| 903 | engine::Cable* clonedCable = new engine::Cable; |
no test coverage detected