<commit_before>#include "Console.hpp"

#include <algorithm>
#include <sstream>

#include <Windows.h>
#include <conio.h> // _getch()
#pragma warning(disable:4996)

#include <io.h>
#include <cctype> //isgraph

#define ps1beg "\xAF["
#define ps1end "]\xAE"

// Utils
namespace
{
    std::vector<std::string> SplitString(const std::string &String, char Delimiter)
    {
        std::vector<std::string> SplitString;
        std::stringstream StringStream(String);
        std::string Token;
        while( std::getline(StringStream, Token, Delimiter) )
        {
            SplitString.push_back(Token);
        }
        return SplitString;
    }
}

namespace Console
{
    Console::Console()
    {
        ConsoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
        SetConsoleOutputCP(437);

        CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo{};
        if( GetConsoleScreenBufferInfo(ConsoleHandle, &ConsoleInfo) )
        {
            ConsoleWidth = ConsoleInfo.dwSize.X;
        }

        SetTextColor(Color::Info);

        PushCommand("help", std::make_shared<Help>());
        PushCommand("history", std::make_shared<History>());
        PushCommand("quit", std::make_shared<Quit>());
    }

    std::string Console::NearestCommand(const std::string & Command) const
    {
        if( !Command.empty() )
        {
            // Find closest matching command
            std::vector<std::string> Suggestions;

            for( const auto &Cmd : Commands )
            {
                if( !Command.compare(0, Command.length(), (Cmd.first), 0, Command.length()) )
                {
                    Suggestions.push_back((Cmd.first));
                }
            }

            if( Suggestions.size() )
            {
                // Return first match, for now
                return Suggestions.front();
            }
        }
        return "";
    }

    Console::~Console()
    {
        Commands.clear();
    }

    void Console::HandleInput(uint32_t KeyCode)
    {
        switch( KeyCode )
        {
        case '\r': // Enter
        {
            SetTextColor(Color::Info);
            // Clear previous suggestion
            if( !CurSuggestion.empty() )
            {
                std::cout.width(CurSuggestion.length());
                std::cout.fill(' ');
                std::cout << "";

                std::cout.width(CurSuggestion.length());
                std::cout.fill('\b');
                std::cout << "";
            }

            std::cout << ps1end << std::endl;

            std::string Command = CurCommand.substr(0, CurCommand.find_first_of(' '));

            if( !Command.empty() )
            {
                // Run command if it exists
                if( Commands.count(Command)
                    && Commands[Command] )
                {
                    if( !Commands[Command]->Run(SplitString(CurCommand, ' ')) )
                    {
                        // Error running command;
                        SetTextColor(Color::Error);
                        std::cout << "Invalid Usage: " << Command << std::endl;
                        SetTextColor(Color::Info);
                        std::cout << Commands[Command]->Info() << std::endl;
                    }
                }
                else
                {
                    SetTextColor(Color::Error);
                    std::cout << "Unknown Command: " << Command << std::endl;
                }

                // Add to history
                PrevCommands.push_back(CurCommand);
                PrevCommand = PrevCommands.end();
                CurCommand.clear();
                CurSuggestion.clear();
            }

            SetTextColor(Color::Info);
            break;
        }
        case '\t': // Tab
        {
            // Tab completion
            if( !CurSuggestion.empty() && !CurCommand.empty() )
            {
                CurCommand = CurSuggestion;
            }
            break;
        }
        case '\x16': // Ctrl + V
        {
            // Paste in clipbard
            if( OpenClipboard(nullptr) )
            {
                std::string Clipboard(reinterpret_cast<char*>(GetClipboardData(CF_TEXT)));
                CloseClipboard();
                for( const char &CurChar : Clipboard )
                {
                    HandleInput(CurChar);
                }
            }
            break;
        }
        case 0x00: // Function Keys
        case 0xE0:
        {
            uint32_t Func = _getch();
            switch( Func )
            {
            case 0x48: // Up
            {
                if( !PrevCommands.empty() )
                {
                    if( PrevCommand != PrevCommands.begin() )
                    {
                        PrevCommand--;
                    }

                    CurCommand = *PrevCommand;
                }
                break;
            }
            case 0x50: // Down
            {
                if( !PrevCommands.empty() )
                {
                    if( PrevCommand != PrevCommands.end() && PrevCommand != PrevCommands.end() - 1 )
                    {
                        PrevCommand++;
                        CurCommand = *PrevCommand;
                    }
                    else if( PrevCommand == PrevCommands.end() - 1 )
                    {
                        PrevCommand++;
                        CurCommand.clear();
                    }
                    else
                    {
                        CurCommand.clear();
                    }
                }
                break;
            }
            }
            break;
        }
        case '\b': // Backspace
        {
            if( !CurCommand.empty() )
            {
                // Remove character from current command
                CurCommand.pop_back();
                // Update Suggestion
                CurSuggestion = NearestCommand(CurCommand);
            }
            break;
        }
        default: // Every other character (alphanum and space)
        {
            if( std::isgraph(KeyCode) || std::isspace(KeyCode) )
            {
                CurCommand.push_back(static_cast<uint8_t>(KeyCode));

                // Update Suggestion
                CurSuggestion = NearestCommand(CurCommand);
            }
            break;
        }
        }
    }

    void Console::PrintLine()
    {
        SetTextColor(Color::Info);
        std::cout << '\r';
        std::cout.width(ConsoleWidth - 1);
        std::cout.fill(' ');
        std::cout << '\r' << ps1beg;
        SetTextColor(Color::Input);

        if( CurSuggestion.length() )
        {
            // Print suggestion
            SetTextColor(Color::Suggestion);
            std::cout << CurSuggestion;
            // Go back
            std::cout.width(CurSuggestion.length());
            std::cout.fill('\b');
            std::cout << "";
        }
        // Print currently typed in command
        SetTextColor(Color::Input);
        std::cout << CurCommand;
        std::cout.flush();
    }

    void Console::PushCommand(const std::string &CommandName, std::shared_ptr<Command> Command)
    {
        Commands[CommandName] = Command;
    }
    void Console::PopCommand(const std::string &CommandName)
    {
        if( Commands.count(CommandName) )
        {
            Commands.erase(CommandName);
        }
    }

    /// Meta commands

    // Help
    Console::Help::Help()
    {
    }

    Console::Help::~Help()
    {
    }

    bool Console::Help::Run(const std::vector<std::string> &Arguments)
    {
        if( Arguments.size() > 1 )
        {
            for( auto it = Arguments.cbegin() + 1; it != Arguments.cend(); ++it )
            {
                if( Console::Instance()->Commands.count(*it) )
                {
                    std::string Padded(*it);
                    Padded.resize(Console::Instance()->ConsoleWidth / 2, '\xC4');
                    SetTextColor(Color::Info);
                    std::cout << Padded << std::endl;
                    SetTextColor(Color::Info^Color::Bright);
                    std::cout << Console::Instance()->Commands[*it]->Info() << std::endl;
                }
                else
                {
                    SetTextColor(Color::Error);
                    std::cout << "Command: " << *it << " not found." << std::endl;
                }
            }
        }
        else
        {
            // Show all command info
            for( const auto &Command : Console::Instance()->Commands )
            {
                std::string Padded(Command.first);
                Padded.resize(Console::Instance()->ConsoleWidth / 2, '\xC4');
                SetTextColor(Color::Info);
                std::cout << Padded << std::endl;
                SetTextColor(Color::Info^Color::Bright);
                std::cout << Command.second->Info(Command.first) << std::endl;
            }
        }
        return true;
    }

    std::string Console::Help::Info(const std::string & Topic) const
    {
        return "Prints help for all commands\n"
            "Type help (command name) (topic) to get help on a specific command";
    }

    // History
    Console::History::History()
    {
    }

    Console::History::~History()
    {
    }

    bool Console::History::Run(const std::vector<std::string>& Arguments)
    {
        SetTextColor(Color::Info);
        for( const auto &Command : Console::Instance()->PrevCommands )
        {
            std::cout << Command << std::endl;
        }
        return true;
    }

    std::string Console::History::Info(const std::string & Topic) const
    {
        return "Displays all previously entered commands";
    }

    // Quit
    Console::Quit::Quit()
    {
    }

    Console::Quit::~Quit()
    {
    }

    bool Console::Quit::Run(const std::vector<std::string>& Arguments)
    {
        std::exit(0);
        return true;
    }

    std::string Console::Quit::Info(const std::string & Topic) const
    {
        return "Quits the application";
    }

    // Static Functions

    void SetTextColor(Color NewColor)
    {
        SetConsoleTextAttribute(
            GetStdHandle(STD_OUTPUT_HANDLE),
            static_cast<std::underlying_type_t<Color>>(NewColor)
        );
    }

    bool AllocateConsole(const std::string& ConsoleTitle)
    {
        // Allocate new console window
        if( !AllocConsole() )
        {
            MessageBox(nullptr, "Unable to allocate console", ConsoleTitle.c_str(), 0);
            return false;
        }

        CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;

        GetConsoleScreenBufferInfo(
            GetStdHandle(STD_OUTPUT_HANDLE),
            &ConsoleInfo
        );

        ConsoleInfo.dwSize.Y = 25;
        ConsoleInfo.dwSize.X = 30;

        SetConsoleScreenBufferSize(
            GetStdHandle(STD_OUTPUT_HANDLE),
            ConsoleInfo.dwSize
        );

        if( !SetConsoleTitle(ConsoleTitle.c_str()) )
        {
            MessageBox(
                nullptr,
                ("Unable set console title (Error code: " + std::to_string(GetLastError()) + ')').c_str(),
                ConsoleTitle.c_str(),
                0
            );
            return false;
        }

        if( EnableMenuItem(GetSystemMenu(GetConsoleWindow(), FALSE), SC_CLOSE | SC_MINIMIZE, MF_GRAYED) == -1 )
        {
            MessageBox(
                nullptr,
                "Unable to enable menu item",
                ConsoleTitle.c_str(),
                0);
            return false;
        }

        if( !DrawMenuBar(GetConsoleWindow()) )
        {
            MessageBox(
                nullptr,
                ("Unable to DrawMenuBar (Error code: " + std::to_string(GetLastError()) + ')').c_str(),
                ConsoleTitle.c_str(),
                0
            );
            return false;
        }

        // Reroute std streams
        //intptr_t hStd;
        //int32_t hConsole;
        //FILE *fp;

        //// Setup std output
        //hStd = reinterpret_cast<intptr_t>(GetStdHandle(STD_OUTPUT_HANDLE));
        //hConsole = _open_osfhandle(hStd, _O_TEXT);
        //fp = _fdopen(hConsole, "w");
        //*stdout = *fp;
        //setvbuf(stdout, nullptr, _IONBF, 0);

        //// Setup std input
        //hStd = reinterpret_cast<intptr_t>(GetStdHandle(STD_INPUT_HANDLE));
        //hConsole = _open_osfhandle(hStd, _O_TEXT);
        //fp = _fdopen(hConsole, "r");
        //*stdin = *fp;
        //setvbuf(stdin, nullptr, _IONBF, 0);

        //std::ios::sync_with_stdio();

        // New stream re-route method
        // MUCH faster
        freopen("CONIN$", "r", stdin);
        freopen("CONOUT$", "w", stdout);
        freopen("CONOUT$", "w", stderr);

        std::wcout.clear();
        std::cout.clear();
        std::wcerr.clear();
        std::cerr.clear();
        std::wcin.clear();
        std::cin.clear();

        return true;
    }

    size_t GetWidth()
    {
        CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
        if( GetConsoleScreenBufferInfo(
            GetStdHandle(STD_OUTPUT_HANDLE),
            &ConsoleInfo) )
        {
            return static_cast<size_t>(ConsoleInfo.dwSize.X);
        }
        return 0;
    }
}<commit_msg>Changed width variable access to use function accessor<commit_after>#include "Console.hpp"

#include <algorithm>
#include <sstream>

#include <Windows.h>
#include <conio.h> // _getch()
#pragma warning(disable:4996)

#include <io.h>
#include <cctype> //isgraph

#define ps1beg "\xAF["
#define ps1end "]\xAE"

// Utils
namespace
{
    std::vector<std::string> SplitString(const std::string &String, char Delimiter)
    {
        std::vector<std::string> SplitString;
        std::stringstream StringStream(String);
        std::string Token;
        while( std::getline(StringStream, Token, Delimiter) )
        {
            SplitString.push_back(Token);
        }
        return SplitString;
    }
}

namespace Console
{
    Console::Console()
    {
        ConsoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
        SetConsoleOutputCP(437);

        CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo{};
        if( GetConsoleScreenBufferInfo(ConsoleHandle, &ConsoleInfo) )
        {
            ConsoleWidth = ConsoleInfo.dwSize.X;
        }

        SetTextColor(Color::Info);

        PushCommand("help", std::make_shared<Help>());
        PushCommand("history", std::make_shared<History>());
        PushCommand("quit", std::make_shared<Quit>());
    }

