<commit_before>/*
 *   Copyright (c) 2008-2018 SLIBIO <https://github.com/SLIBIO>
 *
 *   Permission is hereby granted, free of charge, to any person obtaining a copy
 *   of this software and associated documentation files (the "Software"), to deal
 *   in the Software without restriction, including without limitation the rights
 *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *   copies of the Software, and to permit persons to whom the Software is
 *   furnished to do so, subject to the following conditions:
 *
 *   The above copyright notice and this permission notice shall be included in
 *   all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *   THE SOFTWARE.
 */

#include "slib/ui/common_dialogs.h"

#include "slib/ui/core.h"
#include "slib/ui/mobile_app.h"
#include "slib/ui/label_view.h"

#include "slib/core/file.h"
#include "slib/core/safe_static.h"

namespace slib
{

/***************************************
			AlertDialog
***************************************/

	SLIB_DEFINE_CLASS_DEFAULT_MEMBERS(AlertDialog)

	AlertDialog::AlertDialog()
	{
		flagHyperText = sl_false;
		buttons = AlertDialogButtons::Ok;
		icon = AlertDialogIcon::None;
	}

	class _priv_AlertDialog_RunOnUIThread
	{
	public:
		AlertDialog* alert;
		Ref<Event> event;
		DialogResult result = DialogResult::Cancel;

		void run()
		{
			result = alert->_run();
			event->set();
		}

	};

	DialogResult AlertDialog::_runOnUiThread()
	{
		if (UI::isUiThread()) {
			return _run();
		}
		Ref<Event> ev = Event::create(sl_false);
		if (ev.isNotNull()) {
			_priv_AlertDialog_RunOnUIThread m;
			m.alert = this;
			m.event = ev;
			UI::dispatchToUiThread(SLIB_FUNCTION_CLASS(_priv_AlertDialog_RunOnUIThread, run, &m));
			ev->wait();
			return m.result;
		}
		return DialogResult::Error;
	}

	class _priv_AlertDialog_CallbackRunByShow_UIThread
	{
	public:
		DialogResult result = DialogResult::Error;

		void onComplete(DialogResult _result)
		{
			result = _result;
			UI::quitLoop();
		}

	};

	class _priv_AlertDialog_CallbackRunByShow_NonUIThread
	{
	public:
		DialogResult result = DialogResult::Error;
		Ref<Event> event;

		void onComplete(DialogResult _result)
		{
			result = _result;
			event->set();
		}

	};

	void _priv_AlertDialog_runByShow(AlertDialog* alert, _priv_AlertDialog_CallbackRunByShow_NonUIThread* m)
	{
		if (!(alert->_show())) {
			m->onComplete(DialogResult::Error);
		}
	}

	DialogResult AlertDialog::_runByShow()
	{
		Ref<AlertDialog> alert = new AlertDialog(*this);
		if (alert.isNull()) {
			return DialogResult::Error;
		}
		if (UI::isUiThread()) {
			_priv_AlertDialog_CallbackRunByShow_UIThread m;
			alert->onComplete = SLIB_FUNCTION_CLASS(_priv_AlertDialog_CallbackRunByShow_UIThread, onComplete, &m);
			if (alert->_show()) {
				UI::runLoop();
				return m.result;
			}
		} else {
			Ref<Event> ev = Event::create(sl_false);
			if (ev.isNotNull()) {
				_priv_AlertDialog_CallbackRunByShow_NonUIThread m;
				m.event = ev;
				alert->onComplete = SLIB_FUNCTION_CLASS(_priv_AlertDialog_CallbackRunByShow_NonUIThread, onComplete, &m);
				UI::dispatchToUiThread(Function<void()>::bind(&_priv_AlertDialog_runByShow, alert.get(), &m));
				ev->wait();
				return m.result;
			}
		}
		return DialogResult::Error;
	}

	void _priv_AlertDialog_Show(const Ref<AlertDialog>& alert)
	{
		if (!(alert->_show())) {
			alert->_onResult(DialogResult::Error);
		}
	}

	void AlertDialog::_showOnUiThread()
	{
		Ref<AlertDialog> alert = getReferable();
		if (alert.isNotNull()) {
			if (UI::isUiThread()) {
				_priv_AlertDialog_Show(alert);
			} else {
				UI::dispatchToUiThread(Function<void()>::bind(&_priv_AlertDialog_Show, alert));
			}
		}
	}

	void _priv_AlertDialog_showByRun(const Ref<AlertDialog>& alert)
	{
		DialogResult result = alert->_run();
		alert->_onResult(result);
	}

	void AlertDialog::_showByRun()
	{
		Ref<AlertDialog> alert = getReferable();
		if (alert.isNotNull()) {
			UI::dispatchToUiThread(Function<void()>::bind(&_priv_AlertDialog_showByRun, alert));
		}
	}

	void AlertDialog::_onResult(DialogResult result)
	{
		onComplete(result);
		switch (result) {
			case DialogResult::Ok:
				onOk();
				break;
			case DialogResult::Yes:
				onYes();
				break;
			case DialogResult::No:
				onNo();
				break;
			case DialogResult::Cancel:
				onCancel();
				break;
			case DialogResult::Error:
				onError();
				if (onComplete.isNull() && onError.isNull()) {
					if (buttons == AlertDialogButtons::Ok) {
						onOk();
					} else if (buttons == AlertDialogButtons::YesNo) {
						onNo();
					} else {
						onCancel();
					}
				}
			default:
				break;
		}
	}

	AlertDialog* AlertDialog::getReferable()
	{
		if (getReferenceCount() > 0) {
			return this;
		} else {
			return new AlertDialog(*this);
		}
	}

/***************************************
		FileDialog
***************************************/

	SLIB_DEFINE_CLASS_DEFAULT_MEMBERS(FileDialog)

	FileDialog::FileDialog()
	{
		type = FileDialogType::OpenFile;
		flagShowHiddenFiles = sl_true;
	}

	void FileDialog::addFilter(const String& title, const String& patterns)
	{
		Filter filter;
		filter.title = title;
		filter.patterns = patterns;
		filters.add(filter);
	}

	class _priv_FileDialog_RunOnUIThread
	{
	public:
		FileDialog* dlg;
		Ref<Event> event;
		sl_bool result = sl_false;

		void run()
		{
			result = dlg->_run();
			event->set();
		}

	};

	sl_bool FileDialog::_runOnUiThread()
	{
		if (UI::isUiThread()) {
			return _run();
		}
		Ref<Event> ev = Event::create(sl_false);
		if (ev.isNotNull()) {
			_priv_FileDialog_RunOnUIThread m;
			m.dlg = this;
			m.event = ev;
			UI::dispatchToUiThread(SLIB_FUNCTION_CLASS(_priv_FileDialog_RunOnUIThread, run, &m));
			ev->wait();
			return m.result;
		}
		return sl_false;
	}

	List<String> FileDialog::openFiles(const Ref<Window>& parent)
	{
		FileDialog dlg;
		dlg.type = FileDialogType::OpenFiles;
		dlg.parent = parent;
		if (dlg.run()) {
			return dlg.selectedPaths;
		}
		return sl_null;
	}

	String FileDialog::openFile(const Ref<Window>& parent)
	{
		FileDialog dlg;
		dlg.type = FileDialogType::OpenFile;
		dlg.parent = parent;
		if (dlg.run()) {
			return dlg.selectedPath;
		}
		return sl_null;
	}

	String FileDialog::saveFile(const Ref<Window>& parent)
	{
		FileDialog dlg;
		dlg.type = FileDialogType::SaveFile;
		dlg.parent = parent;
		if (dlg.run()) {
			return dlg.selectedPath;
		}
		return sl_null;
	}

	String FileDialog::selectDirectory(const Ref<Window>& parent)
	{
		FileDialog dlg;
		dlg.type = FileDialogType::SelectDirectory;
		dlg.parent = parent;
		if (dlg.run()) {
			return dlg.selectedPath;
		}
		return sl_null;
	}

	SLIB_DEFINE_CLASS_DEFAULT_MEMBERS(TakePhotoResult)

	TakePhotoResult::TakePhotoResult()
	 : flagSuccess(sl_false), flagCancel(sl_false)
	{
	}

	String TakePhotoResult::getFilePath()
	{
		return filePath;
	}

	Memory TakePhotoResult::getFileContent()
	{
		if (fileContent.isNotNull()) {
			return fileContent;
		}
		if (filePath.isNotEmpty()) {
			return File::readAllBytes(filePath);
		}
		return sl_null;
	}

	Ref<Drawable> TakePhotoResult::getDrawable()
	{
		if (drawable.isNotNull()) {
			return drawable;
		}
		return PlatformDrawable::loadFromMemory(getFileContent());
	}

	Ref<Bitmap> TakePhotoResult::getBitmap()
	{
		if (drawable.isNotNull()) {
			return drawable->toBitmap();
		}
		return Bitmap::loadFromMemory(getFileContent());
	}

	Ref<Image> TakePhotoResult::getImage()
	{
		if (drawable.isNotNull()) {
			return drawable->toImage();
		}
		return Image::loadFromMemory(getFileContent());
	}

	SLIB_DEFINE_CLASS_DEFAULT_MEMBERS(TakePhoto)

	TakePhoto::TakePhoto()
	{
	}

	void TakePhoto::takeFromCamera(const Function<void(TakePhotoResult&)>& onComplete)
	{
		TakePhoto takePhoto;
		takePhoto.onComplete = onComplete;
		takePhoto.takeFromCamera();
	}

	void TakePhoto::chooseFromLibrary(const Function<void(TakePhotoResult&)>& onComplete)
	{
		TakePhoto takePhoto;
		takePhoto.onComplete = onComplete;
		takePhoto.chooseFromLibrary();
	}

#if !defined(SLIB_UI_IS_IOS) && !defined(SLIB_UI_IS_ANDROID)
	void TakePhoto::takeFromCamera()
	{
	}

	void TakePhoto::chooseFromLibrary()
	{
	}
#endif

/***************************************
 			Toast
***************************************/

	SLIB_DEFINE_CLASS_DEFAULT_MEMBERS(Toast)

	Toast::Toast()
	{
		duration = getDefaultDuration();
		font = getDefaultFont();
	}

	class _priv_ToastManager
	{
	public:
		Mutex lock;
		Ref<LabelView> currentToast;
		Ref<Animation> animation;

	public:
		void show(Toast* toast)
		{
			MutexLocker locker(&lock);
			if (currentToast.isNotNull()) {
				currentToast->removeFromParent();
				currentToast.setNull();
				animation.setNull();
			}
			Ref<Font> font = toast->font;
			if (font.isNull()) {
				return;
			}
			Ref<View> parent = toast->parent;
			if (parent.isNull()) {
				Ref<MobileApp> app = MobileApp::getApp();
				if (app.isNotNull()) {
					parent = app->getContentView();
				} else {
					Ref<UIApp> ui = UIApp::getApp();
					if (ui.isNotNull()) {
						Ref<Window> window = ui->getMainWindow();
						if (window.isNotNull()) {
							parent = window->getContentView();
						}
					}
				}
			}
			if (parent.isNull()) {
				return;
			}
			Ref<LabelView> view = new LabelView;
			if (view.isNull()) {
				return;
			}
			view->setText(toast->text, UIUpdateMode::Init);
			view->setMultiLine(MultiLineMode::WordWrap, UIUpdateMode::Init);
			view->setWidthWrapping(UIUpdateMode::Init);
			view->setHeightWrapping(UIUpdateMode::Init);
			view->setMaximumWidth((sl_ui_len)(parent->getWidth() * 0.9f), UIUpdateMode::Init);
			view->setFont(font, UIUpdateMode::Init);
			view->setTextColor(Color::White, UIUpdateMode::Init);
			view->setBackgroundColor(Color(0, 0, 0, 160), UIUpdateMode::Init);
			view->setBoundRadius(font->getFontHeight() / 3, UIUpdateMode::Init);
			view->setPadding((sl_ui_pos)(font->getFontHeight() / 3), UIUpdateMode::Init);
			view->setCenterInParent(UIUpdateMode::Init);
			view->setClipping(sl_true, UIUpdateMode::Init);
			currentToast = view;
			parent->addChild(view);
			animation = view->startAlphaAnimation(0, 1, 0.3f, sl_null, AnimationCurve::Linear, AnimationFlags::NotSelfAlive);

			auto weak = ToWeakRef(view);
			UI::dispatchToUiThread([this, weak]() {
				auto view = ToRef(weak);
				if (view.isNull()) {
					return;
				}
				MutexLocker locker(&lock);
				if (currentToast.isNotNull()) {
					animation = currentToast->startAlphaAnimation(1, 0, 0.3f, [this, weak]() {
						auto view = ToRef(weak);
						if (view.isNull()) {
							return;
						}
						MutexLocker locker(&lock);
						if (currentToast.isNotNull()) {
							currentToast->removeFromParent();
							currentToast.setNull();
							animation.setNull();
						}
					}, AnimationCurve::Linear, AnimationFlags::NotSelfAlive);
				}
			}, (sl_uint32)(toast->duration * 1000));
		}

	};
	SLIB_SAFE_STATIC_GETTER(_priv_ToastManager, _priv_getToastManager)

