<commit_before>// Copyright (c) 2015, Matthew Malensek.
// Distributed under the BSD 2-Clause License (see LICENSE.txt for details)

#include "HotkeyManager.h"

#include "Logger.h"
#include "SyntheticKeyboard.h"

HotkeyManager *HotkeyManager::instance = NULL;

std::unordered_map<UINT, std::wstring> HotkeyManager::_vkStringMap = {
    { VK_SELECT, L"Select" },
    { VK_PRINT, L"Print" },
    { VK_EXECUTE, L"Execute" },
    { VK_SNAPSHOT, L"Print Screen" },
    { VK_HELP, L"Help" },
    { VK_SLEEP, L"Sleep" },
    { VK_BROWSER_BACK, L"Browser Back" },
    { VK_BROWSER_FORWARD, L"Browser Forward" },
    { VK_BROWSER_REFRESH, L"Browser Refresh" },
    { VK_BROWSER_STOP, L"Browser Stop" },
    { VK_BROWSER_SEARCH, L"Browser Search" },
    { VK_BROWSER_FAVORITES, L"Browser Favorites" },
    { VK_BROWSER_HOME, L"Browser Home" },
    { VK_VOLUME_MUTE, L"Mute" },
    { VK_VOLUME_DOWN, L"Volume Down" },
    { VK_VOLUME_UP, L"Volume Up" },
};

HotkeyManager *HotkeyManager::Instance() {
    return instance;
}

HotkeyManager *HotkeyManager::Instance(HWND notifyWnd) {
    if (instance == NULL) {
        instance = new HotkeyManager();
        instance->Hook();
        instance->_notifyWnd = notifyWnd;
    }

    return instance;
}

HotkeyManager::HotkeyManager() :
_fixWin(false) {

}

HotkeyManager::~HotkeyManager() {
    while (true) {
        auto i = _keyCombinations.begin();
        if (_keyCombinations.size() > 0) {
            Unregister(*i);
        } else {
            break;
        }
    }
    _hookCombinations.clear();

    Unhook();
}

void HotkeyManager::Shutdown() {
    delete instance;
    instance = NULL;
}

bool HotkeyManager::Hook() {
    _mouseHook = SetWindowsHookEx(WH_MOUSE_LL,
        LowLevelMouseProc, NULL, NULL);

    _keyHook = SetWindowsHookEx(WH_KEYBOARD_LL,
        LowLevelKeyboardProc, NULL, NULL);

    return _mouseHook && _keyHook;
}

bool HotkeyManager::Unhook() {
    BOOL unMouse = UnhookWindowsHookEx(_mouseHook);
    BOOL unKey = UnhookWindowsHookEx(_keyHook);
    return unMouse && unKey;
}

void HotkeyManager::Register(int keyCombination) {
    if (_keyCombinations.count(keyCombination) > 0) {
        CLOG(L"Hotkey combination [%d] already registered", keyCombination);
        return;
    } else {
        _keyCombinations.insert(keyCombination);
    }

    /* get VK_* value; */
    int vk = 0xFF & keyCombination;

    if ((keyCombination >> 20) > 0 /* uses a HKM_MOUSE_* flag */
        || vk == VK_LBUTTON
        || vk == VK_RBUTTON
        || vk == VK_MBUTTON) {

        /* mouse-based hotkeys; we are done */
        CLOG(L"Registered new mouse-based hotkey: %d", keyCombination);
        return;
    }

    /* keyboard-only hotkeys; use WinAPI */
    int mods = (0xF0000 & keyCombination) >> 16;
    if (!RegisterHotKey(_notifyWnd, keyCombination, mods, vk)) {
        CLOG(L"Failed to register hotkey [%d]\n"
            L"Mods: %d, VK: %d\n"
            L"Placing in hook list", keyCombination, mods, vk);
        _hookCombinations.insert(keyCombination);
        return;
    }

    CLOG(L"Registered new keyboard-based hotkey: %d", keyCombination);
}

bool HotkeyManager::Unregister(int keyCombination) {
    CLOG(L"Unregistering hotkey combination: %d", keyCombination);

    if (_keyCombinations.count(keyCombination) <= 0) {
        QCLOG(L"Hotkey combination [%d] was not previously registered",
            keyCombination);
        return false;
    }

    _keyCombinations.erase(keyCombination);
    if ((keyCombination >> 20) == 0) {
        /* This hotkey isn't mouse-based; unregister with Windows */
        if (!UnregisterHotKey(_notifyWnd, keyCombination)) {
            CLOG(L"Failed to unregister hotkey: %d", keyCombination);
            return false;
        }
    }
    return true;
}

LRESULT CALLBACK
HotkeyManager::KeyProc(int nCode, WPARAM wParam, LPARAM lParam) {
    if (nCode >= 0) {
        if (wParam == WM_KEYUP) {
            KBDLLHOOKSTRUCT *kbInfo = (KBDLLHOOKSTRUCT *) lParam;
            if ((kbInfo->vkCode == VK_LWIN || kbInfo->vkCode == VK_RWIN)
                && _fixWin) {
                /* WIN+Mouse combination used; we need to prevent the
                 * system from only seeing a WIN keypress (and usually
                 * popping up the start menu).  We simulate WIN+VK_NONAME. */
                SyntheticKeyboard::SimulateKeypress(VK_NONAME, false);
                _fixWin = false;
                return CallNextHookEx(NULL, nCode, wParam, lParam);
            }
        }

        if (_hookCombinations.size() <= 0) {
            return CallNextHookEx(NULL, nCode, wParam, lParam);
        }

        if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) {
            KBDLLHOOKSTRUCT *kbInfo = (KBDLLHOOKSTRUCT *) lParam;

            DWORD vk = kbInfo->vkCode;
            int mods = HotkeyManager::IsModifier(vk);

            if (mods) {
                _modifiers |= mods;
                return CallNextHookEx(NULL, nCode, wParam, lParam);
            } else {
                /* Is this an extended key? */
                int ext = (kbInfo->flags & 0x1) << EXT_OFFSET;
                int keys = _modifiers | ext | vk;
                if (_hookCombinations.count(keys) > 0) {
                    SendMessage(_notifyWnd, WM_HOTKEY,
                        keys, _modifiers >> MOD_OFFSET);
                    return (LRESULT) 1;
                }
            }
        }

        if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP) {
            KBDLLHOOKSTRUCT *kbInfo = (KBDLLHOOKSTRUCT *) lParam;
            int m = HotkeyManager::IsModifier(kbInfo->vkCode);
            if (m) {
                _modifiers ^= m;
            }
            return CallNextHookEx(NULL, nCode, wParam, lParam);
        }
    }

    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

LRESULT CALLBACK
HotkeyManager::MouseProc(int nCode, WPARAM wParam, LPARAM lParam) {
    if (nCode >= 0) {
        int mouseState = 0;
        MSLLHOOKSTRUCT *msInfo;

        switch (wParam) {

            case WM_LBUTTONDOWN:
                mouseState += VK_LBUTTON;
                break;

            case WM_MBUTTONDOWN:
                mouseState += VK_MBUTTON;
                break;

            case WM_RBUTTONDOWN:
                mouseState += VK_RBUTTON;
                break;

            case WM_XBUTTONDOWN: {
                msInfo = (MSLLHOOKSTRUCT *) lParam;

                int button = msInfo->mouseData >> 16 & 0xFFFF;
                if (button == 1)
                    mouseState += HKM_MOUSE_XB1;
                else if (button == 2)
                    mouseState += HKM_MOUSE_XB2;

                break;
            }

            case WM_MOUSEWHEEL: {
                /* Note: WM_MOUSEWHEEL on a hook is a little different, so we
                 * need to grab the wheel delta info from the lParam. */
                msInfo = (MSLLHOOKSTRUCT *) lParam;

                if ((int) msInfo->mouseData > 0) {
                    mouseState += HKM_MOUSE_WHUP;
                } else {
                    mouseState += HKM_MOUSE_WHDN;
                }

                break;
            }
        }

        if (mouseState > 0) {
            mouseState += Modifiers();

            if (_keyCombinations.count(mouseState) > 0) {
                PostMessage(_notifyWnd, WM_HOTKEY,
                    mouseState, mouseState & 0xF0000);

                if (mouseState & HKM_MOD_WIN) {
                    _fixWin = true; /* enable fix right before WIN goes up */
                }

                return 1; /* processed the message; eat it */
            }
        }
    }
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

LRESULT CALLBACK
HotkeyManager::LowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam) {
    return HotkeyManager::instance->MouseProc(nCode, wParam, lParam);
}

LRESULT CALLBACK
HotkeyManager::LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
    return HotkeyManager::instance->KeyProc(nCode, wParam, lParam);
}

int HotkeyManager::IsModifier(int vk) {
    switch (vk) {
    case VK_MENU:
    case VK_LMENU:
    case VK_RMENU:
        return HKM_MOD_ALT;

    case VK_CONTROL:
    case VK_LCONTROL:
    case VK_RCONTROL:
        return HKM_MOD_CTRL;

    case VK_SHIFT:
    case VK_LSHIFT:
    case VK_RSHIFT:
        return HKM_MOD_SHF;

    case VK_LWIN:
    case VK_RWIN:
        return HKM_MOD_WIN;
    }

    return 0;
}

bool HotkeyManager::IsMouseKey(int vk) {
    if (vk < 0x07 && vk != VK_CANCEL) {
        return true;
    }

    if (vk & (0xF << MOUSE_OFFSET)) {
        /* Has wheel or xbutton flags */
        return true;
    }

    return false;
}

int HotkeyManager::Modifiers() {
    int mods = 0;
    mods += (GetKeyState(VK_MENU) & 0x8000) << 1;
    mods += (GetKeyState(VK_CONTROL) & 0x8000) << 2;
    mods += (GetKeyState(VK_SHIFT) & 0x8000) << 3;
    mods += (GetKeyState(VK_LWIN) & 0x8000) << 4;
    mods += (GetKeyState(VK_RWIN) & 0x8000) << 4;
    return mods;
}

int HotkeyManager::ModifiersAsync() {
    int mods = 0;
    mods += (GetAsyncKeyState(VK_MENU) & 0x8000) << 1;
    mods += (GetAsyncKeyState(VK_CONTROL) & 0x8000) << 2;
    mods += (GetAsyncKeyState(VK_SHIFT) & 0x8000) << 3;
    mods += (GetAsyncKeyState(VK_LWIN) & 0x8000) << 4;
    mods += (GetAsyncKeyState(VK_RWIN) & 0x8000) << 4;
    return mods;
}

std::wstring HotkeyManager::HotkeysToModString(int combination,
    std::wstring separator) {

    std::wstring str = L"";
    if (combination & HKM_MOD_ALT) {
        str += VKToString(VK_MENU) + separator;
    }
    if (combination & HKM_MOD_CTRL) {
        str += VKToString(VK_CONTROL) + separator;
    }
    if (combination & HKM_MOD_SHF) {
        str += VKToString(VK_SHIFT) + separator;
    }
    if (combination & HKM_MOD_WIN) {
        str += L"Win" + separator;
    }

    return str;
}

std::wstring HotkeyManager::HotkeysToString(int combination,
    std::wstring separator) {

    std::wstring mods = HotkeysToModString(combination, separator);
    int vk = combination & 0xFF;

    std::wstring str;
    if (IsMouseKey(vk)) {
        str = MouseString(combination);
    } else {
        bool ext = (combination & 0x100) > 0;
        str = VKToString(vk, ext);
    }

    return mods + str;
}

std::wstring HotkeyManager::MouseString(int combination) {
    int vk = combination & 0xFF;
    if (vk > 0) {
        switch (vk) {
        case VK_LBUTTON:
            return L"Mouse 1";
        case VK_RBUTTON:
            return L"Mouse 2";
        case VK_MBUTTON:
            return L"Mouse 3";
        }
    }

    int flags = combination & (0xF << MOUSE_OFFSET);
    if (flags == HKM_MOUSE_XB1) {
        return L"Mouse 4";
    } else if (flags == HKM_MOUSE_XB2) {
        return L"Mouse 5";
    } else if (flags == HKM_MOUSE_WHUP) {
        return L"Mouse Wheel Up";
    } else if (flags == HKM_MOUSE_WHDN) {
        return L"Mouse Wheel Down";
    }

    return L"";
}