    std::string Console::NearestCommand(const std::string & Command) const
    {
        if( !Command.empty() )
        {
            // Find closest matching command
            std::vector<std::string> Suggestions;

            for( const auto &Cmd : Commands )
            {
                if( !Command.compare(0, Command.length(), (Cmd.first), 0, Command.length()) )
                {
                    Suggestions.push_back((Cmd.first));
                }
            }

            if( Suggestions.size() )
            {
                // Return first match, for now
                return Suggestions.front();
            }
        }
        return "";
    }

    Console::~Console()
    {
        Commands.clear();
    }

    void Console::HandleInput(uint32_t KeyCode)
    {
        switch( KeyCode )
        {
        case '\r': // Enter
        {
            SetTextColor(Color::Info);
            // Clear previous suggestion
            if( !CurSuggestion.empty() )
            {
                std::cout.width(CurSuggestion.length());
                std::cout.fill(' ');
                std::cout << "";

                std::cout.width(CurSuggestion.length());
                std::cout.fill('\b');
                std::cout << "";
            }

            std::cout << ps1end << std::endl;

            std::string Command = CurCommand.substr(0, CurCommand.find_first_of(' '));

            if( !Command.empty() )
            {
                // Run command if it exists
                if( Commands.count(Command)
                    && Commands[Command] )
                {
                    if( !Commands[Command]->Run(SplitString(CurCommand, ' ')) )
                    {
                        // Error running command;
                        SetTextColor(Color::Error);
                        std::cout << "Invalid Usage: " << Command << std::endl;
                        SetTextColor(Color::Info);
                        std::cout << Commands[Command]->Info() << std::endl;
                    }
                }
                else
                {
                    SetTextColor(Color::Error);
                    std::cout << "Unknown Command: " << Command << std::endl;
                }

                // Add to history
                PrevCommands.push_back(CurCommand);
                PrevCommand = PrevCommands.end();
                CurCommand.clear();
                CurSuggestion.clear();
            }

            SetTextColor(Color::Info);
            break;
        }
        case '\t': // Tab
        {
            // Tab completion
            if( !CurSuggestion.empty() && !CurCommand.empty() )
            {
                CurCommand = CurSuggestion;
            }
            break;
        }
        case '\x16': // Ctrl + V
        {
            // Paste in clipbard
            if( OpenClipboard(nullptr) )
            {
                std::string Clipboard(reinterpret_cast<char*>(GetClipboardData(CF_TEXT)));
                CloseClipboard();
                for( const char &CurChar : Clipboard )
                {
                    HandleInput(CurChar);
                }
            }
            break;
        }
        case 0x00: // Function Keys
        case 0xE0:
        {
            uint32_t Func = _getch();
            switch( Func )
            {
            case 0x48: // Up
            {
                if( !PrevCommands.empty() )
                {
                    if( PrevCommand != PrevCommands.begin() )
                    {
                        PrevCommand--;
                    }

                    CurCommand = *PrevCommand;
                }
                break;
            }
            case 0x50: // Down
            {
                if( !PrevCommands.empty() )
                {
                    if( PrevCommand != PrevCommands.end() && PrevCommand != PrevCommands.end() - 1 )
                    {
                        PrevCommand++;
                        CurCommand = *PrevCommand;
                    }
                    else if( PrevCommand == PrevCommands.end() - 1 )
                    {
                        PrevCommand++;
                        CurCommand.clear();
                    }
                    else
                    {
                        CurCommand.clear();
                    }
                }
                break;
            }
            }
            break;
        }
        case '\b': // Backspace
        {
            if( !CurCommand.empty() )
            {
                // Remove character from current command
                CurCommand.pop_back();
                // Update Suggestion
                CurSuggestion = NearestCommand(CurCommand);
            }
            break;
        }
        default: // Every other character (alphanum and space)
        {
            if( std::isgraph(KeyCode) || std::isspace(KeyCode) )
            {
                CurCommand.push_back(static_cast<uint8_t>(KeyCode));

                // Update Suggestion
                CurSuggestion = NearestCommand(CurCommand);
            }
            break;
        }
        }
    }

    void Console::PrintLine()
    {
        SetTextColor(Color::Info);
        std::cout << '\r';
        std::cout.width(GetWidth() - 1);
        std::cout.fill(' ');
        std::cout << '\r' << ps1beg;
        SetTextColor(Color::Input);

        if( CurSuggestion.length() )
        {
            // Print suggestion
            SetTextColor(Color::Suggestion);
            std::cout << CurSuggestion;
            // Go back
            std::cout.width(CurSuggestion.length());
            std::cout.fill('\b');
            std::cout << "";
        }
        // Print currently typed in command
        SetTextColor(Color::Input);
        std::cout << CurCommand;
        std::cout.flush();
    }

    void Console::PushCommand(const std::string &CommandName, std::shared_ptr<Command> Command)
    {
        Commands[CommandName] = Command;
    }
    void Console::PopCommand(const std::string &CommandName)
    {
        if( Commands.count(CommandName) )
        {
            Commands.erase(CommandName);
        }
    }

    /// Meta commands

    // Help
    Console::Help::Help()
    {
    }

    Console::Help::~Help()
    {
    }

    bool Console::Help::Run(const std::vector<std::string> &Arguments)
    {
        if( Arguments.size() > 1 )
        {
            for( auto it = Arguments.cbegin() + 1; it != Arguments.cend(); ++it )
            {
                if( Console::Instance()->Commands.count(*it) )
                {
                    std::string Padded(*it);
                    Padded.resize(Console::Instance()->ConsoleWidth / 2, '\xC4');
                    SetTextColor(Color::Info);
                    std::cout << Padded << std::endl;
                    SetTextColor(Color::Info^Color::Bright);
                    std::cout << Console::Instance()->Commands[*it]->Info() << std::endl;
                }
                else
                {
                    SetTextColor(Color::Error);
                    std::cout << "Command: " << *it << " not found." << std::endl;
                }
            }
        }
        else
        {
            // Show all command info
            for( const auto &Command : Console::Instance()->Commands )
            {
                std::string Padded(Command.first);
                Padded.resize(Console::Instance()->ConsoleWidth / 2, '\xC4');
                SetTextColor(Color::Info);
                std::cout << Padded << std::endl;
                SetTextColor(Color::Info^Color::Bright);
                std::cout << Command.second->Info(Command.first) << std::endl;
            }
        }
        return true;
    }

    std::string Console::Help::Info(const std::string & Topic) const
    {
        return "Prints help for all commands\n"
            "Type help (command name) (topic) to get help on a specific command";
    }

    // History
    Console::History::History()
    {
    }

    Console::History::~History()
    {
    }

    bool Console::History::Run(const std::vector<std::string>& Arguments)
    {
        SetTextColor(Color::Info);
        for( const auto &Command : Console::Instance()->PrevCommands )
        {
            std::cout << Command << std::endl;
        }
        return true;
    }

    std::string Console::History::Info(const std::string & Topic) const
    {
        return "Displays all previously entered commands";
    }

    // Quit
    Console::Quit::Quit()
    {
    }

    Console::Quit::~Quit()
    {
    }

    bool Console::Quit::Run(const std::vector<std::string>& Arguments)
    {
        std::exit(0);
        return true;
    }

    std::string Console::Quit::Info(const std::string & Topic) const
    {
        return "Quits the application";
    }

    // Static Functions

    void SetTextColor(Color NewColor)
    {
        SetConsoleTextAttribute(
            GetStdHandle(STD_OUTPUT_HANDLE),
            static_cast<std::underlying_type_t<Color>>(NewColor)
        );
    }

    bool AllocateConsole(const std::string& ConsoleTitle)
    {
        // Allocate new console window
        if( !AllocConsole() )
        {
            MessageBox(nullptr, "Unable to allocate console", ConsoleTitle.c_str(), 0);
            return false;
        }

        CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;

        GetConsoleScreenBufferInfo(
            GetStdHandle(STD_OUTPUT_HANDLE),
            &ConsoleInfo
        );

        ConsoleInfo.dwSize.Y = 25;
        ConsoleInfo.dwSize.X = 30;

        SetConsoleScreenBufferSize(
            GetStdHandle(STD_OUTPUT_HANDLE),
            ConsoleInfo.dwSize
        );

        if( !SetConsoleTitle(ConsoleTitle.c_str()) )
        {
            MessageBox(
                nullptr,
                ("Unable set console title (Error code: " + std::to_string(GetLastError()) + ')').c_str(),
                ConsoleTitle.c_str(),
                0
            );
            return false;
        }

        if( EnableMenuItem(GetSystemMenu(GetConsoleWindow(), FALSE), SC_CLOSE | SC_MINIMIZE, MF_GRAYED) == -1 )
        {
            MessageBox(
                nullptr,
                "Unable to enable menu item",
                ConsoleTitle.c_str(),
                0);
            return false;
        }

        if( !DrawMenuBar(GetConsoleWindow()) )
        {
            MessageBox(
                nullptr,
                ("Unable to DrawMenuBar (Error code: " + std::to_string(GetLastError()) + ')').c_str(),
                ConsoleTitle.c_str(),
                0
            );
            return false;
        }

        // Reroute std streams
        //intptr_t hStd;
        //int32_t hConsole;
        //FILE *fp;

        //// Setup std output
        //hStd = reinterpret_cast<intptr_t>(GetStdHandle(STD_OUTPUT_HANDLE));
        //hConsole = _open_osfhandle(hStd, _O_TEXT);
        //fp = _fdopen(hConsole, "w");
        //*stdout = *fp;
        //setvbuf(stdout, nullptr, _IONBF, 0);

        //// Setup std input
        //hStd = reinterpret_cast<intptr_t>(GetStdHandle(STD_INPUT_HANDLE));
        //hConsole = _open_osfhandle(hStd, _O_TEXT);
        //fp = _fdopen(hConsole, "r");
        //*stdin = *fp;
        //setvbuf(stdin, nullptr, _IONBF, 0);

        //std::ios::sync_with_stdio();

        // New stream re-route method
        // MUCH faster
        freopen("CONIN$", "r", stdin);
        freopen("CONOUT$", "w", stdout);
        freopen("CONOUT$", "w", stderr);

        std::wcout.clear();
        std::cout.clear();
        std::wcerr.clear();
        std::cerr.clear();
        std::wcin.clear();
        std::cin.clear();

        return true;
    }

    size_t GetWidth()
    {
        CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
        if( GetConsoleScreenBufferInfo(
            GetStdHandle(STD_OUTPUT_HANDLE),
            &ConsoleInfo) )
        {
            return static_cast<size_t>(ConsoleInfo.dwSize.X);
        }
        return 0;
    }
}<|endoftext|>


<commit_before>/***
Copyright (C) 2013 Aniket Deole <aniket.deole@gmail.com>
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License version 2.1, as published
by the Free Software Foundation.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranties of
MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with this program.  If not, see <http://www.gnu.org/licenses/>.
***/

#ifndef _TAGDATA_HH_
#define _TAGDATA_HH_

class TagData {
private:
  int primary_key;

  std::string title;
public:

  TagData () {}

  TagData (int p_key, std::string t) {
    primary_key = p_key;
    title = t;
  }

  int getPrimaryKey () { return primary_key; }

  std::string getTitle () { return title; }
};

#endif
<commit_msg>Removed tagdata.hh from repo<commit_after><|endoftext|>


<commit_before>#include "BarTreeVisualization.h"

BarTreeVisualization::BarTreeVisualization(QWidget *parent) : QWebView(parent) {
    this->mainFrame = this->page()->mainFrame();
    connect(this, SIGNAL(loadFinished(bool)), SLOT(loadHasFinished(bool)));

    // See explanation at BarTreeVisualization::timerEvent().
    startTimer(BARTREEVISUALIZATION_DATA_REFRESH_INTERVAL);

    // Load the visualization in the web view.
    this->load(QUrl("qrc:/BarTree/BarTree.html"));
}

void BarTreeVisualization::loadHasFinished(bool ok) {
    this->pageHasLoaded = ok;
}

void BarTreeVisualization::eventSequenceChanged(QVector<Event *> * events) {
    this->mutex.lock();
    this->nextEventSequence = events;
    this->mutex.unlock();
}

void BarTreeVisualization::highlightEventLocation(int msecs, const QString & eventType) {
    qDebug() << eventType;
}

/**
 * Periodically (BARTREEVISUALIZATION_DATA_REFRESH_INTERVAL intervals) poll
 * to see if there's a new event sequence to draw. We do this using polling
 * to prevent continuous redrawing. The number of redrawings per second is
 * now effectively limited to 1/BARTREEVISUALIZATION_DATA_REFRESH_INTERVAL.
 */