	void Toast::show()
	{
		_priv_ToastManager* manager = _priv_getToastManager();
		if (manager) {
			manager->show(this);
		}
	}

	void Toast::show(const String& text)
	{
		Toast toast;
		toast.text = text;
		toast.show();
	}

	float _g_priv_Toast_defaultDuration = 2.0f;

	float Toast::getDefaultDuration()
	{
		return _g_priv_Toast_defaultDuration;
	}

	void Toast::setDefaultDuration(float duration)
	{
		_g_priv_Toast_defaultDuration = duration;
	}

	SLIB_STATIC_ZERO_INITIALIZED(AtomicRef<Font>, _g_priv_Toast_defaultFont)

	Ref<Font> Toast::getDefaultFont()
	{
		if (SLIB_SAFE_STATIC_CHECK_FREED(_g_priv_Toast_defaultFont)) {
			return sl_null;
		}
		if (_g_priv_Toast_defaultFont.isNull()) {
			_g_priv_Toast_defaultFont = Font::create("Arial", UI::dpToPixel(20));
		}
		return _g_priv_Toast_defaultFont;
	}

	void Toast::setDefaultFont(const Ref<Font>& font)
	{
		if (SLIB_SAFE_STATIC_CHECK_FREED(_g_priv_Toast_defaultFont)) {
			return;
		}
		_g_priv_Toast_defaultFont = font;
	}

}
<commit_msg>ui/Toast: make the toast view as instanced<commit_after>/*
 *   Copyright (c) 2008-2018 SLIBIO <https://github.com/SLIBIO>
 *
 *   Permission is hereby granted, free of charge, to any person obtaining a copy
 *   of this software and associated documentation files (the "Software"), to deal
 *   in the Software without restriction, including without limitation the rights
 *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *   copies of the Software, and to permit persons to whom the Software is
 *   furnished to do so, subject to the following conditions:
 *
 *   The above copyright notice and this permission notice shall be included in
 *   all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *   THE SOFTWARE.
 */

#include "slib/ui/common_dialogs.h"

#include "slib/ui/core.h"
#include "slib/ui/mobile_app.h"
#include "slib/ui/label_view.h"

#include "slib/core/file.h"
#include "slib/core/safe_static.h"

namespace slib
{

/***************************************
			AlertDialog
***************************************/

	SLIB_DEFINE_CLASS_DEFAULT_MEMBERS(AlertDialog)

	AlertDialog::AlertDialog()
	{
		flagHyperText = sl_false;
		buttons = AlertDialogButtons::Ok;
		icon = AlertDialogIcon::None;
	}

	class _priv_AlertDialog_RunOnUIThread
	{
	public:
		AlertDialog* alert;
		Ref<Event> event;
		DialogResult result = DialogResult::Cancel;

		void run()
		{
			result = alert->_run();
			event->set();
		}

	};

	DialogResult AlertDialog::_runOnUiThread()
	{
		if (UI::isUiThread()) {
			return _run();
		}
		Ref<Event> ev = Event::create(sl_false);
		if (ev.isNotNull()) {
			_priv_AlertDialog_RunOnUIThread m;
			m.alert = this;
			m.event = ev;
			UI::dispatchToUiThread(SLIB_FUNCTION_CLASS(_priv_AlertDialog_RunOnUIThread, run, &m));
			ev->wait();
			return m.result;
		}
		return DialogResult::Error;
	}

	class _priv_AlertDialog_CallbackRunByShow_UIThread
	{
	public:
		DialogResult result = DialogResult::Error;

		void onComplete(DialogResult _result)
		{
			result = _result;
			UI::quitLoop();
		}

	};

	class _priv_AlertDialog_CallbackRunByShow_NonUIThread
	{
	public:
		DialogResult result = DialogResult::Error;
		Ref<Event> event;

		void onComplete(DialogResult _result)
		{
			result = _result;
			event->set();
		}

	};

	void _priv_AlertDialog_runByShow(AlertDialog* alert, _priv_AlertDialog_CallbackRunByShow_NonUIThread* m)
	{
		if (!(alert->_show())) {
			m->onComplete(DialogResult::Error);
		}
	}

	DialogResult AlertDialog::_runByShow()
	{
		Ref<AlertDialog> alert = new AlertDialog(*this);
		if (alert.isNull()) {
			return DialogResult::Error;
		}
		if (UI::isUiThread()) {
			_priv_AlertDialog_CallbackRunByShow_UIThread m;
			alert->onComplete = SLIB_FUNCTION_CLASS(_priv_AlertDialog_CallbackRunByShow_UIThread, onComplete, &m);
			if (alert->_show()) {
				UI::runLoop();
				return m.result;
			}
		} else {
			Ref<Event> ev = Event::create(sl_false);
			if (ev.isNotNull()) {
				_priv_AlertDialog_CallbackRunByShow_NonUIThread m;
				m.event = ev;
				alert->onComplete = SLIB_FUNCTION_CLASS(_priv_AlertDialog_CallbackRunByShow_NonUIThread, onComplete, &m);
				UI::dispatchToUiThread(Function<void()>::bind(&_priv_AlertDialog_runByShow, alert.get(), &m));
				ev->wait();
				return m.result;
			}
		}
		return DialogResult::Error;
	}

	void _priv_AlertDialog_Show(const Ref<AlertDialog>& alert)
	{
		if (!(alert->_show())) {
			alert->_onResult(DialogResult::Error);
		}
	}

	void AlertDialog::_showOnUiThread()
	{
		Ref<AlertDialog> alert = getReferable();
		if (alert.isNotNull()) {
			if (UI::isUiThread()) {
				_priv_AlertDialog_Show(alert);
			} else {
				UI::dispatchToUiThread(Function<void()>::bind(&_priv_AlertDialog_Show, alert));
			}
		}
	}

	void _priv_AlertDialog_showByRun(const Ref<AlertDialog>& alert)
	{
		DialogResult result = alert->_run();
		alert->_onResult(result);
	}

	void AlertDialog::_showByRun()
	{
		Ref<AlertDialog> alert = getReferable();
		if (alert.isNotNull()) {
			UI::dispatchToUiThread(Function<void()>::bind(&_priv_AlertDialog_showByRun, alert));
		}
	}

	void AlertDialog::_onResult(DialogResult result)
	{
		onComplete(result);
		switch (result) {
			case DialogResult::Ok:
				onOk();
				break;
			case DialogResult::Yes:
				onYes();
				break;
			case DialogResult::No:
				onNo();
				break;
			case DialogResult::Cancel:
				onCancel();
				break;
			case DialogResult::Error:
				onError();
				if (onComplete.isNull() && onError.isNull()) {
					if (buttons == AlertDialogButtons::Ok) {
						onOk();
					} else if (buttons == AlertDialogButtons::YesNo) {
						onNo();
					} else {
						onCancel();
					}
				}
			default:
				break;
		}
	}

	AlertDialog* AlertDialog::getReferable()
	{
		if (getReferenceCount() > 0) {
			return this;
		} else {
			return new AlertDialog(*this);
		}
	}

/***************************************
		FileDialog
***************************************/

	SLIB_DEFINE_CLASS_DEFAULT_MEMBERS(FileDialog)

	FileDialog::FileDialog()
	{
		type = FileDialogType::OpenFile;
		flagShowHiddenFiles = sl_true;
	}

	void FileDialog::addFilter(const String& title, const String& patterns)
	{
		Filter filter;
		filter.title = title;
		filter.patterns = patterns;
		filters.add(filter);
	}

	class _priv_FileDialog_RunOnUIThread
	{
	public:
		FileDialog* dlg;
		Ref<Event> event;
		sl_bool result = sl_false;

		void run()
		{
			result = dlg->_run();
			event->set();
		}

	};

	sl_bool FileDialog::_runOnUiThread()
	{
		if (UI::isUiThread()) {
			return _run();
		}
		Ref<Event> ev = Event::create(sl_false);
		if (ev.isNotNull()) {
			_priv_FileDialog_RunOnUIThread m;
			m.dlg = this;
			m.event = ev;
			UI::dispatchToUiThread(SLIB_FUNCTION_CLASS(_priv_FileDialog_RunOnUIThread, run, &m));
			ev->wait();
			return m.result;
		}
		return sl_false;
	}

	List<String> FileDialog::openFiles(const Ref<Window>& parent)
	{
		FileDialog dlg;
		dlg.type = FileDialogType::OpenFiles;
		dlg.parent = parent;
		if (dlg.run()) {
			return dlg.selectedPaths;
		}
		return sl_null;
	}

	String FileDialog::openFile(const Ref<Window>& parent)
	{
		FileDialog dlg;
		dlg.type = FileDialogType::OpenFile;
		dlg.parent = parent;
		if (dlg.run()) {
			return dlg.selectedPath;
		}
		return sl_null;
	}

	String FileDialog::saveFile(const Ref<Window>& parent)
	{
		FileDialog dlg;
		dlg.type = FileDialogType::SaveFile;
		dlg.parent = parent;
		if (dlg.run()) {
			return dlg.selectedPath;
		}
		return sl_null;
	}

	String FileDialog::selectDirectory(const Ref<Window>& parent)
	{
		FileDialog dlg;
		dlg.type = FileDialogType::SelectDirectory;
		dlg.parent = parent;
		if (dlg.run()) {
			return dlg.selectedPath;
		}
		return sl_null;
	}

	SLIB_DEFINE_CLASS_DEFAULT_MEMBERS(TakePhotoResult)

	TakePhotoResult::TakePhotoResult()
	 : flagSuccess(sl_false), flagCancel(sl_false)
	{
	}

	String TakePhotoResult::getFilePath()
	{
		return filePath;
	}

	Memory TakePhotoResult::getFileContent()
	{
		if (fileContent.isNotNull()) {
			return fileContent;
		}
		if (filePath.isNotEmpty()) {
			return File::readAllBytes(filePath);
		}
		return sl_null;
	}

	Ref<Drawable> TakePhotoResult::getDrawable()
	{
		if (drawable.isNotNull()) {
			return drawable;
		}
		return PlatformDrawable::loadFromMemory(getFileContent());
	}

	Ref<Bitmap> TakePhotoResult::getBitmap()
	{
		if (drawable.isNotNull()) {
			return drawable->toBitmap();
		}
		return Bitmap::loadFromMemory(getFileContent());
	}

	Ref<Image> TakePhotoResult::getImage()
	{
		if (drawable.isNotNull()) {
			return drawable->toImage();
		}
		return Image::loadFromMemory(getFileContent());
	}

	SLIB_DEFINE_CLASS_DEFAULT_MEMBERS(TakePhoto)

	TakePhoto::TakePhoto()
	{
	}

	void TakePhoto::takeFromCamera(const Function<void(TakePhotoResult&)>& onComplete)
	{
		TakePhoto takePhoto;
		takePhoto.onComplete = onComplete;
		takePhoto.takeFromCamera();
	}

	void TakePhoto::chooseFromLibrary(const Function<void(TakePhotoResult&)>& onComplete)
	{
		TakePhoto takePhoto;
		takePhoto.onComplete = onComplete;
		takePhoto.chooseFromLibrary();
	}

#if !defined(SLIB_UI_IS_IOS) && !defined(SLIB_UI_IS_ANDROID)
	void TakePhoto::takeFromCamera()
	{
	}

	void TakePhoto::chooseFromLibrary()
	{
	}
#endif

/***************************************
 			Toast
***************************************/

	SLIB_DEFINE_CLASS_DEFAULT_MEMBERS(Toast)

	Toast::Toast()
	{
		duration = getDefaultDuration();
		font = getDefaultFont();
	}

	class _priv_ToastManager
	{
	public:
		Mutex lock;
		Ref<LabelView> currentToast;
		Ref<Animation> animation;