std::wstring HotkeyManager::VKToString(unsigned int vk, bool extendedKey) {
    /* GetKeyNameText expects the following:
     * 16-23: scan code
     *    24: extended key flag
     *    25: 'do not care' bit (don't distinguish between L/R keys) */
    unsigned int scanCode = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
    scanCode = scanCode << 16;

    if (vk == VK_RSHIFT) {
        /* For some reason, the right shift key ends up having its extended
         * key flag set and confuses GetKeyNameText. */
        extendedKey = false;
    }

    int extended = extendedKey ? 0x1 : 0x0;
    scanCode |= extended << 24;

    wchar_t buf[256] = {};
    GetKeyNameText(scanCode, buf, 256);
    return std::wstring(buf);
}<commit_msg>If a hardcoded string mapping exists, use it<commit_after>// Copyright (c) 2015, Matthew Malensek.
// Distributed under the BSD 2-Clause License (see LICENSE.txt for details)

#include "HotkeyManager.h"

#include "Logger.h"
#include "SyntheticKeyboard.h"

HotkeyManager *HotkeyManager::instance = NULL;

std::unordered_map<UINT, std::wstring> HotkeyManager::_vkStringMap = {
    { VK_SELECT, L"Select" },
    { VK_PRINT, L"Print" },
    { VK_EXECUTE, L"Execute" },
    { VK_SNAPSHOT, L"Print Screen" },
    { VK_HELP, L"Help" },
    { VK_SLEEP, L"Sleep" },
    { VK_BROWSER_BACK, L"Browser Back" },
    { VK_BROWSER_FORWARD, L"Browser Forward" },
    { VK_BROWSER_REFRESH, L"Browser Refresh" },
    { VK_BROWSER_STOP, L"Browser Stop" },
    { VK_BROWSER_SEARCH, L"Browser Search" },
    { VK_BROWSER_FAVORITES, L"Browser Favorites" },
    { VK_BROWSER_HOME, L"Browser Home" },
    { VK_VOLUME_MUTE, L"Mute" },
    { VK_VOLUME_DOWN, L"Volume Down" },
    { VK_VOLUME_UP, L"Volume Up" },
};

HotkeyManager *HotkeyManager::Instance() {
    return instance;
}

HotkeyManager *HotkeyManager::Instance(HWND notifyWnd) {
    if (instance == NULL) {
        instance = new HotkeyManager();
        instance->Hook();
        instance->_notifyWnd = notifyWnd;
    }

    return instance;
}

HotkeyManager::HotkeyManager() :
_fixWin(false) {

}

HotkeyManager::~HotkeyManager() {
    while (true) {
        auto i = _keyCombinations.begin();
        if (_keyCombinations.size() > 0) {
            Unregister(*i);
        } else {
            break;
        }
    }
    _hookCombinations.clear();

    Unhook();
}

void HotkeyManager::Shutdown() {
    delete instance;
    instance = NULL;
}

bool HotkeyManager::Hook() {
    _mouseHook = SetWindowsHookEx(WH_MOUSE_LL,
        LowLevelMouseProc, NULL, NULL);

    _keyHook = SetWindowsHookEx(WH_KEYBOARD_LL,
        LowLevelKeyboardProc, NULL, NULL);

    return _mouseHook && _keyHook;
}

bool HotkeyManager::Unhook() {
    BOOL unMouse = UnhookWindowsHookEx(_mouseHook);
    BOOL unKey = UnhookWindowsHookEx(_keyHook);
    return unMouse && unKey;
}

void HotkeyManager::Register(int keyCombination) {
    if (_keyCombinations.count(keyCombination) > 0) {
        CLOG(L"Hotkey combination [%d] already registered", keyCombination);
        return;
    } else {
        _keyCombinations.insert(keyCombination);
    }

    /* get VK_* value; */
    int vk = 0xFF & keyCombination;

    if ((keyCombination >> 20) > 0 /* uses a HKM_MOUSE_* flag */
        || vk == VK_LBUTTON
        || vk == VK_RBUTTON
        || vk == VK_MBUTTON) {

        /* mouse-based hotkeys; we are done */
        CLOG(L"Registered new mouse-based hotkey: %d", keyCombination);
        return;
    }

    /* keyboard-only hotkeys; use WinAPI */
    int mods = (0xF0000 & keyCombination) >> 16;
    if (!RegisterHotKey(_notifyWnd, keyCombination, mods, vk)) {
        CLOG(L"Failed to register hotkey [%d]\n"
            L"Mods: %d, VK: %d\n"
            L"Placing in hook list", keyCombination, mods, vk);
        _hookCombinations.insert(keyCombination);
        return;
    }

    CLOG(L"Registered new keyboard-based hotkey: %d", keyCombination);
}

bool HotkeyManager::Unregister(int keyCombination) {
    CLOG(L"Unregistering hotkey combination: %d", keyCombination);

    if (_keyCombinations.count(keyCombination) <= 0) {
        QCLOG(L"Hotkey combination [%d] was not previously registered",
            keyCombination);
        return false;
    }

    _keyCombinations.erase(keyCombination);
    if ((keyCombination >> 20) == 0) {
        /* This hotkey isn't mouse-based; unregister with Windows */
        if (!UnregisterHotKey(_notifyWnd, keyCombination)) {
            CLOG(L"Failed to unregister hotkey: %d", keyCombination);
            return false;
        }
    }
    return true;
}

LRESULT CALLBACK
HotkeyManager::KeyProc(int nCode, WPARAM wParam, LPARAM lParam) {
    if (nCode >= 0) {
        if (wParam == WM_KEYUP) {
            KBDLLHOOKSTRUCT *kbInfo = (KBDLLHOOKSTRUCT *) lParam;
            if ((kbInfo->vkCode == VK_LWIN || kbInfo->vkCode == VK_RWIN)
                && _fixWin) {
                /* WIN+Mouse combination used; we need to prevent the
                 * system from only seeing a WIN keypress (and usually
                 * popping up the start menu).  We simulate WIN+VK_NONAME. */
                SyntheticKeyboard::SimulateKeypress(VK_NONAME, false);
                _fixWin = false;
                return CallNextHookEx(NULL, nCode, wParam, lParam);
            }
        }

        if (_hookCombinations.size() <= 0) {
            return CallNextHookEx(NULL, nCode, wParam, lParam);
        }

        if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) {
            KBDLLHOOKSTRUCT *kbInfo = (KBDLLHOOKSTRUCT *) lParam;

            DWORD vk = kbInfo->vkCode;
            int mods = HotkeyManager::IsModifier(vk);

            if (mods) {
                _modifiers |= mods;
                return CallNextHookEx(NULL, nCode, wParam, lParam);
            } else {
                /* Is this an extended key? */
                int ext = (kbInfo->flags & 0x1) << EXT_OFFSET;
                int keys = _modifiers | ext | vk;
                if (_hookCombinations.count(keys) > 0) {
                    SendMessage(_notifyWnd, WM_HOTKEY,
                        keys, _modifiers >> MOD_OFFSET);
                    return (LRESULT) 1;
                }
            }
        }

        if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP) {
            KBDLLHOOKSTRUCT *kbInfo = (KBDLLHOOKSTRUCT *) lParam;
            int m = HotkeyManager::IsModifier(kbInfo->vkCode);
            if (m) {
                _modifiers ^= m;
            }
            return CallNextHookEx(NULL, nCode, wParam, lParam);
        }
    }

    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

LRESULT CALLBACK
HotkeyManager::MouseProc(int nCode, WPARAM wParam, LPARAM lParam) {
    if (nCode >= 0) {
        int mouseState = 0;
        MSLLHOOKSTRUCT *msInfo;

        switch (wParam) {

            case WM_LBUTTONDOWN:
                mouseState += VK_LBUTTON;
                break;

            case WM_MBUTTONDOWN:
                mouseState += VK_MBUTTON;
                break;

            case WM_RBUTTONDOWN:
                mouseState += VK_RBUTTON;
                break;

            case WM_XBUTTONDOWN: {
                msInfo = (MSLLHOOKSTRUCT *) lParam;

                int button = msInfo->mouseData >> 16 & 0xFFFF;
                if (button == 1)
                    mouseState += HKM_MOUSE_XB1;
                else if (button == 2)
                    mouseState += HKM_MOUSE_XB2;

                break;
            }

            case WM_MOUSEWHEEL: {
                /* Note: WM_MOUSEWHEEL on a hook is a little different, so we
                 * need to grab the wheel delta info from the lParam. */
                msInfo = (MSLLHOOKSTRUCT *) lParam;

                if ((int) msInfo->mouseData > 0) {
                    mouseState += HKM_MOUSE_WHUP;
                } else {
                    mouseState += HKM_MOUSE_WHDN;
                }

                break;
            }
        }

        if (mouseState > 0) {
            mouseState += Modifiers();

            if (_keyCombinations.count(mouseState) > 0) {
                PostMessage(_notifyWnd, WM_HOTKEY,
                    mouseState, mouseState & 0xF0000);

                if (mouseState & HKM_MOD_WIN) {
                    _fixWin = true; /* enable fix right before WIN goes up */
                }

                return 1; /* processed the message; eat it */
            }
        }
    }
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

LRESULT CALLBACK
HotkeyManager::LowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam) {
    return HotkeyManager::instance->MouseProc(nCode, wParam, lParam);
}

LRESULT CALLBACK
HotkeyManager::LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
    return HotkeyManager::instance->KeyProc(nCode, wParam, lParam);
}

int HotkeyManager::IsModifier(int vk) {
    switch (vk) {
    case VK_MENU:
    case VK_LMENU:
    case VK_RMENU:
        return HKM_MOD_ALT;

    case VK_CONTROL:
    case VK_LCONTROL:
    case VK_RCONTROL:
        return HKM_MOD_CTRL;

    case VK_SHIFT:
    case VK_LSHIFT:
    case VK_RSHIFT:
        return HKM_MOD_SHF;

    case VK_LWIN:
    case VK_RWIN:
        return HKM_MOD_WIN;
    }

    return 0;
}

bool HotkeyManager::IsMouseKey(int vk) {
    if (vk < 0x07 && vk != VK_CANCEL) {
        return true;
    }

    if (vk & (0xF << MOUSE_OFFSET)) {
        /* Has wheel or xbutton flags */
        return true;
    }

    return false;
}

int HotkeyManager::Modifiers() {
    int mods = 0;
    mods += (GetKeyState(VK_MENU) & 0x8000) << 1;
    mods += (GetKeyState(VK_CONTROL) & 0x8000) << 2;
    mods += (GetKeyState(VK_SHIFT) & 0x8000) << 3;
    mods += (GetKeyState(VK_LWIN) & 0x8000) << 4;
    mods += (GetKeyState(VK_RWIN) & 0x8000) << 4;
    return mods;
}

int HotkeyManager::ModifiersAsync() {
    int mods = 0;
    mods += (GetAsyncKeyState(VK_MENU) & 0x8000) << 1;
    mods += (GetAsyncKeyState(VK_CONTROL) & 0x8000) << 2;
    mods += (GetAsyncKeyState(VK_SHIFT) & 0x8000) << 3;
    mods += (GetAsyncKeyState(VK_LWIN) & 0x8000) << 4;
    mods += (GetAsyncKeyState(VK_RWIN) & 0x8000) << 4;
    return mods;
}

std::wstring HotkeyManager::HotkeysToModString(int combination,
    std::wstring separator) {

    std::wstring str = L"";
    if (combination & HKM_MOD_ALT) {
        str += VKToString(VK_MENU) + separator;
    }
    if (combination & HKM_MOD_CTRL) {
        str += VKToString(VK_CONTROL) + separator;
    }
    if (combination & HKM_MOD_SHF) {
        str += VKToString(VK_SHIFT) + separator;
    }
    if (combination & HKM_MOD_WIN) {
        str += L"Win" + separator;
    }

    return str;
}

std::wstring HotkeyManager::HotkeysToString(int combination,
    std::wstring separator) {

    std::wstring mods = HotkeysToModString(combination, separator);
    int vk = combination & 0xFF;

    std::wstring str;
    if (IsMouseKey(vk)) {
        str = MouseString(combination);
    } else {
        bool ext = (combination & 0x100) > 0;
        str = VKToString(vk, ext);
    }

    return mods + str;
}