void BarTreeVisualization::timerEvent(QTimerEvent *) {
    // If the page hasn't been loaded yet, then definitely don't send an
    // event sequence to the web view yet!
    if (!this->pageHasLoaded)
        return;

    // Make the next event sequence the current, if any, and send it to the
    // web view.
    if (this->nextEventSequence != NULL) {
        this->mutex.lock();
        this->currentEventSequence = this->nextEventSequence;
        this->nextEventSequence = NULL;
        this->mutex.unlock();
        this->sendEventSequenceToWebView();
    }
}

/**
 * Convert the current event sequence into the data format the JavaScript
 * code expects, but then as QVariants. QxtJSON::stringify() will then
 * encode this data as JSON, which the JavaScript code can use directly.
 */
void BarTreeVisualization::sendEventSequenceToWebView() {
    QVariantMap data;
    QMap<QString, int> inputTypeFrequency;
    QMap<QString, QMap<QString, int> > eventTypeFrequency;
    QMap<QString, QMap<QString, QMap<QString, int> > > modifierFrequency;
    QString inputType, eventType, modifier, details;
    QStringList detailsList;
    Event * event;

    // Count frequencies.
    foreach (event, *this->currentEventSequence) {
        inputType = event->getInputType();
        eventType = event->getEventType();
        details = event->getDetails();

        if (eventType.contains("Mouse"))
            inputType = QString("Mouse");
        else
            inputType = QString("Keyboard");

        // Ignore MouseButtonRelease events.
        if (eventType.compare(QString("MouseButtonRelease")) == 0)
            continue;

        // Clean up event type for optimized display.
        eventType.replace("Key", "");
        eventType.replace("Mouse", "");
        // Keyboard.
        eventType.replace("Release", "Press");
        // Mouse.
        eventType.replace("ButtonPress", "Click");
        eventType.replace("ButtonDblClick", "Double");

        // Extract nice & clean "modifier" from event details.
        modifier = (inputType == "Mouse") ? "none" : "something";
        detailsList = details.split(';');
        if (inputType.compare("Mouse") == 0) {
            modifier = detailsList[2];
            if (modifier.length() == 0)
                modifier = "none";
        }
        else {
            detailsList[2].replace(":", "");
            bool modifierKeyPressed = (detailsList[2].length() > 0);
            bool regularKeyPressed = (detailsList[1].length() > 0);
            if (modifierKeyPressed && regularKeyPressed) {
                modifier = detailsList[2] + "+" + detailsList[1];
            }
            else if (modifierKeyPressed && !regularKeyPressed) {
                modifier = detailsList[2];
            }
            else if (!modifierKeyPressed && regularKeyPressed) {
                modifier = detailsList[1];
            }
            else {
                modifier = "other";
            }
        }

        // Init inputTypeFrequency (1st level frequency counts).
        if (!inputTypeFrequency.contains(inputType)) {
            inputTypeFrequency.insert(inputType, 0);
        }

        // Init eventTypeFrequency (2nd level frequency counts).
        if (!eventTypeFrequency.contains(inputType)) {
            QMap<QString, int> map;
            eventTypeFrequency.insert(inputType, map);
        }
        if (!eventTypeFrequency[inputType].contains(eventType)) {
            eventTypeFrequency[inputType].insert(eventType, 0);
        }

        // Init modifierFrequency (3rd level frequency counts).
        if (!modifierFrequency.contains(inputType)) {
            QMap<QString, QMap<QString, int> > map;
            modifierFrequency.insert(inputType, map);
        }
        if (!modifierFrequency[inputType].contains(eventType)) {
            QMap<QString, int> map;
            modifierFrequency[inputType].insert(eventType, map);
        }
        if (!modifierFrequency[inputType][eventType].contains(modifier)) {
            modifierFrequency[inputType][eventType].insert(modifier, 0);
        }

        // Incrementalization.
        inputTypeFrequency.insert(inputType, inputTypeFrequency.value(inputType) + 1);
        eventTypeFrequency[inputType].insert(eventType, eventTypeFrequency[inputType][eventType] + 1);
        modifierFrequency[inputType][eventType].insert(modifier, modifierFrequency[inputType][eventType][modifier] + 1);
    }

    // Build QVariantMap that represents the data in the format expected by the
    // JavaScript code.
    QString keyL1, keyL2, keyL3;
    QVariantMap level1, level2, level3, tmp;
    double totalL1, totalL2, totalL3;

    totalL1 = BarTreeVisualization::calcTotalFreq(inputTypeFrequency);
    level1.clear();
    foreach (keyL1, inputTypeFrequency.keys()) {

        totalL2 = BarTreeVisualization::calcTotalFreq(eventTypeFrequency[keyL1]);
        level2.clear();
        foreach (keyL2, eventTypeFrequency[keyL1].keys()) {

            totalL3 = BarTreeVisualization::calcTotalFreq(modifierFrequency[keyL1][keyL2]);
            level3.clear();
            foreach(keyL3, modifierFrequency[keyL1][keyL2].keys()) {
                tmp.clear();
                tmp.insert("count",     modifierFrequency[keyL1][keyL2][keyL3]);
                tmp.insert("frequency", modifierFrequency[keyL1][keyL2][keyL3] / totalL3);
                level3.insert(keyL3, tmp);
            }

            tmp.clear();
            tmp.insert("count",     eventTypeFrequency[keyL1][keyL2]);
            tmp.insert("frequency", eventTypeFrequency[keyL1][keyL2] / totalL2);
            tmp.insert("children", level3);
            level2.insert(keyL2, tmp);
        }

        tmp.clear();
        tmp.insert("count",     inputTypeFrequency[keyL1]);
        tmp.insert("frequency", inputTypeFrequency[keyL1] / totalL1);
        tmp.insert("children", level2);

        // Assign color depending on input type.
        tmp.insert("color", (keyL1 == "Mouse") ? BARTREEVISUALIZATION_COLOR_MOUSE : BARTREEVISUALIZATION_COLOR_KEYBOARD);

        level1.insert(keyL1, tmp);
    }
    data = level1;

    QString json = QxtJSON::stringify(data);
    qDebug() << "Generated JSON:" << json;
    this->mainFrame->evaluateJavaScript(QString("barTreeView.loadData(%1);").arg(json));
}

/**
 * Helper method to calculate the total frequency of a level.
 */
double BarTreeVisualization::calcTotalFreq(const QMap<QString, int> & frequencies) {
    static QString key;
    static double totalFrequency;

    totalFrequency = 0;
    foreach(key, frequencies.keys()) {
        totalFrequency += frequencies[key];
    }

    return totalFrequency;
}
<commit_msg>Ignore MouseMove events, as they don't show anything meaningful.<commit_after>#include "BarTreeVisualization.h"

BarTreeVisualization::BarTreeVisualization(QWidget *parent) : QWebView(parent) {
    this->mainFrame = this->page()->mainFrame();
    connect(this, SIGNAL(loadFinished(bool)), SLOT(loadHasFinished(bool)));

    // See explanation at BarTreeVisualization::timerEvent().
    startTimer(BARTREEVISUALIZATION_DATA_REFRESH_INTERVAL);

    // Load the visualization in the web view.
    this->load(QUrl("qrc:/BarTree/BarTree.html"));
}

void BarTreeVisualization::loadHasFinished(bool ok) {
    this->pageHasLoaded = ok;
}

void BarTreeVisualization::eventSequenceChanged(QVector<Event *> * events) {
    this->mutex.lock();
    this->nextEventSequence = events;
    this->mutex.unlock();
}

void BarTreeVisualization::highlightEventLocation(int msecs, const QString & eventType) {
    qDebug() << eventType;
}

/**
 * Periodically (BARTREEVISUALIZATION_DATA_REFRESH_INTERVAL intervals) poll
 * to see if there's a new event sequence to draw. We do this using polling
 * to prevent continuous redrawing. The number of redrawings per second is
 * now effectively limited to 1/BARTREEVISUALIZATION_DATA_REFRESH_INTERVAL.
 */
void BarTreeVisualization::timerEvent(QTimerEvent *) {
    // If the page hasn't been loaded yet, then definitely don't send an
    // event sequence to the web view yet!
    if (!this->pageHasLoaded)
        return;

    // Make the next event sequence the current, if any, and send it to the
    // web view.
    if (this->nextEventSequence != NULL) {
        this->mutex.lock();
        this->currentEventSequence = this->nextEventSequence;
        this->nextEventSequence = NULL;
        this->mutex.unlock();
        this->sendEventSequenceToWebView();
    }
}

/**
 * Convert the current event sequence into the data format the JavaScript
 * code expects, but then as QVariants. QxtJSON::stringify() will then
 * encode this data as JSON, which the JavaScript code can use directly.
 */
void BarTreeVisualization::sendEventSequenceToWebView() {
    QVariantMap data;
    QMap<QString, int> inputTypeFrequency;
    QMap<QString, QMap<QString, int> > eventTypeFrequency;
    QMap<QString, QMap<QString, QMap<QString, int> > > modifierFrequency;
    QString inputType, eventType, modifier, details;
    QStringList detailsList;
    Event * event;

    // Count frequencies.
    foreach (event, *this->currentEventSequence) {
        inputType = event->getInputType();
        eventType = event->getEventType();
        details = event->getDetails();

        // Ignore MouseButtonRelease events.
        if (eventType.compare(QString("MouseButtonRelease")) == 0)
            continue;

        // Ignore MouseMove events.
        if (eventType.compare(QString("MouseMove")) == 0)
            continue;

        if (eventType.contains("Mouse"))
            inputType = QString("Mouse");
        else
            inputType = QString("Keyboard");

        // Clean up event type for optimized display.
        eventType.replace("Key", "");
        eventType.replace("Mouse", "");
        // Keyboard.
        eventType.replace("Release", "Press");
        // Mouse.
        eventType.replace("ButtonPress", "Click");
        eventType.replace("ButtonDblClick", "Double");

        // Extract nice & clean "modifier" from event details.
        modifier = (inputType == "Mouse") ? "none" : "something";
        detailsList = details.split(';');
        if (inputType.compare("Mouse") == 0) {
            modifier = detailsList[2];
            if (modifier.length() == 0)
                modifier = "none";
        }
        else {
            detailsList[2].replace(":", "");
            bool modifierKeyPressed = (detailsList[2].length() > 0);
            bool regularKeyPressed = (detailsList[1].length() > 0);
            if (modifierKeyPressed && regularKeyPressed) {
                modifier = detailsList[2] + "+" + detailsList[1];
            }
            else if (modifierKeyPressed && !regularKeyPressed) {
                modifier = detailsList[2];
            }
            else if (!modifierKeyPressed && regularKeyPressed) {
                modifier = detailsList[1];
            }
            else {
                modifier = "other";
            }
        }

        // Init inputTypeFrequency (1st level frequency counts).
        if (!inputTypeFrequency.contains(inputType)) {
            inputTypeFrequency.insert(inputType, 0);
        }

        // Init eventTypeFrequency (2nd level frequency counts).
        if (!eventTypeFrequency.contains(inputType)) {
            QMap<QString, int> map;
            eventTypeFrequency.insert(inputType, map);
        }
        if (!eventTypeFrequency[inputType].contains(eventType)) {
            eventTypeFrequency[inputType].insert(eventType, 0);
        }

        // Init modifierFrequency (3rd level frequency counts).
        if (!modifierFrequency.contains(inputType)) {
            QMap<QString, QMap<QString, int> > map;
            modifierFrequency.insert(inputType, map);
        }
        if (!modifierFrequency[inputType].contains(eventType)) {
            QMap<QString, int> map;
            modifierFrequency[inputType].insert(eventType, map);
        }
        if (!modifierFrequency[inputType][eventType].contains(modifier)) {
            modifierFrequency[inputType][eventType].insert(modifier, 0);
        }

        // Incrementalization.
        inputTypeFrequency.insert(inputType, inputTypeFrequency.value(inputType) + 1);
        eventTypeFrequency[inputType].insert(eventType, eventTypeFrequency[inputType][eventType] + 1);
        modifierFrequency[inputType][eventType].insert(modifier, modifierFrequency[inputType][eventType][modifier] + 1);
    }

    // Build QVariantMap that represents the data in the format expected by the
    // JavaScript code.
    QString keyL1, keyL2, keyL3;
    QVariantMap level1, level2, level3, tmp;
    double totalL1, totalL2, totalL3;

    totalL1 = BarTreeVisualization::calcTotalFreq(inputTypeFrequency);
    level1.clear();
    foreach (keyL1, inputTypeFrequency.keys()) {

        totalL2 = BarTreeVisualization::calcTotalFreq(eventTypeFrequency[keyL1]);
        level2.clear();
        foreach (keyL2, eventTypeFrequency[keyL1].keys()) {

            totalL3 = BarTreeVisualization::calcTotalFreq(modifierFrequency[keyL1][keyL2]);
            level3.clear();
            foreach(keyL3, modifierFrequency[keyL1][keyL2].keys()) {
                tmp.clear();
                tmp.insert("count",     modifierFrequency[keyL1][keyL2][keyL3]);
                tmp.insert("frequency", modifierFrequency[keyL1][keyL2][keyL3] / totalL3);
                level3.insert(keyL3, tmp);
            }

            tmp.clear();
            tmp.insert("count",     eventTypeFrequency[keyL1][keyL2]);
            tmp.insert("frequency", eventTypeFrequency[keyL1][keyL2] / totalL2);
            tmp.insert("children", level3);
            level2.insert(keyL2, tmp);
        }

        tmp.clear();
        tmp.insert("count",     inputTypeFrequency[keyL1]);
        tmp.insert("frequency", inputTypeFrequency[keyL1] / totalL1);
        tmp.insert("children", level2);

        // Assign color depending on input type.
        tmp.insert("color", (keyL1 == "Mouse") ? BARTREEVISUALIZATION_COLOR_MOUSE : BARTREEVISUALIZATION_COLOR_KEYBOARD);

        level1.insert(keyL1, tmp);
    }
    data = level1;

    QString json = QxtJSON::stringify(data);
    qDebug() << "Generated JSON:" << json;
    this->mainFrame->evaluateJavaScript(QString("barTreeView.loadData(%1);").arg(json));
}