	public:
		void show(Toast* toast)
		{
			MutexLocker locker(&lock);
			if (currentToast.isNotNull()) {
				currentToast->removeFromParent();
				currentToast.setNull();
				animation.setNull();
			}
			Ref<Font> font = toast->font;
			if (font.isNull()) {
				return;
			}
			Ref<View> parent = toast->parent;
			if (parent.isNull()) {
				Ref<MobileApp> app = MobileApp::getApp();
				if (app.isNotNull()) {
					parent = app->getContentView();
				} else {
					Ref<UIApp> ui = UIApp::getApp();
					if (ui.isNotNull()) {
						Ref<Window> window = ui->getMainWindow();
						if (window.isNotNull()) {
							parent = window->getContentView();
						}
					}
				}
			}
			if (parent.isNull()) {
				return;
			}
			Ref<LabelView> view = new LabelView;
			if (view.isNull()) {
				return;
			}
			view->setCreatingInstance(sl_true);
			view->setText(toast->text, UIUpdateMode::Init);
			view->setMultiLine(MultiLineMode::WordWrap, UIUpdateMode::Init);
			view->setWidthWrapping(UIUpdateMode::Init);
			view->setHeightWrapping(UIUpdateMode::Init);
			view->setMaximumWidth((sl_ui_len)(parent->getWidth() * 0.9f), UIUpdateMode::Init);
			view->setFont(font, UIUpdateMode::Init);
			view->setTextColor(Color::White, UIUpdateMode::Init);
			view->setBackgroundColor(Color(0, 0, 0, 160), UIUpdateMode::Init);
			view->setBoundRadius(font->getFontHeight() / 3, UIUpdateMode::Init);
			view->setPadding((sl_ui_pos)(font->getFontHeight() / 3), UIUpdateMode::Init);
			view->setCenterInParent(UIUpdateMode::Init);
			view->setClipping(sl_true, UIUpdateMode::Init);
			currentToast = view;
			parent->addChild(view);
			animation = view->startAlphaAnimation(0, 1, 0.3f, sl_null, AnimationCurve::Linear, AnimationFlags::NotSelfAlive);

			auto weak = ToWeakRef(view);
			UI::dispatchToUiThread([this, weak]() {
				auto view = ToRef(weak);
				if (view.isNull()) {
					return;
				}
				MutexLocker locker(&lock);
				if (currentToast.isNotNull()) {
					animation = currentToast->startAlphaAnimation(1, 0, 0.3f, [this, weak]() {
						auto view = ToRef(weak);
						if (view.isNull()) {
							return;
						}
						MutexLocker locker(&lock);
						if (currentToast.isNotNull()) {
							currentToast->removeFromParent();
							currentToast.setNull();
							animation.setNull();
						}
					}, AnimationCurve::Linear, AnimationFlags::NotSelfAlive);
				}
			}, (sl_uint32)(toast->duration * 1000));
		}

	};
	SLIB_SAFE_STATIC_GETTER(_priv_ToastManager, _priv_getToastManager)

	void Toast::show()
	{
		_priv_ToastManager* manager = _priv_getToastManager();
		if (manager) {
			manager->show(this);
		}
	}

	void Toast::show(const String& text)
	{
		Toast toast;
		toast.text = text;
		toast.show();
	}

	float _g_priv_Toast_defaultDuration = 2.0f;

	float Toast::getDefaultDuration()
	{
		return _g_priv_Toast_defaultDuration;
	}

	void Toast::setDefaultDuration(float duration)
	{
		_g_priv_Toast_defaultDuration = duration;
	}

	SLIB_STATIC_ZERO_INITIALIZED(AtomicRef<Font>, _g_priv_Toast_defaultFont)

	Ref<Font> Toast::getDefaultFont()
	{
		if (SLIB_SAFE_STATIC_CHECK_FREED(_g_priv_Toast_defaultFont)) {
			return sl_null;
		}
		if (_g_priv_Toast_defaultFont.isNull()) {
			_g_priv_Toast_defaultFont = Font::create("Arial", UI::dpToPixel(20));
		}
		return _g_priv_Toast_defaultFont;
	}

	void Toast::setDefaultFont(const Ref<Font>& font)
	{
		if (SLIB_SAFE_STATIC_CHECK_FREED(_g_priv_Toast_defaultFont)) {
			return;
		}
		_g_priv_Toast_defaultFont = font;
	}

}
<|endoftext|>


<commit_before>#include "player.hpp"
#include "lvl.hpp"
#include <vector>
#include <string>
#include <cstdio>

void fatal(const char*, ...);
extern "C" unsigned long hashbytes(unsigned char[], unsigned int);

struct Plat2d {

	static const unsigned int Ops[];
	static const unsigned int Nops;

	enum { UnitCost = true };

	typedef double Cost;
	static const double InfCost = -1;

	typedef int Oper;
	static const int Nop = -1;

	Plat2d(FILE*);

	struct State {
		State(void) { }

		State(unsigned int x, unsigned int y, unsigned int z,
			unsigned int w, unsigned int h) : player(x, y, w, h) { }

		Player player;
	};

	struct PackedState {
		// hash does nothing since the hash table
		// for plat2d states doesn't store anything.
		unsigned long hash(void) {
			unsigned int ix = x * 1e5;
			unsigned int iy = y * 1e5;
			unsigned int idy = dy * 1e5;

			static const unsigned int sz = sizeof(ix) +
				sizeof(iy) + sizeof(idy) + sizeof(jframes);
			unsigned char bytes[sz];

			unsigned int i = 0;
			for (unsigned int j = 0; j < sizeof(ix); j++) {
				bytes[i++] = ix & 0xFF;
				ix >>= 8;
			}
			for (unsigned int j = 0; j < sizeof(iy); j++) {
				bytes[i++] = iy & 0xFF;
				iy >>= 8;
			}
			for (unsigned int j = 0; j < sizeof(idy); j++) {
				bytes[i++] = idy & 0xFF;
				idy >>= 8;
			}
			bytes[i++] = jframes;
			assert (i <= sz);
			return hashbytes(bytes, i);
		}

		bool eq(PackedState &o) const {
			return jframes == o.jframes &&
				doubleeq(x, o.x) &&
				doubleeq(y, o.y) &&
				doubleeq(dy, o.dy);
		}

		double x, y, dy;
		// The body's fall flag is packed as the high-order bit
		// of jframes.
		unsigned char jframes;
	};

	struct Undo {
		Undo(State&, Oper) { }
	};

	State initialstate(void);

	Cost h(State &s) {
		return heuclidean(s);
	}

	Cost heuclidean(State &s) {
		const Point &loc = s.player.body.bbox.min;
		Point topleft(gx * Tile::Width, (gy-1) * Tile::Height);
		double min = Point::distance(loc, topleft);

		Point botleft(gx * Tile::Width, gy * Tile::Height);
		double d = Point::distance(loc, botleft);
		if (d < min)
			min = d;

		Point topright((gx+1) * Tile::Width, (gy-1) * Tile::Height);
		d = Point::distance(loc, topright);
		if (d < min)
			min = d;

		Point botright((gx+1) * Tile::Width, gy * Tile::Height);
		d = Point::distance(loc, botright);
		if (d < min)
			min = d;

		return min + 0.5;
	}

	Cost d(State &s) {
		return 0;
	}

	bool isgoal(State &s) {
		Lvl::Blkinfo bi = lvl.majorblk(s.player.body.bbox);
		return bi.x == gx && bi.y == gy;
	}

	unsigned int nops(State &s) {
		return Nops;
	}

	Oper nthop(State &s, unsigned int n) {
		return Ops[n];
	}

	Oper revop(State &s, Oper op) {
		if (op == Player::Left)
			return Player::Right;
		if (op == Player::Right)
			return Player::Left;
		return Nop;
	}

	void undo(State &s, Undo &u) { }

	State &apply(State &buf, State &s, Cost &c, Oper op) {
		assert (op != Nop);
		buf = s;
		buf.player.act(lvl, (unsigned int) op);
		c = Point::distance(s.player.body.bbox.min,
			buf.player.body.bbox.min);
		return buf;
	}

	void pack(PackedState &dst, State &src) {
		dst.x = src.player.body.bbox.min.x;
		dst.y = src.player.body.bbox.min.y;
		dst.dy = src.player.body.dy;
		dst.jframes = src.player.jframes;
		if (src.player.body.fall)
			dst.jframes |= 1 << 7;
	}

	State &unpack(State &buf, PackedState &pkd) {
		buf.player.jframes = pkd.jframes & 0x7F;
		buf.player.body.fall = pkd.jframes & (1 << 7);
		buf.player.body.dy = pkd.dy;
		buf.player.body.bbox.min.x = pkd.x;
		buf.player.body.bbox.min.y = pkd.y;
		buf.player.body.bbox.max.x = pkd.x + Player::Width;
		buf.player.body.bbox.max.y = pkd.y + Player::Height;
		return buf;
	}

	static void dumpstate(FILE *out, State &s) {
		fprintf(out, "%g, %g\n", s.player.loc().x, s.player.loc().y);
	}

	unsigned int gx, gy;	// goal tile location
	Lvl lvl;
};

// controlstr converts a vector of controls to an ASCII string.
std::string controlstr(const std::vector<unsigned int>&);

// controlvec converts a string of controls back into a vector.
std::vector<unsigned int> controlvec(const std::string&);
<commit_msg>plat2d: fix heuristic glitch.<commit_after>#include "player.hpp"
#include "lvl.hpp"
#include <vector>
#include <string>
#include <cstdio>

void fatal(const char*, ...);
extern "C" unsigned long hashbytes(unsigned char[], unsigned int);

struct Plat2d {

	static const unsigned int Ops[];
	static const unsigned int Nops;

	enum { UnitCost = true };

	typedef double Cost;
	static const double InfCost = -1;

	typedef int Oper;
	static const int Nop = -1;

	Plat2d(FILE*);

	struct State {
		State(void) { }

		State(unsigned int x, unsigned int y, unsigned int z,
			unsigned int w, unsigned int h) : player(x, y, w, h) { }

		Player player;
	};

	struct PackedState {
		// hash does nothing since the hash table
		// for plat2d states doesn't store anything.
		unsigned long hash(void) {
			unsigned int ix = x * 1e5;
			unsigned int iy = y * 1e5;
			unsigned int idy = dy * 1e5;

			static const unsigned int sz = sizeof(ix) +
				sizeof(iy) + sizeof(idy) + sizeof(jframes);
			unsigned char bytes[sz];

			unsigned int i = 0;
			for (unsigned int j = 0; j < sizeof(ix); j++) {
				bytes[i++] = ix & 0xFF;
				ix >>= 8;
			}
			for (unsigned int j = 0; j < sizeof(iy); j++) {
				bytes[i++] = iy & 0xFF;
				iy >>= 8;
			}
			for (unsigned int j = 0; j < sizeof(idy); j++) {
				bytes[i++] = idy & 0xFF;
				idy >>= 8;
			}
			bytes[i++] = jframes;
			assert (i <= sz);
			return hashbytes(bytes, i);
		}

		bool eq(PackedState &o) const {
			return jframes == o.jframes &&
				doubleeq(x, o.x) &&
				doubleeq(y, o.y) &&
				doubleeq(dy, o.dy);
		}

		double x, y, dy;
		// The body's fall flag is packed as the high-order bit
		// of jframes.
		unsigned char jframes;
	};

	struct Undo {
		Undo(State&, Oper) { }
	};

	State initialstate(void);

	Cost h(State &s) {
		return heuclidean(s);
	}

	Cost heuclidean(State &s) {
		static const double W = Tile::Width;
		static const double H = Tile::Height;
		const Point &loc = s.player.body.bbox.min;
		Point topleft(gx * W, (gy-1) * H);
		double min = Point::distance(loc, topleft);

		Point botleft(gx * W, gy * H);
		double d = Point::distance(loc, botleft);
		if (d < min)
			min = d;

		Point topright((gx+1) * W, (gy-1) * H);
		d = Point::distance(loc, topright);
		if (d < min)
			min = d;

		Point botright((gx+1) * W, gy * H);
		d = Point::distance(loc, botright);
		if (d < min)
			min = d;

		return min;
	}

	Cost d(State &s) {
		return 0;
	}

	bool isgoal(State &s) {
		Lvl::Blkinfo bi = lvl.majorblk(s.player.body.bbox);
		return bi.x == gx && bi.y == gy;
	}

	unsigned int nops(State &s) {
		return Nops;
	}

	Oper nthop(State &s, unsigned int n) {
		return Ops[n];
	}

	Oper revop(State &s, Oper op) {
		if (op == Player::Left)
			return Player::Right;
		if (op == Player::Right)
			return Player::Left;
		return Nop;
	}

	void undo(State &s, Undo &u) { }

	State &apply(State &buf, State &s, Cost &c, Oper op) {
		assert (op != Nop);
		buf = s;
		buf.player.act(lvl, (unsigned int) op);
		c = Point::distance(s.player.body.bbox.min,
			buf.player.body.bbox.min);
		return buf;
	}