std::wstring HotkeyManager::MouseString(int combination) {
    int vk = combination & 0xFF;
    if (vk > 0) {
        switch (vk) {
        case VK_LBUTTON:
            return L"Mouse 1";
        case VK_RBUTTON:
            return L"Mouse 2";
        case VK_MBUTTON:
            return L"Mouse 3";
        }
    }

    int flags = combination & (0xF << MOUSE_OFFSET);
    if (flags == HKM_MOUSE_XB1) {
        return L"Mouse 4";
    } else if (flags == HKM_MOUSE_XB2) {
        return L"Mouse 5";
    } else if (flags == HKM_MOUSE_WHUP) {
        return L"Mouse Wheel Up";
    } else if (flags == HKM_MOUSE_WHDN) {
        return L"Mouse Wheel Down";
    }

    return L"";
}

std::wstring HotkeyManager::VKToString(unsigned int vk, bool extendedKey) {
    if (_vkStringMap.find(vk) != _vkStringMap.end()) {
        return _vkStringMap[vk];
    }
    /* GetKeyNameText expects the following:
     * 16-23: scan code
     *    24: extended key flag
     *    25: 'do not care' bit (don't distinguish between L/R keys) */
    unsigned int scanCode = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
    scanCode = scanCode << 16;

    if (vk == VK_RSHIFT) {
        /* For some reason, the right shift key ends up having its extended
         * key flag set and confuses GetKeyNameText. */
        extendedKey = false;
    }

    int extended = extendedKey ? 0x1 : 0x0;
    scanCode |= extended << 24;

    wchar_t buf[256] = {};
    GetKeyNameText(scanCode, buf, 256);
    return std::wstring(buf);
}<|endoftext|>


<commit_before>/** \mainpage

# Overview

This library consists of the modules:
- **Vector space operations**. This module defines the algorithms:
    - add
    - subtract
    - multiply
    - divide
- **Euclidean space operations**. This module defines the algorithms:
    - norm
    - squared_norm
    - dot
    - distance

Go to the **Modules** pages to learn more about each module.

# Design and Rationale

This library deals with arithmetic operations on mathematical vectors.
C++ libraries that deals with this can usually be divided into two categories:
-# Those that implement a vector class and overload its arithmetic operators.
   Examples of this are: `std::valarray`, `Eigen`, `Blitz++`, `OpenCV`, `GLM`.
-# Those that implement general algorithms that work on any container or
   range of iterators. An example of this is the standard library and the headers
   `<algorithm>` and `<numeric>`.

This library follows the second approach. It implements some common arithmetic
algorithms that are missing in the standard library.

The two approaches have different advantages and disadvantages:
-# The advantage of the first approach is that the syntax can be very concise,
   when you overload the arithmetic operators to act directly on the vector class.
   This approach can also have performance advantages, since it couples the
   algorithm to the data structure. It is then possible to use techniques
   like *expression templates* or *aligned memory* and *vectorization*.
-# The advantage of the second approach is that it allows *generic programming*.
   Code that uses such libraries is more general since it is less coupled to a
   specific data structure. This is good for readability and reuse of the code.
*/

#pragma once

#include "algorithms_range.hpp"
#include "algorithms_container.hpp"

#include "vector_space_range.hpp"
#include "vector_space_container.hpp"

#include "euclidean_space_range.hpp"
#include "euclidean_space_container.hpp"
<commit_msg>Add examples to documentation<commit_after>/** \mainpage

# Overview

This library consists of the modules:
- **Vector space operations**. This module defines the algorithms:
    - add
    - subtract
    - multiply
    - divide
- **Euclidean space operations**. This module defines the algorithms:
    - norm
    - squared_norm
    - dot
    - distance

Go to the **Modules** pages to learn more about each module.

# Design and Rationale

This library deals with arithmetic operations on mathematical vectors.
C++ libraries that deals with this can usually be divided into two categories:
-# Those that implement a vector class and overload its arithmetic operators.
   Examples of this are: `std::valarray`, `Eigen`, `Blitz++`, `OpenCV`, `GLM`.
-# Those that implement general algorithms that work on any container or
   range of iterators. An example of this is the standard library and the headers
   `<algorithm>` and `<numeric>`.

This library follows the second approach. It implements some common arithmetic
algorithms that are missing in the standard library.

The two approaches have different advantages and disadvantages:
-# The advantage of the first approach is that the syntax can be very concise,
   when you overload the arithmetic operators to act directly on the vector class.
   This approach can also have performance advantages, since it couples the
   algorithm to the data structure. It is then possible to use techniques
   like *expression templates* or *aligned memory* and *vectorization*.
-# The advantage of the second approach is that it allows *generic programming*.
   Code that uses such libraries is more general since it is less coupled to a
   specific data structure. This is good for readability and reuse of the code.

# Examples

The algorithms can be used on arbitrary containers like this:
```
// Returns the projection of a on b.
std::vector<float> project(const std::vector<float>& a, const std::vector<float>& b)
{
    const auto scaling = dot(a, b) / dot(b, b);
    auto projection = b;
    multiply(scaling, b, projection);
    return projection;
}

// Normalize a gray scale image to be between 0 and 1.
my_image normalized_image(const my_image& image)
{
    if (image.empty())
        return image;

    const auto bounds  = std::minmax_element(image.begin(), image.end());
    const auto minimum = *bounds.first;
    const auto maximum = *bounds.second;
    const auto diff    = maximum - minium;

    if (diff == 0)
        return image;

    auto normalized_image = image;
    subtract(normalized_image, minimum, normalized_image);
    divide(normalized_image, diff, normalized_image);

    return normalized_image;
}
```
*/

#pragma once

#include "algorithms_range.hpp"
#include "algorithms_container.hpp"

#include "vector_space_range.hpp"
#include "vector_space_container.hpp"

#include "euclidean_space_range.hpp"
#include "euclidean_space_container.hpp"
<|endoftext|>


<commit_before><commit_msg>#54 Adaptation for test code<commit_after><|endoftext|>