/**
 * Helper method to calculate the total frequency of a level.
 */
double BarTreeVisualization::calcTotalFreq(const QMap<QString, int> & frequencies) {
    static QString key;
    static double totalFrequency;

    totalFrequency = 0;
    foreach(key, frequencies.keys()) {
        totalFrequency += frequencies[key];
    }

    return totalFrequency;
}
<|endoftext|>


<commit_before>#include "HeatMapVisualization.h"

HeatMapVisualization::HeatMapVisualization(Database *database) : QWidget() {

    lastEvents = NULL;
    height = database->getResolution().height();
    width = database->getResolution().width();

    heatMap = new int*[height];
    for (int i = 0; i < height; ++i)
        heatMap[i] = new int[width];

    clickHeatMap = new int*[height];
    for (int i = 0; i < height; ++i)
        clickHeatMap[i] = new int[width];

    mouseRoute = new QVector<QPoint>();

    clearHeatMap();

    marge = 20;
    mouseRouteInterval = 1;
    clickDeviation = 10;

    showLeftClicks = showRightClicks = showSingleClicks = showDoubleClicks = showMouseMoveRoute = true;
    showMouseMove = false;

    screenWidth = (QApplication::desktop()->width() >= 1920) ? 1050: 470;
    screenWidth = (screenWidth > width) ? width : screenWidth;
    screenHeight = (QApplication::desktop()->width() > 800) ? 500 : 300;
    screenHeight = (screenHeight > height) ? height : screenHeight;

    heatMapImage = new QImage(width, height, QImage::Format_ARGB32_Premultiplied);

    scene = new HeatMapVisQGraphicsScene();
    view = new QGraphicsView(scene);
    view->setRenderHints(QPainter::Antialiasing);
    scene->setSceneRect(0, 0, screenWidth, screenHeight);
    connect(scene, SIGNAL(clicked(QPointF)), SLOT(pixelSelected(QPointF)));

    mainLayout = new QVBoxLayout();
    mainLayout->addWidget(view, 0, Qt::AlignCenter);

    createCheckBoxes();
    determineAvailableBackgroundImages(database->getMaxEventTime());

    renderVisualization();

    setLayout(mainLayout);
}

void HeatMapVisualization::update(QVector<Event*> *events = NULL) {
    clearHeatMap();

    if(events != NULL)
        lastEvents = events;
    else if(lastEvents != NULL)
        events = lastEvents;
    else
        return;

    lastEventTime = events->isEmpty() ? 0 : events->last()->getTime();

    scene->removeItem(scene->numClicksLabel);
    scene->numClicksLabel = NULL;

    int counter = 0, mouseRouteX = 0, mouseRouteY = 0;
    for(int i = 0; i < events->count(); ++i) {
        if((showSingleClicks && events->at(i)->getEventType().compare("MouseButtonPress") == 0) ||
           (showDoubleClicks && events->at(i)->getEventType().compare("MouseButtonDblClick") == 0) ||
           ((showMouseMove || showMouseMoveRoute) && events->at(i)->getEventType().compare("MouseMove") == 0)) {
            // enkel muiskliks (enkel en dubbel)

            QString details = events->at(i)->getDetails();
            details.remove("\"");

            QStringList args = details.split(";");
            if (args.count() >= 2) {
                int x = args.at(0).toInt();
                int y = args.at(1).toInt();

                //qDebug() << details << x  << y;
                if (x >= 0 && y >= 0 && x < height && y < width)  {
                    if (showMouseMove && events->at(i)->getEventType().compare("MouseMove") == 0)
                        clickOnHeatMap(x,y);
                    if ((showSingleClicks || showDoubleClicks) && args.count() == 3 && events->at(i)->getEventType().compare("MouseMove") != 0) {
                        // controleren: links en rechts filteren indien nodig (checkBoxes)
                        if (showLeftClicks && args.at(2).indexOf("L") != -1)// args.at(2) == ""L"	" "
                            clickOnHeatMap(x,y);
                        else if (showRightClicks && args.at(2).indexOf("R") != -1)
                            clickOnHeatMap(x,y);
                        clickHeatMap[x][y]++;
                    }
                    if (showMouseMoveRoute && events->at(i)->getEventType().compare("MouseMove") == 0) { //muisbewegingen tekenen => gemiddeld punt berekenen per mouseRouteInterval bewegingen
                        if (counter < mouseRouteInterval) {
                            mouseRouteX += x;
                            mouseRouteY += y;
                            counter++;
                        }
                        if(counter == mouseRouteInterval) {
                            mouseRoute->append(QPoint(mouseRouteY/mouseRouteInterval, mouseRouteX/mouseRouteInterval));
                            counter = 0;
                            mouseRouteX = 0;
                            mouseRouteY = 0;
                        }
                    }
                }
            }
        }
    }

    renderVisualization();
}

void HeatMapVisualization::clickOnHeatMap(int x, int y) {
    // waar geklikt is, een hoge score toekennen, de marge errond score alsmaar verkleinen
    for (int k = -marge; k <= marge && x + k < height; ++k)
        for (int l = -marge; l <= marge && y+l < width; ++l)
            if(x + k > 0 && y + l > 0) {
                heatMap[x + k][y + l] += (marge + 1) - max(abs(k),abs(l));
            }

    if (maxClicks < heatMap[x][y])//hoogst voorkomend aantal kliks bijhouden
        maxClicks = heatMap[x][y];
}

void HeatMapVisualization::renderVisualization() {
    QColor color;
    int h, v, s, a;

    for (int i = 0; i < height; ++i) {
        for (int j = 0; j < width; ++j) {
            if (heatMap[i][j] == 0) {// er is niet geklikt
                h = 255;
                v = 0;
                s = 255;
                a = 0;
                color.setHsv(h,v,s,a);
                heatMapImage->setPixel(j, i, color.rgba());
            }
            else {// geklikt
                s = 255;
                float value = float(heatMap[i][j])/float(maxClicks);// verhouding: is op deze plaats veel geklikt

                v = value * 255.0;

                a = v * 1/30;

                if (value > 0.333){//matig tot veel geklikt => de kleuren geel tot rood hiervoor gebruiken
                    float inversDiff = 3/2, min = 1/3;
                    int color = 60;//==colorRange
                    //0 rood
                    //60 geel
                    h = color - (inversDiff * (value - min)) * color;

                    if (heatMap[i][j] > maxClicks) {
                        h = 0;
                        v = 255;//in deze omgeving is heel veel geklikt
                        s = 230;// => extra benadrukken

                    }
                }
                else {// weinig geklikt (marge rond klik) => blauw tinten
                    float inversDiff = 3/1, min = 0;
                    int color = 240;
                    int colorRange = 180;
                    //60 geel
                    //240 blauw
                    h = color - (inversDiff * (value - min)) * colorRange;
                }
                //qDebug() << "color " << h << v << s;
                color.setHsv(h,v,s,a);
                heatMapImage->setPixel(j, i, color.rgba());
            }
        }
    }

    //muisbewigingen tekenen
    QPainter painter;
    painter.begin(heatMapImage);
    for(int i = 0; i + 1 < mouseRoute->size(); ++i){
        QLineF line(mouseRoute->at(i), mouseRoute->at(i+1));

        painter.drawLine(line);
    }
    painter.end();

    scene->setBackgroundImage(determineBackgroundImage(lastEventTime));
    scene->setForegroundImage(heatMapImage);
    scene->invalidate();
}

void HeatMapVisualization::highlightEventLocation(int msec) {
    update();
    for (int i = 0; i < lastEvents->size(); ++i)
        if (lastEvents->at(i)->getTime() == msec) {
            QString details = lastEvents->at(i)->getDetails();
            details.remove("\"");

            QStringList args = details.split(";");
            if (args.count() >= 2) {
                QPoint p = QPoint(args.at(1).toInt(), args.at(0).toInt());

                QPainter painter;
                painter.begin(heatMapImage);
                painter.setBrush(QBrush(QColor(255,0,255)));
                painter.drawEllipse(p, 5, 5);

                painter.end();
            }
        }
    scene->setBackgroundImage(&(determineBackgroundImage(lastEventTime)->scaled(QSize(screenWidth,screenHeight), Qt::KeepAspectRatio)));
    scene->setForegroundImage(heatMapImage);
    scene->invalidate();
}