	void pack(PackedState &dst, State &src) {
		dst.x = src.player.body.bbox.min.x;
		dst.y = src.player.body.bbox.min.y;
		dst.dy = src.player.body.dy;
		dst.jframes = src.player.jframes;
		if (src.player.body.fall)
			dst.jframes |= 1 << 7;
	}

	State &unpack(State &buf, PackedState &pkd) {
		buf.player.jframes = pkd.jframes & 0x7F;
		buf.player.body.fall = pkd.jframes & (1 << 7);
		buf.player.body.dy = pkd.dy;
		buf.player.body.bbox.min.x = pkd.x;
		buf.player.body.bbox.min.y = pkd.y;
		buf.player.body.bbox.max.x = pkd.x + Player::Width;
		buf.player.body.bbox.max.y = pkd.y + Player::Height;
		return buf;
	}

	static void dumpstate(FILE *out, State &s) {
		fprintf(out, "%g, %g\n", s.player.loc().x, s.player.loc().y);
	}

	unsigned int gx, gy;	// goal tile location
	Lvl lvl;
};

// controlstr converts a vector of controls to an ASCII string.
std::string controlstr(const std::vector<unsigned int>&);

// controlvec converts a string of controls back into a vector.
std::vector<unsigned int> controlvec(const std::string&);
<|endoftext|>


<commit_before>/*
 * exception.cpp
 *
 *  Created on: Jun 5, 2017
 *      Author: jiang
 */

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>

#include <metaverse/bitcoin/error.hpp>
#include <metaverse/explorer/extensions/exception.hpp>

namespace libbitcoin {
namespace explorer {

explorer_exception::explorer_exception(uint32_t code, const std::string& message) :
		code_{code}, message_{message}
{
}

std::ostream& operator<<(std::ostream& out, const explorer_exception& ex)
{
	boost::format fmt{"{\"code\":%d, \"message\":\"%s\", \"result\":null}"};
	out << (fmt % ex.code() % ex.message());
	return out;
}

console_result capture_excode(std::stringstream& sstream, std::pair<uint32_t, std::string>& ex_pair)
{
	std::stringstream sin;
	sin.str(sstream.str());
	sstream.str(""); // clear
	// parse json
	using namespace boost::property_tree;
	ptree pt;
	read_json(sin, pt);

	std::string code = pt.get<std::string>("code");
	if (code == "")
		return console_result::failure;
	std::string msg = pt.get<std::string>("message");

	std::stringstream ss;
	ss << code, ss >> ex_pair.first;
	ex_pair.second = msg;
	return console_result::okay;
}

} //namespace explorer
} //namespace libbitcoin
<commit_msg>add try catch<commit_after>/*
 * exception.cpp
 *
 *  Created on: Jun 5, 2017
 *      Author: jiang
 */

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>

#include <metaverse/bitcoin/error.hpp>
#include <metaverse/explorer/extensions/exception.hpp>

namespace libbitcoin {
namespace explorer {

explorer_exception::explorer_exception(uint32_t code, const std::string& message) :
		code_{code}, message_{message}
{
}

std::ostream& operator<<(std::ostream& out, const explorer_exception& ex)
{
	boost::format fmt{"{\"code\":%d, \"message\":\"%s\", \"result\":null}"};
	out << (fmt % ex.code() % ex.message());
	return out;
}

console_result capture_excode(std::stringstream& sstream, std::pair<uint32_t, std::string>& ex_pair)
{
	std::stringstream sin;
	sin.str(sstream.str());
	sstream.str(""); // clear
	// parse json
	using namespace boost::property_tree;
	try
	{
		ptree pt;
		read_json(sin, pt);

		std::string code = pt.get<std::string>("code");
		if (code == "")
			return console_result::failure;
		std::string msg = pt.get<std::string>("message");
		std::stringstream ss;
		ss << code, ss >> ex_pair.first;
		ex_pair.second = msg;
		return console_result::okay;
	}
	catch (...)
	{
		return console_result::failure;
	}
}

} //namespace explorer
} //namespace libbitcoin
<|endoftext|>


<commit_before>//
// This file is part of the Marble Virtual Globe.
//
// This program is free software licensed under the GNU LGPL. You can
// find a copy of this license in LICENSE.txt in the top directory of
// the source code.
//
// Copyright 2004-2007 Torsten Rahn <tackat@kde.org>
// Copyright 2007      Inge Wallin  <ingwa@kde.org>
// Copyright 2008-2009      Patrick Spendrin <ps_ml@gmx.de>
//

// Own
#include "GeoDataPlacemark.h"

// Private
#include "GeoDataPlacemark_p.h"

#include "GeoDataMultiGeometry.h"
#include "GeoDataCoordinates.h"

// Qt
#include <QtCore/QDataStream>
#include "MarbleDebug.h"
#include "GeoDataTrack.h"

namespace Marble
{
GeoDataPlacemark::GeoDataPlacemark()
    : GeoDataFeature( new GeoDataPlacemarkPrivate )
{
    p()->m_geometry->setParent( this );
}

GeoDataPlacemark::GeoDataPlacemark( const GeoDataPlacemark& other )
: GeoDataFeature( other )
{
    p()->m_geometry->setParent( this );
}

GeoDataPlacemark::GeoDataPlacemark( const QString& name )
    : GeoDataFeature( new GeoDataPlacemarkPrivate )
{
    d->m_name = name;
    p()->m_geometry->setParent( this );
}

GeoDataPlacemark::~GeoDataPlacemark()
{
}

bool GeoDataPlacemark::operator==( const GeoDataPlacemark& other ) const
{
    return p() == other.p();
}

GeoDataPlacemarkPrivate* GeoDataPlacemark::p() const
{
    return static_cast<GeoDataPlacemarkPrivate*>(d);
}

GeoDataGeometry* GeoDataPlacemark::geometry() const
{
    return p()->m_geometry;
}

GeoDataLookAt *GeoDataPlacemark::lookAt() const
{
    return p()->m_lookAt;
}

void GeoDataPlacemark::setLookAt( GeoDataLookAt *lookAt)
{
    detach();
    delete p()->m_lookAt;
    p()->m_lookAt = lookAt;
}

GeoDataCoordinates GeoDataPlacemark::coordinate( const QDateTime &dateTime, bool *iconAtCoordinates ) const
{
    bool hasIcon = false;
    GeoDataCoordinates coord;

    if( p()->m_geometry ) {
        // Beware: comparison between pointers, not strings.
        if ( p()->m_geometry->nodeType() == GeoDataTypes::GeoDataPointType ) {
            hasIcon = true;
            coord = GeoDataCoordinates( *static_cast<GeoDataPoint *>( p()->m_geometry ) );
        } else if ( p()->m_geometry->nodeType() == GeoDataTypes::GeoDataMultiGeometryType ) {
            GeoDataMultiGeometry *multiGeometry = static_cast<GeoDataMultiGeometry *>( p()->m_geometry );

            QVector<GeoDataGeometry*>::ConstIterator it = multiGeometry->constBegin();
            QVector<GeoDataGeometry*>::ConstIterator end = multiGeometry->constEnd();
            for ( ; it != end; ++it ) {
                if ( (*it)->nodeType() == GeoDataTypes::GeoDataPointType ) {
                    hasIcon = true;
                    break;
                }
            }

            coord = p()->m_geometry->latLonAltBox().center();
        } else if ( p()->m_geometry->nodeType() == GeoDataTypes::GeoDataTrackType ) {
            GeoDataTrack *track = static_cast<GeoDataTrack *>( p()->m_geometry );
            hasIcon = track->size() != 0 && track->firstWhen() <= dateTime;
            coord = track->coordinatesAt( dateTime );
        } else {
            coord = p()->m_geometry->latLonAltBox().center();
        }
    }

    if ( iconAtCoordinates != 0 ) {
        *iconAtCoordinates = hasIcon;
    }
    return coord;
}

void GeoDataPlacemark::coordinate( qreal& lon, qreal& lat, qreal& alt ) const
{
    return coordinate().geoCoordinates( lon, lat, alt );
}

void GeoDataPlacemark::setCoordinate( qreal lon, qreal lat, qreal alt, GeoDataPoint::Unit _unit)
{
    setGeometry( new GeoDataPoint(lon, lat, alt, _unit ) );
}

void GeoDataPlacemark::setCoordinate( const GeoDataPoint &point )
{
    setGeometry ( new GeoDataPoint( point ) );
}

void GeoDataPlacemark::setGeometry( GeoDataGeometry *entry )
{
    detach();
    delete p()->m_geometry;
    p()->m_geometry = entry;
    p()->m_geometry->setParent( this );
}

qreal GeoDataPlacemark::area() const
{
    return p()->m_area;
}

void GeoDataPlacemark::setArea( qreal area )
{
    detach();
    p()->m_area = area;
}

qint64 GeoDataPlacemark::population() const
{
    return p()->m_population;
}

void GeoDataPlacemark::setPopulation( qint64 population )
{
    detach();
    p()->m_population = population;
}

const QString GeoDataPlacemark::state() const
{
    return p()->m_state;
}

void GeoDataPlacemark::setState( const QString &state )
{
    detach();
    p()->m_state = state;
}

const QString GeoDataPlacemark::countryCode() const
{
    return p()->m_countrycode;
}

void GeoDataPlacemark::setCountryCode( const QString &countrycode )
{
    detach();
    p()->m_countrycode = countrycode;
}

void GeoDataPlacemark::pack( QDataStream& stream ) const
{
    GeoDataFeature::pack( stream );

    stream << p()->m_countrycode;
    stream << p()->m_area;
    stream << p()->m_population;
    if ( p()->m_geometry )
    {
        stream << p()->m_geometry->geometryId();
        p()->m_geometry->pack( stream );
    }
    else
    {
        stream << InvalidGeometryId;
    }
}

QXmlStreamWriter& GeoDataPlacemark::pack( QXmlStreamWriter& stream ) const
{
    stream.writeStartElement( "placemark" );

    stream.writeEndElement();
    return stream;
}

QXmlStreamWriter& GeoDataPlacemark::operator <<( QXmlStreamWriter& stream ) const
{
    pack( stream );
    return stream;
}

void GeoDataPlacemark::unpack( QDataStream& stream )
{
    detach();
    GeoDataFeature::unpack( stream );

    stream >> p()->m_countrycode;
    stream >> p()->m_area;
    stream >> p()->m_population;
    int geometryId;
    stream >> geometryId;
    switch( geometryId ) {
        case InvalidGeometryId:
            break;
        case GeoDataPointId:
            {
            GeoDataPoint* point = new GeoDataPoint;
            point->unpack( stream );
            delete p()->m_geometry;
            p()->m_geometry = point;
            }
            break;
        case GeoDataLineStringId:
            {
            GeoDataLineString* lineString = new GeoDataLineString;
            lineString->unpack( stream );
            delete p()->m_geometry;
            p()->m_geometry = lineString;
            }
            break;
        case GeoDataLinearRingId:
            {
            GeoDataLinearRing* linearRing = new GeoDataLinearRing;
            linearRing->unpack( stream );
            delete p()->m_geometry;
            p()->m_geometry = linearRing;
            }
            break;
        case GeoDataPolygonId:
            {
            GeoDataPolygon* polygon = new GeoDataPolygon;
            polygon->unpack( stream );
            delete p()->m_geometry;
            p()->m_geometry = polygon;
            }
            break;
        case GeoDataMultiGeometryId:
            {
            GeoDataMultiGeometry* multiGeometry = new GeoDataMultiGeometry;
            multiGeometry->unpack( stream );
            delete p()->m_geometry;
            p()->m_geometry = multiGeometry;
            }
            break;
        case GeoDataModelId:
            break;
        default: break;
    };
}

}
<commit_msg>remove return-statement in method returning void<commit_after>//
// This file is part of the Marble Virtual Globe.
//
// This program is free software licensed under the GNU LGPL. You can
// find a copy of this license in LICENSE.txt in the top directory of
// the source code.
//
// Copyright 2004-2007 Torsten Rahn <tackat@kde.org>
// Copyright 2007      Inge Wallin  <ingwa@kde.org>
// Copyright 2008-2009      Patrick Spendrin <ps_ml@gmx.de>
//

// Own
#include "GeoDataPlacemark.h"

// Private
#include "GeoDataPlacemark_p.h"

#include "GeoDataMultiGeometry.h"
#include "GeoDataCoordinates.h"

// Qt
#include <QtCore/QDataStream>
#include "MarbleDebug.h"
#include "GeoDataTrack.h"