<commit_before>/**
 * The MIT License
 *
 * Copyright (C) 2017 Kiyofumi Kondoh
 *
 * 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 "stdafx.h"

#include "include/cef_client.h"
#include "include/wrapper/cef_helpers.h"

//#define USE_CEF_VIEWS

#if defined(USE_CEF_VIEWS)
#include "include/cef_command_line.h"
#include "include/views/cef_browser_view.h"
#include "include/views/cef_window.h"

namespace {

class WindowDelegate
    : public CefWindowDelegate
{
public:
    explicit WindowDelegate(CefRefPtr<CefBrowserView> browser_view)
        : browser_view_(browser_view)
    {
    }

private:
    CefRefPtr<CefBrowserView>   browser_view_;

    IMPLEMENT_REFCOUNTING(WindowDelegate);
    DISALLOW_COPY_AND_ASSIGN(WindowDelegate);
};

} // namespace {
#endif // defined(USE_CEF_VIEWS)

static
bool
isUseViews(void)
{
    bool use_views = false;

#if defined(USE_CEF_VIEWS)
#if defined(OS_WIN) || defined(OS_LINUX)
    CefRefPtr<CefCommandLine> command_line = CefCommandLine::GetGlobalCommandLine();

    use_views = command_line->HasSwitch("use-views");
#endif // defined(OS_WIN) || defined(OS_LINUX)
#endif // defined(USE_CEF_VIEWS)

    return use_views;
}

void createBrowser(
    CefRefPtr<CefClient> client
    , const CefString& startup_url
    , const CefBrowserSettings& settings
)
{
    CEF_REQUIRE_UI_THREAD();

    const bool use_views = isUseViews();

    if ( use_views )
    {
#if defined(USE_CEF_VIEWS)
        CefRefPtr<CefBrowserView> browser_view =
            CefBrowserView::CreateBrowserView(
                client, startup_url, settings, NULL, NULL
                );
        CefWindow::CreateTopLevelWindow( new WindowDelegate(browser_view) );
#endif // defined(USE_CEF_VIEWS)
    }
    else
    {
        CefWindowInfo   window_info;

#if defined(OS_WIN)
        CefWindowHandle parent = NULL;
        const CefString kName = "test_cef";
        window_info.SetAsPopup( parent, kName );
#endif // defined(OS_WIN)

        CefRefPtr<CefRequestContext> request_context = NULL;

        CefBrowserHost::CreateBrowser(
            window_info, client, startup_url, settings, request_context
            );

    }

}

<commit_msg>add window size at startup<commit_after>/**
 * The MIT License
 *
 * Copyright (C) 2017 Kiyofumi Kondoh
 *
 * 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 "stdafx.h"

#include "include/cef_client.h"
#include "include/wrapper/cef_helpers.h"

//#define USE_CEF_VIEWS

#if defined(USE_CEF_VIEWS)
#include "include/cef_command_line.h"
#include "include/views/cef_browser_view.h"
#include "include/views/cef_window.h"

namespace {

class WindowDelegate
    : public CefWindowDelegate
{
public:
    explicit WindowDelegate(CefRefPtr<CefBrowserView> browser_view)
        : browser_view_(browser_view)
    {
    }

private:
    CefRefPtr<CefBrowserView>   browser_view_;

    IMPLEMENT_REFCOUNTING(WindowDelegate);
    DISALLOW_COPY_AND_ASSIGN(WindowDelegate);
};

} // namespace {
#endif // defined(USE_CEF_VIEWS)

static
bool
isUseViews(void)
{
    bool use_views = false;

#if defined(USE_CEF_VIEWS)
#if defined(OS_WIN) || defined(OS_LINUX)
    CefRefPtr<CefCommandLine> command_line = CefCommandLine::GetGlobalCommandLine();

    use_views = command_line->HasSwitch("use-views");
#endif // defined(OS_WIN) || defined(OS_LINUX)
#endif // defined(USE_CEF_VIEWS)

    return use_views;
}

void createBrowser(
    CefRefPtr<CefClient> client
    , const CefString& startup_url
    , const CefBrowserSettings& settings
)
{
    CEF_REQUIRE_UI_THREAD();

    const bool use_views = isUseViews();

    if ( use_views )
    {
#if defined(USE_CEF_VIEWS)
        CefRefPtr<CefBrowserView> browser_view =
            CefBrowserView::CreateBrowserView(
                client, startup_url, settings, NULL, NULL
                );
        CefWindow::CreateTopLevelWindow( new WindowDelegate(browser_view) );
#endif // defined(USE_CEF_VIEWS)
    }
    else
    {
        CefWindowInfo   window_info;

#if defined(OS_WIN)
        CefWindowHandle parent = NULL;
        const CefString kName = "test_cef";
        window_info.SetAsPopup( parent, kName );

        window_info.width = 1280;
        window_info.height = 720;
#endif // defined(OS_WIN)

        CefRefPtr<CefRequestContext> request_context = NULL;

        CefBrowserHost::CreateBrowser(
            window_info, client, startup_url, settings, request_context
            );

    }

}

<|endoftext|>


<commit_before>#include "GolfBall.hpp"
#include "../Resources.hpp"
#include "Default3D.vert.hpp"
#include "Default3D.geom.hpp"
#include "Default3D.frag.hpp"
#include "glm\gtc\constants.hpp"
#include "../Util/Log.hpp"

GolfBall::GolfBall(BallType ballType, TerrainObject* terrain) : Object() {
    active = false;
	if (ballType == TWOPIECE){
		restitution = 0.78f;
	}
	else {
		restitution = 0.68f;
	}
	this->terrain = terrain;
	groundLevel = this->terrain->Position().y;

	modelGeometry = new Geometry::Model("Resources/Models/rock/Rock.bin");
	std::string diffusePath = "Resources/Models/rock/diffuse.tga";
	std::string normalPath = "Resources/Models/rock/normal.tga";
	std::string specularPath = "Resources/Models/rock/specular.tga";
	modelObject = new ModelObject(modelGeometry, diffusePath, normalPath, specularPath);
	modelObject->SetPosition(2.f, 0.f, 0.f);
	modelObject->SetScale(glm::vec3(0.01f, 0.01f, 0.01f));

    /// @todo Mass based on explosive material.
    mass = 0.0459f;
    this->ballType = ballType;

	this->sphere.position = modelObject->Position();
	SetRadius(0.0214f);
}

GolfBall::~GolfBall() {
	delete modelObject;
	delete modelGeometry;
}

void GolfBall::Update(double time, const glm::vec3& wind) {
    if (active) {
		modelObject->Move(static_cast<float>(time)*velocity);
		sphere.position = modelObject->Position();

		float horizontal = -static_cast<float>(time)*angularVelocity.x*(glm::pi<float>());
		float vertical = -static_cast<float>(time)*angularVelocity.y*(glm::pi<float>());
		float tilt = -static_cast<float>(time)*angularVelocity.z*(glm::pi<float>());

		if ((sphere.position.y - sphere.radius) < groundLevel){
			float mu = 0.11f;
			glm::vec3 surfaceNormal = glm::vec3(0.f, 1.f, 0.f);

			modelObject->SetPosition(sphere.position.x, groundLevel + sphere.radius, sphere.position.z);

			glm::vec3 eRoh = surfaceNormal;
			glm::vec3 er = glm::normalize(glm::cross(-surfaceNormal, eRoh));
			glm::vec3 velocityCrosseRoh = glm::normalize(cross(velocity, eRoh));
			glm::vec3 eNormal = glm::cross(glm::normalize(glm::cross(velocityCrosseRoh, eRoh)), eRoh);
			glm::vec3 muNormal = mu*eNormal;
			glm::vec3 velocityProjectedOneRoh = glm::dot(velocity, eRoh)*eRoh;
			glm::vec3 velocityAfterCollisionProjectedOneRoh = restitution*velocityProjectedOneRoh;
			velocity = velocity + (velocityAfterCollisionProjectedOneRoh - velocityProjectedOneRoh)*(eRoh);
			//angularVelocity = ((mass*sphere.radius)/(2*sphere.radius))*(velocityAfterCollisionProjectedOneRoh - velocityProjectedOneRoh)*(glm::cross(glm::cross(eRoh,eNormal),eNormal));
		}

		modelObject->Rotate(horizontal, vertical, tilt);

        float v = glm::length(velocity);
		float w = glm::length(angularVelocity);
		float u = glm::length(velocity - wind);
		float Cm = (sqrt(1.f + 0.31f*(v/w))-1.f)/20.f;
		float Fm = 0.5f*Cm*1.23*area*u*u;

        glm::vec3 eU = (velocity - wind) / u;

        /// Calculate drive force.
        float cD;
        if (ballType == TWOPIECE) {
            cD = v < 65.f ? -0.0051f * v + 0.53f : 0.21f;
        } else {
            cD = v < 60.f ? -0.0084f * v + 0.73f : 0.22f;
        }
        glm::vec3 dragForce = -0.5f * 1.23f * area * cD * u * u * eU;

		glm::vec3 magnusForce = Fm*(cross(eU, (angularVelocity / w)));

        // Calculate gravitational force.
        glm::vec3 gravitationForce = glm::vec3(0.f, mass * -9.82f, 0.f);

        glm::vec3 acceleration = (dragForce + magnusForce + gravitationForce) / mass;
        velocity += acceleration * static_cast<float>(time);
    }
}

void GolfBall::Render(Camera* camera, const glm::vec2& screenSize) const{
	modelObject->Render(camera, screenSize);
}

void GolfBall::Strike(ClubType club, glm::vec3 clubVelocity) {
	active = true;
	float translatedVelocity = sqrt(pow(clubVelocity.x, 2) + pow(clubVelocity.z, 2));

	angularVelocity = glm::vec3(0.f, 0.f, -360.f*(5.f / 7.f)*(sin(club.loft)*translatedVelocity));
	float massCoefficient = club.mass / (club.mass + mass);
	float velocitybx = translatedVelocity*massCoefficient*((1 + restitution)*pow(cos(club.loft), 2) + (2.f / 7.f)*pow(sin(club.loft), 2));
	float velocityby = translatedVelocity*massCoefficient*sin(club.loft)*cos(club.loft)*((5.f / 7.f) + restitution);

	float horizontalAngle = atan(clubVelocity.x / clubVelocity.z);
	velocity = glm::vec3(velocitybx*sin(horizontalAngle), velocityby, velocitybx*cos(horizontalAngle));

}

void GolfBall::SetRadius(float radius) {
	sphere.radius = radius;
    SetScale(2.f * glm::vec3(radius, radius, radius));
    area = glm::pi<float>() * radius * radius;
}
<commit_msg>Work on collisions<commit_after>#include "GolfBall.hpp"
#include "../Resources.hpp"
#include "Default3D.vert.hpp"
#include "Default3D.geom.hpp"
#include "Default3D.frag.hpp"
#include "glm\gtc\constants.hpp"
#include "../Util/Log.hpp"

GolfBall::GolfBall(BallType ballType, TerrainObject* terrain) : Object() {
    active = false;
	if (ballType == TWOPIECE){
		restitution = 0.78f;
	}
	else {
		restitution = 0.68f;
	}
	this->terrain = terrain;
	groundLevel = this->terrain->Position().y;

	modelGeometry = new Geometry::Model("Resources/Models/rock/Rock.bin");
	std::string diffusePath = "Resources/Models/rock/diffuse.tga";
	std::string normalPath = "Resources/Models/rock/normal.tga";
	std::string specularPath = "Resources/Models/rock/specular.tga";
	modelObject = new ModelObject(modelGeometry, diffusePath, normalPath, specularPath);
	modelObject->SetPosition(2.f, 0.f, 0.f);
	modelObject->SetScale(glm::vec3(0.01f, 0.01f, 0.01f));

    /// @todo Mass based on explosive material.
    mass = 0.0459f;
    this->ballType = ballType;

	this->sphere.position = modelObject->Position();
	SetRadius(0.0214f);
}

GolfBall::~GolfBall() {
	delete modelObject;
	delete modelGeometry;
}

void GolfBall::Update(double time, const glm::vec3& wind) {
    if (active) {
		modelObject->Move(static_cast<float>(time)*velocity);
		sphere.position = modelObject->Position();

		float horizontal = -static_cast<float>(time)*angularVelocity.x*(glm::pi<float>());
		float vertical = -static_cast<float>(time)*angularVelocity.y*(glm::pi<float>());
		float tilt = -static_cast<float>(time)*angularVelocity.z*(glm::pi<float>());

		if ((sphere.position.y - sphere.radius) < groundLevel){
			float mu = 0.30f;
			glm::vec3 surfaceNormal = glm::vec3(0.f, 1.f, 0.f);

			modelObject->SetPosition(sphere.position.x, groundLevel + sphere.radius, sphere.position.z);

			glm::vec3 eRoh = normalize(surfaceNormal);
			glm::vec3 velocityCrosseRoh = glm::normalize(glm::cross(velocity, eRoh));
			glm::vec3 eNormal = glm::normalize(glm::cross(velocityCrosseRoh, eRoh));
			glm::vec3 muNormal = mu*eNormal;
			glm::vec3 velocityProjectedOneRoh = glm::dot(velocity, eRoh)*eRoh;
			glm::vec3 velocityAfterCollisionProjectedOneRoh = restitution*velocityProjectedOneRoh;
			glm::vec3 eRotation = glm::normalize(glm::cross(-surfaceNormal, eRoh));
			velocity = velocity + (velocityAfterCollisionProjectedOneRoh - velocityProjectedOneRoh)*(eRoh + muNormal);
			//angularVelocity = ((mass*sphere.radius)/(2*sphere.radius))*(velocityAfterCollisionProjectedOneRoh - velocityProjectedOneRoh)*(glm::cross(glm::cross(eRoh,eNormal),eNormal));
		}

		modelObject->Rotate(horizontal, vertical, tilt);

        float v = glm::length(velocity);
		float w = glm::length(angularVelocity);
		float u = glm::length(velocity - wind);
		float Cm = (sqrt(1.f + 0.31f*(v/w))-1.f)/20.f;
		float Fm = 0.5f*Cm*1.23*area*u*u;

        glm::vec3 eU = (velocity - wind) / u;

        /// Calculate drive force.
        float cD;
        if (ballType == TWOPIECE) {
            cD = v < 65.f ? -0.0051f * v + 0.53f : 0.21f;
        } else {
            cD = v < 60.f ? -0.0084f * v + 0.73f : 0.22f;
        }
        glm::vec3 dragForce = -0.5f * 1.23f * area * cD * u * u * eU;

		glm::vec3 magnusForce = Fm*(cross(eU, (angularVelocity / w)));

        // Calculate gravitational force.
        glm::vec3 gravitationForce = glm::vec3(0.f, mass * -9.82f, 0.f);

        glm::vec3 acceleration = (dragForce + magnusForce + gravitationForce) / mass;
        velocity += acceleration * static_cast<float>(time);
    }
}

void GolfBall::Render(Camera* camera, const glm::vec2& screenSize) const{
	modelObject->Render(camera, screenSize);
}

void GolfBall::Strike(ClubType club, glm::vec3 clubVelocity) {
	active = true;
	float translatedVelocity = sqrt(pow(clubVelocity.x, 2) + pow(clubVelocity.z, 2));

	angularVelocity = glm::vec3(0.f, 0.f, -360.f*(5.f / 7.f)*(sin(club.loft)*translatedVelocity));
	float massCoefficient = club.mass / (club.mass + mass);
	float velocitybx = translatedVelocity*massCoefficient*((1 + restitution)*pow(cos(club.loft), 2) + (2.f / 7.f)*pow(sin(club.loft), 2));
	float velocityby = translatedVelocity*massCoefficient*sin(club.loft)*cos(club.loft)*((5.f / 7.f) + restitution);

	float horizontalAngle = atan(clubVelocity.x / clubVelocity.z);
	velocity = glm::vec3(velocitybx*sin(horizontalAngle), velocityby, velocitybx*cos(horizontalAngle));

}

void GolfBall::SetRadius(float radius) {
	sphere.radius = radius;
    SetScale(2.f * glm::vec3(radius, radius, radius));
    area = glm::pi<float>() * radius * radius;
}
<|endoftext|>


<commit_before><commit_msg>Fix polarity usage of clock status bits<commit_after><|endoftext|>


<commit_before>// Copyright (C) 2016 Elviss Strazdins
// This file is part of the Ouzel engine.

#include <functional>
#include "Button.h"
#include "Engine.h"
#include "Sprite.h"
#include "EventHander.h"
#include "EventDispatcher.h"
#include "Layer.h"
#include "Camera.h"
#include "Utils.h"

namespace ouzel
{
    std::shared_ptr<Button> Button::create(const std::string& normal, const std::string& selected, const std::string& pressed, const std::string& disabled, const std::function<void(const VoidPtr&)>& callback)
    {
        std::shared_ptr<Button> result = std::make_shared<Button>();

        if (!result->init(normal, selected, pressed, disabled, callback))
        {
            result.reset();
        }

        return result;
    }

    Button::Button()
    {

    }

    Button::~Button()
    {
        Engine::getInstance()->getEventDispatcher()->removeEventHandler(_eventHandler);
    }

    bool Button::init(const std::string& normal, const std::string& selected, const std::string& pressed, const std::string& disabled, const std::function<void(const VoidPtr&)>& callback)
    {
        _eventHandler = std::make_shared<EventHandler>();

        _eventHandler->mouseHandler = std::bind(&Button::handleMouse, this, std::placeholders::_1, std::placeholders::_2);
        _eventHandler->touchHandler = std::bind(&Button::handleTouch, this, std::placeholders::_1, std::placeholders::_2);
        _eventHandler->gamepadHandler = std::bind(&Button::handleGamepad, this, std::placeholders::_1, std::placeholders::_2);

        Engine::getInstance()->getEventDispatcher()->addEventHandler(_eventHandler);

        if (!normal.empty())
        {
            _normalSprite = std::make_shared<Sprite>();
            if (_normalSprite->initFromFile(normal))
            {
                _boundingBox = _normalSprite->getBoundingBox();
                addChild(_normalSprite);
            }
        }

        if (!selected.empty())
        {
            _selectedSprite = std::make_shared<Sprite>();
            if (_selectedSprite->initFromFile(selected))
            {
                addChild(_selectedSprite);
            }
        }

        if (!pressed.empty())
        {
            _pressedSprite = std::make_shared<Sprite>();
            if (_pressedSprite->initFromFile(pressed))
            {
                addChild(_pressedSprite);
            }
        }

        if (!disabled.empty())
        {
            _disabledSprite = std::make_shared<Sprite>();
            if (_disabledSprite->initFromFile(disabled))
            {
                addChild(_disabledSprite);
            }
        }

        _callback = callback;

        updateSprite();

        return true;
    }

    void Button::setEnabled(bool enabled)
    {
        Widget::setEnabled(enabled);

        _selected = false;
        _pointerOver = false;
        _pressed = false;

        updateSprite();
    }

    bool Button::handleMouse(const MouseEventPtr& event, const VoidPtr& sender)
    {
        OUZEL_UNUSED(sender);

        if (!_enabled) return true;

        switch (event->type)
        {
            case Event::Type::MOUSE_DOWN:
            {
                if (_pointerOver)
                {
                    _pressed = true;
                    updateSprite();
                }
                break;
            }
            case Event::Type::MOUSE_UP:
            {
                if (_pointerOver && _pressed)
                {
                    _pressed = false;
                    updateSprite();

                    if (_callback)
                    {
                        _callback(shared_from_this());
                    }
                }
                break;
            }
            case Event::Type::MOUSE_MOVE:
            {
                if (LayerPtr layer = _layer.lock())
                {
                    Vector2 worldLocation = layer->getCamera()->screenToWorldLocation(event->position);
                    checkPointer(worldLocation);
                    updateSprite();
                }
                break;
            }
            default:
                break;
        }

        return true;
    }

    bool Button::handleTouch(const TouchEventPtr& event, const VoidPtr& sender)
    {
        OUZEL_UNUSED(sender);

        if (!_enabled) return true;

        switch (event->type)
        {
            case Event::Type::TOUCH_BEGIN:
            {
                if (LayerPtr layer = _layer.lock())
                {
                    Vector2 worldLocation = layer->getCamera()->screenToWorldLocation(event->position);
                    checkPointer(worldLocation);

                    if (_pointerOver)
                    {
                        _pressed = true;
                    }

                    updateSprite();
                }
                break;
            }
            case Event::Type::TOUCH_MOVE:
            {
                if (LayerPtr layer = _layer.lock())
                {
                    Vector2 worldLocation = layer->getCamera()->screenToWorldLocation(event->position);
                    checkPointer(worldLocation);
                    updateSprite();
                }
                break;
            }
            case Event::Type::TOUCH_END:
            {
                if (_pointerOver && _pressed)
                {
                    if (_callback)
                    {
                        _callback(shared_from_this());
                    }
                }

                _pressed = false;
                updateSprite();
                break;
            }
            default:
                break;
        }

        return true;
    }

    bool Button::handleGamepad(const GamepadEventPtr& event, const VoidPtr& sender)
    {
        OUZEL_UNUSED(event);
        OUZEL_UNUSED(sender);

        return true;
    }

    void Button::checkPointer(const Vector2& worldLocation)
    {
        if (pointOn(worldLocation))
        {
            _pointerOver = true;
        }
        else
        {
            _pointerOver = false;
        }
    }

    void Button::updateSprite()
    {
        if (_normalSprite) _normalSprite->setVisible(false);
        if (_selectedSprite) _selectedSprite->setVisible(false);
        if (_pressedSprite) _pressedSprite->setVisible(false);
        if (_disabledSprite) _disabledSprite->setVisible(false);

        if (_enabled)
        {
            if (_pressed && _pointerOver && _pressedSprite)
            {
                _pressedSprite->setVisible(true);
            }
            else if (_selected && _selectedSprite)
            {
                _selectedSprite->setVisible(true);
            }
            else if (_normalSprite)
            {
                _normalSprite->setVisible(true);
            }
        }
        else if (_disabledSprite)
        {
            _disabledSprite->setVisible(true);
        }
    }
}
<commit_msg>Change the order of updating Button<commit_after>// Copyright (C) 2016 Elviss Strazdins
// This file is part of the Ouzel engine.

#include <functional>
#include "Button.h"
#include "Engine.h"
#include "Sprite.h"
#include "EventHander.h"
#include "EventDispatcher.h"
#include "Layer.h"
#include "Camera.h"
#include "Utils.h"

namespace ouzel
{
    std::shared_ptr<Button> Button::create(const std::string& normal, const std::string& selected, const std::string& pressed, const std::string& disabled, const std::function<void(const VoidPtr&)>& callback)
    {
        std::shared_ptr<Button> result = std::make_shared<Button>();

        if (!result->init(normal, selected, pressed, disabled, callback))
        {
            result.reset();
        }

        return result;
    }

    Button::Button()
    {

    }

    Button::~Button()
    {
        Engine::getInstance()->getEventDispatcher()->removeEventHandler(_eventHandler);
    }

    bool Button::init(const std::string& normal, const std::string& selected, const std::string& pressed, const std::string& disabled, const std::function<void(const VoidPtr&)>& callback)
    {
        _eventHandler = std::make_shared<EventHandler>();

        _eventHandler->mouseHandler = std::bind(&Button::handleMouse, this, std::placeholders::_1, std::placeholders::_2);
        _eventHandler->touchHandler = std::bind(&Button::handleTouch, this, std::placeholders::_1, std::placeholders::_2);
        _eventHandler->gamepadHandler = std::bind(&Button::handleGamepad, this, std::placeholders::_1, std::placeholders::_2);

        Engine::getInstance()->getEventDispatcher()->addEventHandler(_eventHandler);

        if (!normal.empty())
        {
            _normalSprite = std::make_shared<Sprite>();
            if (_normalSprite->initFromFile(normal))
            {
                _boundingBox = _normalSprite->getBoundingBox();
                addChild(_normalSprite);
            }
        }

        if (!selected.empty())
        {
            _selectedSprite = std::make_shared<Sprite>();
            if (_selectedSprite->initFromFile(selected))
            {
                addChild(_selectedSprite);
            }
        }

        if (!pressed.empty())
        {
            _pressedSprite = std::make_shared<Sprite>();
            if (_pressedSprite->initFromFile(pressed))
            {
                addChild(_pressedSprite);
            }
        }

        if (!disabled.empty())
        {
            _disabledSprite = std::make_shared<Sprite>();
            if (_disabledSprite->initFromFile(disabled))
            {
                addChild(_disabledSprite);
            }
        }

        _callback = callback;

        updateSprite();

        return true;
    }

    void Button::setEnabled(bool enabled)
    {
        Widget::setEnabled(enabled);

        _selected = false;
        _pointerOver = false;
        _pressed = false;

        updateSprite();
    }

    bool Button::handleMouse(const MouseEventPtr& event, const VoidPtr& sender)
    {
        OUZEL_UNUSED(sender);

        if (!_enabled) return true;

        switch (event->type)
        {
            case Event::Type::MOUSE_DOWN:
            {
                if (_pointerOver)
                {
                    _pressed = true;
                    updateSprite();
                }
                break;
            }
            case Event::Type::MOUSE_UP:
            {
                if (_pointerOver && _pressed)
                {
                    _pressed = false;
                    updateSprite();

                    if (_callback)
                    {
                        _callback(shared_from_this());
                    }
                }
                break;
            }
            case Event::Type::MOUSE_MOVE:
            {
                if (LayerPtr layer = _layer.lock())
                {
                    Vector2 worldLocation = layer->getCamera()->screenToWorldLocation(event->position);
                    checkPointer(worldLocation);
                    updateSprite();
                }
                break;
            }
            default:
                break;
        }

        return true;
    }

    bool Button::handleTouch(const TouchEventPtr& event, const VoidPtr& sender)
    {
        OUZEL_UNUSED(sender);

        if (!_enabled) return true;

        switch (event->type)
        {
            case Event::Type::TOUCH_BEGIN:
            {
                if (LayerPtr layer = _layer.lock())
                {
                    Vector2 worldLocation = layer->getCamera()->screenToWorldLocation(event->position);
                    checkPointer(worldLocation);

                    if (_pointerOver)
                    {
                        _pressed = true;
                    }

                    updateSprite();
                }
                break;
            }
            case Event::Type::TOUCH_MOVE:
            {
                if (LayerPtr layer = _layer.lock())
                {
                    Vector2 worldLocation = layer->getCamera()->screenToWorldLocation(event->position);
                    checkPointer(worldLocation);
                    updateSprite();
                }
                break;
            }
            case Event::Type::TOUCH_END:
            {
                if (_pointerOver && _pressed)
                {
                    _pressed = false;
                    updateSprite();

                    if (_callback)
                    {
                        _callback(shared_from_this());
                    }
                }

                break;
            }
            default:
                break;
        }

        return true;
    }

    bool Button::handleGamepad(const GamepadEventPtr& event, const VoidPtr& sender)
    {
        OUZEL_UNUSED(event);
        OUZEL_UNUSED(sender);

        return true;
    }

    void Button::checkPointer(const Vector2& worldLocation)
    {
        if (pointOn(worldLocation))
        {
            _pointerOver = true;
        }
        else
        {
            _pointerOver = false;
        }
    }

    void Button::updateSprite()
    {
        if (_normalSprite) _normalSprite->setVisible(false);
        if (_selectedSprite) _selectedSprite->setVisible(false);
        if (_pressedSprite) _pressedSprite->setVisible(false);
        if (_disabledSprite) _disabledSprite->setVisible(false);

        if (_enabled)
        {
            if (_pressed && _pointerOver && _pressedSprite)
            {
                _pressedSprite->setVisible(true);
            }
            else if (_selected && _selectedSprite)
            {
                _selectedSprite->setVisible(true);
            }
            else if (_normalSprite)
            {
                _normalSprite->setVisible(true);
            }
        }
        else if (_disabledSprite)
        {
            _disabledSprite->setVisible(true);
        }
    }
}
<|endoftext|>


<commit_before>//
//  File:       EEvent.hpp
//  Class:      EEvent
//  Author:     Jesper Persson and Sebastian Odbjer
//              All code is our own except where credited to others.
//
//	Copyright (c) 2012 by Catch22. All Rights Reserved.
//  Date: 		02/10-2012
//
//	License: The following code is licensed under the Catch22-License
//
//  Description:
//	Enumiration of different types of events.
//
//
#pragma once

enum EEvent
{
	DID_JUMP,
	DID_COLLIDE,
	DID_THROW,
	GAME_STARTED,
	GAME_PAUSED,
	GAME_OVER,
    PBODY_CREATED_PHYSICS,
    PBODY_CREATED_ACTORS,
    PBODY_DESTROYED_PHYSICS,
    COLLIDED_WITH_PLAYER,
    WAS_DESTROYED,
    PBODY_DESTROYED_PHYSICS
};

<commit_msg>Removed dublicated code in EEvent<commit_after>//
//  File:       EEvent.hpp
//  Class:      EEvent
//  Author:     Jesper Persson and Sebastian Odbjer
//              All code is our own except where credited to others.
//
//	Copyright (c) 2012 by Catch22. All Rights Reserved.
//  Date: 		02/10-2012
//
//	License: The following code is licensed under the Catch22-License
//
//  Description:
//	Enumiration of different types of events.
//
//
#pragma once

enum EEvent
{
	DID_JUMP,
	DID_COLLIDE,
	DID_THROW,
	GAME_STARTED,
	GAME_PAUSED,
	GAME_OVER,
    PBODY_CREATED_PHYSICS,
    PBODY_CREATED_ACTORS,
    PBODY_DESTROYED_PHYSICS,
    COLLIDED_WITH_PLAYER,
    WAS_DESTROYED,
};

<|endoftext|>


<commit_before>/*
  NUI3 - C++ cross-platform GUI framework for OpenGL based applications
  Copyright (C) 2002-2003 Sebastien Metrot & Vincent Caron

  licence: see nui3/LICENCE.TXT
*/