void HeatMapVisualization::pixelSelected(QPointF p) {
    int numClicks = 0;
    for (int i = -clickDeviation; i <= clickDeviation && p.y() + i < height; ++i)
        for (int j = -clickDeviation; j <= clickDeviation && p.x() + j < width; ++j)
            if(p.x() + i > 0 && p.y() + j > 0)
                numClicks += clickHeatMap[int(p.y()) + i][int(p.x()) + j];

    if(scene->numClicksLabel == NULL)
        scene->numClicksLabel = scene->addText("");

    scene->numClicksLabel->setHtml("
" + QString::number(numClicks) + "
");
    scene->numClicksLabel->setPos(p);

    scene->invalidate();
}

int HeatMapVisualization::max(int a, int b) {
    return (a>b)?a:b;
}

void HeatMapVisualization::clearHeatMap() {
    maxClicks = 0;

    for (int i = 0; i < height; ++i)
        for (int j = 0; j < width; ++j)
            heatMap[i][j] = 0;

    for (int i = 0; i < height; ++i)
        for (int j = 0; j < width; ++j)
            clickHeatMap[i][j] = 0;

    mouseRoute->clear();
}

void HeatMapVisualization::updateParameters(int state) {
    Q_UNUSED(state);

    showLeftClicks = showLeftClicksCheckBox->isChecked();
    showRightClicks = showRightClicksCheckBox->isChecked();
    showSingleClicks = showSingleClicksCheckBox->isChecked();
    showDoubleClicks = showDoubleClicksCheckBox->isChecked();
    showMouseMove = showMouseMoveCheckBox->isChecked();
    showMouseMoveRoute = showMouseMoveRouteCheckBox->isChecked();

    showLeftClicksCheckBox->setEnabled(true);
    showRightClicksCheckBox->setEnabled(true);
    showSingleClicksCheckBox->setEnabled(true);
    showDoubleClicksCheckBox->setEnabled(true);
    showMouseMoveCheckBox->setEnabled(true);
    showMouseMoveRouteCheckBox->setEnabled(true);

    if(!showSingleClicks && !showDoubleClicks) {
        showLeftClicksCheckBox->setEnabled(false);
        showRightClicksCheckBox->setEnabled(false);
    }

    update();
}

void HeatMapVisualization::updateMarge(int marge) {
    this->marge = marge;

    update();
}

void HeatMapVisualization::updateMouseRouteInterval(int interval) {
    mouseRouteInterval = interval;

    update();
}

void HeatMapVisualization::createCheckBoxes() {
    QGroupBox *box1 = new QGroupBox();
    QVBoxLayout *vbox1 = new QVBoxLayout();
    showLeftClicksCheckBox = new QCheckBox("Show left-clicks");
    showRightClicksCheckBox = new QCheckBox("Show right-clicks");
    vbox1->addWidget(showLeftClicksCheckBox);
    vbox1->addWidget(showRightClicksCheckBox);
    box1->setLayout(vbox1);

    QGroupBox *box2 = new QGroupBox();
    QVBoxLayout *vbox2 = new QVBoxLayout();
    showSingleClicksCheckBox = new QCheckBox("Show single-clicks");
    showDoubleClicksCheckBox = new QCheckBox("Show double-clicks");
    vbox2->addWidget(showSingleClicksCheckBox);
    vbox2->addWidget(showDoubleClicksCheckBox);
    box2->setLayout(vbox2);

    QGroupBox *box3 = new QGroupBox();
    QVBoxLayout *vbox3 = new QVBoxLayout();
    showMouseMoveRouteCheckBox = new QCheckBox("Show mouse route");
    showMouseMoveCheckBox = new QCheckBox("Show mouse moves");
    vbox3->addWidget(showMouseMoveRouteCheckBox);
    vbox3->addWidget(showMouseMoveCheckBox);
    box3->setLayout(vbox3);

    showLeftClicksCheckBox->setChecked(showLeftClicks);
    showRightClicksCheckBox->setChecked(showRightClicks);
    showSingleClicksCheckBox->setChecked(showSingleClicks);
    showDoubleClicksCheckBox->setChecked(showDoubleClicks);
    showMouseMoveCheckBox->setChecked(showMouseMove);
    showMouseMoveRouteCheckBox->setChecked(showMouseMoveRoute);

    connect(showLeftClicksCheckBox, SIGNAL(stateChanged(int)), SLOT(updateParameters(int)));
    connect(showRightClicksCheckBox, SIGNAL(stateChanged(int)), SLOT(updateParameters(int)));
    connect(showSingleClicksCheckBox, SIGNAL(stateChanged(int)), SLOT(updateParameters(int)));
    connect(showDoubleClicksCheckBox, SIGNAL(stateChanged(int)), SLOT(updateParameters(int)));
    connect(showMouseMoveCheckBox, SIGNAL(stateChanged(int)), SLOT(updateParameters(int)));
    connect(showMouseMoveRouteCheckBox, SIGNAL(stateChanged(int)), SLOT(updateParameters(int)));

    QLabel *margeLabel = new QLabel("Click radius: ");
    margeSpinBox = new QSpinBox();
    margeSpinBox->setRange(1, 100);
    margeSpinBox->setValue(marge);
    connect(margeSpinBox, SIGNAL(valueChanged(int)), SLOT(updateMarge(int)));

    QLabel *mouseRouteIntervalLabel = new QLabel("Mouse move accuracy: ");
    mouseRouteIntervalSpinBox = new QSpinBox();
    mouseRouteIntervalSpinBox->setRange(1, 100);
    mouseRouteIntervalSpinBox->setValue(mouseRouteInterval);
    connect(mouseRouteIntervalSpinBox, SIGNAL(valueChanged(int)), SLOT(updateMouseRouteInterval(int)));

    QGroupBox *box4 = new QGroupBox();
    QVBoxLayout *vbox4 = new QVBoxLayout();
    QHBoxLayout *hbox4_1 = new QHBoxLayout();
    hbox4_1->addWidget(margeLabel);
    hbox4_1->addWidget(margeSpinBox);
    QHBoxLayout *hbox4_2 = new QHBoxLayout();
    hbox4_2->addWidget(mouseRouteIntervalLabel);
    hbox4_2->addWidget(mouseRouteIntervalSpinBox);
    vbox4->addLayout(hbox4_1);
    vbox4->addLayout(hbox4_2);
    box4->setLayout(vbox4);

    QHBoxLayout *inputLayout = new QHBoxLayout();
    inputLayout->addWidget(box1);
    inputLayout->addWidget(box2);
    inputLayout->addWidget(box3);
    inputLayout->addWidget(box4);

    mainLayout->addLayout(inputLayout);
}

void HeatMapVisualization::determineAvailableBackgroundImages(int msecs) {
    availableBackgroundImages = new QHash<int, bool>();
    QString filename;
    foreach(filename, QDir("./screenshots").entryList())
        if(filename.contains(".png"))
            availableBackgroundImages->insert((filename.split('.')[0]).toInt(), true);
}

QImage* HeatMapVisualization::determineBackgroundImage(int msecs) {
    return (availableBackgroundImages->contains(msecs)) ?
            new QImage("./screenshots/" + QString::number(msecs) + ".png")
                :
            new QImage();
}
<commit_msg>Fixed determineBackgroundImage(), which now actually searches a bit around before deciding to just return an empty background image.<commit_after>#include "HeatMapVisualization.h"

HeatMapVisualization::HeatMapVisualization(Database *database) : QWidget() {

    lastEvents = NULL;
    height = database->getResolution().height();
    width = database->getResolution().width();

    heatMap = new int*[height];
    for (int i = 0; i < height; ++i)
        heatMap[i] = new int[width];

    clickHeatMap = new int*[height];
    for (int i = 0; i < height; ++i)
        clickHeatMap[i] = new int[width];

    mouseRoute = new QVector<QPoint>();

    clearHeatMap();

    marge = 20;
    mouseRouteInterval = 1;
    clickDeviation = 10;

    showLeftClicks = showRightClicks = showSingleClicks = showDoubleClicks = showMouseMoveRoute = true;
    showMouseMove = false;

    screenWidth = (QApplication::desktop()->width() >= 1920) ? 1050: 470;
    screenWidth = (screenWidth > width) ? width : screenWidth;
    screenHeight = (QApplication::desktop()->width() > 800) ? 500 : 300;
    screenHeight = (screenHeight > height) ? height : screenHeight;

    heatMapImage = new QImage(width, height, QImage::Format_ARGB32_Premultiplied);

    scene = new HeatMapVisQGraphicsScene();
    view = new QGraphicsView(scene);
    view->setRenderHints(QPainter::Antialiasing);
    scene->setSceneRect(0, 0, screenWidth, screenHeight);
    connect(scene, SIGNAL(clicked(QPointF)), SLOT(pixelSelected(QPointF)));

    mainLayout = new QVBoxLayout();
    mainLayout->addWidget(view, 0, Qt::AlignCenter);

    createCheckBoxes();

    determineAvailableBackgroundImages(database->getMaxEventTime());
    renderVisualization();

    setLayout(mainLayout);
}

void HeatMapVisualization::update(QVector<Event*> *events = NULL) {
    clearHeatMap();

    if(events != NULL)
        lastEvents = events;
    else if(lastEvents != NULL)
        events = lastEvents;
    else
        return;

    lastEventTime = events->isEmpty() ? 0 : events->last()->getTime();

    scene->removeItem(scene->numClicksLabel);
    scene->numClicksLabel = NULL;

    int counter = 0, mouseRouteX = 0, mouseRouteY = 0;
    for(int i = 0; i < events->count(); ++i) {
        if((showSingleClicks && events->at(i)->getEventType().compare("MouseButtonPress") == 0) ||
           (showDoubleClicks && events->at(i)->getEventType().compare("MouseButtonDblClick") == 0) ||
           ((showMouseMove || showMouseMoveRoute) && events->at(i)->getEventType().compare("MouseMove") == 0)) {
            // enkel muiskliks (enkel en dubbel)

            QString details = events->at(i)->getDetails();
            details.remove("\"");

            QStringList args = details.split(";");
            if (args.count() >= 2) {
                int x = args.at(0).toInt();
                int y = args.at(1).toInt();

                //qDebug() << details << x  << y;
                if (x >= 0 && y >= 0 && x < height && y < width)  {
                    if (showMouseMove && events->at(i)->getEventType().compare("MouseMove") == 0)
                        clickOnHeatMap(x,y);
                    if ((showSingleClicks || showDoubleClicks) && args.count() == 3 && events->at(i)->getEventType().compare("MouseMove") != 0) {
                        // controleren: links en rechts filteren indien nodig (checkBoxes)
                        if (showLeftClicks && args.at(2).indexOf("L") != -1)// args.at(2) == ""L"	" "
                            clickOnHeatMap(x,y);
                        else if (showRightClicks && args.at(2).indexOf("R") != -1)
                            clickOnHeatMap(x,y);
                        clickHeatMap[x][y]++;
                    }
                    if (showMouseMoveRoute && events->at(i)->getEventType().compare("MouseMove") == 0) { //muisbewegingen tekenen => gemiddeld punt berekenen per mouseRouteInterval bewegingen
                        if (counter < mouseRouteInterval) {
                            mouseRouteX += x;
                            mouseRouteY += y;
                            counter++;
                        }
                        if(counter == mouseRouteInterval) {
                            mouseRoute->append(QPoint(mouseRouteY/mouseRouteInterval, mouseRouteX/mouseRouteInterval));
                            counter = 0;
                            mouseRouteX = 0;
                            mouseRouteY = 0;
                        }
                    }
                }
            }
        }
    }

    renderVisualization();
}

void HeatMapVisualization::clickOnHeatMap(int x, int y) {
    // waar geklikt is, een hoge score toekennen, de marge errond score alsmaar verkleinen
    for (int k = -marge; k <= marge && x + k < height; ++k)
        for (int l = -marge; l <= marge && y+l < width; ++l)
            if(x + k > 0 && y + l > 0) {
                heatMap[x + k][y + l] += (marge + 1) - max(abs(k),abs(l));
            }

    if (maxClicks < heatMap[x][y])//hoogst voorkomend aantal kliks bijhouden
        maxClicks = heatMap[x][y];
}

void HeatMapVisualization::renderVisualization() {
    QColor color;
    int h, v, s, a;

    for (int i = 0; i < height; ++i) {
        for (int j = 0; j < width; ++j) {
            if (heatMap[i][j] == 0) {// er is niet geklikt
                h = 255;
                v = 0;
                s = 255;
                a = 0;
                color.setHsv(h,v,s,a);
                heatMapImage->setPixel(j, i, color.rgba());
            }
            else {// geklikt
                s = 255;
                float value = float(heatMap[i][j])/float(maxClicks);// verhouding: is op deze plaats veel geklikt

                v = value * 255.0;

                a = v * 1/30;

                if (value > 0.333){//matig tot veel geklikt => de kleuren geel tot rood hiervoor gebruiken
                    float inversDiff = 3/2, min = 1/3;
                    int color = 60;//==colorRange
                    //0 rood
                    //60 geel
                    h = color - (inversDiff * (value - min)) * color;

                    if (heatMap[i][j] > maxClicks) {
                        h = 0;
                        v = 255;//in deze omgeving is heel veel geklikt
                        s = 230;// => extra benadrukken

                    }
                }
                else {// weinig geklikt (marge rond klik) => blauw tinten
                    float inversDiff = 3/1, min = 0;
                    int color = 240;
                    int colorRange = 180;
                    //60 geel
                    //240 blauw
                    h = color - (inversDiff * (value - min)) * colorRange;
                }
                //qDebug() << "color " << h << v << s;
                color.setHsv(h,v,s,a);
                heatMapImage->setPixel(j, i, color.rgba());
            }
        }
    }

    //muisbewigingen tekenen
    QPainter painter;
    painter.begin(heatMapImage);
    for(int i = 0; i + 1 < mouseRoute->size(); ++i){
        QLineF line(mouseRoute->at(i), mouseRoute->at(i+1));

        painter.drawLine(line);
    }
    painter.end();

    scene->setBackgroundImage(determineBackgroundImage(lastEventTime));
    scene->setForegroundImage(heatMapImage);
    scene->invalidate();
}

void HeatMapVisualization::highlightEventLocation(int msec) {
    update();
    for (int i = 0; i < lastEvents->size(); ++i)
        if (lastEvents->at(i)->getTime() == msec) {
            QString details = lastEvents->at(i)->getDetails();
            details.remove("\"");

            QStringList args = details.split(";");
            if (args.count() >= 2) {
                QPoint p = QPoint(args.at(1).toInt(), args.at(0).toInt());

                QPainter painter;
                painter.begin(heatMapImage);
                painter.setBrush(QBrush(QColor(255,0,255)));
                painter.drawEllipse(p, 5, 5);

                painter.end();
            }
        }
    scene->setBackgroundImage(&(determineBackgroundImage(lastEventTime)->scaled(QSize(screenWidth,screenHeight), Qt::KeepAspectRatio)));
    scene->setForegroundImage(heatMapImage);
    scene->invalidate();
}

void HeatMapVisualization::pixelSelected(QPointF p) {
    int numClicks = 0;
    for (int i = -clickDeviation; i <= clickDeviation && p.y() + i < height; ++i)
        for (int j = -clickDeviation; j <= clickDeviation && p.x() + j < width; ++j)
            if(p.x() + i > 0 && p.y() + j > 0)
                numClicks += clickHeatMap[int(p.y()) + i][int(p.x()) + j];

    if(scene->numClicksLabel == NULL)
        scene->numClicksLabel = scene->addText("");

    scene->numClicksLabel->setHtml("
" + QString::number(numClicks) + "
");
    scene->numClicksLabel->setPos(p);

    scene->invalidate();
}

int HeatMapVisualization::max(int a, int b) {
    return (a>b)?a:b;
}

void HeatMapVisualization::clearHeatMap() {
    maxClicks = 0;

    for (int i = 0; i < height; ++i)
        for (int j = 0; j < width; ++j)
            heatMap[i][j] = 0;

    for (int i = 0; i < height; ++i)
        for (int j = 0; j < width; ++j)
            clickHeatMap[i][j] = 0;

    mouseRoute->clear();
}

void HeatMapVisualization::updateParameters(int state) {
    Q_UNUSED(state);

    showLeftClicks = showLeftClicksCheckBox->isChecked();
    showRightClicks = showRightClicksCheckBox->isChecked();
    showSingleClicks = showSingleClicksCheckBox->isChecked();
    showDoubleClicks = showDoubleClicksCheckBox->isChecked();
    showMouseMove = showMouseMoveCheckBox->isChecked();
    showMouseMoveRoute = showMouseMoveRouteCheckBox->isChecked();

    showLeftClicksCheckBox->setEnabled(true);
    showRightClicksCheckBox->setEnabled(true);
    showSingleClicksCheckBox->setEnabled(true);
    showDoubleClicksCheckBox->setEnabled(true);
    showMouseMoveCheckBox->setEnabled(true);
    showMouseMoveRouteCheckBox->setEnabled(true);

    if(!showSingleClicks && !showDoubleClicks) {
        showLeftClicksCheckBox->setEnabled(false);
        showRightClicksCheckBox->setEnabled(false);
    }

    update();
}

void HeatMapVisualization::updateMarge(int marge) {
    this->marge = marge;

    update();
}

void HeatMapVisualization::updateMouseRouteInterval(int interval) {
    mouseRouteInterval = interval;

    update();
}

void HeatMapVisualization::createCheckBoxes() {
    QGroupBox *box1 = new QGroupBox();
    QVBoxLayout *vbox1 = new QVBoxLayout();
    showLeftClicksCheckBox = new QCheckBox("Show left-clicks");
    showRightClicksCheckBox = new QCheckBox("Show right-clicks");
    vbox1->addWidget(showLeftClicksCheckBox);
    vbox1->addWidget(showRightClicksCheckBox);
    box1->setLayout(vbox1);

    QGroupBox *box2 = new QGroupBox();
    QVBoxLayout *vbox2 = new QVBoxLayout();
    showSingleClicksCheckBox = new QCheckBox("Show single-clicks");
    showDoubleClicksCheckBox = new QCheckBox("Show double-clicks");
    vbox2->addWidget(showSingleClicksCheckBox);
    vbox2->addWidget(showDoubleClicksCheckBox);
    box2->setLayout(vbox2);

    QGroupBox *box3 = new QGroupBox();
    QVBoxLayout *vbox3 = new QVBoxLayout();
    showMouseMoveRouteCheckBox = new QCheckBox("Show mouse route");
    showMouseMoveCheckBox = new QCheckBox("Show mouse moves");
    vbox3->addWidget(showMouseMoveRouteCheckBox);
    vbox3->addWidget(showMouseMoveCheckBox);
    box3->setLayout(vbox3);

    showLeftClicksCheckBox->setChecked(showLeftClicks);
    showRightClicksCheckBox->setChecked(showRightClicks);
    showSingleClicksCheckBox->setChecked(showSingleClicks);
    showDoubleClicksCheckBox->setChecked(showDoubleClicks);
    showMouseMoveCheckBox->setChecked(showMouseMove);
    showMouseMoveRouteCheckBox->setChecked(showMouseMoveRoute);

    connect(showLeftClicksCheckBox, SIGNAL(stateChanged(int)), SLOT(updateParameters(int)));
    connect(showRightClicksCheckBox, SIGNAL(stateChanged(int)), SLOT(updateParameters(int)));
    connect(showSingleClicksCheckBox, SIGNAL(stateChanged(int)), SLOT(updateParameters(int)));
    connect(showDoubleClicksCheckBox, SIGNAL(stateChanged(int)), SLOT(updateParameters(int)));
    connect(showMouseMoveCheckBox, SIGNAL(stateChanged(int)), SLOT(updateParameters(int)));
    connect(showMouseMoveRouteCheckBox, SIGNAL(stateChanged(int)), SLOT(updateParameters(int)));

    QLabel *margeLabel = new QLabel("Click radius: ");
    margeSpinBox = new QSpinBox();
    margeSpinBox->setRange(1, 100);
    margeSpinBox->setValue(marge);
    connect(margeSpinBox, SIGNAL(valueChanged(int)), SLOT(updateMarge(int)));

    QLabel *mouseRouteIntervalLabel = new QLabel("Mouse move accuracy: ");
    mouseRouteIntervalSpinBox = new QSpinBox();
    mouseRouteIntervalSpinBox->setRange(1, 100);
    mouseRouteIntervalSpinBox->setValue(mouseRouteInterval);
    connect(mouseRouteIntervalSpinBox, SIGNAL(valueChanged(int)), SLOT(updateMouseRouteInterval(int)));

    QGroupBox *box4 = new QGroupBox();
    QVBoxLayout *vbox4 = new QVBoxLayout();
    QHBoxLayout *hbox4_1 = new QHBoxLayout();
    hbox4_1->addWidget(margeLabel);
    hbox4_1->addWidget(margeSpinBox);
    QHBoxLayout *hbox4_2 = new QHBoxLayout();
    hbox4_2->addWidget(mouseRouteIntervalLabel);
    hbox4_2->addWidget(mouseRouteIntervalSpinBox);
    vbox4->addLayout(hbox4_1);
    vbox4->addLayout(hbox4_2);
    box4->setLayout(vbox4);

    QHBoxLayout *inputLayout = new QHBoxLayout();
    inputLayout->addWidget(box1);
    inputLayout->addWidget(box2);
    inputLayout->addWidget(box3);
    inputLayout->addWidget(box4);

    mainLayout->addLayout(inputLayout);
}

void HeatMapVisualization::determineAvailableBackgroundImages(int msecs) {
    availableBackgroundImages = new QHash<int, bool>();
    QString filename;
    foreach(filename, QDir("./screenshots").entryList())
        if(filename.contains(".png"))
            availableBackgroundImages->insert((filename.split('.')[0]).toInt(), true);
}

QImage* HeatMapVisualization::determineBackgroundImage(int msecs) {
    while(msecs > 0) {
        if(availableBackgroundImages->contains(msecs))
            return new QImage("./screenshots/" + QString::number(msecs) + ".png");
        --msecs;
    }
    return new QImage();
}
<|endoftext|>


<commit_before>#pragma once
//=====================================================================//
/*! @file
    @brief  SEEDA03 (RX64M) メイン
	@copyright Copyright 2017 Kunihito Hiramatsu All Right Reserved.
    @author 平松邦仁 (hira@rvf-rc45.net)
*/
//=====================================================================//
#include "common/renesas.hpp"

#include "common/rspi_io.hpp"
#include "common/spi_io.hpp"
#include "common/sdc_io.hpp"
#include "common/command.hpp"
#include "common/fifo.hpp"
#include "common/format.hpp"
#include "common/input.hpp"
#include "common/flash_man.hpp"
#include "common/time.h"

#include "chip/LTC2348_16.hpp"
#include "sample.hpp"

#include "chip/EUI_XX.hpp"

#include "GR/core/http_server.hpp"
#include "GR/core/ftp_server.hpp"

#ifdef DEBUG
#define CLIENT_DEBUG
#define NETS_DEBUG
#define WRITE_FILE_DEBUG
#endif

namespace seeda {

	static const int seeda_version_ = 234;
	static const uint32_t build_id_ = B_ID;

	typedef utils::command<256> CMD;

#ifdef SEEDA
	typedef device::PORT<device::PORT6, device::bitpos::B7> SW1;
	typedef device::PORT<device::PORT6, device::bitpos::B6> SW2;
#endif

	// Soft SDC 用　SPI 定義（SPI）
#ifdef SEEDA
	typedef device::PORT<device::PORTD, device::bitpos::B6> MISO;
	typedef device::PORT<device::PORTD, device::bitpos::B4> MOSI;
	typedef device::PORT<device::PORTD, device::bitpos::B5> SPCK;
	typedef device::spi_io<MISO, MOSI, SPCK> SPI;

	typedef device::PORT<device::PORTD, device::bitpos::B3> SDC_SELECT;	///< カード選択信号
	typedef device::NULL_PORT  SDC_POWER;	///< カード電源制御（常に電源ＯＮ）
	typedef device::PORT<device::PORTE, device::bitpos::B6> SDC_DETECT;	///< カード検出
#else
	// SDC 用　SPI 定義（RSPI）
	typedef device::rspi_io<device::RSPI> SPI;
	typedef device::PORT<device::PORTC, device::bitpos::B4> SDC_SELECT;	///< カード選択信号
	typedef device::NULL_PORT  SDC_POWER;	///< カード電源制御（常に電源ＯＮ）
	typedef device::PORT<device::PORTB, device::bitpos::B7> SDC_DETECT;	///< カード検出
#endif
	typedef utils::sdc_io<SPI, SDC_SELECT, SDC_POWER, SDC_DETECT> SDC;

#ifdef SEEDA
	// LTC2348-16 A/D 制御ポート定義
	typedef device::PORT<device::PORT4, device::bitpos::B0> LTC_CSN;   // P40(141)
	typedef device::PORT<device::PORTC, device::bitpos::B6> LTC_CNV;   // PC6(61)
	typedef device::PORT<device::PORTD, device::bitpos::B0> LTC_BUSY;  // PD0/IRQ0(126)
	typedef device::PORT<device::PORT5, device::bitpos::B3> LTC_PD;    // P53(53)
	typedef device::PORT<device::PORT5, device::bitpos::B6> LTC_SDI;   // P56(50)
	typedef device::PORT<device::PORT8, device::bitpos::B6> LTC_SCKO;  // P86(41)
	typedef device::PORT<device::PORT8, device::bitpos::B7> LTC_SCKI;  // P87(39)
	typedef device::PORT<device::PORT2, device::bitpos::B0> LTC_SDO0;  // P20(37)
	typedef device::PORT<device::PORT2, device::bitpos::B1> LTC_SDO2;  // P21(36)
	typedef device::PORT<device::PORT2, device::bitpos::B2> LTC_SDO4;  // P22(35)
	typedef device::PORT<device::PORT2, device::bitpos::B3> LTC_SDO6;  // P23(34)
	typedef struct chip::LTC2348_SDO_t<LTC_SCKO, LTC_SDO0, LTC_SDO2, LTC_SDO4, LTC_SDO6> LTC_SDO;
	typedef chip::LTC2348_16<LTC_CSN, LTC_CNV, LTC_BUSY, LTC_PD, LTC_SDI, LTC_SCKI, LTC_SDO> EADC;
#endif

	typedef device::flash_io FLASH_IO;
	typedef utils::flash_man<FLASH_IO> FLASH_MAN;

	typedef net::http_server<SDC, 16, 8192> HTTP;
	typedef HTTP::http_format http_format;

	typedef net::ftp_server<SDC> FTP;

	//-----------------------------------------------------------------//
	/*!
		@brief  SDC_IO クラスへの参照
		@return SDC_IO クラス
	*/
	//-----------------------------------------------------------------//
	SDC& at_sdc();

#ifdef SEEDA
	//-----------------------------------------------------------------//
	/*!
		@brief  EADC クラスへの参照
		@return EADC クラス
	*/
	//-----------------------------------------------------------------//
	EADC& at_eadc();
#endif

	//-----------------------------------------------------------------//
	/*!
		@brief  EADC サーバー
	*/
	//-----------------------------------------------------------------//
	void eadc_server();

	//-----------------------------------------------------------------//
	/*!
		@brief  EADC サーバー許可
		@param[in]	ena	「false」の場合不許可
	*/
	//-----------------------------------------------------------------//
	void enable_eadc_server(bool ena = true);

	//-----------------------------------------------------------------//
	/*!
		@brief  時間の作成
		@param[in]	date	日付
		@param[in]	time	時間
	*/
	//-----------------------------------------------------------------//
	size_t make_time(const char* date, const char* time);

	//-----------------------------------------------------------------//
	/*!
		@brief  GMT 時間の設定
		@param[in]	t	GMT 時間
	*/
	//-----------------------------------------------------------------//
	void set_time(time_t t);

	//-----------------------------------------------------------------//
	/*!
		@brief  時間の表示
		@param[in]	t		時間
		@param[in]	dst		出力文字列
		@param[in]	size	文字列の大きさ
		@return 生成された文字列の長さ
	*/
	//-----------------------------------------------------------------//
	int disp_time(time_t t, char* dst = nullptr, uint32_t size = 0);

	//-----------------------------------------------------------------//
	/*!
		@brief  設定スイッチの状態を取得
		@return 設定スイッチの状態
	*/
	//-----------------------------------------------------------------//
	uint8_t get_switch()
	{
#ifdef SEEDA
		return static_cast<uint8_t>(!SW1::P()) | (static_cast<uint8_t>(!SW2::P()) << 1);
#else
		return 0;  // for only develope mode
#endif
	}

	//-----------------------------------------------------------------//
	/*!
		@brief  A/D サンプルの参照
		@param[in]	ch	チャネル（０～７）
		@return A/D サンプル
	*/
	//-----------------------------------------------------------------//
	sample_t& at_sample(uint8_t ch);

	//-----------------------------------------------------------------//
	/*!
		@brief  A/D サンプルの取得
		@return A/D サンプル
	*/
	//-----------------------------------------------------------------//
	const sample_data& get_sample_data();

	//-----------------------------------------------------------------//
	/*!
		@brief  内臓 A/D 変換値の取得
		@param[in]	ch	チャネル（５、６、７）
		@return A/D 変換値
	*/
	//-----------------------------------------------------------------//
	uint16_t get_adc(uint32_t ch);
}

extern "C" {

	//-----------------------------------------------------------------//
	/*!
		@brief  GMT 時間の取得
		@return GMT 時間
	*/
	//-----------------------------------------------------------------//
	time_t get_time();

};
<commit_msg>update version NO<commit_after>#pragma once
//=====================================================================//
/*! @file
    @brief  SEEDA03 (RX64M) メイン
	@copyright Copyright 2017 Kunihito Hiramatsu All Right Reserved.
    @author 平松邦仁 (hira@rvf-rc45.net)
*/
//=====================================================================//
#include "common/renesas.hpp"

#include "common/rspi_io.hpp"
#include "common/spi_io.hpp"
#include "common/sdc_io.hpp"
#include "common/command.hpp"
#include "common/fifo.hpp"
#include "common/format.hpp"
#include "common/input.hpp"
#include "common/flash_man.hpp"
#include "common/time.h"

#include "chip/LTC2348_16.hpp"
#include "sample.hpp"

#include "chip/EUI_XX.hpp"

#include "GR/core/http_server.hpp"
#include "GR/core/ftp_server.hpp"

#ifdef DEBUG
#define CLIENT_DEBUG
#define NETS_DEBUG
#define WRITE_FILE_DEBUG
#endif

namespace seeda {

	static const int seeda_version_ = 235;
	static const uint32_t build_id_ = B_ID;

	typedef utils::command<256> CMD;

#ifdef SEEDA
	typedef device::PORT<device::PORT6, device::bitpos::B7> SW1;
	typedef device::PORT<device::PORT6, device::bitpos::B6> SW2;
#endif

	// Soft SDC 用　SPI 定義（SPI）
#ifdef SEEDA
	typedef device::PORT<device::PORTD, device::bitpos::B6> MISO;
	typedef device::PORT<device::PORTD, device::bitpos::B4> MOSI;
	typedef device::PORT<device::PORTD, device::bitpos::B5> SPCK;
	typedef device::spi_io<MISO, MOSI, SPCK> SPI;

	typedef device::PORT<device::PORTD, device::bitpos::B3> SDC_SELECT;	///< カード選択信号
	typedef device::NULL_PORT  SDC_POWER;	///< カード電源制御（常に電源ＯＮ）
	typedef device::PORT<device::PORTE, device::bitpos::B6> SDC_DETECT;	///< カード検出
#else
	// SDC 用　SPI 定義（RSPI）
	typedef device::rspi_io<device::RSPI> SPI;
	typedef device::PORT<device::PORTC, device::bitpos::B4> SDC_SELECT;	///< カード選択信号
	typedef device::NULL_PORT  SDC_POWER;	///< カード電源制御（常に電源ＯＮ）
	typedef device::PORT<device::PORTB, device::bitpos::B7> SDC_DETECT;	///< カード検出
#endif
	typedef utils::sdc_io<SPI, SDC_SELECT, SDC_POWER, SDC_DETECT> SDC;

#ifdef SEEDA
	// LTC2348-16 A/D 制御ポート定義
	typedef device::PORT<device::PORT4, device::bitpos::B0> LTC_CSN;   // P40(141)
	typedef device::PORT<device::PORTC, device::bitpos::B6> LTC_CNV;   // PC6(61)
	typedef device::PORT<device::PORTD, device::bitpos::B0> LTC_BUSY;  // PD0/IRQ0(126)
	typedef device::PORT<device::PORT5, device::bitpos::B3> LTC_PD;    // P53(53)
	typedef device::PORT<device::PORT5, device::bitpos::B6> LTC_SDI;   // P56(50)
	typedef device::PORT<device::PORT8, device::bitpos::B6> LTC_SCKO;  // P86(41)
	typedef device::PORT<device::PORT8, device::bitpos::B7> LTC_SCKI;  // P87(39)
	typedef device::PORT<device::PORT2, device::bitpos::B0> LTC_SDO0;  // P20(37)
	typedef device::PORT<device::PORT2, device::bitpos::B1> LTC_SDO2;  // P21(36)
	typedef device::PORT<device::PORT2, device::bitpos::B2> LTC_SDO4;  // P22(35)
	typedef device::PORT<device::PORT2, device::bitpos::B3> LTC_SDO6;  // P23(34)
	typedef struct chip::LTC2348_SDO_t<LTC_SCKO, LTC_SDO0, LTC_SDO2, LTC_SDO4, LTC_SDO6> LTC_SDO;
	typedef chip::LTC2348_16<LTC_CSN, LTC_CNV, LTC_BUSY, LTC_PD, LTC_SDI, LTC_SCKI, LTC_SDO> EADC;
#endif

	typedef device::flash_io FLASH_IO;
	typedef utils::flash_man<FLASH_IO> FLASH_MAN;

	typedef net::http_server<SDC, 16, 8192> HTTP;
	typedef HTTP::http_format http_format;

	typedef net::ftp_server<SDC> FTP;

	//-----------------------------------------------------------------//
	/*!
		@brief  SDC_IO クラスへの参照
		@return SDC_IO クラス
	*/
	//-----------------------------------------------------------------//
	SDC& at_sdc();

#ifdef SEEDA
	//-----------------------------------------------------------------//
	/*!
		@brief  EADC クラスへの参照
		@return EADC クラス
	*/
	//-----------------------------------------------------------------//
	EADC& at_eadc();
#endif

	//-----------------------------------------------------------------//
	/*!
		@brief  EADC サーバー
	*/
	//-----------------------------------------------------------------//
	void eadc_server();

	//-----------------------------------------------------------------//
	/*!
		@brief  EADC サーバー許可
		@param[in]	ena	「false」の場合不許可
	*/
	//-----------------------------------------------------------------//
	void enable_eadc_server(bool ena = true);

	//-----------------------------------------------------------------//
	/*!
		@brief  時間の作成
		@param[in]	date	日付
		@param[in]	time	時間
	*/
	//-----------------------------------------------------------------//
	size_t make_time(const char* date, const char* time);

	//-----------------------------------------------------------------//
	/*!
		@brief  GMT 時間の設定
		@param[in]	t	GMT 時間
	*/
	//-----------------------------------------------------------------//
	void set_time(time_t t);

	//-----------------------------------------------------------------//
	/*!
		@brief  時間の表示
		@param[in]	t		時間
		@param[in]	dst		出力文字列
		@param[in]	size	文字列の大きさ
		@return 生成された文字列の長さ
	*/
	//-----------------------------------------------------------------//
	int disp_time(time_t t, char* dst = nullptr, uint32_t size = 0);

	//-----------------------------------------------------------------//
	/*!
		@brief  設定スイッチの状態を取得
		@return 設定スイッチの状態
	*/
	//-----------------------------------------------------------------//
	uint8_t get_switch()
	{
#ifdef SEEDA
		return static_cast<uint8_t>(!SW1::P()) | (static_cast<uint8_t>(!SW2::P()) << 1);
#else
		return 0;  // for only develope mode
#endif
	}

	//-----------------------------------------------------------------//
	/*!
		@brief  A/D サンプルの参照
		@param[in]	ch	チャネル（０～７）
		@return A/D サンプル
	*/
	//-----------------------------------------------------------------//
	sample_t& at_sample(uint8_t ch);

	//-----------------------------------------------------------------//
	/*!
		@brief  A/D サンプルの取得
		@return A/D サンプル
	*/
	//-----------------------------------------------------------------//
	const sample_data& get_sample_data();

	//-----------------------------------------------------------------//
	/*!
		@brief  内臓 A/D 変換値の取得
		@param[in]	ch	チャネル（５、６、７）
		@return A/D 変換値
	*/
	//-----------------------------------------------------------------//
	uint16_t get_adc(uint32_t ch);
}

extern "C" {

	//-----------------------------------------------------------------//
	/*!
		@brief  GMT 時間の取得
		@return GMT 時間
	*/
	//-----------------------------------------------------------------//
	time_t get_time();

};
<|endoftext|>


<commit_before>#include <feedlist.h>
#include <itemlist.h>
#include <itemview.h>
#include <help.h>
#include <filebrowser.h>
#include <urlview.h>
#include <selecttag.h>
#include <search.h>

#include <formaction.h>
#include <feedlist_formaction.h>
#include <itemlist_formaction.h>
#include <itemview_formaction.h>
#include <help_formaction.h>
#include <urlview_formaction.h>
#include <selecttag_formaction.h>
#include <search_formaction.h>

#include <logger.h>
#include <reloadthread.h>
#include <exception.h>

#include <iostream>
#include <iomanip>
#include <fstream>

#include <assert.h>
#include <libgen.h>
#include <sys/types.h>
#include <dirent.h>
#include <grp.h>
#include <pwd.h>
#include <unistd.h>
#include <config.h>
#include <sys/param.h>
#include <string.h>

extern "C" {
#include <stfl.h>
}

#include <view.h>
#include <rss.h>
#include <htmlrenderer.h>
#include <cstring>
#include <cstdio>
#include <iostream>
#include <sstream>

using namespace newsbeuter;

view::view(controller * c) : ctrl(c), cfg(0), keys(0), mtx(0) {
	mtx = new mutex();

	feedlist = new feedlist_formaction(this, feedlist_str);
	itemlist = new itemlist_formaction(this, itemlist_str);
	itemview = new itemview_formaction(this, itemview_str);
	helpview = new help_formaction(this, help_str);
	filebrowser = new filebrowser_formaction(this, filebrowser_str);
	urlview = new urlview_formaction(this, urlview_str);
	selecttag = new selecttag_formaction(this, selecttag_str);
	search = new search_formaction(this, search_str);

	// push the dialog to start with onto the stack
	formaction_stack.push_front(feedlist);
}

view::~view() {
	stfl::reset();
	delete mtx;
	delete feedlist;
	delete itemlist;
	delete itemview;
	delete helpview;
	delete filebrowser;
	delete urlview;
	delete selecttag;
	delete search;
}

void view::set_config_container(configcontainer * cfgcontainer) {
	cfg = cfgcontainer;
}

void view::set_keymap(keymap * k) {
	keys = k;
}

void view::set_status(const char * msg) {
	mtx->lock();
	GetLogger().log(LOG_DEBUG, "view::set_status: after mtx->lock; formaction_stack.size = %u", formaction_stack.size());
	if (formaction_stack.size() > 0 && (*formaction_stack.begin()) != NULL) {
		stfl::form * form = (*formaction_stack.begin())->get_form();
		GetLogger().log(LOG_DEBUG, "view::set_status: form = %p", form);
		form->set("msg",msg);
		GetLogger().log(LOG_DEBUG, "view::set_status: after form.set");
		form->run(-1);
		GetLogger().log(LOG_DEBUG, "view::set_status: after form.run");
	}
	mtx->unlock();
}

void view::show_error(const char * msg) {
	set_status(msg);
}

void view::run() {

	feedlist->init();
	itemlist->init();

	while (formaction_stack.size() > 0) {
		formaction * fa = *(formaction_stack.begin());

		fa->prepare();

		const char * event = fa->get_form()->run(1000);
		if (!event || strcmp(event,"TIMEOUT")==0) continue;

		operation op = keys->get_operation(event);

		fa->process_operation(op);
	}

	stfl::reset();
}

std::string view::run_modal(formaction * f, const std::string& value) {
	f->init();
	unsigned int stacksize = formaction_stack.size();

	formaction_stack.push_front(f);

	while (formaction_stack.size() > stacksize) {
		formaction * fa = *(formaction_stack.begin());

		fa->prepare();

		const char * event = fa->get_form()->run(1000);
		if (!event || strcmp(event,"TIMEOUT")==0) continue;

		operation op = keys->get_operation(event);

		fa->process_operation(op);
	}

	if (value == "")
		return "";
	else
		return f->get_value(value);
}

std::string view::get_filename_suggestion(const std::string& s) {
	std::string retval;
	for (unsigned int i=0;i<s.length();++i) {
		if (isalnum(s[i]))
			retval.append(1,s[i]);
		else if (s[i] == '/' || s[i] == ' ' || s[i] == '\r' || s[i] == '\n')
			retval.append(1,'_');
	}
	if (retval.length() == 0)
		retval = "article.txt";
	else
		retval.append(".txt");
	GetLogger().log(LOG_DEBUG,"view::get_filename_suggestion: %s -> %s", s.c_str(), retval.c_str());
	return retval;
}

void view::write_item(const rss_item& item, const std::string& filename) {
	std::vector<std::string> lines;
	std::vector<linkpair> links; // not used

	std::string title(_("Title: "));
	title.append(item.title());
	lines.push_back(title);

	std::string author(_("Author: "));
	author.append(item.author());
	lines.push_back(author);

	std::string date(_("Date: "));
	date.append(item.pubDate());
	lines.push_back(date);

	lines.push_back(std::string(""));

	htmlrenderer rnd(80);
	rnd.render(item.description(), lines, links, item.feedurl());

	std::fstream f;
	f.open(filename.c_str(),std::fstream::out);
	if (!f.is_open())
		throw exception(errno);

	for (std::vector<std::string>::iterator it=lines.begin();it!=lines.end();++it) {
		f << *it << std::endl;
	}
}

void view::open_in_browser(const std::string& url) {
	formaction_stack.push_front(NULL); // we don't want a thread to write over the browser
	std::string cmdline;
	std::string browser = cfg->get_configvalue("browser");
	if (browser != "")
		cmdline.append(browser);
	else
		cmdline.append("lynx");
	cmdline.append(" '");
	cmdline.append(url);
	cmdline.append("'");
	stfl::reset();
	GetLogger().log(LOG_DEBUG, "view::open_in_browser: running `%s'", cmdline.c_str());
	::system(cmdline.c_str());
	formaction_stack.pop_front();
}

void view::set_feedlist(std::vector<rss_feed>& feeds) {
	mtx->lock();
	feedlist->set_feedlist(feeds);
	mtx->unlock();
}

void view::set_tags(const std::vector<std::string>& t) {
	feedlist->set_tags(t);
}

void view::push_itemlist(unsigned int pos) {
	rss_feed * feed = ctrl->get_feed(pos);
	GetLogger().log(LOG_DEBUG, "view::push_itemlist: retrieved feed at position %d (address = %p)", pos, feed);
	itemlist->set_feed(feed);
	itemlist->set_pos(pos);
	itemlist->init();
	formaction_stack.push_front(itemlist);
}

void view::push_itemview(rss_feed * f, const std::string& guid) {
	itemview->set_feed(f);
	itemview->set_guid(guid);
	itemview->init();
	formaction_stack.push_front(itemview);
}

void view::push_help() {
	formaction_stack.push_front(helpview);
}

void view::push_urlview(const std::vector<linkpair>& links) {
	urlview->set_links(links);
	formaction_stack.push_front(urlview);
}

std::string view::run_filebrowser(filebrowser_type type, const std::string& default_filename, const std::string& dir) {
	filebrowser->set_dir(dir);
	filebrowser->set_default_filename(default_filename);
	filebrowser->set_type(type);
	return run_modal(filebrowser, "filenametext");
}

std::string view::select_tag(const std::vector<std::string>& tags) {
	selecttag->set_tags(tags);
	run_modal(selecttag, "");
	return selecttag->get_tag();
}

void view::run_search(const std::string& feedurl) {
	search->set_feedurl(feedurl);
	search->init();
	formaction_stack.push_front(search);
}

bool view::get_next_unread() {
	if (itemlist->jump_to_next_unread_item(false)) {
		itemview->init();
		itemview->set_feed(itemlist->get_feed());
		itemview->set_guid(itemlist->get_guid());
		return true;
	} else if (feedlist->jump_to_next_unread_feed()) {
		itemlist->set_feed(feedlist->get_feed());
		itemlist->init();
		if (itemlist->jump_to_next_unread_item(true)) {
			itemview->init();
			itemview->set_feed(itemlist->get_feed());
			itemview->set_guid(itemlist->get_guid());
			return true;
		}
	}
	return false;
}

void view::pop_current_formaction() {
	formaction_stack.pop_front();
	if (formaction_stack.size() > 0) {
		formaction * f = (*formaction_stack.begin());
		if (f) {
			f->set_redraw(true);
			f->get_form()->set("msg","");
		}
	}
}

<commit_msg>Andreas Krennmair: 	fixed bug where a feed could be opened even if it contained 0 items.<commit_after>#include <feedlist.h>
#include <itemlist.h>
#include <itemview.h>
#include <help.h>
#include <filebrowser.h>
#include <urlview.h>
#include <selecttag.h>
#include <search.h>

#include <formaction.h>
#include <feedlist_formaction.h>
#include <itemlist_formaction.h>
#include <itemview_formaction.h>
#include <help_formaction.h>
#include <urlview_formaction.h>
#include <selecttag_formaction.h>
#include <search_formaction.h>

#include <logger.h>
#include <reloadthread.h>
#include <exception.h>

#include <iostream>
#include <iomanip>
#include <fstream>

#include <assert.h>
#include <libgen.h>
#include <sys/types.h>
#include <dirent.h>
#include <grp.h>
#include <pwd.h>
#include <unistd.h>
#include <config.h>
#include <sys/param.h>
#include <string.h>

extern "C" {
#include <stfl.h>
}

#include <view.h>
#include <rss.h>
#include <htmlrenderer.h>
#include <cstring>
#include <cstdio>
#include <iostream>
#include <sstream>

using namespace newsbeuter;

view::view(controller * c) : ctrl(c), cfg(0), keys(0), mtx(0) {
	mtx = new mutex();

	feedlist = new feedlist_formaction(this, feedlist_str);
	itemlist = new itemlist_formaction(this, itemlist_str);
	itemview = new itemview_formaction(this, itemview_str);
	helpview = new help_formaction(this, help_str);
	filebrowser = new filebrowser_formaction(this, filebrowser_str);
	urlview = new urlview_formaction(this, urlview_str);
	selecttag = new selecttag_formaction(this, selecttag_str);
	search = new search_formaction(this, search_str);

	// push the dialog to start with onto the stack
	formaction_stack.push_front(feedlist);
}

view::~view() {
	stfl::reset();
	delete mtx;
	delete feedlist;
	delete itemlist;
	delete itemview;
	delete helpview;
	delete filebrowser;
	delete urlview;
	delete selecttag;
	delete search;
}

void view::set_config_container(configcontainer * cfgcontainer) {
	cfg = cfgcontainer;
}

void view::set_keymap(keymap * k) {
	keys = k;
}

void view::set_status(const char * msg) {
	mtx->lock();
	GetLogger().log(LOG_DEBUG, "view::set_status: after mtx->lock; formaction_stack.size = %u", formaction_stack.size());
	if (formaction_stack.size() > 0 && (*formaction_stack.begin()) != NULL) {
		stfl::form * form = (*formaction_stack.begin())->get_form();
		GetLogger().log(LOG_DEBUG, "view::set_status: form = %p", form);
		form->set("msg",msg);
		GetLogger().log(LOG_DEBUG, "view::set_status: after form.set");
		form->run(-1);
		GetLogger().log(LOG_DEBUG, "view::set_status: after form.run");
	}
	mtx->unlock();
}

void view::show_error(const char * msg) {
	set_status(msg);
}

void view::run() {

	feedlist->init();
	itemlist->init();

	while (formaction_stack.size() > 0) {
		formaction * fa = *(formaction_stack.begin());

		fa->prepare();

		const char * event = fa->get_form()->run(1000);
		if (!event || strcmp(event,"TIMEOUT")==0) continue;

		operation op = keys->get_operation(event);

		fa->process_operation(op);
	}

	stfl::reset();
}

std::string view::run_modal(formaction * f, const std::string& value) {
	f->init();
	unsigned int stacksize = formaction_stack.size();

	formaction_stack.push_front(f);

	while (formaction_stack.size() > stacksize) {
		formaction * fa = *(formaction_stack.begin());

		fa->prepare();

		const char * event = fa->get_form()->run(1000);
		if (!event || strcmp(event,"TIMEOUT")==0) continue;

		operation op = keys->get_operation(event);

		fa->process_operation(op);
	}

	if (value == "")
		return "";
	else
		return f->get_value(value);
}

std::string view::get_filename_suggestion(const std::string& s) {
	std::string retval;
	for (unsigned int i=0;i<s.length();++i) {
		if (isalnum(s[i]))
			retval.append(1,s[i]);
		else if (s[i] == '/' || s[i] == ' ' || s[i] == '\r' || s[i] == '\n')
			retval.append(1,'_');
	}
	if (retval.length() == 0)
		retval = "article.txt";
	else
		retval.append(".txt");
	GetLogger().log(LOG_DEBUG,"view::get_filename_suggestion: %s -> %s", s.c_str(), retval.c_str());
	return retval;
}

void view::write_item(const rss_item& item, const std::string& filename) {
	std::vector<std::string> lines;
	std::vector<linkpair> links; // not used

	std::string title(_("Title: "));
	title.append(item.title());
	lines.push_back(title);

	std::string author(_("Author: "));
	author.append(item.author());
	lines.push_back(author);

	std::string date(_("Date: "));
	date.append(item.pubDate());
	lines.push_back(date);

	lines.push_back(std::string(""));

	htmlrenderer rnd(80);
	rnd.render(item.description(), lines, links, item.feedurl());

	std::fstream f;
	f.open(filename.c_str(),std::fstream::out);
	if (!f.is_open())
		throw exception(errno);

	for (std::vector<std::string>::iterator it=lines.begin();it!=lines.end();++it) {
		f << *it << std::endl;
	}
}

void view::open_in_browser(const std::string& url) {
	formaction_stack.push_front(NULL); // we don't want a thread to write over the browser
	std::string cmdline;
	std::string browser = cfg->get_configvalue("browser");
	if (browser != "")
		cmdline.append(browser);
	else
		cmdline.append("lynx");
	cmdline.append(" '");
	cmdline.append(url);
	cmdline.append("'");
	stfl::reset();
	GetLogger().log(LOG_DEBUG, "view::open_in_browser: running `%s'", cmdline.c_str());
	::system(cmdline.c_str());
	formaction_stack.pop_front();
}

void view::set_feedlist(std::vector<rss_feed>& feeds) {
	mtx->lock();
	feedlist->set_feedlist(feeds);
	mtx->unlock();
}

void view::set_tags(const std::vector<std::string>& t) {
	feedlist->set_tags(t);
}

void view::push_itemlist(unsigned int pos) {
	rss_feed * feed = ctrl->get_feed(pos);
	GetLogger().log(LOG_DEBUG, "view::push_itemlist: retrieved feed at position %d (address = %p)", pos, feed);
	if (feed->items().size() > 0) {
		itemlist->set_feed(feed);
		itemlist->set_pos(pos);
		itemlist->init();
		formaction_stack.push_front(itemlist);
	} else {
		show_error(_("Error: feed contains no items!"));
	}
}

void view::push_itemview(rss_feed * f, const std::string& guid) {
	itemview->set_feed(f);
	itemview->set_guid(guid);
	itemview->init();
	formaction_stack.push_front(itemview);
}

void view::push_help() {
	formaction_stack.push_front(helpview);
}

void view::push_urlview(const std::vector<linkpair>& links) {
	urlview->set_links(links);
	formaction_stack.push_front(urlview);
}

std::string view::run_filebrowser(filebrowser_type type, const std::string& default_filename, const std::string& dir) {
	filebrowser->set_dir(dir);
	filebrowser->set_default_filename(default_filename);
	filebrowser->set_type(type);
	return run_modal(filebrowser, "filenametext");
}

std::string view::select_tag(const std::vector<std::string>& tags) {
	selecttag->set_tags(tags);
	run_modal(selecttag, "");
	return selecttag->get_tag();
}

void view::run_search(const std::string& feedurl) {
	search->set_feedurl(feedurl);
	search->init();
	formaction_stack.push_front(search);
}

bool view::get_next_unread() {
	if (itemlist->jump_to_next_unread_item(false)) {
		itemview->init();
		itemview->set_feed(itemlist->get_feed());
		itemview->set_guid(itemlist->get_guid());
		return true;
	} else if (feedlist->jump_to_next_unread_feed()) {
		itemlist->set_feed(feedlist->get_feed());
		itemlist->init();
		if (itemlist->jump_to_next_unread_item(true)) {
			itemview->init();
			itemview->set_feed(itemlist->get_feed());
			itemview->set_guid(itemlist->get_guid());
			return true;
		}
	}
	return false;
}

void view::pop_current_formaction() {
	formaction_stack.pop_front();
	if (formaction_stack.size() > 0) {
		formaction * f = (*formaction_stack.begin());
		if (f) {
			f->set_redraw(true);
			f->get_form()->set("msg","");
		}
	}
}

<|endoftext|>