namespace Marble
{
GeoDataPlacemark::GeoDataPlacemark()
    : GeoDataFeature( new GeoDataPlacemarkPrivate )
{
    p()->m_geometry->setParent( this );
}

GeoDataPlacemark::GeoDataPlacemark( const GeoDataPlacemark& other )
: GeoDataFeature( other )
{
    p()->m_geometry->setParent( this );
}

GeoDataPlacemark::GeoDataPlacemark( const QString& name )
    : GeoDataFeature( new GeoDataPlacemarkPrivate )
{
    d->m_name = name;
    p()->m_geometry->setParent( this );
}

GeoDataPlacemark::~GeoDataPlacemark()
{
}

bool GeoDataPlacemark::operator==( const GeoDataPlacemark& other ) const
{
    return p() == other.p();
}

GeoDataPlacemarkPrivate* GeoDataPlacemark::p() const
{
    return static_cast<GeoDataPlacemarkPrivate*>(d);
}

GeoDataGeometry* GeoDataPlacemark::geometry() const
{
    return p()->m_geometry;
}

GeoDataLookAt *GeoDataPlacemark::lookAt() const
{
    return p()->m_lookAt;
}

void GeoDataPlacemark::setLookAt( GeoDataLookAt *lookAt)
{
    detach();
    delete p()->m_lookAt;
    p()->m_lookAt = lookAt;
}

GeoDataCoordinates GeoDataPlacemark::coordinate( const QDateTime &dateTime, bool *iconAtCoordinates ) const
{
    bool hasIcon = false;
    GeoDataCoordinates coord;

    if( p()->m_geometry ) {
        // Beware: comparison between pointers, not strings.
        if ( p()->m_geometry->nodeType() == GeoDataTypes::GeoDataPointType ) {
            hasIcon = true;
            coord = GeoDataCoordinates( *static_cast<GeoDataPoint *>( p()->m_geometry ) );
        } else if ( p()->m_geometry->nodeType() == GeoDataTypes::GeoDataMultiGeometryType ) {
            GeoDataMultiGeometry *multiGeometry = static_cast<GeoDataMultiGeometry *>( p()->m_geometry );

            QVector<GeoDataGeometry*>::ConstIterator it = multiGeometry->constBegin();
            QVector<GeoDataGeometry*>::ConstIterator end = multiGeometry->constEnd();
            for ( ; it != end; ++it ) {
                if ( (*it)->nodeType() == GeoDataTypes::GeoDataPointType ) {
                    hasIcon = true;
                    break;
                }
            }

            coord = p()->m_geometry->latLonAltBox().center();
        } else if ( p()->m_geometry->nodeType() == GeoDataTypes::GeoDataTrackType ) {
            GeoDataTrack *track = static_cast<GeoDataTrack *>( p()->m_geometry );
            hasIcon = track->size() != 0 && track->firstWhen() <= dateTime;
            coord = track->coordinatesAt( dateTime );
        } else {
            coord = p()->m_geometry->latLonAltBox().center();
        }
    }

    if ( iconAtCoordinates != 0 ) {
        *iconAtCoordinates = hasIcon;
    }
    return coord;
}

void GeoDataPlacemark::coordinate( qreal& lon, qreal& lat, qreal& alt ) const
{
    coordinate().geoCoordinates( lon, lat, alt );
}

void GeoDataPlacemark::setCoordinate( qreal lon, qreal lat, qreal alt, GeoDataPoint::Unit _unit)
{
    setGeometry( new GeoDataPoint(lon, lat, alt, _unit ) );
}

void GeoDataPlacemark::setCoordinate( const GeoDataPoint &point )
{
    setGeometry ( new GeoDataPoint( point ) );
}

void GeoDataPlacemark::setGeometry( GeoDataGeometry *entry )
{
    detach();
    delete p()->m_geometry;
    p()->m_geometry = entry;
    p()->m_geometry->setParent( this );
}

qreal GeoDataPlacemark::area() const
{
    return p()->m_area;
}

void GeoDataPlacemark::setArea( qreal area )
{
    detach();
    p()->m_area = area;
}

qint64 GeoDataPlacemark::population() const
{
    return p()->m_population;
}

void GeoDataPlacemark::setPopulation( qint64 population )
{
    detach();
    p()->m_population = population;
}

const QString GeoDataPlacemark::state() const
{
    return p()->m_state;
}

void GeoDataPlacemark::setState( const QString &state )
{
    detach();
    p()->m_state = state;
}

const QString GeoDataPlacemark::countryCode() const
{
    return p()->m_countrycode;
}

void GeoDataPlacemark::setCountryCode( const QString &countrycode )
{
    detach();
    p()->m_countrycode = countrycode;
}

void GeoDataPlacemark::pack( QDataStream& stream ) const
{
    GeoDataFeature::pack( stream );

    stream << p()->m_countrycode;
    stream << p()->m_area;
    stream << p()->m_population;
    if ( p()->m_geometry )
    {
        stream << p()->m_geometry->geometryId();
        p()->m_geometry->pack( stream );
    }
    else
    {
        stream << InvalidGeometryId;
    }
}

QXmlStreamWriter& GeoDataPlacemark::pack( QXmlStreamWriter& stream ) const
{
    stream.writeStartElement( "placemark" );

    stream.writeEndElement();
    return stream;
}

QXmlStreamWriter& GeoDataPlacemark::operator <<( QXmlStreamWriter& stream ) const
{
    pack( stream );
    return stream;
}

void GeoDataPlacemark::unpack( QDataStream& stream )
{
    detach();
    GeoDataFeature::unpack( stream );

    stream >> p()->m_countrycode;
    stream >> p()->m_area;
    stream >> p()->m_population;
    int geometryId;
    stream >> geometryId;
    switch( geometryId ) {
        case InvalidGeometryId:
            break;
        case GeoDataPointId:
            {
            GeoDataPoint* point = new GeoDataPoint;
            point->unpack( stream );
            delete p()->m_geometry;
            p()->m_geometry = point;
            }
            break;
        case GeoDataLineStringId:
            {
            GeoDataLineString* lineString = new GeoDataLineString;
            lineString->unpack( stream );
            delete p()->m_geometry;
            p()->m_geometry = lineString;
            }
            break;
        case GeoDataLinearRingId:
            {
            GeoDataLinearRing* linearRing = new GeoDataLinearRing;
            linearRing->unpack( stream );
            delete p()->m_geometry;
            p()->m_geometry = linearRing;
            }
            break;
        case GeoDataPolygonId:
            {
            GeoDataPolygon* polygon = new GeoDataPolygon;
            polygon->unpack( stream );
            delete p()->m_geometry;
            p()->m_geometry = polygon;
            }
            break;
        case GeoDataMultiGeometryId:
            {
            GeoDataMultiGeometry* multiGeometry = new GeoDataMultiGeometry;
            multiGeometry->unpack( stream );
            delete p()->m_geometry;
            p()->m_geometry = multiGeometry;
            }
            break;
        case GeoDataModelId:
            break;
        default: break;
    };
}

}
<|endoftext|>


<commit_before>/*
  Q Light Controller
  ioplugin_stub.cpp

  Copyright (c) Heikki Junnila

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0.txt

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

#include <QtPlugin>
#include "iopluginstub.h"

/*****************************************************************************
 * Initialization
 *****************************************************************************/

IOPluginStub::~IOPluginStub()
{
}

void IOPluginStub::init()
{
    m_configureCalled = 0;
    m_canConfigure = false;
    m_universe = QByteArray(int(4 * 512), char(0));
}

QString IOPluginStub::name()
{
    return QString("I/O Plugin Stub");
}

int IOPluginStub::capabilities() const
{
    return QLCIOPlugin::Output | QLCIOPlugin::Input;
}

/*****************************************************************************
 * Outputs
 *****************************************************************************/

void IOPluginStub::openOutput(quint32 output)
{
    if (m_openOutputs.contains(output) == false && output < 4)
        m_openOutputs.append(output);
}

void IOPluginStub::closeOutput(quint32 output)
{
    m_openOutputs.removeAll(output);
}

QStringList IOPluginStub::outputs()
{
    QStringList list;
    for (quint32 i = 0; i < 4; i++)
        list << QString("%1: Stub %1").arg(i + 1);
    return list;
}

QString IOPluginStub::pluginInfo()
{
    return QString("This is a plugin stub for testing.");
}

QString IOPluginStub::outputInfo(quint32 output)
{
    Q_UNUSED(output);
    return QString("This is a plugin stub for testing.");
}

void IOPluginStub::writeUniverse(quint32 universe, quint32 output, const QByteArray &data)
{
    Q_UNUSED(universe)

    m_universe = m_universe.replace(output * 512, data.size(), data);
}

/*****************************************************************************
 * Inputs
 *****************************************************************************/

void IOPluginStub::openInput(quint32 input)
{
    if (m_openInputs.contains(input) == false && input < 4)
        m_openInputs.append(input);
}

void IOPluginStub::closeInput(quint32 input)
{
    m_openInputs.removeAll(input);
}

QStringList IOPluginStub::inputs()
{
    QStringList list;
    for (quint32 i = 0; i < 4; i++)
        list << QString("%1: Stub %1").arg(i + 1);
    return list;
}

QString IOPluginStub::inputInfo(quint32 input)
{
    Q_UNUSED(input);
    return QString("This is a plugin stub for testing.");
}

/*****************************************************************************
 * Configuration
 *****************************************************************************/

void IOPluginStub::configure()
{
    m_configureCalled++;
    emit configurationChanged();
}

bool IOPluginStub::canConfigure()
{
    return m_canConfigure;
}

/*****************************************************************************
 * Plugin export
 *****************************************************************************/
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
Q_EXPORT_PLUGIN2(iopluginstub, IOPluginStub)
#endif
<commit_msg>Fix unit test<commit_after>/*
  Q Light Controller
  ioplugin_stub.cpp

  Copyright (c) Heikki Junnila

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0.txt

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

#include <QtPlugin>
#include "iopluginstub.h"

/*****************************************************************************
 * Initialization
 *****************************************************************************/

IOPluginStub::~IOPluginStub()
{
}

void IOPluginStub::init()
{
    m_configureCalled = 0;
    m_canConfigure = false;
    m_universe = QByteArray(int(4 * 512), char(0));
}

QString IOPluginStub::name()
{
    return QString("I/O Plugin Stub");
}

int IOPluginStub::capabilities() const
{
    return QLCIOPlugin::Output | QLCIOPlugin::Input;
}

/*****************************************************************************
 * Outputs
 *****************************************************************************/

void IOPluginStub::openOutput(quint32 output)
{
    if (m_openOutputs.contains(output) == false && output < 4)
        m_openOutputs.append(output);
}

void IOPluginStub::closeOutput(quint32 output)
{
    m_openOutputs.removeAll(output);
}

QStringList IOPluginStub::outputs()
{
    QStringList list;
    for (quint32 i = 0; i < 4; i++)
        list << QString("%1: Stub %1").arg(i + 1);
    return list;
}

QString IOPluginStub::pluginInfo()
{
    return QString("This is a plugin stub for testing.");
}

void IOPluginStub::setParameter(QString name, QVariant &value)
{
    Q_UNUSED(name); Q_UNUSED(value);
}

QString IOPluginStub::outputInfo(quint32 output)
{
    Q_UNUSED(output);
    return QString("This is a plugin stub for testing.");
}

void IOPluginStub::writeUniverse(quint32 universe, quint32 output, const QByteArray &data)
{
    Q_UNUSED(universe)

    m_universe = m_universe.replace(output * 512, data.size(), data);
}

/*****************************************************************************
 * Inputs
 *****************************************************************************/

void IOPluginStub::openInput(quint32 input)
{
    if (m_openInputs.contains(input) == false && input < 4)
        m_openInputs.append(input);
}

void IOPluginStub::closeInput(quint32 input)
{
    m_openInputs.removeAll(input);
}

QStringList IOPluginStub::inputs()
{
    QStringList list;
    for (quint32 i = 0; i < 4; i++)
        list << QString("%1: Stub %1").arg(i + 1);
    return list;
}

QString IOPluginStub::inputInfo(quint32 input)
{
    Q_UNUSED(input);
    return QString("This is a plugin stub for testing.");
}

/*****************************************************************************
 * Configuration
 *****************************************************************************/

void IOPluginStub::configure()
{
    m_configureCalled++;
    emit configurationChanged();
}

bool IOPluginStub::canConfigure()
{
    return m_canConfigure;
}

/*****************************************************************************
 * Plugin export
 *****************************************************************************/
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
Q_EXPORT_PLUGIN2(iopluginstub, IOPluginStub)
#endif
<|endoftext|>


