gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

editor.cpp (10273B)


      1 #include "editor.h"
      2 
      3 #include "uiObject.h"
      4 
      5 #include "baseLib/filesystem.h"
      6 
      7 namespace genericUI
      8 {
      9 	Editor::Editor(EditorInterface& _interface) : m_interface(_interface)
     10 	{
     11 	}
     12 
     13 	void Editor::create(const std::string& _jsonFilename)
     14 	{
     15 		uint32_t jsonSize;
     16 		const auto jsonData = m_interface.getResourceByFilename(_jsonFilename, jsonSize);
     17 
     18 		juce::var json;
     19 		const auto error = juce::JSON::parse(juce::String(std::string(jsonData, jsonSize)), json);
     20 
     21 		if (error.failed())
     22 			throw std::runtime_error("Failed to load json, " + error.getErrorMessage().toStdString());
     23 
     24 		m_jsonFilename = _jsonFilename;
     25 
     26 		m_rootObject.reset(new UiObject(nullptr, json));
     27 
     28 		std::set<std::string> textures;
     29 		m_rootObject->collectVariants(textures, "texture");
     30 
     31 		for (const auto& texture : textures)
     32 		{
     33 			const auto dataName = texture + ".png";
     34 
     35 			uint32_t dataSize;
     36 			const auto* data = m_interface.getResourceByFilename(dataName, dataSize);
     37 			if (!data)
     38 				throw std::runtime_error("Failed to find image named " + dataName);
     39 			auto drawable = juce::Drawable::createFromImageData(data, dataSize);
     40 #ifdef _DEBUG
     41 			drawable->setName(dataName);
     42 #endif
     43 			m_drawables.insert(std::make_pair(texture, std::move(drawable)));
     44 		}
     45 
     46 		std::set<std::string> fonts;
     47 		m_rootObject->collectVariants(fonts, "fontFile");
     48 
     49 		for(const auto& fontFile : fonts)
     50 		{
     51 			const auto dataName = fontFile + ".ttf";
     52 
     53 			uint32_t dataSize;
     54 			const auto* data = m_interface.getResourceByFilename(dataName, dataSize);
     55 			if (!data)
     56 				throw std::runtime_error("Failed to find font named " + dataName);
     57 			auto font = juce::Font(juce::Typeface::createSystemTypefaceFor(data, dataSize));
     58 			m_fonts.insert(std::make_pair(fontFile, std::move(font)));
     59 		}
     60 
     61 		m_rootObject->createJuceTree(*this);
     62 		m_rootObject->createTabGroups(*this);
     63 		m_rootObject->createControllerLinks(*this);
     64 		m_rootObject->registerTemplates(*this);
     65 
     66 		m_scale = m_rootObject->getPropertyFloat("scale", 1.0f);
     67 	}
     68 
     69 	std::string Editor::exportToFolder(const std::string& _folder) const
     70 	{
     71 		if(!m_rootObject)
     72 			return "Nothing to export";
     73 
     74 		baseLib::filesystem::createDirectory(_folder);
     75 
     76 		std::string subfolder = m_jsonFilename;
     77 		const auto dotIndex = m_jsonFilename.rfind('.');
     78 		if(dotIndex != std::string::npos)
     79 			subfolder = subfolder.substr(0, dotIndex);
     80 		const auto underscoreIndex = m_jsonFilename.find('_');
     81 		if(underscoreIndex != std::string::npos)
     82 			subfolder = subfolder.substr(underscoreIndex+1);
     83 
     84 		const auto folder = _folder + subfolder + '/';
     85 
     86 		baseLib::filesystem::createDirectory(folder);
     87 
     88 		std::stringstream errors;
     89 
     90 		auto writeFile = [this, &folder, &errors](const std::string& _name)
     91 		{
     92 			uint32_t dataSize;
     93 			const auto data = m_interface.getResourceByFilename(_name, dataSize);
     94 
     95 			FILE* hFile = fopen((folder + _name).c_str(), "wb");
     96 
     97 			if(!hFile)
     98 			{
     99 				errors << "Failed to create file " << folder << _name << '\n';
    100 			}
    101 			else
    102 			{
    103 				const auto writtenCount = fwrite(data, 1, dataSize, hFile);
    104 				(void)fclose(hFile);
    105 				if(writtenCount != dataSize)
    106 					errors << "Failed to write " << dataSize << " bytes to " << folder << _name << '\n';
    107 			}
    108 		};
    109 
    110 		auto writeData = [this, writeFile](const std::set<std::string>& _names, const std::string& _ext)
    111 		{
    112 			for (const auto& name : _names)
    113 			{
    114 				const auto dataName = name + _ext;
    115 				writeFile(dataName);
    116 			}
    117 		};
    118 
    119 		std::set<std::string> textures;
    120 		std::set<std::string> fonts;
    121 
    122 		m_rootObject->collectVariants(textures, "texture");
    123 		m_rootObject->collectVariants(fonts, "fontFile");
    124 
    125 		writeFile(m_jsonFilename);
    126 
    127 		writeData(textures, ".png");
    128 		writeData(fonts, ".ttf");
    129 
    130 		return errors.str();
    131 	}
    132 
    133 	juce::Drawable* Editor::getImageDrawable(const std::string& _texture)
    134 	{
    135 		const auto it = m_drawables.find(_texture);
    136 		return it == m_drawables.end() ? nullptr : it->second.get();
    137 	}
    138 
    139 	std::unique_ptr<juce::Drawable> Editor::createImageDrawable(const std::string& _texture)
    140 	{
    141 		const auto existing = getImageDrawable(_texture);
    142 		return existing ? existing->createCopy() : nullptr;
    143 	}
    144 
    145 	const juce::Font& Editor::getFont(const std::string& _fontFile)
    146 	{
    147 		const auto it = m_fonts.find(_fontFile);
    148 		if(it == m_fonts.end())
    149 			throw std::runtime_error("Unable to find font named " + _fontFile);
    150 		return it->second;
    151 	}
    152 
    153 	void Editor::registerComponent(const std::string& _name, juce::Component* _component)
    154 	{
    155 		const auto itExisting = m_componentsByName.find(_name);
    156 
    157 		if(itExisting != m_componentsByName.end())
    158 		{
    159 			itExisting->second.push_back(_component);
    160 		}
    161 		else
    162 		{
    163 			m_componentsByName.insert(std::make_pair(_name, std::vector{_component}));
    164 		}
    165 
    166 		const auto param = _component->getProperties()["parametername"].toString().toStdString();
    167 		if(param.empty())
    168 			return;
    169 
    170 		const auto itParamExisting = m_componentsByParameter.find(param);
    171 
    172 		if(itParamExisting != m_componentsByParameter.end())
    173 		{
    174 			itParamExisting->second.push_back(_component);
    175 		}
    176 		else
    177 		{
    178 			m_componentsByParameter.insert(std::make_pair(param, std::vector{_component}));
    179 		}
    180 	}
    181 
    182 	void Editor::registerTabGroup(TabGroup* _group)
    183 	{
    184 		const auto& n = _group->getName();
    185 
    186 		if(m_tabGroupsByName.find(n) != m_tabGroupsByName.end())
    187 			throw std::runtime_error("tab group " + n + " is already defined");
    188 
    189 		m_tabGroupsByName.insert(std::make_pair(n, _group));
    190 	}
    191 
    192 	const std::vector<juce::Component*>& Editor::findComponents(const std::map<std::string, std::vector<juce::Component*>>& _components, const std::string& _name, const uint32_t _expectedCount, const std::string& _typename)
    193 	{
    194 		const auto it = _components.find(_name);
    195 		if(it != _components.end())
    196 		{
    197 			if(_expectedCount && it->second.size() != _expectedCount)
    198 			{
    199 				std::stringstream ss;
    200 				ss << "Expected to find " << _expectedCount << " components with " << _typename << ' ' << _name << " but found " << it->second.size();
    201 				throw std::runtime_error(ss.str());
    202 			}
    203 			return it->second;
    204 		}
    205 
    206 		if(_expectedCount)
    207 		{
    208 			std::stringstream ss;
    209 			ss << "Unable to find component with " << _typename << ' ' << _name << ", expected to find " << _expectedCount << " components";
    210 			throw std::runtime_error(ss.str());
    211 		}
    212 
    213 		static std::vector<juce::Component*> empty;
    214 		return empty;
    215 	}
    216 
    217 	const std::vector<juce::Component*>& Editor::findComponents(const std::string& _name, const uint32_t _expectedCount/* = 0*/) const
    218 	{
    219 		return findComponents(m_componentsByName, _name, _expectedCount, "name");
    220 	}
    221 
    222 	const std::vector<juce::Component*>& Editor::findComponentsByParam(const std::string& _name, const uint32_t _expectedCount) const
    223 	{
    224 		return findComponents(m_componentsByParameter, _name, _expectedCount, "parameter");
    225 	}
    226 
    227 	juce::Component* Editor::findComponent(const std::string& _name, const bool _mustExist/* = true*/) const
    228 	{
    229 		const auto comps = findComponents(_name);
    230 		if(comps.size() > 1)
    231 			throw std::runtime_error("Failed to find unique component named " + _name + ", found more than one object with that name");
    232 		if(_mustExist && comps.empty())
    233 			throw std::runtime_error("Failed to find component named " + _name);
    234 		return comps.empty() ? nullptr : comps.front();
    235 	}
    236 
    237 	juce::Component* Editor::findComponentByParam(const std::string& _param, const bool _mustExist) const
    238 	{
    239 		const auto comps = findComponentsByParam(_param);
    240 		if(comps.size() > 1)
    241 			throw std::runtime_error("Failed to find unique component with parameter " + _param + ", found more than one object with that parameter");
    242 		if(_mustExist && comps.empty())
    243 			throw std::runtime_error("Failed to find component with parameter " + _param);
    244 		return comps.empty() ? nullptr : comps.front();
    245 	}
    246 
    247 	size_t Editor::getConditionCountRecursive() const
    248 	{
    249 		return m_rootObject ? m_rootObject->getConditionCountRecursive() : 0;
    250 	}
    251 
    252 	size_t Editor::getControllerLinkCountRecursive() const
    253 	{
    254 		return m_rootObject ? m_rootObject->getControllerLinkCountRecursive() : 0;
    255 	}
    256 
    257 	void Editor::registerTemplate(const std::shared_ptr<UiObject>& _value)
    258 	{
    259 		const auto name = _value->getName();
    260 
    261 		if (name.empty())
    262 			throw std::runtime_error("Every template needs to have a name");
    263 
    264 		if (m_templates.find(name) != m_templates.end())
    265 			throw std::runtime_error("Template with name '" + name + "' exists more than once");
    266 
    267 		m_templates.insert({ name, _value });
    268 	}
    269 
    270 	void Editor::setEnabled(juce::Component& _component, const bool _enable)
    271 	{
    272 		if(_component.getProperties().contains("disabledAlpha"))
    273 		{
    274 			const float a = _component.getProperties()["disabledAlpha"];
    275 
    276 			_component.setAlpha(_enable ? 1.0f : a);
    277 			_component.setEnabled(_enable);
    278 		}
    279 		else
    280 		{
    281 			_component.setVisible(_enable);
    282 		}
    283 	}
    284 
    285 	void Editor::setCurrentPart(const uint8_t _part)
    286 	{
    287 		m_rootObject->setCurrentPart(*this, _part);
    288 	}
    289 
    290 	void Editor::updateKeyValueConditions(const std::string& _key, const std::string& _value) const
    291 	{
    292 		m_rootObject->updateKeyValueConditions(_key, _value);
    293 	}
    294 
    295 	std::shared_ptr<UiObject> Editor::getTemplate(const std::string& _name) const
    296 	{
    297 		const auto& it = m_templates.find(_name);
    298 		if (it == m_templates.end())
    299 			return {};
    300 		return it->second;
    301 	}
    302 
    303 	bool Editor::resizeDrawableImage(juce::DrawableImage& _drawable, const uint32_t _percent)
    304 	{
    305 		if(_percent == 100)
    306 			return true;
    307 		if(_percent < 1)
    308 			return false;
    309 
    310 		auto image = _drawable.getImage();
    311 		const auto x = image.getBounds().getX();
    312 		const auto y = image.getBounds().getY();
    313 		const auto w = image.getWidth();
    314 		const auto h = image.getHeight();
    315 
    316 		const int percent = static_cast<int>(_percent);
    317 
    318 		image = image.rescaled(w * percent / 100, h * percent / 100);
    319 
    320 		_drawable.setImage(image);
    321 		auto b = image.getBounds();
    322 		b.setSize(w, h);
    323 		b.setPosition(x, y);
    324 		_drawable.setBounds(b);
    325 		_drawable.setBoundingBox(b.toFloat());
    326 		return true;
    327 	}
    328 
    329 	void Editor::paint(juce::Graphics& g)
    330 	{
    331 #if JUCE_MAJOR_VERSION >= 8 && defined(_WIN32)
    332 		// This makes a huge difference on Windows, especially when the whole window is downscaled
    333 		g.getInternalContext().setInterpolationQuality(juce::Graphics::highResamplingQuality);
    334 #endif
    335 		Component::paint(g);
    336 	}
    337 
    338 	bool Editor::selectTabWithComponent(const juce::Component* _component) const
    339 	{
    340 		for (const auto& it : m_tabGroupsByName)
    341 		{
    342 			auto* tabGroup = it.second;
    343 			if (tabGroup->selectTabWithComponent(_component))
    344 				return true;
    345 		}
    346 		return false;
    347 	}
    348 }