#include "nui.h"
#include NGL_CONFIG_H

#ifdef HAVE_FREETYPE

#include "nglKernel.h"
#include "nglFontInstance.h"

nglFontInstance::FontListType nglFontInstance::gFontList;

/*
 * Life cycle
 */

nglFontInstance::nglFontInstance (const nglPath& rPath, uint Face)
{
  //NGL_DEBUG( NGL_LOG(_T("font"), NGL_LOG_DEBUG, _T("nglFontInstance::nglFontInstance 0x%x (%ls - %d)\n"), this, rPath.GetChars(), Face);)
  mPath     = rPath;
  mFace     = Face;
  mpMemBase = NULL;
  mMemSize  = 0;
  mOwnMemory = false;
  mStatic = false;

  Acquire();
}

nglFontInstance::nglFontInstance (const FT_Byte* pBase, FT_Long Size, uint Face, bool StaticBuffer)
{
  //NGL_DEBUG( NGL_LOG(_T("font"), NGL_LOG_DEBUG, _T("nglFontInstance::nglFontInstance 0x%x (memory %d - %d)\n"), this, Size, Face);)
  nglString fake;
  fake.Format(_T("/memory/%p,0x%x"), pBase, Size);

  mPath     = fake;
  mFace     = Face;
  mpMemBase = pBase;
  mMemSize  = Size;
  mOwnMemory = false;
  mStatic   = StaticBuffer;

  Acquire();
}