<commit_before>/*
*
* Copyright 2015 Telefonica Investigacion y Desarrollo, S.A.U
*
* This file is part of Orion Context Broker.
*
* Orion Context Broker is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Orion Context Broker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
* General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Orion Context Broker. If not, see http://www.gnu.org/licenses/.
*
* For those usages not covered by this license please contact with
* iot_support at tid dot es
*
* Author: Ken Zangelin
*/
#include <string>
#include <vector>

#include "common/statistics.h"
#include "common/clockFunctions.h"
#include "common/string.h"

#include "rest/ConnectionInfo.h"
#include "rest/OrionError.h"
#include "ngsi/ParseData.h"
#include "apiTypesV2/Entities.h"
#include "rest/EntityTypeInfo.h"
#include "serviceRoutinesV2/getEntities.h"
#include "serviceRoutines/postQueryContext.h"

/* ****************************************************************************
*
* getEntities -
*
* GET /v2/entities
*
* Payload In:  None
* Payload Out: Entities
*
* URI parameters:
*   - limit=NUMBER
*   - offset=NUMBER
*   - count=true/false
*   - id
*   - idPattern
*   - q
*   - geometry
*   - coords
*   - options=keyValues
*
* 01. Fill in QueryContextRequest
* 02. Call standard op postQueryContext
* 03. Render Entities response
* 04. Cleanup and return result
*/
std::string getEntities
(
  ConnectionInfo*            ciP,
  int                        components,
  std::vector<std::string>&  compV,
  ParseData*                 parseDataP
)
{
  LM_M(("In getEntities"));

  Entities     entities;
  std::string  answer;
  std::string  pattern    = ".*"; // all entities, default value
  std::string  id         = ciP->uriParam["id"];
  std::string  idPattern  = ciP->uriParam["idPattern"];
  std::string  q          = ciP->uriParam["q"];
  std::string  geometry   = ciP->uriParam["geometry"];
  std::string  coords     = ciP->uriParam["coords"];
  std::string  out;

  if ((idPattern != "") && (id != ""))
  {
    OrionError oe(SccBadRequest, "Incompatible parameters: id, IdPattern");

    TIMED_RENDER(answer = oe.render(ciP, ""));
    return answer;
  }
  else if (id != "")
  {
    pattern = "";

    // FIXME P5: a more efficient query could be possible (this ends as a regex
    // at MongoDB and regex are *expensive* in performance terms)
    std::vector<std::string> idsV;

    stringSplit(id, ',', idsV);

    for (unsigned int ix = 0; ix != idsV.size(); ++ix)
    {
      if (ix != 0)
      {
        pattern += "|^";
      }
      else
      {
        pattern += "^";
      }

      pattern += idsV[ix] + "$";
    }
  }
  else if (idPattern != "")
  {
    pattern = idPattern;
  }

  // Making sure geometry and coords are not used individually
  if ((coords != "") && (geometry == ""))
  {
    OrionError   oe(SccBadRequest, "URI param /coords/ used without /geometry/");

    TIMED_RENDER(out = oe.render(ciP, ""));
    return out;
  }
  else if ((geometry != "") && (coords == ""))
  {
    OrionError oe(SccBadRequest, "URI param /geometry/ used without /coords/");

    TIMED_RENDER(out = oe.render(ciP, ""));

    return out;
  }

  // Making sure geometry is valid (if present)
  orion::Geometry           geo;
  std::vector<std::string>  coordsV;

  if (geometry != "")
  {
    std::string  errorString;

    if (geo.parse(geometry.c_str(), &errorString) != 0)
    {
      OrionError oe(SccBadRequest, std::string("error parsing geometry: ") + errorString);

      TIMED_RENDER(out = oe.render(ciP, ""));

      return out;
    }

    if ((geo.areaType != "polygon") && (geo.areaType != "circle"))
    {
      OrionError oe(SccBadRequest, "URI param /geometry/ must be either /polygon/ or /circle/");

      TIMED_RENDER(out = oe.render(ciP, ""));

      return out;
    }

    //
    // As 'geometry' is present, so is 'coords' - checking coords
    //
    int noOfCoords = stringSplit(coords, ';', coordsV);

    if (noOfCoords == 0)
    {
      OrionError oe(SccBadRequest, "URI param /coords/ has no coordinates");

      TIMED_RENDER(out = oe.render(ciP, ""));

      return out;
    }

    if ((geo.areaType == "circle") && (noOfCoords != 1))
    {
      OrionError oe(SccBadRequest, "Too many coordinates for circle");

      TIMED_RENDER(out = oe.render(ciP, ""));

      return out;
    }

    if ((geo.areaType == "polygon") && (noOfCoords < 3))
    {
      OrionError oe(SccBadRequest, "Too few coordinates for polygon");

      TIMED_RENDER(out = oe.render(ciP, ""));

      return out;
    }
  }

  //
  // 01. Fill in QueryContextRequest - type "" is valid for all types
  //
  parseDataP->qcr.res.fill(pattern, ciP->uriParam["type"], "true", EntityTypeEmptyOrNotEmpty, "");

  // If URI param 'q' is given, its value must be put in a scope
  if (q != "")
  {
    Scope* scopeP = new Scope(SCOPE_TYPE_SIMPLE_QUERY, q);

    parseDataP->qcr.res.restriction.scopeVector.push_back(scopeP);
  }

  // If URI params 'geometry' and 'coords' are given, another scope is to be created for this
  if ((coords != "") && (geometry != ""))
  {
    Scope*       scopeP = new Scope(SCOPE_TYPE_LOCATION, "");
    std::string  errorString;

    if (scopeP->fill(&geo, coordsV, &errorString) != 0)
    {
      OrionError oe(SccBadRequest, errorString);

      TIMED_RENDER(out = oe.render(ciP, ""));

      return out;
    }

    parseDataP->qcr.res.restriction.scopeVector.push_back(scopeP);
  }

  // 02. Call standard op postQueryContext
  answer = postQueryContext(ciP, components, compV, parseDataP);

  if (ciP->httpStatusCode != SccOk)
  {
    // Something went wrong in the query, an invalid pattern for example

    parseDataP->qcr.res.release();

    return answer;
  }

  // 03. Render Entities response
  if (parseDataP->qcrs.res.contextElementResponseVector.size() == 0)
  {
    ciP->httpStatusCode = SccOk;
    answer = "[]";
  }
  else
  {
    entities.fill(&parseDataP->qcrs.res);

    TIMED_RENDER(answer = entities.render(ciP, EntitiesResponse));
  }

  // 04. Cleanup and return result
  entities.release();
  parseDataP->qcr.res.release();

  return answer;
}
<commit_msg>REMOVE LM_M trace<commit_after>/*
*
* Copyright 2015 Telefonica Investigacion y Desarrollo, S.A.U
*
* This file is part of Orion Context Broker.
*
* Orion Context Broker is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Orion Context Broker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
* General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Orion Context Broker. If not, see http://www.gnu.org/licenses/.
*
* For those usages not covered by this license please contact with
* iot_support at tid dot es
*
* Author: Ken Zangelin
*/
#include <string>
#include <vector>

#include "common/statistics.h"
#include "common/clockFunctions.h"
#include "common/string.h"

#include "rest/ConnectionInfo.h"
#include "rest/OrionError.h"
#include "ngsi/ParseData.h"
#include "apiTypesV2/Entities.h"
#include "rest/EntityTypeInfo.h"
#include "serviceRoutinesV2/getEntities.h"
#include "serviceRoutines/postQueryContext.h"

/* ****************************************************************************
*
* getEntities -
*
* GET /v2/entities
*
* Payload In:  None
* Payload Out: Entities
*
* URI parameters:
*   - limit=NUMBER
*   - offset=NUMBER
*   - count=true/false
*   - id
*   - idPattern
*   - q
*   - geometry
*   - coords
*   - options=keyValues
*
* 01. Fill in QueryContextRequest
* 02. Call standard op postQueryContext
* 03. Render Entities response
* 04. Cleanup and return result
*/
std::string getEntities
(
  ConnectionInfo*            ciP,
  int                        components,
  std::vector<std::string>&  compV,
  ParseData*                 parseDataP
)
{
  Entities     entities;
  std::string  answer;
  std::string  pattern    = ".*"; // all entities, default value
  std::string  id         = ciP->uriParam["id"];
  std::string  idPattern  = ciP->uriParam["idPattern"];
  std::string  q          = ciP->uriParam["q"];
  std::string  geometry   = ciP->uriParam["geometry"];
  std::string  coords     = ciP->uriParam["coords"];
  std::string  out;

  if ((idPattern != "") && (id != ""))
  {
    OrionError oe(SccBadRequest, "Incompatible parameters: id, IdPattern");

    TIMED_RENDER(answer = oe.render(ciP, ""));
    return answer;
  }
  else if (id != "")
  {
    pattern = "";

    // FIXME P5: a more efficient query could be possible (this ends as a regex
    // at MongoDB and regex are *expensive* in performance terms)
    std::vector<std::string> idsV;

    stringSplit(id, ',', idsV);

    for (unsigned int ix = 0; ix != idsV.size(); ++ix)
    {
      if (ix != 0)
      {
        pattern += "|^";
      }
      else
      {
        pattern += "^";
      }

      pattern += idsV[ix] + "$";
    }
  }
  else if (idPattern != "")
  {
    pattern = idPattern;
  }

  // Making sure geometry and coords are not used individually
  if ((coords != "") && (geometry == ""))
  {
    OrionError   oe(SccBadRequest, "URI param /coords/ used without /geometry/");

    TIMED_RENDER(out = oe.render(ciP, ""));
    return out;
  }
  else if ((geometry != "") && (coords == ""))
  {
    OrionError oe(SccBadRequest, "URI param /geometry/ used without /coords/");

    TIMED_RENDER(out = oe.render(ciP, ""));

    return out;
  }

  // Making sure geometry is valid (if present)
  orion::Geometry           geo;
  std::vector<std::string>  coordsV;

  if (geometry != "")
  {
    std::string  errorString;

    if (geo.parse(geometry.c_str(), &errorString) != 0)
    {
      OrionError oe(SccBadRequest, std::string("error parsing geometry: ") + errorString);

      TIMED_RENDER(out = oe.render(ciP, ""));

      return out;
    }

    if ((geo.areaType != "polygon") && (geo.areaType != "circle"))
    {
      OrionError oe(SccBadRequest, "URI param /geometry/ must be either /polygon/ or /circle/");

      TIMED_RENDER(out = oe.render(ciP, ""));

      return out;
    }

    //
    // As 'geometry' is present, so is 'coords' - checking coords
    //
    int noOfCoords = stringSplit(coords, ';', coordsV);

    if (noOfCoords == 0)
    {
      OrionError oe(SccBadRequest, "URI param /coords/ has no coordinates");

      TIMED_RENDER(out = oe.render(ciP, ""));

      return out;
    }

    if ((geo.areaType == "circle") && (noOfCoords != 1))
    {
      OrionError oe(SccBadRequest, "Too many coordinates for circle");

      TIMED_RENDER(out = oe.render(ciP, ""));

      return out;
    }

    if ((geo.areaType == "polygon") && (noOfCoords < 3))
    {
      OrionError oe(SccBadRequest, "Too few coordinates for polygon");

      TIMED_RENDER(out = oe.render(ciP, ""));

      return out;
    }
  }

  //
  // 01. Fill in QueryContextRequest - type "" is valid for all types
  //
  parseDataP->qcr.res.fill(pattern, ciP->uriParam["type"], "true", EntityTypeEmptyOrNotEmpty, "");

  // If URI param 'q' is given, its value must be put in a scope
  if (q != "")
  {
    Scope* scopeP = new Scope(SCOPE_TYPE_SIMPLE_QUERY, q);

    parseDataP->qcr.res.restriction.scopeVector.push_back(scopeP);
  }

  // If URI params 'geometry' and 'coords' are given, another scope is to be created for this
  if ((coords != "") && (geometry != ""))
  {
    Scope*       scopeP = new Scope(SCOPE_TYPE_LOCATION, "");
    std::string  errorString;

    if (scopeP->fill(&geo, coordsV, &errorString) != 0)
    {
      OrionError oe(SccBadRequest, errorString);

      TIMED_RENDER(out = oe.render(ciP, ""));

      return out;
    }

    parseDataP->qcr.res.restriction.scopeVector.push_back(scopeP);
  }

  // 02. Call standard op postQueryContext
  answer = postQueryContext(ciP, components, compV, parseDataP);

  if (ciP->httpStatusCode != SccOk)
  {
    // Something went wrong in the query, an invalid pattern for example

    parseDataP->qcr.res.release();

    return answer;
  }

  // 03. Render Entities response
  if (parseDataP->qcrs.res.contextElementResponseVector.size() == 0)
  {
    ciP->httpStatusCode = SccOk;
    answer = "[]";
  }
  else
  {
    entities.fill(&parseDataP->qcrs.res);

    TIMED_RENDER(answer = entities.render(ciP, EntitiesResponse));
  }

  // 04. Cleanup and return result
  entities.release();
  parseDataP->qcr.res.release();

  return answer;
}
<|endoftext|>


