| @@ -6,6 +6,8 @@ | |||||
| #include <app/ParamWidget.hpp> | #include <app/ParamWidget.hpp> | ||||
| #include <plugin/Model.hpp> | #include <plugin/Model.hpp> | ||||
| #include <engine/Module.hpp> | #include <engine/Module.hpp> | ||||
| #include <history.hpp> | |||||
| #include <list> | |||||
| namespace rack { | namespace rack { | ||||
| @@ -54,12 +56,17 @@ struct ModuleWidget : widget::OpaqueWidget { | |||||
| ParamWidget* getParam(int paramId); | ParamWidget* getParam(int paramId); | ||||
| PortWidget* getInput(int portId); | PortWidget* getInput(int portId); | ||||
| PortWidget* getOutput(int portId); | PortWidget* getOutput(int portId); | ||||
| /** Scans children widgets recursively for all ParamWidgets. */ | |||||
| std::list<ParamWidget*> getParams(); | |||||
| std::list<PortWidget*> getPorts(); | |||||
| std::list<PortWidget*> getInputs(); | |||||
| std::list<PortWidget*> getOutputs(); | |||||
| void draw(const DrawArgs& args) override; | void draw(const DrawArgs& args) override; | ||||
| void drawShadow(const DrawArgs& args); | void drawShadow(const DrawArgs& args); | ||||
| /** Override to add context menu entries to your subclass. | /** Override to add context menu entries to your subclass. | ||||
| It is recommended to add a blank ui::MenuEntry first for spacing. | |||||
| It is recommended to add a blank `ui::MenuSeparator` first for spacing. | |||||
| */ | */ | ||||
| virtual void appendContextMenu(ui::Menu* menu) {} | virtual void appendContextMenu(ui::Menu* menu) {} | ||||
| @@ -98,12 +105,14 @@ struct ModuleWidget : widget::OpaqueWidget { | |||||
| Called when the user clicks Randomize in the context menu. | Called when the user clicks Randomize in the context menu. | ||||
| */ | */ | ||||
| void randomizeAction(); | void randomizeAction(); | ||||
| void appendDisconnectActions(history::ComplexAction* complexAction); | |||||
| void disconnectAction(); | void disconnectAction(); | ||||
| void cloneAction(); | void cloneAction(); | ||||
| void bypassAction(); | void bypassAction(); | ||||
| /** Deletes `this` */ | /** Deletes `this` */ | ||||
| void removeAction(); | void removeAction(); | ||||
| void createContextMenu(); | void createContextMenu(); | ||||
| void createSelectionContextMenu(); | |||||
| INTERNAL math::Vec& dragOffset(); | INTERNAL math::Vec& dragOffset(); | ||||
| INTERNAL bool& dragEnabled(); | INTERNAL bool& dragEnabled(); | ||||
| @@ -52,12 +52,12 @@ struct RackWidget : widget::OpaqueWidget { | |||||
| // Module methods | // Module methods | ||||
| /** Adds a module and adds it to the Engine | |||||
| Ownership rules work like add/removeChild() | |||||
| /** Adds a module and adds it to the Engine, adopting ownership. | |||||
| */ | */ | ||||
| void addModule(ModuleWidget* mw); | void addModule(ModuleWidget* mw); | ||||
| void addModuleAtMouse(ModuleWidget* mw); | void addModuleAtMouse(ModuleWidget* mw); | ||||
| /** Removes the module and transfers ownership to the caller */ | |||||
| /** Removes the module and transfers ownership to the caller. | |||||
| */ | |||||
| void removeModule(ModuleWidget* mw); | void removeModule(ModuleWidget* mw); | ||||
| /** Sets a module's box if non-colliding. Returns true if set */ | /** Sets a module's box if non-colliding. Returns true if set */ | ||||
| bool requestModulePos(ModuleWidget* mw, math::Vec pos); | bool requestModulePos(ModuleWidget* mw, math::Vec pos); | ||||
| @@ -65,21 +65,31 @@ struct RackWidget : widget::OpaqueWidget { | |||||
| void setModulePosNearest(ModuleWidget* mw, math::Vec pos); | void setModulePosNearest(ModuleWidget* mw, math::Vec pos); | ||||
| void setModulePosForce(ModuleWidget* mw, math::Vec pos); | void setModulePosForce(ModuleWidget* mw, math::Vec pos); | ||||
| ModuleWidget* getModule(int64_t moduleId); | ModuleWidget* getModule(int64_t moduleId); | ||||
| bool isEmpty(); | |||||
| std::list<ModuleWidget*> getModules(); | |||||
| bool hasModules(); | |||||
| void updateModuleOldPositions(); | void updateModuleOldPositions(); | ||||
| history::ComplexAction* getModuleDragAction(); | history::ComplexAction* getModuleDragAction(); | ||||
| void updateModuleSelections(); | void updateModuleSelections(); | ||||
| int getNumSelectedModules(); | |||||
| std::list<ModuleWidget*> getSelectedModules(); | |||||
| void resetSelectedModulesAction(); | |||||
| void randomizeSelectedModulesAction(); | |||||
| void disconnectSelectedModulesAction(); | |||||
| void bypassSelectedModulesAction(); | |||||
| void deleteSelectedModulesAction(); | |||||
| // Cable methods | // Cable methods | ||||
| void clearCables(); | void clearCables(); | ||||
| void clearCablesAction(); | void clearCablesAction(); | ||||
| /** Removes all complete cables connected to the port */ | |||||
| /** Removes all cables connected to the port */ | |||||
| void clearCablesOnPort(PortWidget* port); | void clearCablesOnPort(PortWidget* port); | ||||
| /** Adds a complete cable. | |||||
| Ownership rules work like add/removeChild() | |||||
| /** Adds a complete cable and adopts ownership. | |||||
| */ | */ | ||||
| void addCable(CableWidget* cw); | void addCable(CableWidget* cw); | ||||
| /** Removes cable and releases ownership to caller. | |||||
| */ | |||||
| void removeCable(CableWidget* cw); | void removeCable(CableWidget* cw); | ||||
| /** Takes ownership of `cw` and adds it as a child if it isn't already. */ | /** Takes ownership of `cw` and adds it as a child if it isn't already. */ | ||||
| void setIncompleteCable(CableWidget* cw); | void setIncompleteCable(CableWidget* cw); | ||||
| @@ -87,6 +97,7 @@ struct RackWidget : widget::OpaqueWidget { | |||||
| /** Returns the most recently added complete cable connected to the given Port, i.e. the top of the stack. */ | /** Returns the most recently added complete cable connected to the given Port, i.e. the top of the stack. */ | ||||
| CableWidget* getTopCable(PortWidget* port); | CableWidget* getTopCable(PortWidget* port); | ||||
| CableWidget* getCable(int64_t cableId); | CableWidget* getCable(int64_t cableId); | ||||
| std::list<CableWidget*> getCompleteCables(); | |||||
| /** Returns all cables attached to port, complete or not. */ | /** Returns all cables attached to port, complete or not. */ | ||||
| std::list<CableWidget*> getCablesOnPort(PortWidget* port); | std::list<CableWidget*> getCablesOnPort(PortWidget* port); | ||||
| }; | }; | ||||
| @@ -159,6 +159,51 @@ PortWidget* ModuleWidget::getOutput(int portId) { | |||||
| }); | }); | ||||
| } | } | ||||
| template <class T, typename F> | |||||
| void doIfTypeRecursive(widget::Widget* w, F f) { | |||||
| T* t = dynamic_cast<T*>(w); | |||||
| if (t) | |||||
| f(t); | |||||
| for (widget::Widget* child : w->children) { | |||||
| doIfTypeRecursive<T>(child, f); | |||||
| } | |||||
| } | |||||
| std::list<ParamWidget*> ModuleWidget::getParams() { | |||||
| std::list<ParamWidget*> pws; | |||||
| doIfTypeRecursive<ParamWidget>(this, [&](ParamWidget* pw) { | |||||
| pws.push_back(pw); | |||||
| }); | |||||
| return pws; | |||||
| } | |||||
| std::list<PortWidget*> ModuleWidget::getPorts() { | |||||
| std::list<PortWidget*> pws; | |||||
| doIfTypeRecursive<PortWidget>(this, [&](PortWidget* pw) { | |||||
| pws.push_back(pw); | |||||
| }); | |||||
| return pws; | |||||
| } | |||||
| std::list<PortWidget*> ModuleWidget::getInputs() { | |||||
| std::list<PortWidget*> pws; | |||||
| doIfTypeRecursive<PortWidget>(this, [&](PortWidget* pw) { | |||||
| if (pw->type == engine::Port::INPUT) | |||||
| pws.push_back(pw); | |||||
| }); | |||||
| return pws; | |||||
| } | |||||
| std::list<PortWidget*> ModuleWidget::getOutputs() { | |||||
| std::list<PortWidget*> pws; | |||||
| doIfTypeRecursive<PortWidget>(this, [&](PortWidget* pw) { | |||||
| if (pw->type == engine::Port::OUTPUT) | |||||
| pws.push_back(pw); | |||||
| }); | |||||
| return pws; | |||||
| } | |||||
| void ModuleWidget::draw(const DrawArgs& args) { | void ModuleWidget::draw(const DrawArgs& args) { | ||||
| nvgScissor(args.vg, RECT_ARGS(args.clipBox)); | nvgScissor(args.vg, RECT_ARGS(args.clipBox)); | ||||
| @@ -325,7 +370,12 @@ void ModuleWidget::onButton(const ButtonEvent& e) { | |||||
| if (!e.isConsumed()) { | if (!e.isConsumed()) { | ||||
| // Open context menu on right-click | // Open context menu on right-click | ||||
| if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) { | if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) { | ||||
| createContextMenu(); | |||||
| if (internal->selected) { | |||||
| createSelectionContextMenu(); | |||||
| } | |||||
| else { | |||||
| createContextMenu(); | |||||
| } | |||||
| e.consume(this); | e.consume(this); | ||||
| } | } | ||||
| } | } | ||||
| @@ -615,21 +665,10 @@ void ModuleWidget::saveDialog() { | |||||
| save(path); | save(path); | ||||
| } | } | ||||
| template <class T, typename F> | |||||
| void doOfType(widget::Widget* w, F f) { | |||||
| T* t = dynamic_cast<T*>(w); | |||||
| if (t) | |||||
| f(t); | |||||
| for (widget::Widget* child : w->children) { | |||||
| doOfType<T>(child, f); | |||||
| } | |||||
| } | |||||
| void ModuleWidget::disconnect() { | void ModuleWidget::disconnect() { | ||||
| doOfType<PortWidget>(this, [&](PortWidget* pw) { | |||||
| for (PortWidget* pw : getPorts()) { | |||||
| APP->scene->rack->clearCablesOnPort(pw); | APP->scene->rack->clearCablesOnPort(pw); | ||||
| }); | |||||
| } | |||||
| } | } | ||||
| void ModuleWidget::resetAction() { | void ModuleWidget::resetAction() { | ||||
| @@ -662,30 +701,31 @@ void ModuleWidget::randomizeAction() { | |||||
| APP->history->push(h); | APP->history->push(h); | ||||
| } | } | ||||
| static void disconnectActions(ModuleWidget* mw, history::ComplexAction* complexAction) { | |||||
| // Add CableRemove action for all cables | |||||
| doOfType<PortWidget>(mw, [&](PortWidget* pw) { | |||||
| void ModuleWidget::appendDisconnectActions(history::ComplexAction* complexAction) { | |||||
| for (PortWidget* pw : getPorts()) { | |||||
| for (CableWidget* cw : APP->scene->rack->getCablesOnPort(pw)) { | for (CableWidget* cw : APP->scene->rack->getCablesOnPort(pw)) { | ||||
| if (!cw->isComplete()) | if (!cw->isComplete()) | ||||
| continue; | continue; | ||||
| // Avoid creating duplicate actions for self-patched cables | |||||
| if (pw->type == engine::Port::INPUT && cw->outputPort->module == mw->module) | |||||
| continue; | |||||
| // history::CableRemove | // history::CableRemove | ||||
| history::CableRemove* h = new history::CableRemove; | history::CableRemove* h = new history::CableRemove; | ||||
| h->setCable(cw); | h->setCable(cw); | ||||
| complexAction->push(h); | complexAction->push(h); | ||||
| // Delete cable | |||||
| APP->scene->rack->removeCable(cw); | |||||
| delete cw; | |||||
| } | } | ||||
| }); | |||||
| }; | |||||
| } | } | ||||
| void ModuleWidget::disconnectAction() { | void ModuleWidget::disconnectAction() { | ||||
| history::ComplexAction* complexAction = new history::ComplexAction; | history::ComplexAction* complexAction = new history::ComplexAction; | ||||
| complexAction->name = "disconnect cables"; | complexAction->name = "disconnect cables"; | ||||
| disconnectActions(this, complexAction); | |||||
| APP->history->push(complexAction); | |||||
| appendDisconnectActions(complexAction); | |||||
| disconnect(); | |||||
| if (!complexAction->isEmpty()) | |||||
| APP->history->push(complexAction); | |||||
| else | |||||
| delete complexAction; | |||||
| } | } | ||||
| void ModuleWidget::cloneAction() { | void ModuleWidget::cloneAction() { | ||||
| @@ -722,11 +762,8 @@ void ModuleWidget::cloneAction() { | |||||
| h->push(hma); | h->push(hma); | ||||
| // Clone cables attached to input ports | // Clone cables attached to input ports | ||||
| doOfType<PortWidget>(this, [&](PortWidget* pw) { | |||||
| if (pw->type != engine::Port::INPUT) | |||||
| return; | |||||
| std::list<CableWidget*> cables = APP->scene->rack->getCablesOnPort(pw); | |||||
| for (CableWidget* cw : cables) { | |||||
| for (PortWidget* pw : getInputs()) { | |||||
| for (CableWidget* cw : APP->scene->rack->getCablesOnPort(pw)) { | |||||
| // Create cable attached to cloned ModuleWidget's input | // Create cable attached to cloned ModuleWidget's input | ||||
| engine::Cable* clonedCable = new engine::Cable; | engine::Cable* clonedCable = new engine::Cable; | ||||
| clonedCable->id = -1; | clonedCable->id = -1; | ||||
| @@ -750,7 +787,7 @@ void ModuleWidget::cloneAction() { | |||||
| hca->setCable(clonedCw); | hca->setCable(clonedCw); | ||||
| h->push(hca); | h->push(hca); | ||||
| } | } | ||||
| }); | |||||
| } | |||||
| APP->history->push(h); | APP->history->push(h); | ||||
| } | } | ||||
| @@ -769,7 +806,7 @@ void ModuleWidget::bypassAction() { | |||||
| void ModuleWidget::removeAction() { | void ModuleWidget::removeAction() { | ||||
| history::ComplexAction* complexAction = new history::ComplexAction; | history::ComplexAction* complexAction = new history::ComplexAction; | ||||
| complexAction->name = "remove module"; | complexAction->name = "remove module"; | ||||
| disconnectActions(this, complexAction); | |||||
| appendDisconnectActions(complexAction); | |||||
| // history::ModuleRemove | // history::ModuleRemove | ||||
| history::ModuleRemove* moduleRemove = new history::ModuleRemove; | history::ModuleRemove* moduleRemove = new history::ModuleRemove; | ||||
| @@ -778,7 +815,7 @@ void ModuleWidget::removeAction() { | |||||
| APP->history->push(complexAction); | APP->history->push(complexAction); | ||||
| // This disconnects cables, removes the module, and transfers ownership to caller | |||||
| // This removes the module and transfers ownership to caller | |||||
| APP->scene->rack->removeModule(this); | APP->scene->rack->removeModule(this); | ||||
| delete this; | delete this; | ||||
| } | } | ||||
| @@ -1018,6 +1055,38 @@ void ModuleWidget::createContextMenu() { | |||||
| appendContextMenu(menu); | appendContextMenu(menu); | ||||
| } | } | ||||
| void ModuleWidget::createSelectionContextMenu() { | |||||
| ui::Menu* menu = createMenu(); | |||||
| int n = APP->scene->rack->getNumSelectedModules(); | |||||
| menu->addChild(createMenuLabel(string::f("%d selected %s", n, n == 1 ? "module" : "modules"))); | |||||
| // Initialize | |||||
| menu->addChild(createMenuItem("Initialize", "", [=]() { | |||||
| APP->scene->rack->resetSelectedModulesAction(); | |||||
| })); | |||||
| // Randomize | |||||
| menu->addChild(createMenuItem("Randomize", "", [=]() { | |||||
| APP->scene->rack->randomizeSelectedModulesAction(); | |||||
| })); | |||||
| // Disconnect cables | |||||
| menu->addChild(createMenuItem("Disconnect cables", "", [=]() { | |||||
| APP->scene->rack->disconnectSelectedModulesAction(); | |||||
| })); | |||||
| // Bypass | |||||
| menu->addChild(createMenuItem("Bypass", "", [=]() { | |||||
| APP->scene->rack->bypassSelectedModulesAction(); | |||||
| })); | |||||
| // Delete | |||||
| menu->addChild(createMenuItem("Delete", "", [=]() { | |||||
| APP->scene->rack->deleteSelectedModulesAction(); | |||||
| })); | |||||
| } | |||||
| math::Vec& ModuleWidget::dragOffset() { | math::Vec& ModuleWidget::dragOffset() { | ||||
| return internal->dragOffset; | return internal->dragOffset; | ||||
| } | } | ||||
| @@ -612,7 +612,17 @@ ModuleWidget* RackWidget::getModule(int64_t moduleId) { | |||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| bool RackWidget::isEmpty() { | |||||
| std::list<ModuleWidget*> RackWidget::getModules() { | |||||
| std::list<ModuleWidget*> mws; | |||||
| for (widget::Widget* w : internal->moduleContainer->children) { | |||||
| ModuleWidget* mw = dynamic_cast<ModuleWidget*>(w); | |||||
| assert(mw); | |||||
| mws.push_back(mw); | |||||
| } | |||||
| return mws; | |||||
| } | |||||
| bool RackWidget::hasModules() { | |||||
| return internal->moduleContainer->children.empty(); | return internal->moduleContainer->children.empty(); | ||||
| } | } | ||||
| @@ -659,6 +669,107 @@ void RackWidget::updateModuleSelections() { | |||||
| } | } | ||||
| } | } | ||||
| int RackWidget::getNumSelectedModules() { | |||||
| int count = 0; | |||||
| for (widget::Widget* w : internal->moduleContainer->children) { | |||||
| ModuleWidget* mw = dynamic_cast<ModuleWidget*>(w); | |||||
| assert(mw); | |||||
| if (mw->selected()) | |||||
| count++; | |||||
| } | |||||
| return count; | |||||
| } | |||||
| std::list<ModuleWidget*> RackWidget::getSelectedModules() { | |||||
| std::list<ModuleWidget*> mws; | |||||
| for (widget::Widget* w : internal->moduleContainer->children) { | |||||
| ModuleWidget* mw = dynamic_cast<ModuleWidget*>(w); | |||||
| assert(mw); | |||||
| if (mw->selected()) | |||||
| mws.push_back(mw); | |||||
| } | |||||
| return mws; | |||||
| } | |||||
| void RackWidget::resetSelectedModulesAction() { | |||||
| history::ComplexAction* complexAction = new history::ComplexAction; | |||||
| complexAction->name = "reset modules"; | |||||
| for (ModuleWidget* mw : getSelectedModules()) { | |||||
| assert(mw->module); | |||||
| // history::ModuleChange | |||||
| history::ModuleChange* h = new history::ModuleChange; | |||||
| h->moduleId = mw->module->id; | |||||
| h->oldModuleJ = mw->toJson(); | |||||
| APP->engine->resetModule(mw->module); | |||||
| h->newModuleJ = mw->toJson(); | |||||
| complexAction->push(h); | |||||
| } | |||||
| APP->history->push(complexAction); | |||||
| } | |||||
| void RackWidget::randomizeSelectedModulesAction() { | |||||
| history::ComplexAction* complexAction = new history::ComplexAction; | |||||
| complexAction->name = "randomize modules"; | |||||
| for (ModuleWidget* mw : getSelectedModules()) { | |||||
| assert(mw->module); | |||||
| // history::ModuleChange | |||||
| history::ModuleChange* h = new history::ModuleChange; | |||||
| h->moduleId = mw->module->id; | |||||
| h->oldModuleJ = mw->toJson(); | |||||
| APP->engine->randomizeModule(mw->module); | |||||
| h->newModuleJ = mw->toJson(); | |||||
| complexAction->push(h); | |||||
| } | |||||
| APP->history->push(complexAction); | |||||
| } | |||||
| void RackWidget::disconnectSelectedModulesAction() { | |||||
| history::ComplexAction* complexAction = new history::ComplexAction; | |||||
| complexAction->name = "disconnect cables"; | |||||
| for (ModuleWidget* mw : getSelectedModules()) { | |||||
| mw->appendDisconnectActions(complexAction); | |||||
| } | |||||
| if (!complexAction->isEmpty()) | |||||
| APP->history->push(complexAction); | |||||
| else | |||||
| delete complexAction; | |||||
| } | |||||
| void RackWidget::bypassSelectedModulesAction() { | |||||
| // TODO | |||||
| } | |||||
| void RackWidget::deleteSelectedModulesAction() { | |||||
| history::ComplexAction* complexAction = new history::ComplexAction; | |||||
| complexAction->name = "remove modules"; | |||||
| for (ModuleWidget* mw : getSelectedModules()) { | |||||
| mw->appendDisconnectActions(complexAction); | |||||
| // history::ModuleRemove | |||||
| history::ModuleRemove* moduleRemove = new history::ModuleRemove; | |||||
| moduleRemove->setModule(mw); | |||||
| complexAction->push(moduleRemove); | |||||
| removeModule(mw); | |||||
| delete mw; | |||||
| } | |||||
| APP->history->push(complexAction); | |||||
| } | |||||
| void RackWidget::clearCables() { | void RackWidget::clearCables() { | ||||
| incompleteCable = NULL; | incompleteCable = NULL; | ||||
| internal->cableContainer->clearChildren(); | internal->cableContainer->clearChildren(); | ||||
| @@ -669,12 +780,7 @@ void RackWidget::clearCablesAction() { | |||||
| history::ComplexAction* complexAction = new history::ComplexAction; | history::ComplexAction* complexAction = new history::ComplexAction; | ||||
| complexAction->name = "clear cables"; | complexAction->name = "clear cables"; | ||||
| for (widget::Widget* w : internal->cableContainer->children) { | |||||
| CableWidget* cw = dynamic_cast<CableWidget*>(w); | |||||
| assert(cw); | |||||
| if (!cw->isComplete()) | |||||
| continue; | |||||
| for (CableWidget* cw : getCompleteCables()) { | |||||
| // history::CableRemove | // history::CableRemove | ||||
| history::CableRemove* h = new history::CableRemove; | history::CableRemove* h = new history::CableRemove; | ||||
| h->setCable(cw); | h->setCable(cw); | ||||
| @@ -685,6 +791,7 @@ void RackWidget::clearCablesAction() { | |||||
| APP->history->push(complexAction); | APP->history->push(complexAction); | ||||
| else | else | ||||
| delete complexAction; | delete complexAction; | ||||
| clearCables(); | clearCables(); | ||||
| } | } | ||||
| @@ -756,6 +863,17 @@ CableWidget* RackWidget::getCable(int64_t cableId) { | |||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| std::list<CableWidget*> RackWidget::getCompleteCables() { | |||||
| std::list<CableWidget*> cws; | |||||
| for (widget::Widget* w : internal->cableContainer->children) { | |||||
| CableWidget* cw = dynamic_cast<CableWidget*>(w); | |||||
| assert(cw); | |||||
| if (cw->isComplete()) | |||||
| cws.push_back(cw); | |||||
| } | |||||
| return cws; | |||||
| } | |||||
| std::list<CableWidget*> RackWidget::getCablesOnPort(PortWidget* port) { | std::list<CableWidget*> RackWidget::getCablesOnPort(PortWidget* port) { | ||||
| assert(port); | assert(port); | ||||
| std::list<CableWidget*> cws; | std::list<CableWidget*> cws; | ||||
| @@ -79,7 +79,7 @@ void PatchManager::clear() { | |||||
| static bool promptClear(std::string text) { | static bool promptClear(std::string text) { | ||||
| if (APP->history->isSaved()) | if (APP->history->isSaved()) | ||||
| return true; | return true; | ||||
| if (APP->scene->rack->isEmpty()) | |||||
| if (APP->scene->rack->hasModules()) | |||||
| return true; | return true; | ||||
| return osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, text.c_str()); | return osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, text.c_str()); | ||||
| } | } | ||||