nglFontInstance::nglFontInstance (const nglFontInstance& rInstance)
{
  mPath     = rInstance.mPath;
  mFace     = rInstance.mFace;
  mpMemBase = rInstance.mpMemBase;
  mMemSize  = rInstance.mMemSize;
  //mOwnMemory = rInstance.mOwnMemory;
  mStatic = rInstance.mStatic;
  mOwnMemory = !mStatic;

  Acquire();
  //NGL_DEBUG( NGL_LOG(_T("font"), NGL_LOG_DEBUG, _T("nglFontInstance::nglFontInstance COPY CTOR 0x%x (%ls - %d)\n"), this, mPath.GetChars(), mFace);)
}

nglFontInstance::~nglFontInstance()
{
  //NGL_DEBUG( NGL_LOG(_T("font"), NGL_LOG_DEBUG, _T("nglFontInstance::~nglFontInstance DTOR 0x%x (%ls - %d)\n"), this, mPath.GetChars(), mFace);)
  if (mOwnMemory)
    delete[] mpMemBase;
}

nglPath nglFontInstance::GetPath() const
{
  return mPath;
}

uint nglFontInstance::GetFace() const
{
  return mFace;
}

FTC_FaceID nglFontInstance::Install(const nglFontInstance& rInstance)
{
  nglFontInstance* font;

  FontListType::iterator it = gFontList.begin();
  FontListType::iterator end = gFontList.end();
  for (; it != end; ++it)
  {
    font = *it;
    if ((rInstance.mPath == font->mPath) && (rInstance.mFace == font->mFace))
    {
      font->Acquire();
      return (FTC_FaceID)font;
    }
  }

  // Not matching installed font : create (copy), install and return it
  font = new nglFontInstance(rInstance);
  if (!font)
    return (FTC_FaceID)0;

  NGL_DEBUG( NGL_LOG(_T("font"), NGL_LOG_DEBUG, _T("Install '%ls' %d"), font->mPath.GetPathName().GetChars(), font->mFace); )
  gFontList.insert(font);
  return (FTC_FaceID)font;
}

FTC_FaceID nglFontInstance::Uninstall(const nglFontInstance& rInstance)
{
  nglFontInstance* font;

  FontListType::iterator it = gFontList.begin();
  FontListType::iterator end = gFontList.end();
  for (; it != end; ++it)
  {
    font = *it;
    if ((rInstance.mPath == font->mPath) && (rInstance.mFace == font->mFace))
    {
      if (!font->Release())
      {
        NGL_DEBUG( NGL_LOG(_T("font"), NGL_LOG_DEBUG, _T("Uninstall '%ls' %d"), font->mPath.GetPathName().GetChars(), font->mFace); )
        delete font;
        gFontList.erase(it);
      }

      return (FTC_FaceID)font;
    }
  }

  return NULL;
}

nglFontInstance* nglFontInstance::Lookup (const FTC_FaceID FaceID)
{
  nglFontInstance* ifont = (nglFontInstance*)FaceID;
#ifdef _DEBUG_
  /* Check that it's really a pointer to a nglFontInstance,
   * parse our instance list for that.
   */
  FontListType::iterator it = gFontList.find(ifont);
  FontListType::iterator end = gFontList.end();
  NGL_ASSERT(it != end);
#endif // _DEBUG_

  return ifont;
}

FT_Error nglFontInstance::FaceRequestHandler (FTC_FaceID FaceID, FT_Library pLibrary, FT_Pointer pData, FT_Face* pFace)
{
  nglFontInstance* ifont = Lookup(FaceID);

  return ifont ? ifont->OnFaceRequest(pLibrary, pData, pFace) : FT_Err_Cannot_Open_Resource;
}

FT_Error nglFontInstance::OnFaceRequest (FT_Library pLibrary, FT_Pointer pData, FT_Face* pFace)
{
  NGL_DEBUG( NGL_LOG(_T("font"), NGL_LOG_DEBUG, _T("Hard loading '%ls'"), mPath.GetPathName().GetChars(), mFace); )

  if (!mpMemBase) // Load the file in memory instead of counting on ft2 to understand our pathes...
  {
    nglIStream* pFile = mPath.OpenRead();
    if (!pFile || pFile->GetState() != eStreamReady)
    {
      delete pFile;
      return FT_Err_Invalid_Argument;
    }

    mMemSize = (FT_Long)pFile->Available();
    FT_Byte* pMem = new FT_Byte[mMemSize];
    NGL_ASSERT(pMem != NULL);
    mpMemBase = pMem;
    mOwnMemory = true;
    if (pFile->ReadUInt8(pMem, mMemSize) != mMemSize)
    {
      delete pFile;
      return FT_Err_Invalid_Argument;
    }

    delete pFile;
  }

  std::string tmp(mPath.GetPathName().GetStdString());
  return mpMemBase ?
    FT_New_Memory_Face(pLibrary, mpMemBase, mMemSize, mFace, pFace) :
    FT_New_Face(pLibrary, tmp.c_str(), mFace, pFace);

}

void nglFontInstance::Dump()
{
  NGL_DEBUG( NGL_LOG(_T("font"), NGL_LOG_INFO, _T("nglFontInstance::Dump\n"));)

  FontListType::iterator i;
  nglFontInstance* pFontInstance = NULL;
  uint s = gFontList.size();
  uint c = 0;

  NGL_DEBUG( NGL_LOG(_T("font"), NGL_LOG_INFO, _T("Dumping font instances (%d)\n"), s);)

    for (i = gFontList.begin(); i != gFontList.end(); ++i)
    {
      pFontInstance = *i;
      NGL_DEBUG( NGL_LOG(_T("font"), NGL_LOG_INFO, _T("installed font instance %d/%d: 0x%x\n"), c, s, pFontInstance);)
      //if (pFontInstance)
      //  delete pFontInstance;
      c++;
    }
}

void nglDumpFontInstances()
{
  nglFontInstance::Dump();
}

void nglFontInstance::OnExit()
{
  FontListType::iterator i;
  nglFontInstance* pFontInstance = NULL;
  uint c = 0;

  NGL_DEBUG( uint cnt = gFontList.size(); NGL_LOG(_T("font"), NGL_LOG_INFO, _T("Releasing %d installed font%ls"), cnt, PLURAL(cnt)); )

  for (i = gFontList.begin(); i != gFontList.end(); ++i)
  {
    pFontInstance = *i;
    if (pFontInstance)
      delete pFontInstance;
    c++;
  }

  gFontList.clear();
}

#endif // HAVE_FREETYPE
<commit_msg>fixed font deletion crash<commit_after>/*
  NUI3 - C++ cross-platform GUI framework for OpenGL based applications
  Copyright (C) 2002-2003 Sebastien Metrot & Vincent Caron

  licence: see nui3/LICENCE.TXT
*/

#include "nui.h"
#include NGL_CONFIG_H

#ifdef HAVE_FREETYPE

#include "nglKernel.h"
#include "nglFontInstance.h"

nglFontInstance::FontListType nglFontInstance::gFontList;

/*
 * Life cycle
 */

nglFontInstance::nglFontInstance (const nglPath& rPath, uint Face)
{
  //NGL_DEBUG( NGL_LOG(_T("font"), NGL_LOG_DEBUG, _T("nglFontInstance::nglFontInstance 0x%x (%ls - %d)\n"), this, rPath.GetChars(), Face);)
  mPath     = rPath;
  mFace     = Face;
  mpMemBase = NULL;
  mMemSize  = 0;
  mOwnMemory = false;
  mStatic = false;

  Acquire();
}

nglFontInstance::nglFontInstance (const FT_Byte* pBase, FT_Long Size, uint Face, bool StaticBuffer)
{
  //NGL_DEBUG( NGL_LOG(_T("font"), NGL_LOG_DEBUG, _T("nglFontInstance::nglFontInstance 0x%x (memory %d - %d)\n"), this, Size, Face);)
  nglString fake;
  fake.Format(_T("/memory/%p,0x%x"), pBase, Size);

  mPath     = fake;
  mFace     = Face;
  mpMemBase = pBase;
  mMemSize  = Size;
  mOwnMemory = false;
  mStatic   = StaticBuffer;

  Acquire();
}

nglFontInstance::nglFontInstance (const nglFontInstance& rInstance)
{
  mPath     = rInstance.mPath;
  mFace     = rInstance.mFace;
  mpMemBase = rInstance.mpMemBase;
  mMemSize  = rInstance.mMemSize;
  //mOwnMemory = rInstance.mOwnMemory;
  mStatic = rInstance.mStatic;
  mOwnMemory = !mStatic;

  Acquire();
  //NGL_DEBUG( NGL_LOG(_T("font"), NGL_LOG_DEBUG, _T("nglFontInstance::nglFontInstance COPY CTOR 0x%x (%ls - %d)\n"), this, mPath.GetChars(), mFace);)
}

nglFontInstance::~nglFontInstance()
{
  //NGL_DEBUG( NGL_LOG(_T("font"), NGL_LOG_DEBUG, _T("nglFontInstance::~nglFontInstance DTOR 0x%x (%ls - %d)\n"), this, mPath.GetChars(), mFace);)
  if (mOwnMemory)
    delete[] mpMemBase;
}

nglPath nglFontInstance::GetPath() const
{
  return mPath;
}

uint nglFontInstance::GetFace() const
{
  return mFace;
}

FTC_FaceID nglFontInstance::Install(const nglFontInstance& rInstance)
{
  nglFontInstance* font;

  FontListType::iterator it = gFontList.begin();
  FontListType::iterator end = gFontList.end();
  for (; it != end; ++it)
  {
    font = *it;
    if ((rInstance.mPath == font->mPath) && (rInstance.mFace == font->mFace))
    {
      font->Acquire();
      return (FTC_FaceID)font;
    }
  }

  // Not matching installed font : create (copy), install and return it
  font = new nglFontInstance(rInstance);
  if (!font)
    return (FTC_FaceID)0;

  NGL_DEBUG( NGL_LOG(_T("font"), NGL_LOG_DEBUG, _T("Install '%ls' %d"), font->mPath.GetPathName().GetChars(), font->mFace); )
  gFontList.insert(font);
  return (FTC_FaceID)font;
}

FTC_FaceID nglFontInstance::Uninstall(const nglFontInstance& rInstance)
{
  nglFontInstance* font;

  FontListType::iterator it = gFontList.begin();
  FontListType::iterator end = gFontList.end();
  for (; it != end; ++it)
  {
    font = *it;
    if ((rInstance.mPath == font->mPath) && (rInstance.mFace == font->mFace))
    {
      if (!font->Release())
      {
        NGL_DEBUG( NGL_LOG(_T("font"), NGL_LOG_DEBUG, _T("Uninstall '%ls' %d"), font->mPath.GetPathName().GetChars(), font->mFace); )
        gFontList.erase(it);
      }

      return (FTC_FaceID)font;
    }
  }

  return NULL;
}

nglFontInstance* nglFontInstance::Lookup (const FTC_FaceID FaceID)
{
  nglFontInstance* ifont = (nglFontInstance*)FaceID;
#ifdef _DEBUG_
  /* Check that it's really a pointer to a nglFontInstance,
   * parse our instance list for that.
   */
  FontListType::iterator it = gFontList.find(ifont);
  FontListType::iterator end = gFontList.end();
  NGL_ASSERT(it != end);
#endif // _DEBUG_

  return ifont;
}

FT_Error nglFontInstance::FaceRequestHandler (FTC_FaceID FaceID, FT_Library pLibrary, FT_Pointer pData, FT_Face* pFace)
{
  nglFontInstance* ifont = Lookup(FaceID);

  return ifont ? ifont->OnFaceRequest(pLibrary, pData, pFace) : FT_Err_Cannot_Open_Resource;
}

FT_Error nglFontInstance::OnFaceRequest (FT_Library pLibrary, FT_Pointer pData, FT_Face* pFace)
{
  NGL_DEBUG( NGL_LOG(_T("font"), NGL_LOG_DEBUG, _T("Hard loading '%ls'"), mPath.GetPathName().GetChars(), mFace); )

  if (!mpMemBase) // Load the file in memory instead of counting on ft2 to understand our pathes...
  {
    nglIStream* pFile = mPath.OpenRead();
    if (!pFile || pFile->GetState() != eStreamReady)
    {
      delete pFile;
      return FT_Err_Invalid_Argument;
    }

    mMemSize = (FT_Long)pFile->Available();
    FT_Byte* pMem = new FT_Byte[mMemSize];
    NGL_ASSERT(pMem != NULL);
    mpMemBase = pMem;
    mOwnMemory = true;
    if (pFile->ReadUInt8(pMem, mMemSize) != mMemSize)
    {
      delete pFile;
      return FT_Err_Invalid_Argument;
    }

    delete pFile;
  }

  std::string tmp(mPath.GetPathName().GetStdString());
  return mpMemBase ?
    FT_New_Memory_Face(pLibrary, mpMemBase, mMemSize, mFace, pFace) :
    FT_New_Face(pLibrary, tmp.c_str(), mFace, pFace);

}