<commit_before>
#include "CPipelineState.h"
#include <ionWindow.h>
#include <glad/glad.h>

namespace ion
{
	namespace Graphics
	{
		namespace GL
		{
			CPipelineState::CPipelineState(CWindow * Window)
			{
				this->Window = Window;
				this->PrimitiveType = GL_TRIANGLES;
			}

			CPipelineState::~CPipelineState()
			{
			}

			void CPipelineState::SetProgram(SharedPointer<IShaderProgram> inShaderProgram)
			{
				if (inShaderProgram)
				{
					ShaderProgram = std::dynamic_pointer_cast<CShaderProgram>(inShaderProgram);
					if (! ShaderProgram->Linked)
					{
						ShaderProgram->Link();

						if (! ShaderProgram->Linked)
						{
							Log::Error("Failed to link shader prograg in PipelineState creation, unsetting shader.");
							ShaderProgram = nullptr;
							return;
						}
					}

					UnboundUniforms = KeySet(ShaderProgram->Uniforms);
					UnboundAttributes = KeySet(ShaderProgram->Attributes);
					UnboundAttributes.erase("gl_VertexID");

					Loaded = false;
				}
			}

			void CPipelineState::SetVertexBuffer(uint const Index, SharedPointer<IVertexBuffer> inVertexBuffer)
			{
				if (Index >= VertexBuffers.size())
				{
					VertexBuffers.resize(Index + 1);
				}

				VertexBuffers[Index] = std::dynamic_pointer_cast<CVertexBuffer>(inVertexBuffer);
				// Bound VBOs are not part of VAO state

				Loaded = false;
			}

			void CPipelineState::SetIndexBuffer(SharedPointer<IIndexBuffer> inIndexBuffer)
			{
				IndexBuffer = std::dynamic_pointer_cast<CIndexBuffer>(inIndexBuffer);
				Window->MakeContextCurrent();
				SafeGLCall(glBindVertexArray, (VertexArrayHandle));
				SafeGLCall(glBindBuffer, (GL_ELEMENT_ARRAY_BUFFER, IndexBuffer->Handle));
				SafeGLCall(glBindVertexArray, (0));

				Loaded = false;
			}

			void CPipelineState::SetUniform(string const & Name, SharedPointer<IUniform> Uniform)
			{
				if (! ShaderProgram)
				{
					Log::Error("Cannot set uniforms or textures on a PipelineState with no specified shader program.");
					return;
				}

				if (Uniform)
				{
					if (UnboundUniforms.count(Name) == 1)
					{
						Uniforms[Name] = Uniform;
						UnboundUniforms.erase(Name);
					}
					else
					{
						Log::Warn("Attempting to set uniform or texture '%s' that is not required by shader, ignoring.", Name);
					}
				}
				else
				{
					if (Uniforms.erase(Name) == 1)
					{
						UnboundUniforms.insert(Name);
					}
					else
					{
						Log::Error("Attempting to remove uniform or texture '%s' that was never specified, ignoring.", Name);
					}
				}
			}

			void CPipelineState::SetTexture(string const & Name, SharedPointer<ITexture> Texture)
			{
				if (! ShaderProgram)
				{
					Log::Error("Cannot set uniforms or textures on a PipelineState with no specified shader program.");
					return;
				}

				if (Texture)
				{
					if (UnboundUniforms.count(Name) == 1)
					{
						Textures[Name] = Texture;
						UnboundUniforms.erase(Name);
					}
					else
					{
						Log::Warn("Attempting to set uniform or texture '%s' that is not required by shader, ignoring.", Name);
					}
				}
				else
				{
					if (Textures.erase(Name) == 1)
					{
						UnboundUniforms.insert(Name);
					}
					else
					{
						Log::Error("Attempting to remove uniform or texture '%s' that was never specified, ignoring.", Name);
					}
				}
			}

			void CPipelineState::SetPrimitiveType(EPrimitiveType const PrimitiveType)
			{
				switch (PrimitiveType)
				{
				default:
				case EPrimitiveType::Triangle:
					this->PrimitiveType = GL_TRIANGLES;
					break;
				case EPrimitiveType::Point:
					this->PrimitiveType = GL_POINTS;
					break;
				case EPrimitiveType::Line:
					this->PrimitiveType = GL_LINES;
					break;
				case EPrimitiveType::LineStrip:
					this->PrimitiveType = GL_LINE_STRIP;
					break;
				}
			}

			void CPipelineState::SetFeatureEnabled(EDrawFeature const Feature, bool const Enabled)
			{
				switch (Feature)
				{
				case EDrawFeature::Wireframe:
					DrawWireframe = Enabled;
					break;
				case EDrawFeature::CullFront:
					CullFront = Enabled;
					break;
				case EDrawFeature::CullBack:
					CullBack = Enabled;
					break;
				case EDrawFeature::DisableDepthTest:
					DisableDepthTest = Enabled;
					break;
				case EDrawFeature::DisableDepthWrite:
					DisableDepthWrite = Enabled;
					break;
				case EDrawFeature::PolygonOffset:
					PolygonOffset = Enabled;
					break;
				}
			}

			void CPipelineState::SetPolygonOffsetAmount(float const Amount)
			{
				PolygonOffsetAmount = Amount;
			}

			void CPipelineState::SetBlendMode(EBlendMode const BlendMode)
			{
				this->BlendMode = BlendMode;
			}

			void CPipelineState::OfferUniform(string const & Name, SharedPointer<IUniform> Uniform)
			{
				if (! ShaderProgram)
				{
					Log::Error("Cannot set uniforms or textures on a PipelineState with no specified shader program.");
					return;
				}

				if (! Uniform)
				{
					Log::Error("Invalid paramter to CPipelineState::OfferUniform: expected non-null Uniform");
					return;
				}

				if (UnboundUniforms.count(Name) == 1)
				{
					Uniforms[Name] = Uniform;
					UnboundUniforms.erase(Name);
				}
			}

			void CPipelineState::OfferTexture(string const & Name, SharedPointer<ITexture> Texture)
			{
				if (! ShaderProgram)
				{
					Log::Error("Cannot set uniforms or textures on a PipelineState with no specified shader program.");
					return;
				}

				if (! Texture)
				{
					Log::Error("Invalid paramter to CPipelineState::OfferTexture: expected non-null Texture");
					return;
				}

				if (UnboundUniforms.count(Name) == 1)
				{
					Textures[Name] = Texture;
					UnboundUniforms.erase(Name);
				}
			}

			void CPipelineState::IgnoreUniform(string const & Name)
			{
				UnboundUniforms.erase(Name);
			}

			set<string> CPipelineState::GetUnboundUniforms() const
			{
				return UnboundUniforms;
			}

			void CPipelineState::Load()
			{
				if (! ShaderProgram || VertexBuffers.empty() || ! IndexBuffer)
				{
					Log::Error("Attempting to load an invalid PipelineState");
					return;
				}

				Window->MakeContextCurrent();
				CheckedGLCall(glUseProgram(ShaderProgram->Handle));
				CheckedGLCall(glBindVertexArray(VertexArrayHandle));

				for (auto & VertexBuffer : VertexBuffers)
				{

					CheckedGLCall(glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer->Handle));

					//////////////////////////////
					// Set up VBOs (attributes) //
					//////////////////////////////

					// Calculate stride of VBO data
					size_t TotalStride = 0;
					for (auto & InputLayoutElement : VertexBuffer->InputLayout)
					{
						TotalStride += GetAttributeTypeSize(InputLayoutElement.Type) * InputLayoutElement.Components;
					}

					size_t CurrentOffset = 0;
					for (auto & InputLayoutElement : VertexBuffer->InputLayout)
					{
						pair<uint, uint> AttributeInfo;
						if (TryMapAccess(ShaderProgram->Attributes, InputLayoutElement.Name, AttributeInfo))
						{
							uint const AttributeLocation = AttributeInfo.first;
							uint const AttributeType = AttributeInfo.second;

							// Validate Attribute Type (does the VBO layout match what the shader wants?)
							{
								bool IsAttributeTypeCorrect = false;
								string ShaderAttributeTypeString = "Unknown";
								switch (AttributeType)
								{
								default:
									Log::Error("Unexpected type for attribute %s: %u", InputLayoutElement.Name, AttributeType);
									break;
								case GL_FLOAT:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Float && InputLayoutElement.Components == 1);
									ShaderAttributeTypeString = "GL_FLOAT";
									break;
								case GL_FLOAT_VEC2:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Float && InputLayoutElement.Components == 2);
									ShaderAttributeTypeString = "GL_FLOAT_VEC2";
									break;
								case GL_FLOAT_VEC3:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Float && InputLayoutElement.Components == 3);
									ShaderAttributeTypeString = "GL_FLOAT_VEC3";
									break;
								case GL_FLOAT_VEC4:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Float && InputLayoutElement.Components == 4);
									ShaderAttributeTypeString = "GL_FLOAT_VEC4";
									break;
								case GL_INT:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Int && InputLayoutElement.Components == 1);
									ShaderAttributeTypeString = "GL_INT";
									break;
								case GL_INT_VEC2:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Int && InputLayoutElement.Components == 2);
									ShaderAttributeTypeString = "GL_INT_VEC2";
									break;
								case GL_INT_VEC3:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Int && InputLayoutElement.Components == 3);
									ShaderAttributeTypeString = "GL_INT_VEC3";
									break;
								case GL_INT_VEC4:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Int && InputLayoutElement.Components == 4);
									ShaderAttributeTypeString = "GL_INT_VEC4";
									break;
								}

								if (! IsAttributeTypeCorrect)
								{
									Log::Error("Mistmatch for attribute type '%s': VBO supplied %d components of type %s but shader expected '%s'",
										InputLayoutElement.Name,
										InputLayoutElement.Components,
										GetAttributeTypeString(InputLayoutElement.Type),
										ShaderAttributeTypeString);
								}
							}

							CheckedGLCall(glEnableVertexAttribArray(AttributeLocation));

							switch (AttributeType)
							{
							case GL_FLOAT:
							case GL_FLOAT_VEC2:
							case GL_FLOAT_VEC3:
							case GL_FLOAT_VEC4:
								CheckedGLCall(glVertexAttribPointer(
									AttributeLocation,
									InputLayoutElement.Components,
									GetAttributeTypeOpenGLEnum(InputLayoutElement.Type),
									GL_FALSE,
									(int) TotalStride,
									(void *) CurrentOffset));
								break;
							case GL_INT:
							case GL_INT_VEC2:
							case GL_INT_VEC3:
							case GL_INT_VEC4:
								CheckedGLCall(glVertexAttribIPointer(
									AttributeLocation,
									InputLayoutElement.Components,
									GetAttributeTypeOpenGLEnum(InputLayoutElement.Type),
									(int) TotalStride,
									(void *) CurrentOffset));
								break;
							}

							if (VertexBuffer->Instancing)
							{
								CheckedGLCall(glVertexAttribDivisor(AttributeLocation, 1));
							}

							UnboundAttributes.erase(InputLayoutElement.Name);
						}

						CurrentOffset += GetAttributeTypeSize(InputLayoutElement.Type) * InputLayoutElement.Components;
					}

					CheckedGLCall(glBindBuffer(GL_ARRAY_BUFFER, 0)); // Remember, VBOs are not part of VAO state (that's why we never leave them set in the VAO)
				}

				std::for_each(UnboundAttributes.begin(), UnboundAttributes.end(), [](string const & Attribuite)
				{
					Log::Error("Attribute expected by shader but not provided by VBO: %s", Attribuite);
				});

				CheckedGLCall(glBindVertexArray(0));
				CheckedGLCall(glUseProgram(0));

				/////////////////////
				// Set up Uniforms //
				/////////////////////

				BoundUniforms.clear();
				for (auto const & it : Uniforms)
				{
					uint Handle = 0;
					if (TryMapAccess(ShaderProgram->Uniforms, it.first, Handle))
					{
						BoundUniforms[Handle] = it.second;
					}
				}
				for (auto const & it : Textures)
				{
					uint Handle = 0;
					if (TryMapAccess(ShaderProgram->Uniforms, it.first, Handle))
					{
						BoundTextures[Handle] = it.second;
					}
				}

				std::for_each(UnboundUniforms.begin(), UnboundUniforms.end(), [](string const & Uniform)
				{
					Log::Error("Uniform expected by shader but not provided by PSO: %s", Uniform);
				});

				Loaded = true;
			}

		}
	}
}
<commit_msg>[ionGraphicsGL] Setting index buffer does not trigger a reload<commit_after>
#include "CPipelineState.h"
#include <ionWindow.h>
#include <glad/glad.h>