void nglFontInstance::Dump()
{
  NGL_DEBUG( NGL_LOG(_T("font"), NGL_LOG_INFO, _T("nglFontInstance::Dump\n"));)

  FontListType::iterator i;
  nglFontInstance* pFontInstance = NULL;
  uint s = gFontList.size();
  uint c = 0;

  NGL_DEBUG( NGL_LOG(_T("font"), NGL_LOG_INFO, _T("Dumping font instances (%d)\n"), s);)

    for (i = gFontList.begin(); i != gFontList.end(); ++i)
    {
      pFontInstance = *i;
      NGL_DEBUG( NGL_LOG(_T("font"), NGL_LOG_INFO, _T("installed font instance %d/%d: 0x%x\n"), c, s, pFontInstance);)
      //if (pFontInstance)
      //  delete pFontInstance;
      c++;
    }
}

void nglDumpFontInstances()
{
  nglFontInstance::Dump();
}

void nglFontInstance::OnExit()
{
  FontListType::iterator i;
  nglFontInstance* pFontInstance = NULL;
  uint c = 0;

  NGL_DEBUG( uint cnt = gFontList.size(); NGL_LOG(_T("font"), NGL_LOG_INFO, _T("Releasing %d installed font%ls"), cnt, PLURAL(cnt)); )

  for (i = gFontList.begin(); i != gFontList.end(); ++i)
  {
    pFontInstance = *i;
    if (pFontInstance)
      delete pFontInstance;
    c++;
  }

  gFontList.clear();
}

#endif // HAVE_FREETYPE
<|endoftext|>


<commit_before><commit_msg> fibonacci lcm<commit_after><|endoftext|>


<commit_before>/// @file Connection.cpp
#include <Common/HighQueuePch.h>
#include "Connection.h"
#include <HighQueue/details/HQAllocator.h>
#include <HighQueue/details/HQEntry.h>
#include <HighQueue/details/HQResolver.h>

using namespace HighQueue;

Connection::Connection()
{
}

Connection::~Connection()
{
    if(memoryPool_ && header_)
    {
        header_->releaseInternalMessages();
    }
}

void Connection::close()
{
    if(memoryPool_ && header_)
    {
        header_->releaseInternalMessages();
    }
}

void Connection::createLocal(
      const std::string & name,
      const CreationParameters & parameters,
      const MemoryPoolPtr & pool)
{
    memoryPool_ = pool;
    if(!memoryPool_)
    {
        memoryPool_.reset(new MemoryPool(parameters.messageSize_, parameters.messageCount_));
    }

    const size_t allocatedSize = spaceNeededForHeader(parameters);
    queueMemory_.reset(new byte_t[allocatedSize]);
    try
    {
        byte_t * block = queueMemory_.get();
        byte_t * alignedBlock = HQAllocator::align(block, CacheLineSize);
        size_t availableSize = allocatedSize - (alignedBlock - block);

        HQAllocator allocator(availableSize, sizeof(HQHeader));
        header_ = new (alignedBlock)HQHeader(name, allocator, parameters, &memoryPool_->getPool());
    }
    catch(...)
    {
        memoryPool_.reset();
        queueMemory_.reset();
        header_ = 0;
        throw;
    }
}

HQHeader * Connection::getHeader() const
{
    if(header_)
    {
        return header_;
    }
    throw std::runtime_error("Using uninitialized Connection");
}

void Connection::openOrCreateShared(const std::string & name, const CreationParameters & parameters)
{
// todo
}

void Connection::openExistingShared(const std::string & name)
{
// todo
}

size_t Connection::spaceNeededForHeader(const CreationParameters & parameters)
{
    size_t headerSize = HQAllocator::align(sizeof(HQHeader), CacheLineSize);
    size_t entriesSize = HighQEntry::alignedSize() * parameters.entryCount_;
    size_t positionsSize = CacheLineSize * 3; // note the assumption that each position fitx in a single cache line
    size_t cacheAlignmentSize = CacheLineSize;
    return headerSize + entriesSize + positionsSize + CacheLineSize;
}

void Connection::allocate(Message & message)
{
    if(!memoryPool_)
    {
        throw std::runtime_error("Using uninitialized Connection");
    }
    memoryPool_->allocate(message);
}

bool Connection::tryAllocate(Message & message)
{
    if(!memoryPool_)
    {
        throw std::runtime_error("Using uninitialized Connection");
    }
    return memoryPool_->tryAllocate(message);
}

size_t Connection::getMessageCapacity()const
{
    if(!memoryPool_)
    {
        throw std::runtime_error("Using uninitialized Connection");
    }
    return memoryPool_->getBlockCapacity();
}
<commit_msg>Detect use of uninitialized Connection,<commit_after>/// @file Connection.cpp
#include <Common/HighQueuePch.h>
#include "Connection.h"
#include <HighQueue/details/HQAllocator.h>
#include <HighQueue/details/HQEntry.h>
#include <HighQueue/details/HQResolver.h>

using namespace HighQueue;

Connection::Connection()
: header_(0)
{
}

Connection::~Connection()
{
    if(memoryPool_ && header_)
    {
        header_->releaseInternalMessages();
    }
}

void Connection::close()
{
    if(memoryPool_ && header_)
    {
        header_->releaseInternalMessages();
    }
}

void Connection::createLocal(
      const std::string & name,
      const CreationParameters & parameters,
      const MemoryPoolPtr & pool)
{
    memoryPool_ = pool;
    if(!memoryPool_)
    {
        memoryPool_.reset(new MemoryPool(parameters.messageSize_, parameters.messageCount_));
    }

    const size_t allocatedSize = spaceNeededForHeader(parameters);
    queueMemory_.reset(new byte_t[allocatedSize]);
    try
    {
        byte_t * block = queueMemory_.get();
        byte_t * alignedBlock = HQAllocator::align(block, CacheLineSize);
        size_t availableSize = allocatedSize - (alignedBlock - block);

        HQAllocator allocator(availableSize, sizeof(HQHeader));
        header_ = new (alignedBlock)HQHeader(name, allocator, parameters, &memoryPool_->getPool());
    }
    catch(...)
    {
        memoryPool_.reset();
        queueMemory_.reset();
        header_ = 0;
        throw;
    }
}

HQHeader * Connection::getHeader() const
{
    if(header_)
    {
        return header_;
    }
    throw std::runtime_error("Using uninitialized Connection");
}

void Connection::openOrCreateShared(const std::string & name, const CreationParameters & parameters)
{
// todo
}

void Connection::openExistingShared(const std::string & name)
{
// todo
}

size_t Connection::spaceNeededForHeader(const CreationParameters & parameters)
{
    size_t headerSize = HQAllocator::align(sizeof(HQHeader), CacheLineSize);
    size_t entriesSize = HighQEntry::alignedSize() * parameters.entryCount_;
    size_t positionsSize = CacheLineSize * 3; // note the assumption that each position fitx in a single cache line
    size_t cacheAlignmentSize = CacheLineSize;
    return headerSize + entriesSize + positionsSize + CacheLineSize;
}

void Connection::allocate(Message & message)
{
    if(!memoryPool_)
    {
        throw std::runtime_error("Using uninitialized Connection");
    }
    memoryPool_->allocate(message);
}

bool Connection::tryAllocate(Message & message)
{
    if(!memoryPool_)
    {
        throw std::runtime_error("Using uninitialized Connection");
    }
    return memoryPool_->tryAllocate(message);
}

size_t Connection::getMessageCapacity()const
{
    if(!memoryPool_)
    {
        throw std::runtime_error("Using uninitialized Connection");
    }
    return memoryPool_->getBlockCapacity();
}
<|endoftext|>


<commit_before>// Filename: IBSourceSpecFactory.C
// Created on 28 Apr 2011 by Boyce Griffith
//
// Copyright (c) 2002-2010, Boyce Griffith
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//    * Redistributions of source code must retain the above copyright notice,
//      this list of conditions and the following disclaimer.
//
//    * Redistributions in binary form must reproduce the above copyright
//      notice, this list of conditions and the following disclaimer in the
//      documentation and/or other materials provided with the distribution.
//
//    * Neither the name of New York University nor the names of its
//      contributors may be used to endorse or promote products derived from
//      this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#include "IBSourceSpecFactory.h"

/////////////////////////////// INCLUDES /////////////////////////////////////

#ifndef included_IBAMR_config
#include <IBAMR_config.h>
#define included_IBAMR_config
#endif

#ifndef included_SAMRAI_config
#include <SAMRAI_config.h>
#define included_SAMRAI_config
#endif

// IBAMR INCLUDES
#include <ibamr/IBSourceSpec.h>
#include <ibamr/namespaces.h>

// IBTK INCLUDES
#include <ibtk/StreamableManager.h>

/////////////////////////////// NAMESPACE ////////////////////////////////////

namespace IBAMR
{
/////////////////////////////// STATIC ///////////////////////////////////////

int IBSourceSpecFactory::s_class_id = -1;

/////////////////////////////// PUBLIC ///////////////////////////////////////

IBSourceSpecFactory::IBSourceSpecFactory()
{
    setStreamableClassID(StreamableManager::getUnregisteredID());
    return;
}// IBSourceSpecFactory

IBSourceSpecFactory::~IBSourceSpecFactory()
{
    // intentionally blank
    return;
}// ~IBSourceSpecFactory

int
IBSourceSpecFactory::getStreamableClassID() const
{
    return s_class_id;
}// getStreamableClassID

void
IBSourceSpecFactory::setStreamableClassID(
    const int class_id)
{
    s_class_id = class_id;
    return;
}// setStreamableClassID

Pointer<Streamable>
IBSourceSpecFactory::unpackStream(
    AbstractStream& stream,
    const IntVector<NDIM>& offset)
{
    Pointer<IBSourceSpec> ret_val = new IBSourceSpec;
    stream.unpack(&ret_val->d_master_idx,1);
    stream.unpack(&ret_val->d_source_idx,1);
    return ret_val;
}// unpackStream

/////////////////////////////// PROTECTED ////////////////////////////////////

/////////////////////////////// PRIVATE //////////////////////////////////////

/////////////////////////////// NAMESPACE ////////////////////////////////////

} // namespace IBAMR

/////////////////////////////// TEMPLATE INSTANTIATION ///////////////////////

#include <tbox/Pointer.C>
template class Pointer<IBAMR::IBSourceSpecFactory>;

//////////////////////////////////////////////////////////////////////////////
<commit_msg>branch boyceg: bugfix to IBSourceSpecFactory<commit_after>// Filename: IBSourceSpecFactory.C
// Created on 28 Apr 2011 by Boyce Griffith
//
// Copyright (c) 2002-2010, Boyce Griffith
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//    * Redistributions of source code must retain the above copyright notice,
//      this list of conditions and the following disclaimer.
//
//    * Redistributions in binary form must reproduce the above copyright
//      notice, this list of conditions and the following disclaimer in the
//      documentation and/or other materials provided with the distribution.
//
//    * Neither the name of New York University nor the names of its
//      contributors may be used to endorse or promote products derived from
//      this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#include "IBSourceSpecFactory.h"

/////////////////////////////// INCLUDES /////////////////////////////////////

#ifndef included_IBAMR_config
#include <IBAMR_config.h>
#define included_IBAMR_config
#endif

#ifndef included_SAMRAI_config
#include <SAMRAI_config.h>
#define included_SAMRAI_config
#endif

// IBAMR INCLUDES
#include <ibamr/IBSourceSpec.h>
#include <ibamr/namespaces.h>

// IBTK INCLUDES
#include <ibtk/StreamableManager.h>

/////////////////////////////// NAMESPACE ////////////////////////////////////

namespace IBAMR
{
/////////////////////////////// STATIC ///////////////////////////////////////

int IBSourceSpecFactory::s_class_id = -1;

/////////////////////////////// PUBLIC ///////////////////////////////////////

IBSourceSpecFactory::IBSourceSpecFactory()
{
    setStreamableClassID(StreamableManager::getUnregisteredID());
    return;
}// IBSourceSpecFactory

IBSourceSpecFactory::~IBSourceSpecFactory()
{
    // intentionally blank
    return;
}// ~IBSourceSpecFactory

int
IBSourceSpecFactory::getStreamableClassID() const
{
    return s_class_id;
}// getStreamableClassID

void
IBSourceSpecFactory::setStreamableClassID(
    const int class_id)
{
    s_class_id = class_id;
    return;
}// setStreamableClassID

Pointer<Streamable>
IBSourceSpecFactory::unpackStream(
    AbstractStream& stream,
    const IntVector<NDIM>& offset)
{
    Pointer<IBSourceSpec> ret_val = new IBSourceSpec();
    stream.unpack(&ret_val->d_master_idx,1);
    stream.unpack(&ret_val->d_source_idx,1);
    return ret_val;
}// unpackStream

/////////////////////////////// PROTECTED ////////////////////////////////////

/////////////////////////////// PRIVATE //////////////////////////////////////

/////////////////////////////// NAMESPACE ////////////////////////////////////

} // namespace IBAMR

/////////////////////////////// TEMPLATE INSTANTIATION ///////////////////////

#include <tbox/Pointer.C>
template class Pointer<IBAMR::IBSourceSpecFactory>;

//////////////////////////////////////////////////////////////////////////////
<|endoftext|>


<commit_before>#pragma once
using namespace std;

#include <iostream>
#include <vector>
#include <regex>

#include "IEventHandler.hpp"
#include "ICommand.hpp"
#include "Line.hpp"
#include "StringUtility.hpp"

namespace OCScript
{
	// OCScriptのコアクラスです。
	class Core
	{
	private:
		vector<ICommand*> _Commands;
		bool _IsEndOfScript;
		vector<Line> _ScriptStorage;
		unsigned int _CurrentLineIndex;
	public:

		// コンストラクタ
		Core()
		{
			_IsEndOfScript = true;
			_CurrentLineIndex = 0;
		}

		// コマンドを登録します。
		// 引数: ICommandExecutableを実装したコマンドのクラス
		// ※例外が発生する可能性のあるメソッドです
		void AddCommand(ICommand *command)
		{
			if (regex_match(command->GetCommandName(), wregex(L"[ \t]*$")))
				throw exception("コマンド名が不正です。");

			_Commands.push_back(command);
		}

		// スクリプトを実行し終えたかどうかを示す値を取得します。
		bool IsEndOfScript()
		{
			return _IsEndOfScript;
		}

		// 次に実行される行を設定します。
		// 引数: 行の0から始まるインデックス
		void SetCurrentLineIndex(unsigned int lineIndex)
		{
			_CurrentLineIndex = lineIndex;
		}

		// スクリプト文を ScriptStorage に一括読み込みします。
		// 引数: 行区切り文字列のベクタ
		// ※例外が発生する可能性のあるメソッドです
		void LoadScript(const vector<wstring> scriptLines)
		{
			vector<Line> lines;

			int lineIndex = 1;
			for (auto scriptLine : scriptLines)
			{
				scriptLine = StringUtility::Replace(scriptLine, L"\n", L"");

				wsmatch m1, m2;

				// 空行でない
				if (!regex_match(scriptLine, wregex(L"^[ \t]*$")))
				{
					m1 = wsmatch();

					// 構文にマッチする
					if (regex_match(scriptLine, m1, wregex(L"^[ \t]*([a-zA-Z0-9._-]+)[ \t]*\\((.+)\\)[ \t]*;[ \t]*$")))
					{
						wstring commandName = m1[1];
						wstring paramsStr = m1[2];

						vector<wstring> paramsSourceVec = StringUtility::Split(paramsStr, ',');
						vector<wstring> paramsDestVec;
						int paramIndex = 1;
						for (auto paramToken : paramsSourceVec)
						{
							wstring content;
							m1 = wsmatch();
							m2 = wsmatch();

							// クォート付きの引数である
							if (regex_match(paramToken, m1, wregex(L"^[ \t]*\"(.*)\"[ \t]*$")) || regex_match(paramToken, m2, wregex(L"^[ \t]*\'(.*)\'[ \t]*$")))
							{
								if (!m1.empty())
									content = m1[1];
								else if (!m2.empty())
									content = m2[1];
								else
									throw exception(("システムエラーが発生しました。マッチ結果が矛盾しています。(行: " + to_string(lineIndex) + ")").c_str());
							}
							else
							{
								m1 = wsmatch();
								if (!regex_match(paramToken, m1, wregex(L"^[ \t]*([^ \t]*)[ \t]*$")))
									throw exception(("引数の解析時にエラーが発生しました。(行: " + to_string(lineIndex) + ", 引数番号: " + to_string(paramIndex) + ")").c_str());

								content = m1[1];
							}
							paramsDestVec.push_back(content);
							paramIndex++;
						}
						lines.push_back(Line(commandName, paramsDestVec));
					}
					else
						throw exception(("構文エラーが発生しました。(行: " + to_string(lineIndex) + ")").c_str());
				}
				lineIndex++;
			}
			_ScriptStorage = vector<Line>(lines);
			_IsEndOfScript = (_CurrentLineIndex > _ScriptStorage.size() - 1);
		}
		// スクリプト文を ScriptStorage に一括読み込みします。
		// 引数: スクリプトの文字列
		// ※例外が発生する可能性のあるメソッドです
		void LoadScript(const wstring scriptText)
		{
			vector<wstring> scriptLines = StringUtility::Split(scriptText, L'\n');
			LoadScript(scriptLines);
		}

		// 現在の行のスクリプト文を実行して行を進めます。
		// ※例外が発生する可能性のあるメソッドです
		void ExecuteCurrentLine()
		{
			if (_ScriptStorage.empty())
				throw exception("ScriptStorageの中身が空でした。");

			if (_CurrentLineIndex > _ScriptStorage.size() - 1)
				throw exception("スクリプトは最後まで実行されています。");

			auto line = _ScriptStorage[_CurrentLineIndex];

			bool isFoundCommand = false;
			for (auto command : _Commands)
			{
				if (line.GetCommandName() == command->GetCommandName())
				{
					isFoundCommand = true;
					AccessEventArgs accessEventArgs;
					command->Access(&accessEventArgs, line.GetParams());
					if (!accessEventArgs.GetIsCancelNextCommand())
						_CurrentLineIndex++;
				}
			}

			if (!isFoundCommand)
				throw exception("未定義のスクリプト文が呼び出されました。");

			_IsEndOfScript = (_CurrentLineIndex > _ScriptStorage.size() - 1);
		}

		// このメソッドを呼び出すと ICommand を実装した全てのクラスの PreUpdate メソッドが呼び出されます。
		void TriggerPreUpdate()
		{
			for (auto command : _Commands)
			{
				command->PreUpdate();
			}
		}

		// このメソッドを呼び出すと ICommand を実装した全てのクラスの Update メソッドが呼び出されます。
		void TriggerUpdate()
		{
			for (auto command : _Commands)
			{
				command->Update();
			}
		}
	};
}
<commit_msg>Refactoring<commit_after>#pragma once
using namespace std;

#include <iostream>
#include <vector>
#include <regex>

#include "IEventHandler.hpp"
#include "ICommand.hpp"
#include "Line.hpp"
#include "StringUtility.hpp"

namespace OCScript
{
	// OCScriptのコアクラスです。
	class Core
	{
	private:
		vector<ICommand*> _Commands;
		bool _IsEndOfScript;
		vector<Line> _ScriptStorage;
		unsigned int _CurrentLineIndex;
	public:

		// コンストラクタ
		Core()
		{
			_IsEndOfScript = true;
			_CurrentLineIndex = 0;
		}

		// コマンドを登録します。
		// 引数: ICommandExecutableを実装したコマンドのクラス
		// ※例外が発生する可能性のあるメソッドです
		void AddCommand(ICommand *command)
		{
			if (regex_match(command->GetCommandName(), wregex(L"[ \t]*$")))
				throw exception("コマンド名が不正です。");

			_Commands.push_back(command);
		}

		// スクリプトを実行し終えたかどうかを示す値を取得します。
		bool IsEndOfScript()
		{
			return _IsEndOfScript;
		}

		// 次に実行される行を設定します。
		// 引数: 行の0から始まるインデックス
		void SetCurrentLineIndex(unsigned int lineIndex)
		{
			_CurrentLineIndex = lineIndex;
		}

		// スクリプト文を ScriptStorage に一括読み込みします。
		// 引数: 行区切り文字列のベクタ
		// ※例外が発生する可能性のあるメソッドです
		void LoadScript(const vector<wstring> scriptLines)
		{
			vector<Line> lines;

			int lineIndex = 1;
			for (auto scriptLine : scriptLines)
			{
				scriptLine = StringUtility::Replace(scriptLine, L"\n", L"");

				wsmatch m1, m2;

				// 空行でない
				if (!regex_match(scriptLine, wregex(L"^[ \t]*$")))
				{
					m1 = wsmatch();

					// 構文にマッチする
					if (regex_match(scriptLine, m1, wregex(L"^[ \t]*([a-zA-Z0-9._-]+)[ \t]*\\((.+)\\)[ \t]*;[ \t]*$")))
					{
						wstring commandName = m1[1];
						wstring paramsStr = m1[2];

						vector<wstring> paramsSourceVec = StringUtility::Split(paramsStr, ',');
						vector<wstring> paramsDestVec;
						int paramIndex = 1;
						for (auto paramToken : paramsSourceVec)
						{
							wstring content;
							m1 = wsmatch();
							m2 = wsmatch();

							// クォート付きの引数である
							if (regex_match(paramToken, m1, wregex(L"^[ \t]*\"(.*)\"[ \t]*$")) || regex_match(paramToken, m2, wregex(L"^[ \t]*\'(.*)\'[ \t]*$")))
							{
								if (!m1.empty())
									content = m1[1];
								else
									content = m2[1];
							}
							else
							{
								m1 = wsmatch();
								if (!regex_match(paramToken, m1, wregex(L"^[ \t]*([^ \t]*)[ \t]*$")))
									throw exception(("引数の解析時にエラーが発生しました。(行: " + to_string(lineIndex) + ", 引数番号: " + to_string(paramIndex) + ")").c_str());

								content = m1[1];
							}
							paramsDestVec.push_back(content);
							paramIndex++;
						}
						lines.push_back(Line(commandName, paramsDestVec));
					}
					else
						throw exception(("構文エラーが発生しました。(行: " + to_string(lineIndex) + ")").c_str());
				}
				lineIndex++;
			}
			_ScriptStorage = vector<Line>(lines);
			_IsEndOfScript = (_CurrentLineIndex > _ScriptStorage.size() - 1);
		}
		// スクリプト文を ScriptStorage に一括読み込みします。
		// 引数: スクリプトの文字列
		// ※例外が発生する可能性のあるメソッドです
		void LoadScript(const wstring scriptText)
		{
			vector<wstring> scriptLines = StringUtility::Split(scriptText, L'\n');
			LoadScript(scriptLines);
		}

		// 現在の行のスクリプト文を実行して行を進めます。
		// ※例外が発生する可能性のあるメソッドです
		void ExecuteCurrentLine()
		{
			if (_ScriptStorage.empty())
				throw exception("ScriptStorageの中身が空でした。");

			if (_CurrentLineIndex > _ScriptStorage.size() - 1)
				throw exception("スクリプトは最後まで実行されています。");

			auto line = _ScriptStorage[_CurrentLineIndex];

			bool isFoundCommand = false;
			for (auto command : _Commands)
			{
				if (line.GetCommandName() == command->GetCommandName())
				{
					isFoundCommand = true;
					AccessEventArgs accessEventArgs;
					command->Access(&accessEventArgs, line.GetParams());
					if (!accessEventArgs.GetIsCancelNextCommand())
						_CurrentLineIndex++;
				}
			}

			if (!isFoundCommand)
				throw exception("未定義のスクリプト文が呼び出されました。");

			_IsEndOfScript = (_CurrentLineIndex > _ScriptStorage.size() - 1);
		}

		// このメソッドを呼び出すと ICommand を実装した全てのクラスの PreUpdate メソッドが呼び出されます。
		void TriggerPreUpdate()
		{
			for (auto command : _Commands)
			{
				command->PreUpdate();
			}
		}

		// このメソッドを呼び出すと ICommand を実装した全てのクラスの Update メソッドが呼び出されます。
		void TriggerUpdate()
		{
			for (auto command : _Commands)
			{
				command->Update();
			}
		}
	};
}
<|endoftext|>