namespace ion
{
	namespace Graphics
	{
		namespace GL
		{
			CPipelineState::CPipelineState(CWindow * Window)
			{
				this->Window = Window;
				this->PrimitiveType = GL_TRIANGLES;
			}

			CPipelineState::~CPipelineState()
			{
			}

			void CPipelineState::SetProgram(SharedPointer<IShaderProgram> inShaderProgram)
			{
				if (inShaderProgram)
				{
					ShaderProgram = std::dynamic_pointer_cast<CShaderProgram>(inShaderProgram);
					if (! ShaderProgram->Linked)
					{
						ShaderProgram->Link();

						if (! ShaderProgram->Linked)
						{
							Log::Error("Failed to link shader prograg in PipelineState creation, unsetting shader.");
							ShaderProgram = nullptr;
							return;
						}
					}

					UnboundUniforms = KeySet(ShaderProgram->Uniforms);
					UnboundAttributes = KeySet(ShaderProgram->Attributes);
					UnboundAttributes.erase("gl_VertexID");

					Loaded = false;
				}
			}

			void CPipelineState::SetVertexBuffer(uint const Index, SharedPointer<IVertexBuffer> inVertexBuffer)
			{
				if (Index >= VertexBuffers.size())
				{
					VertexBuffers.resize(Index + 1);
				}

				VertexBuffers[Index] = std::dynamic_pointer_cast<CVertexBuffer>(inVertexBuffer);
				// Bound VBOs are not part of VAO state

				Loaded = false;
			}

			void CPipelineState::SetIndexBuffer(SharedPointer<IIndexBuffer> inIndexBuffer)
			{
				IndexBuffer = std::dynamic_pointer_cast<CIndexBuffer>(inIndexBuffer);
				Window->MakeContextCurrent();
				SafeGLCall(glBindVertexArray, (VertexArrayHandle));
				SafeGLCall(glBindBuffer, (GL_ELEMENT_ARRAY_BUFFER, IndexBuffer->Handle));
				SafeGLCall(glBindVertexArray, (0));
			}

			void CPipelineState::SetUniform(string const & Name, SharedPointer<IUniform> Uniform)
			{
				if (! ShaderProgram)
				{
					Log::Error("Cannot set uniforms or textures on a PipelineState with no specified shader program.");
					return;
				}

				if (Uniform)
				{
					if (UnboundUniforms.count(Name) == 1)
					{
						Uniforms[Name] = Uniform;
						UnboundUniforms.erase(Name);
					}
					else
					{
						Log::Warn("Attempting to set uniform or texture '%s' that is not required by shader, ignoring.", Name);
					}
				}
				else
				{
					if (Uniforms.erase(Name) == 1)
					{
						UnboundUniforms.insert(Name);
					}
					else
					{
						Log::Error("Attempting to remove uniform or texture '%s' that was never specified, ignoring.", Name);
					}
				}
			}

			void CPipelineState::SetTexture(string const & Name, SharedPointer<ITexture> Texture)
			{
				if (! ShaderProgram)
				{
					Log::Error("Cannot set uniforms or textures on a PipelineState with no specified shader program.");
					return;
				}

				if (Texture)
				{
					if (UnboundUniforms.count(Name) == 1)
					{
						Textures[Name] = Texture;
						UnboundUniforms.erase(Name);
					}
					else
					{
						Log::Warn("Attempting to set uniform or texture '%s' that is not required by shader, ignoring.", Name);
					}
				}
				else
				{
					if (Textures.erase(Name) == 1)
					{
						UnboundUniforms.insert(Name);
					}
					else
					{
						Log::Error("Attempting to remove uniform or texture '%s' that was never specified, ignoring.", Name);
					}
				}
			}

			void CPipelineState::SetPrimitiveType(EPrimitiveType const PrimitiveType)
			{
				switch (PrimitiveType)
				{
				default:
				case EPrimitiveType::Triangle:
					this->PrimitiveType = GL_TRIANGLES;
					break;
				case EPrimitiveType::Point:
					this->PrimitiveType = GL_POINTS;
					break;
				case EPrimitiveType::Line:
					this->PrimitiveType = GL_LINES;
					break;
				case EPrimitiveType::LineStrip:
					this->PrimitiveType = GL_LINE_STRIP;
					break;
				}
			}

			void CPipelineState::SetFeatureEnabled(EDrawFeature const Feature, bool const Enabled)
			{
				switch (Feature)
				{
				case EDrawFeature::Wireframe:
					DrawWireframe = Enabled;
					break;
				case EDrawFeature::CullFront:
					CullFront = Enabled;
					break;
				case EDrawFeature::CullBack:
					CullBack = Enabled;
					break;
				case EDrawFeature::DisableDepthTest:
					DisableDepthTest = Enabled;
					break;
				case EDrawFeature::DisableDepthWrite:
					DisableDepthWrite = Enabled;
					break;
				case EDrawFeature::PolygonOffset:
					PolygonOffset = Enabled;
					break;
				}
			}

			void CPipelineState::SetPolygonOffsetAmount(float const Amount)
			{
				PolygonOffsetAmount = Amount;
			}

			void CPipelineState::SetBlendMode(EBlendMode const BlendMode)
			{
				this->BlendMode = BlendMode;
			}

			void CPipelineState::OfferUniform(string const & Name, SharedPointer<IUniform> Uniform)
			{
				if (! ShaderProgram)
				{
					Log::Error("Cannot set uniforms or textures on a PipelineState with no specified shader program.");
					return;
				}

				if (! Uniform)
				{
					Log::Error("Invalid paramter to CPipelineState::OfferUniform: expected non-null Uniform");
					return;
				}

				if (UnboundUniforms.count(Name) == 1)
				{
					Uniforms[Name] = Uniform;
					UnboundUniforms.erase(Name);
				}
			}

			void CPipelineState::OfferTexture(string const & Name, SharedPointer<ITexture> Texture)
			{
				if (! ShaderProgram)
				{
					Log::Error("Cannot set uniforms or textures on a PipelineState with no specified shader program.");
					return;
				}

				if (! Texture)
				{
					Log::Error("Invalid paramter to CPipelineState::OfferTexture: expected non-null Texture");
					return;
				}

				if (UnboundUniforms.count(Name) == 1)
				{
					Textures[Name] = Texture;
					UnboundUniforms.erase(Name);
				}
			}

			void CPipelineState::IgnoreUniform(string const & Name)
			{
				UnboundUniforms.erase(Name);
			}

			set<string> CPipelineState::GetUnboundUniforms() const
			{
				return UnboundUniforms;
			}

			void CPipelineState::Load()
			{
				if (! ShaderProgram || VertexBuffers.empty() || ! IndexBuffer)
				{
					Log::Error("Attempting to load an invalid PipelineState");
					return;
				}

				Window->MakeContextCurrent();
				CheckedGLCall(glUseProgram(ShaderProgram->Handle));
				CheckedGLCall(glBindVertexArray(VertexArrayHandle));

				for (auto & VertexBuffer : VertexBuffers)
				{

					CheckedGLCall(glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer->Handle));

					//////////////////////////////
					// Set up VBOs (attributes) //
					//////////////////////////////

					// Calculate stride of VBO data
					size_t TotalStride = 0;
					for (auto & InputLayoutElement : VertexBuffer->InputLayout)
					{
						TotalStride += GetAttributeTypeSize(InputLayoutElement.Type) * InputLayoutElement.Components;
					}

					size_t CurrentOffset = 0;
					for (auto & InputLayoutElement : VertexBuffer->InputLayout)
					{
						pair<uint, uint> AttributeInfo;
						if (TryMapAccess(ShaderProgram->Attributes, InputLayoutElement.Name, AttributeInfo))
						{
							uint const AttributeLocation = AttributeInfo.first;
							uint const AttributeType = AttributeInfo.second;

							// Validate Attribute Type (does the VBO layout match what the shader wants?)
							{
								bool IsAttributeTypeCorrect = false;
								string ShaderAttributeTypeString = "Unknown";
								switch (AttributeType)
								{
								default:
									Log::Error("Unexpected type for attribute %s: %u", InputLayoutElement.Name, AttributeType);
									break;
								case GL_FLOAT:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Float && InputLayoutElement.Components == 1);
									ShaderAttributeTypeString = "GL_FLOAT";
									break;
								case GL_FLOAT_VEC2:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Float && InputLayoutElement.Components == 2);
									ShaderAttributeTypeString = "GL_FLOAT_VEC2";
									break;
								case GL_FLOAT_VEC3:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Float && InputLayoutElement.Components == 3);
									ShaderAttributeTypeString = "GL_FLOAT_VEC3";
									break;
								case GL_FLOAT_VEC4:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Float && InputLayoutElement.Components == 4);
									ShaderAttributeTypeString = "GL_FLOAT_VEC4";
									break;
								case GL_INT:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Int && InputLayoutElement.Components == 1);
									ShaderAttributeTypeString = "GL_INT";
									break;
								case GL_INT_VEC2:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Int && InputLayoutElement.Components == 2);
									ShaderAttributeTypeString = "GL_INT_VEC2";
									break;
								case GL_INT_VEC3:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Int && InputLayoutElement.Components == 3);
									ShaderAttributeTypeString = "GL_INT_VEC3";
									break;
								case GL_INT_VEC4:
									IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Int && InputLayoutElement.Components == 4);
									ShaderAttributeTypeString = "GL_INT_VEC4";
									break;
								}

								if (! IsAttributeTypeCorrect)
								{
									Log::Error("Mistmatch for attribute type '%s': VBO supplied %d components of type %s but shader expected '%s'",
										InputLayoutElement.Name,
										InputLayoutElement.Components,
										GetAttributeTypeString(InputLayoutElement.Type),
										ShaderAttributeTypeString);
								}
							}

							CheckedGLCall(glEnableVertexAttribArray(AttributeLocation));

							switch (AttributeType)
							{
							case GL_FLOAT:
							case GL_FLOAT_VEC2:
							case GL_FLOAT_VEC3:
							case GL_FLOAT_VEC4:
								CheckedGLCall(glVertexAttribPointer(
									AttributeLocation,
									InputLayoutElement.Components,
									GetAttributeTypeOpenGLEnum(InputLayoutElement.Type),
									GL_FALSE,
									(int) TotalStride,
									(void *) CurrentOffset));
								break;
							case GL_INT:
							case GL_INT_VEC2:
							case GL_INT_VEC3:
							case GL_INT_VEC4:
								CheckedGLCall(glVertexAttribIPointer(
									AttributeLocation,
									InputLayoutElement.Components,
									GetAttributeTypeOpenGLEnum(InputLayoutElement.Type),
									(int) TotalStride,
									(void *) CurrentOffset));
								break;
							}

							if (VertexBuffer->Instancing)
							{
								CheckedGLCall(glVertexAttribDivisor(AttributeLocation, 1));
							}

							UnboundAttributes.erase(InputLayoutElement.Name);
						}

						CurrentOffset += GetAttributeTypeSize(InputLayoutElement.Type) * InputLayoutElement.Components;
					}

					CheckedGLCall(glBindBuffer(GL_ARRAY_BUFFER, 0)); // Remember, VBOs are not part of VAO state (that's why we never leave them set in the VAO)
				}

				std::for_each(UnboundAttributes.begin(), UnboundAttributes.end(), [](string const & Attribuite)
				{
					Log::Error("Attribute expected by shader but not provided by VBO: %s", Attribuite);
				});

				CheckedGLCall(glBindVertexArray(0));
				CheckedGLCall(glUseProgram(0));

				/////////////////////
				// Set up Uniforms //
				/////////////////////

				BoundUniforms.clear();
				for (auto const & it : Uniforms)
				{
					uint Handle = 0;
					if (TryMapAccess(ShaderProgram->Uniforms, it.first, Handle))
					{
						BoundUniforms[Handle] = it.second;
					}
				}
				for (auto const & it : Textures)
				{
					uint Handle = 0;
					if (TryMapAccess(ShaderProgram->Uniforms, it.first, Handle))
					{
						BoundTextures[Handle] = it.second;
					}
				}

				std::for_each(UnboundUniforms.begin(), UnboundUniforms.end(), [](string const & Uniform)
				{
					Log::Error("Uniform expected by shader but not provided by PSO: %s", Uniform);
				});

				Loaded = true;
			}

		}
	}
}
<|endoftext|>