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

#include <cstdio>
#include "mazeprinter.h"

static char dirChars[] = "^>v<  ";

void printNorthWalls(Maze *maze, uint16_t row) {
  for (uint16_t col = 0; col < maze->width(); col++) {
    uint16_t cell = row + maze->width() * col;
    printf("o");
    if (maze->hasExit(cell, NORTH)) {
      printf("   ");
    } else {
      printf("---");
    }
  }
  printf("o\n");
}

void printSouthWalls(Maze *maze, uint16_t row) {
  for (uint16_t col = 0; col < maze->width(); col++) {
    uint16_t cell = row + maze->width() * col;
    printf("o");
    if (maze->hasExit(cell, SOUTH)) {
      printf("   ");
    } else {
      printf("---");
    }
  }
  printf("o\n");
}

void MazePrinter::printDirs(Maze *maze) {
  printf("\n");
  for (int row = static_cast<uint16_t>(maze->width() - 1); row >= 0; row--) {
    printNorthWalls(maze, row);
    for (uint16_t col = 0; col < maze->width(); col++) {
      uint16_t cell = row + maze->width() * col;
      if (maze->hasExit(cell, WEST)) {
        printf(" ");
      } else {
        printf("|");
      }
      uint8_t direction = maze->direction(cell);
      char c = ' ';
      if (direction <= WEST) {
        c = dirChars[direction];
      }
      if (cell == maze->goal()) {
        c = '*';
      }
      printf(" %c ", c);
    }
    uint16_t cell = row + maze->width() * (maze->width() - 1);
    if (maze->hasExit(cell, EAST)) {
      printf(" ");
    } else {
      printf("|");
    }
    printf("\n");
  }
  printSouthWalls(maze, 0);
}

void MazePrinter::printVisitedDirs(Maze *maze) {
  printf("\n");
  for (int row = static_cast<uint16_t>(maze->width() - 1); row >= 0; row--) {
    printNorthWalls(maze, row);
    for (uint16_t col = 0; col < maze->width(); col++) {
      uint16_t cell = row + maze->width() * col;
      if (maze->hasExit(cell, WEST)) {
        printf(" ");
      } else {
        printf("|");
      }
      uint8_t direction = maze->direction(cell);
      char c = ' ';
      if (maze->isVisited(cell) && direction <= WEST) {
        c = dirChars[direction];
      }
      if (cell == maze->goal()) {
        c = '*';
      }
      printf(" %c ", c);
    }
    uint16_t cell = row + maze->width() * (maze->width() - 1);
    if (maze->hasExit(cell, EAST)) {
      printf(" ");
    } else {
      printf("|");
    }
    printf("\n");
  }
  printSouthWalls(maze, 0);
}

void MazePrinter::printPlain(Maze *maze) {
  printf("\n");
  for (int row = static_cast<uint16_t>(maze->width() - 1); row >= 0; row--) {
    printNorthWalls(maze, row);
    /* TODO:  this is all rather messy */
    for (uint16_t col = 0; col < maze->width(); col++) {
      uint16_t cell = row + maze->width() * col;
      if (maze->hasExit(cell, WEST)) {
        printf(" ");
      } else {
        printf("|");
      }
      char c = ' ';
      if (cell == maze->goal()) {
        c = '*';
      }
      printf(" %c ", c);
    }
    uint16_t cell = row + maze->width() * (maze->width() - 1);
    if (maze->hasExit(cell, EAST)) {
      printf(" ");
    } else {
      printf("|");
    }
    printf("\n");
  }
  printSouthWalls(maze, 0);
  printf("\n");
}

void MazePrinter::printCDecl(Maze *maze, const char *name) {
  printf("\n\nconst uint8_t %s[] = {\n", name);
  for (uint16_t x = 0; x < maze->width(); x++) {
    printf("   ");
    for (uint16_t y = 0; y < maze->width(); y++) {
      uint16_t cell = x * maze->width() + y;
      printf("0x%02X, ", maze->walls(cell));
    }
    printf("\n");
  }
  printf("   };\n\n");
}

void MazePrinter::printRawDecl(Maze *maze, const char *name) {
  printf("\n\nconst uint8_t %s[] = {\n", name);
  for (uint16_t x = 0; x < maze->width(); x++) {
    printf("   ");
    for (uint16_t y = 0; y < maze->width(); y++) {
      uint16_t cell = x * maze->width() + y;
      printf("0x%02X, ", maze->walls(cell));
    }
    printf("\n");
  }
  printf("   };\n\n");
}

void MazePrinter::printCosts(Maze *maze) {
  printf("\n");
  for (int row = static_cast<uint16_t>(maze->width() - 1); row >= 0; row--) {
    printNorthWalls(maze, row);
    /* TODO:  this is all rather messy */
    for (uint16_t col = 0; col < maze->width(); col++) {
      uint16_t cell = row + maze->width() * col;
      if (maze->hasExit(cell, WEST)) {
        printf(" ");
      } else {
        printf("|");
      }
      uint16_t cost = maze->cost(cell);
      if (cost < MAX_COST) {
        printf("%3d", cost);
      } else {
        printf(" - ");
      }

    }
    uint16_t cell = row + maze->width() * (maze->width() - 1);
    if (maze->hasExit(cell, EAST)) {
      printf(" ");
    } else {
      printf("|");
    }
    printf("\n");
  }
  printSouthWalls(maze, 0);
}

<commit_msg>MazePrinter plain should print 'G' in goal cells and 'S' at start<commit_after>/************************************************************************
*
* Copyright (C) 2017 by Peter Harrison. www.micromouseonline.com
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without l> imitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
************************************************************************/

#include <cstdio>
#include "mazeprinter.h"

static char dirChars[] = "^>v<  ";

void printNorthWalls(Maze *maze, uint16_t row) {
  for (uint16_t col = 0; col < maze->width(); col++) {
    uint16_t cell = row + maze->width() * col;
    printf("o");
    if (maze->hasExit(cell, NORTH)) {
      printf("   ");
    } else {
      printf("---");
    }
  }
  printf("o\n");
}

void printSouthWalls(Maze *maze, uint16_t row) {
  for (uint16_t col = 0; col < maze->width(); col++) {
    uint16_t cell = row + maze->width() * col;
    printf("o");
    if (maze->hasExit(cell, SOUTH)) {
      printf("   ");
    } else {
      printf("---");
    }
  }
  printf("o\n");
}

void MazePrinter::printDirs(Maze *maze) {
  printf("\n");
  for (int row = static_cast<uint16_t>(maze->width() - 1); row >= 0; row--) {
    printNorthWalls(maze, row);
    for (uint16_t col = 0; col < maze->width(); col++) {
      uint16_t cell = row + maze->width() * col;
      if (maze->hasExit(cell, WEST)) {
        printf(" ");
      } else {
        printf("|");
      }
      uint8_t direction = maze->direction(cell);
      char c = ' ';
      if (direction <= WEST) {
        c = dirChars[direction];
      }
      if (cell == maze->goal()) {
        c = '*';
      }
      printf(" %c ", c);
    }
    uint16_t cell = row + maze->width() * (maze->width() - 1);
    if (maze->hasExit(cell, EAST)) {
      printf(" ");
    } else {
      printf("|");
    }
    printf("\n");
  }
  printSouthWalls(maze, 0);
}

void MazePrinter::printVisitedDirs(Maze *maze) {
  printf("\n");
  for (int row = static_cast<uint16_t>(maze->width() - 1); row >= 0; row--) {
    printNorthWalls(maze, row);
    for (uint16_t col = 0; col < maze->width(); col++) {
      uint16_t cell = row + maze->width() * col;
      if (maze->hasExit(cell, WEST)) {
        printf(" ");
      } else {
        printf("|");
      }
      uint8_t direction = maze->direction(cell);
      char c = ' ';
      if (maze->isVisited(cell) && direction <= WEST) {
        c = dirChars[direction];
      }
      if (cell == maze->goal()) {
        c = '*';
      }
      printf(" %c ", c);
    }
    uint16_t cell = row + maze->width() * (maze->width() - 1);
    if (maze->hasExit(cell, EAST)) {
      printf(" ");
    } else {
      printf("|");
    }
    printf("\n");
  }
  printSouthWalls(maze, 0);
}

void MazePrinter::printPlain(Maze *maze) {
  printf("\n");
  for (int row = static_cast<uint16_t>(maze->width() - 1); row >= 0; row--) {
    printNorthWalls(maze, row);
    /* TODO:  this is all rather messy */
    for (uint16_t col = 0; col < maze->width(); col++) {
      uint16_t cell = row + maze->width() * col;
      if (maze->hasExit(cell, WEST)) {
        printf(" ");
      } else {
        printf("|");
      }
      char c = ' ';
      if (cell == maze->goal()) {
        c = 'G';
      }
      if (cell == 0) {
        c = 'S';
      }
      printf(" %c ", c);
    }
    uint16_t cell = row + maze->width() * (maze->width() - 1);
    if (maze->hasExit(cell, EAST)) {
      printf(" ");
    } else {
      printf("|");
    }
    printf("\n");
  }
  printSouthWalls(maze, 0);
  printf("\n");
}

void MazePrinter::printCDecl(Maze *maze, const char *name) {
  printf("\n\nconst uint8_t %s[] = {\n", name);
  for (uint16_t x = 0; x < maze->width(); x++) {
    printf("   ");
    for (uint16_t y = 0; y < maze->width(); y++) {
      uint16_t cell = x * maze->width() + y;
      printf("0x%02X, ", maze->walls(cell));
    }
    printf("\n");
  }
  printf("   };\n\n");
}

void MazePrinter::printRawDecl(Maze *maze, const char *name) {
  printf("\n\nconst uint8_t %s[] = {\n", name);
  for (uint16_t x = 0; x < maze->width(); x++) {
    printf("   ");
    for (uint16_t y = 0; y < maze->width(); y++) {
      uint16_t cell = x * maze->width() + y;
      printf("0x%02X, ", maze->walls(cell));
    }
    printf("\n");
  }
  printf("   };\n\n");
}

void MazePrinter::printCosts(Maze *maze) {
  printf("\n");
  for (int row = static_cast<uint16_t>(maze->width() - 1); row >= 0; row--) {
    printNorthWalls(maze, row);
    /* TODO:  this is all rather messy */
    for (uint16_t col = 0; col < maze->width(); col++) {
      uint16_t cell = row + maze->width() * col;
      if (maze->hasExit(cell, WEST)) {
        printf(" ");
      } else {
        printf("|");
      }
      uint16_t cost = maze->cost(cell);
      if (cost < MAX_COST) {
        printf("%3d", cost);
      } else {
        printf(" - ");
      }

    }
    uint16_t cell = row + maze->width() * (maze->width() - 1);
    if (maze->hasExit(cell, EAST)) {
      printf(" ");
    } else {
      printf("|");
    }
    printf("\n");
  }
  printSouthWalls(maze, 0);
}

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013-2014 Nicholas Corgan (n.corgan@gmail.com)
 *
 * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
 * or copy at http://opensource.org/licenses/MIT)
 */

#include <iostream>
#include <sstream>
#include <stdexcept>

#include <boost/algorithm/string.hpp>
#include <boost/format.hpp>

#include <pkmn/enums.hpp>
#include <pkmn/lists.hpp>
#include <pkmn/paths.hpp>
#include <pkmn/database/queries.hpp>
#include <pkmn/types/shared_ptr.hpp>

#include "SQLiteCpp/SQLiteC++.h"

namespace pkmn
{
    pkmn::shared_ptr<SQLite::Database> db;

    void get_game_list(std::vector<std::string> &game_vec)
    {
        if(!db) db = pkmn::shared_ptr<SQLite::Database>(new SQLite::Database(get_database_path().c_str()));
        game_vec.clear();

        std::string query_str = "SELECT name FROM version_names";
        SQLite::Statement query(*db, query_str.c_str());

        while(query.executeStep())
        {
            std::string game = query.getColumn(0);
            game_vec.push_back(game);
        }
    }

    void get_game_group_list(std::vector<std::string> &game_group_vec)
    {
        //Must be done manually, only really used for GamesComboBox
        game_group_vec.clear();

        game_group_vec.push_back("Red/Blue/Green");
        game_group_vec.push_back("Yellow");
        game_group_vec.push_back("Gold/Silver");
        game_group_vec.push_back("Crystal");
        game_group_vec.push_back("Ruby/Sapphire/Emerald");
        game_group_vec.push_back("Fire Red/Leaf Green");
        game_group_vec.push_back("Diamond/Pearl");
        game_group_vec.push_back("Platinum");
        game_group_vec.push_back("Heart Gold/Soul Silver");
        game_group_vec.push_back("Black/White");
        game_group_vec.push_back("Black 2/White 2");
    }

    void get_item_list(std::vector<std::string> &item_vec, unsigned int game)
    {
        if(!db) db = pkmn::shared_ptr<SQLite::Database>(new SQLite::Database(get_database_path().c_str()));
        item_vec.clear();
        std::vector<std::string> temp_vec;

        unsigned int gen = database::get_generation(game);
        std::ostringstream query_stream;
        query_stream << "SELECT name FROM item_names WHERE local_language_id=9 AND item_id IN "
                     << "(SELECT item_id FROM item_game_indices WHERE generation_id=" << gen;

        /*
         * The database only shows which generation items come from, but it doesn't take
         * into account differences between games within a generation.
         */
        switch(gen)
        {
            case 1:
            {
                query_stream << ")";
                break;
            }

            case 2:
            {
                std::string end = (game == Versions::CRYSTAL) ? ")"
                                                           : " AND game_index NOT IN (70,115,116,129))";
                query_stream << end;
                break;
            }

            case 3:
            {
                std::string end;
                switch(game)
                {
                    case Versions::EMERALD:
                        end = ")";
                        break;

                    case Versions::FIRERED:
                    case Versions::LEAFGREEN:
                        end = " AND game_index<=374)";
                        break;

                    default:
                        end = " AND game_index<=348)";
                        break;
                }
                query_stream << end;
                break;
            }

            case 4:
            {
                std::string end;
                switch(game)
                {
                    case Versions::HEARTGOLD:
                    case Versions::SOULSILVER:
                        end = ")";
                        break;

                    case Versions::PLATINUM:
                        end = " AND game_index<=467)";
                        break;

                    default:
                        end = " AND game_index<=464 AND game_index!=112)";
                        break;
                }
                query_stream << end;
                break;
            }

            case 5:
            {
                std::string end = (game < Versions::BLACK_2) ? " AND game_index<=626)"
                                                         : ")";
                query_stream << end;
                break;
            }

            default:
                query_stream << ")";
                break;
        }
        SQLite::Statement query(*db, query_stream.str().c_str());
        while(query.executeStep()) item_vec.push_back((const char*)(query.getColumn(0)));
    }

    void get_pokedex_order(std::vector<std::pair<unsigned int, unsigned int> > &entry_list, unsigned int pokedex_id)
    {
        if(!db) db = pkmn::shared_ptr<SQLite::Database>(new SQLite::Database(get_database_path().c_str()));
        entry_list.clear();
        std::string query_string(str(boost::format("SELECT species_id,pokedex_number FROM pokemon_dex_numbers WHERE pokedex_id=%d")
                                     % pokedex_id));
        SQLite::Statement query(*db, query_string.c_str());

        while(query.executeStep()) entry_list.push_back(std::make_pair(int(query.getColumn(0)), int(query.getColumn(1))));
    }

    void get_pokemon_list(std::vector<std::string> &pokemon_vec, unsigned int game)
    {
        if(!db) db = pkmn::shared_ptr<SQLite::Database>(new SQLite::Database(get_database_path().c_str()));
        pokemon_vec.clear();

        unsigned int bounds[] = {0,151,251,386,493,649,719};
        unsigned int bound = bounds[database::get_generation(game)];
        std::ostringstream query_stream;
        query_stream << "SELECT name FROM pokemon_species_names WHERE local_language_id=9 AND pokemon_species_id<=" << bound << std::endl;
        SQLite::Statement query(*db, query_stream.str().c_str());

        while(query.executeStep()) pokemon_vec.push_back((const char*)(query.getColumn(0)));
    }

    void get_type_list(std::vector<std::string> &type_vec, unsigned int gen)
    {
        if(!db) db = pkmn::shared_ptr<SQLite::Database>(new SQLite::Database(get_database_path().c_str()));
        type_vec.clear();

        std::string query_string = "SELECT name FROM type_names WHERE local_language_id=9";

        SQLite::Statement type_names_query(*db, query_string.c_str());
        while(type_names_query.executeStep())
        {
            std::string type = std::string((const char*)type_names_query.getColumn(0));
            if(not (gen == 1 and (type == "Steel" or type == "Dark")) and type != "???" and type != "Shadow")
            {
                type_vec.push_back(type);
            }
        }
    }

    void get_ability_list(std::vector<std::string> &ability_vec, unsigned int gen)
    {
        if(!db) db = pkmn::shared_ptr<SQLite::Database>(new SQLite::Database(get_database_path().c_str()));
        ability_vec.clear();

        std::string query_string = "SELECT name FROM ability_names WHERE local_language_id=9";
        SQLite::Statement query(*db, query_string.c_str());
        while(query.executeStep()) ability_vec.push_back((const char*)(db->execAndGet(query_string.c_str())));
    }

    void get_nature_list(std::vector<std::string> &nature_vec)
    {
        if(!db) db = pkmn::shared_ptr<SQLite::Database>(new SQLite::Database(get_database_path().c_str()));
        nature_vec.clear();

        std::string query_str = "SELECT name FROM nature_names";
        SQLite::Statement query(*db, query_str.c_str());

        while(query.executeStep())
        {
            std::string nature = std::string((const char*)query.getColumn(0));
            nature_vec.push_back(nature);
        }
    }

    void get_pokemon_of_type(base_pokemon_vector &pkmn_vector, std::string type1, std::string type2, unsigned int gen, bool lax)
    {
        if(!db) db = pkmn::shared_ptr<SQLite::Database>(new SQLite::Database(get_database_path().c_str()));
        pkmn_vector.clear();

        std::string query_string;
        std::vector<int> applicable_ids;
        int pkmn_id, type1_id, type2_id;

        //Get type IDs
        query_string = "SELECT type_id FROM type_names WHERE name='" + type1 + "'";
        type1_id = int(db->execAndGet(query_string.c_str()));
        if(type2 != "None" and type2 != "Any")
        {
            query_string = "SELECT type_id FROM type_names WHERE name='" + type2 + "'";
            type2_id = int(db->execAndGet(query_string.c_str()));
        }

        if((type2 == "None" or type2 == "Any") and lax)
        {
            //Get IDs of Pokémon
            query_string = "SELECT pokemon_id FROM pokemon_types WHERE type_id=" + to_string(type1_id);
            SQLite::Statement pokemon_types_query(*db, query_string.c_str());

            //Get any Pokémon of specified type (by itself or paired with any other)
            while(pokemon_types_query.executeStep())
            {
                pkmn_id = pokemon_types_query.getColumn(0); //pokemon_id

                query_string = "SELECT species_id FROM pokemon WHERE id=" + to_string(pkmn_id);
                int species_id = db->execAndGet(query_string.c_str());

                //Get generation ID to restrict list
                query_string = "SELECT generation_id FROM pokemon_species WHERE id=" + to_string(species_id);
                int generation_id = db->execAndGet(query_string.c_str());
                if(generation_id <= gen)
                {
                    applicable_ids.push_back(pkmn_id);
                }
            }
        }
        else
        {

            //Get IDs of Pokémon matching first type
            std::vector<int> pkmn_ids;
            query_string = "SELECT pokemon_id FROM pokemon_types WHERE type_id=" + to_string(type1_id);
            SQLite::Statement pokemon_types_id_query(*db, query_string.c_str());

            while(pokemon_types_id_query.executeStep()) pkmn_ids.push_back(pokemon_types_id_query.getColumn(0));

            std::vector<int> to_erase;
            if(type2 == "None")
            {
                //If only one type is specified, find number of entries with that ID and remove duplicates
                for(unsigned int i = 0; i < pkmn_ids.size(); i++)
                {
                    int pkmn_count = 0; //Number of types Pokémon appears in pokemon_moves
                    query_string = "SELECT type_id FROM pokemon_types WHERE pokemon_id=" + to_string(pkmn_ids[i]);
                    SQLite::Statement inner_query(*db, query_string.c_str());
                    while(inner_query.executeStep()) pkmn_count++;

                    if(pkmn_count > 1) to_erase.push_back(i);
                }
            }
            else
            {
                //See if entry exists for other type, add to to_erase if not
                for(unsigned int i = 0; i < pkmn_ids.size(); i++)
                {
                    query_string = "SELECT type_id FROM pokemon_types WHERE pokemon_id=" + to_string(pkmn_ids[i])
                                 + " AND type_id=" + to_string(type2_id);
                    SQLite::Statement inner_query(*db, query_string.c_str());
                    if(not inner_query.executeStep()) to_erase.push_back(i);
                }
            }

            //Erase invalid entries
            for(unsigned int i = to_erase.size()-1; i > 0; i--) pkmn_ids.erase(pkmn_ids.begin() + to_erase[i]);
            pkmn_ids.erase(pkmn_ids.begin() + to_erase[0]);

            //Get identifiers for remaining entries
            for(unsigned int i = 0; i < pkmn_ids.size(); i++)
            {
                query_string = "SELECT species_id FROM pokemon WHERE id=" + to_string(pkmn_ids[i]);
                int species_id = db->execAndGet(query_string.c_str());

                query_string = "SELECT identifier FROM pokemon_species WHERE id=" + to_string(species_id);
                std::string pkmn_name = db->execAndGet(query_string.c_str());

                //Get generation ID to restrict list
                query_string = "SELECT generation_id FROM pokemon_species WHERE id=" + to_string(species_id);
                int generation_id = db->execAndGet(query_string.c_str());
                if(generation_id <= gen) applicable_ids.push_back(pkmn_ids[i]); //ID's that apply to final Pokemon
            }
        }

        //base_pokemon now takes a game ID in its constructor instead of a generation, but this
        //function doesn't discriminate between games in the same generation, so this array
        //guarantees that the given generation will use a game in that generation
        int game_id_from_gen[] = {0,1,4,7,13,17};

        for(unsigned int i = 0; i < applicable_ids.size(); i++)
        {
            //Manually correct for Magnemite and Magneton in Gen 1
            int final_species_id = database::get_species_id(applicable_ids[i]);
            if(not ((database::get_species_name(final_species_id) == "Magnemite" or
                     database::get_species_name(final_species_id) == "Magneton") and gen == 1))
            {
                base_pokemon::sptr b_pkmn = base_pokemon::make(database::get_species_id(applicable_ids[i]), game_id_from_gen[gen]);
                pkmn_vector.push_back(b_pkmn);
            }
        }
    }
} /* namespace pkmn */
<commit_msg>Python stuff works, exposed lists.cpp issue<commit_after>/*
 * Copyright (c) 2013-2014 Nicholas Corgan (n.corgan@gmail.com)
 *
 * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
 * or copy at http://opensource.org/licenses/MIT)
 */

#include <iostream>
#include <sstream>
#include <stdexcept>

#include <boost/algorithm/string.hpp>
#include <boost/format.hpp>

#include <pkmn/enums.hpp>
#include <pkmn/lists.hpp>
#include <pkmn/paths.hpp>
#include <pkmn/database/queries.hpp>
#include <pkmn/types/shared_ptr.hpp>

#include "SQLiteCpp/SQLiteC++.h"

namespace pkmn
{
    pkmn::shared_ptr<SQLite::Database> db;

    void get_game_list(std::vector<std::string> &game_vec)
    {
        if(!db) db = pkmn::shared_ptr<SQLite::Database>(new SQLite::Database(get_database_path().c_str()));
        game_vec.clear();

        std::string query_str = "SELECT name FROM version_names";
        SQLite::Statement query(*db, query_str.c_str());

        while(query.executeStep())
        {
            std::string game = query.getColumn(0);
            game_vec.push_back(game);
        }
    }

    void get_game_group_list(std::vector<std::string> &game_group_vec)
    {
        //Must be done manually, only really used for GamesComboBox
        game_group_vec.clear();

        game_group_vec.push_back("Red/Blue/Green");
        game_group_vec.push_back("Yellow");
        game_group_vec.push_back("Gold/Silver");
        game_group_vec.push_back("Crystal");
        game_group_vec.push_back("Ruby/Sapphire/Emerald");
        game_group_vec.push_back("Fire Red/Leaf Green");
        game_group_vec.push_back("Diamond/Pearl");
        game_group_vec.push_back("Platinum");
        game_group_vec.push_back("Heart Gold/Soul Silver");
        game_group_vec.push_back("Black/White");
        game_group_vec.push_back("Black 2/White 2");
    }

    void get_item_list(std::vector<std::string> &item_vec, unsigned int game)
    {
        if(!db) db = pkmn::shared_ptr<SQLite::Database>(new SQLite::Database(get_database_path().c_str()));
        item_vec.clear();
        std::vector<std::string> temp_vec;

        unsigned int gen = database::get_generation(game);
        std::ostringstream query_stream;
        query_stream << "SELECT name FROM item_names WHERE local_language_id=9 AND item_id IN "
                     << "(SELECT item_id FROM item_game_indices WHERE generation_id=" << gen;

        /*
         * The database only shows which generation items come from, but it doesn't take
         * into account differences between games within a generation.
         */
        switch(gen)
        {
            case 1:
            {
                query_stream << ")";
                break;
            }

            case 2:
            {
                std::string end = (game == Versions::CRYSTAL) ? ")"
                                                           : " AND game_index NOT IN (70,115,116,129))";
                query_stream << end;
                break;
            }

            case 3:
            {
                std::string end;
                switch(game)
                {
                    case Versions::EMERALD:
                        end = ")";
                        break;

                    case Versions::FIRERED:
                    case Versions::LEAFGREEN:
                        end = " AND game_index<=374)";
                        break;

                    default:
                        end = " AND game_index<=348)";
                        break;
                }
                query_stream << end;
                break;
            }

            case 4:
            {
                std::string end;
                switch(game)
                {
                    case Versions::HEARTGOLD:
                    case Versions::SOULSILVER:
                        end = ")";
                        break;

                    case Versions::PLATINUM:
                        end = " AND game_index<=467)";
                        break;

                    default:
                        end = " AND game_index<=464 AND game_index!=112)";
                        break;
                }
                query_stream << end;
                break;
            }

            case 5:
            {
                std::string end = (game < Versions::BLACK_2) ? " AND game_index<=626)"
                                                         : ")";
                query_stream << end;
                break;
            }

            default:
                query_stream << ")";
                break;
        }
        SQLite::Statement query(*db, query_stream.str().c_str());
        while(query.executeStep()) item_vec.push_back((const char*)(query.getColumn(0)));
    }

    void get_pokedex_order(std::vector<std::pair<unsigned int, unsigned int> > &entry_list, unsigned int pokedex_id)
    {
        if(!db) db = pkmn::shared_ptr<SQLite::Database>(new SQLite::Database(get_database_path().c_str()));
        entry_list.clear();
        std::string query_string(str(boost::format("SELECT species_id,pokedex_number FROM pokemon_dex_numbers WHERE pokedex_id=%d")
                                     % pokedex_id));
        SQLite::Statement query(*db, query_string.c_str());

        while(query.executeStep()) entry_list.push_back(std::make_pair(int(query.getColumn(0)), int(query.getColumn(1))));
    }

    void get_pokemon_list(std::vector<std::string> &pokemon_vec, unsigned int game)
    {
        if(!db) db = pkmn::shared_ptr<SQLite::Database>(new SQLite::Database(get_database_path().c_str()));
        pokemon_vec.clear();

        unsigned int bounds[] = {0,151,251,386,493,649,719};
        unsigned int bound = bounds[database::get_generation(game)];
        std::ostringstream query_stream;
        query_stream << "SELECT name FROM pokemon_species_names WHERE local_language_id=9 AND pokemon_species_id<=" << bound << std::endl;
        SQLite::Statement query(*db, query_stream.str().c_str());

        while(query.executeStep()) pokemon_vec.push_back((const char*)(query.getColumn(0)));
    }

    void get_type_list(std::vector<std::string> &type_vec, unsigned int gen)
    {
        if(!db) db = pkmn::shared_ptr<SQLite::Database>(new SQLite::Database(get_database_path().c_str()));
        type_vec.clear();

        std::string query_string = "SELECT name FROM type_names WHERE local_language_id=9";

        SQLite::Statement type_names_query(*db, query_string.c_str());
        while(type_names_query.executeStep())
        {
            std::string type = std::string((const char*)type_names_query.getColumn(0));
            if(not (gen == 1 and (type == "Steel" or type == "Dark")) and type != "???" and type != "Shadow")
            {
                type_vec.push_back(type);
            }
        }
    }

    void get_ability_list(std::vector<std::string> &ability_vec, unsigned int gen)
    {
        if(!db) db = pkmn::shared_ptr<SQLite::Database>(new SQLite::Database(get_database_path().c_str()));
        ability_vec.clear();

        std::string query_string = "SELECT name FROM ability_names WHERE local_language_id=9";
        SQLite::Statement query(*db, query_string.c_str());
        while(query.executeStep()) ability_vec.push_back((const char*)(query.getColumn(0)));
    }

    void get_nature_list(std::vector<std::string> &nature_vec)
    {
        if(!db) db = pkmn::shared_ptr<SQLite::Database>(new SQLite::Database(get_database_path().c_str()));
        nature_vec.clear();

        std::string query_str = "SELECT name FROM nature_names";
        SQLite::Statement query(*db, query_str.c_str());

        while(query.executeStep())
        {
            std::string nature = std::string((const char*)query.getColumn(0));
            nature_vec.push_back(nature);
        }
    }

    void get_pokemon_of_type(base_pokemon_vector &pkmn_vector, std::string type1, std::string type2, unsigned int gen, bool lax)
    {
        if(!db) db = pkmn::shared_ptr<SQLite::Database>(new SQLite::Database(get_database_path().c_str()));
        pkmn_vector.clear();

        std::string query_string;
        std::vector<int> applicable_ids;
        int pkmn_id, type1_id, type2_id;

        //Get type IDs
        query_string = "SELECT type_id FROM type_names WHERE name='" + type1 + "'";
        type1_id = int(db->execAndGet(query_string.c_str()));
        if(type2 != "None" and type2 != "Any")
        {
            query_string = "SELECT type_id FROM type_names WHERE name='" + type2 + "'";
            type2_id = int(db->execAndGet(query_string.c_str()));
        }

        if((type2 == "None" or type2 == "Any") and lax)
        {
            //Get IDs of Pokémon
            query_string = "SELECT pokemon_id FROM pokemon_types WHERE type_id=" + to_string(type1_id);
            SQLite::Statement pokemon_types_query(*db, query_string.c_str());

            //Get any Pokémon of specified type (by itself or paired with any other)
            while(pokemon_types_query.executeStep())
            {
                pkmn_id = pokemon_types_query.getColumn(0); //pokemon_id

                query_string = "SELECT species_id FROM pokemon WHERE id=" + to_string(pkmn_id);
                int species_id = db->execAndGet(query_string.c_str());

                //Get generation ID to restrict list
                query_string = "SELECT generation_id FROM pokemon_species WHERE id=" + to_string(species_id);
                int generation_id = db->execAndGet(query_string.c_str());
                if(generation_id <= gen)
                {
                    applicable_ids.push_back(pkmn_id);
                }
            }
        }
        else
        {

            //Get IDs of Pokémon matching first type
            std::vector<int> pkmn_ids;
            query_string = "SELECT pokemon_id FROM pokemon_types WHERE type_id=" + to_string(type1_id);
            SQLite::Statement pokemon_types_id_query(*db, query_string.c_str());

            while(pokemon_types_id_query.executeStep()) pkmn_ids.push_back(pokemon_types_id_query.getColumn(0));

            std::vector<int> to_erase;
            if(type2 == "None")
            {
                //If only one type is specified, find number of entries with that ID and remove duplicates
                for(unsigned int i = 0; i < pkmn_ids.size(); i++)
                {
                    int pkmn_count = 0; //Number of types Pokémon appears in pokemon_moves
                    query_string = "SELECT type_id FROM pokemon_types WHERE pokemon_id=" + to_string(pkmn_ids[i]);
                    SQLite::Statement inner_query(*db, query_string.c_str());
                    while(inner_query.executeStep()) pkmn_count++;

                    if(pkmn_count > 1) to_erase.push_back(i);
                }
            }
            else
            {
                //See if entry exists for other type, add to to_erase if not
                for(unsigned int i = 0; i < pkmn_ids.size(); i++)
                {
                    query_string = "SELECT type_id FROM pokemon_types WHERE pokemon_id=" + to_string(pkmn_ids[i])
                                 + " AND type_id=" + to_string(type2_id);
                    SQLite::Statement inner_query(*db, query_string.c_str());
                    if(not inner_query.executeStep()) to_erase.push_back(i);
                }
            }

            //Erase invalid entries
            for(unsigned int i = to_erase.size()-1; i > 0; i--) pkmn_ids.erase(pkmn_ids.begin() + to_erase[i]);
            pkmn_ids.erase(pkmn_ids.begin() + to_erase[0]);

            //Get identifiers for remaining entries
            for(unsigned int i = 0; i < pkmn_ids.size(); i++)
            {
                query_string = "SELECT species_id FROM pokemon WHERE id=" + to_string(pkmn_ids[i]);
                int species_id = db->execAndGet(query_string.c_str());

                query_string = "SELECT identifier FROM pokemon_species WHERE id=" + to_string(species_id);
                std::string pkmn_name = db->execAndGet(query_string.c_str());

                //Get generation ID to restrict list
                query_string = "SELECT generation_id FROM pokemon_species WHERE id=" + to_string(species_id);
                int generation_id = db->execAndGet(query_string.c_str());
                if(generation_id <= gen) applicable_ids.push_back(pkmn_ids[i]); //ID's that apply to final Pokemon
            }
        }

        //base_pokemon now takes a game ID in its constructor instead of a generation, but this
        //function doesn't discriminate between games in the same generation, so this array
        //guarantees that the given generation will use a game in that generation
        int game_id_from_gen[] = {0,1,4,7,13,17};

        for(unsigned int i = 0; i < applicable_ids.size(); i++)
        {
            //Manually correct for Magnemite and Magneton in Gen 1
            int final_species_id = database::get_species_id(applicable_ids[i]);
            if(not ((database::get_species_name(final_species_id) == "Magnemite" or
                     database::get_species_name(final_species_id) == "Magneton") and gen == 1))
            {
                base_pokemon::sptr b_pkmn = base_pokemon::make(database::get_species_id(applicable_ids[i]), game_id_from_gen[gen]);
                pkmn_vector.push_back(b_pkmn);
            }
        }
    }
} /* namespace pkmn */
<|endoftext|>


<commit_before>#include "mainwidget.h"

MainWidget::MainWidget(QWidget* parent)
    : QWidget(parent)
    , m_graphicWindows(&m_updateUi)
    , m_hLayout(this)
    , m_frameNumberLCD(12)
    , m_fpsLCD(12)
    , m_frameAdvanceButton(tr("Frame Advance"))
{
    this->setWindowTitle(tr("Blackhole Simulator 2.0 beta"));

    auto container = QWidget::createWindowContainer(&m_graphicWindows);
    container->setFocusPolicy(Qt::StrongFocus);
    container->setFocus();
    container->setMinimumSize(QSize(200, 100));
    QSize screenSize = m_graphicWindows.screen()->size();
    container->setMaximumSize(screenSize);

    m_vLayout.setAlignment(Qt::AlignTop);
    m_hLayout.addWidget(container, Qt::AlignLeft);
    m_hLayout.addLayout(&m_vLayout);

    initUi();
}

void MainWidget::initUi()
{
    // FPS
    auto fpsLayout = new QHBoxLayout;
    auto fpsLabel = new QLabel(tr("FPS"));
    fpsLayout->addWidget(fpsLabel);
    displayStyle(m_fpsLCD);
    fpsLayout->addWidget(&m_fpsLCD);
    m_vLayout.addLayout(fpsLayout);
    connect(&m_updateUi, &UpdateUi::displayFps, this, &MainWidget::displayFPS);

    // Frames
    auto frameNumberLayout = new QHBoxLayout;
    auto frameNumberLabel = new QLabel(tr("Frames"));
    frameNumberLayout->addWidget(frameNumberLabel);
    displayStyle(m_frameNumberLCD);
    frameNumberLayout->addWidget(&m_frameNumberLCD);
    m_vLayout.addLayout(frameNumberLayout);
    connect(&m_updateUi, &UpdateUi::displayFrameNumber, this, &MainWidget::displayFrameNumber);

    // Time/Frame
    auto timePerFrameLayout = new QHBoxLayout;
    auto timePerFrameLabel = new QLabel(tr("Time/Frame (s)"));
    timePerFrameLayout->addWidget(timePerFrameLabel);
    displayStyle(m_timePerFrameValue);
    timePerFrameLayout->addWidget(&m_timePerFrameValue);
    m_vLayout.addLayout(timePerFrameLayout);
    connect(&m_updateUi, &UpdateUi::displayTimePerFrame, this, &MainWidget::displayTimePerFrame);

    // Simulation Time
    auto simTimeLayout = new QHBoxLayout;
    auto simTimeLabel = new QLabel(tr("Time"));
    simTimeLayout->addWidget(simTimeLabel);
    displayStyle(m_simTimeValue);
    simTimeLayout->addWidget(&m_simTimeValue);
    m_vLayout.addLayout(simTimeLayout);

    // Start Button
    m_startButton.setFocusPolicy(Qt::NoFocus);
    m_vLayout.addWidget(&m_startButton);
    connect(&m_startButton, &QPushButton::clicked, &m_graphicWindows, &GraphicWindow::startSim);
    connect(&m_updateUi, &UpdateUi::updateStartButtonText, this, &MainWidget::updateStartButtonText);

    // Frame Advance
    m_frameAdvanceButton.setFocusPolicy(Qt::NoFocus);
    m_vLayout.addWidget(&m_frameAdvanceButton);
    connect(&m_frameAdvanceButton, &QPushButton::clicked, &m_graphicWindows, &GraphicWindow::frameAdvance);

    // Reset
    auto resetBtn = new QPushButton(tr("Reset"));
    resetBtn->setFocusPolicy(Qt::NoFocus);
    m_vLayout.addWidget(resetBtn);
    connect(resetBtn, &QPushButton::clicked, this, &MainWidget::resetInitial);

    // Circle strafing
    auto circleStrafingCB = new QCheckBox(tr("Circle strafing"));
    circleStrafingCB->setFocusPolicy(Qt::NoFocus);
    circleStrafingCB->setChecked(false);
    m_vLayout.addWidget(circleStrafingCB);
    connect(circleStrafingCB, &QCheckBox::stateChanged, &m_graphicWindows, &GraphicWindow::circleStrafing);

    // Grid Lines
    auto gridLinesCB = new QCheckBox(tr("Grid Lines"));
    gridLinesCB->setChecked(true);
    gridLinesCB->setFocusPolicy(Qt::NoFocus);
    m_vLayout.addWidget(gridLinesCB);
    connect(gridLinesCB, &QCheckBox::stateChanged, &m_graphicWindows, &GraphicWindow::enableGridLines);

    // Line Type
    auto btnLineType = new QPushButton(tr("Line Type"));
    btnLineType->setFocusPolicy(Qt::NoFocus);
    m_vLayout.addWidget(btnLineType);
    connect(btnLineType, &QPushButton::clicked, &m_graphicWindows, &GraphicWindow::changeLinePosition);

    // Model Scale
    auto scaleLabel = new QLabel(tr("Model Scale (m):"));
    m_vLayout.addWidget(scaleLabel);

    m_scaleValue.setAlignment(Qt::AlignRight);
    m_vLayout.addWidget(&m_scaleValue);
    connect(&m_updateUi, &UpdateUi::displayModelScale, this, &MainWidget::displayModelScale);
    connect(&m_scaleValue, &QLineEdit::textChanged, &m_graphicWindows, &GraphicWindow::setModelScale);

    m_scaleSlider.setFocusPolicy(Qt::NoFocus);
    m_scaleSlider.setOrientation(Qt::Horizontal);
    m_scaleSlider.setMinimum(0);
    m_scaleSlider.setMaximum(UpdateUi::SCALE_SLIDER_CENTER * 2);
    m_scaleSlider.setSliderPosition(UpdateUi::SCALE_SLIDER_CENTER);
    m_vLayout.addWidget(&m_scaleSlider);
    connect(&m_scaleSlider, &QSlider::sliderMoved, &m_graphicWindows, &GraphicWindow::setModelScaleInt);

    // Number of particles
    auto particleNumLabel = new QLabel(tr("Number of particles"));
    m_vLayout.addWidget(particleNumLabel);
    displayStyle(m_particleNumValue);
    m_vLayout.addWidget(&m_particleNumValue);
    connect(&m_updateUi, &UpdateUi::displayNumberOfParticles, this, &MainWidget::displayNumberOfParticles);

    // Simulation Engine
    auto engineLabel = new QLabel(tr("Simulation Engine"));
    m_vLayout.addWidget(engineLabel);
    displayStyle(m_engineValue);
    m_vLayout.addWidget(&m_engineValue);
    connect(&m_updateUi, &UpdateUi::displayEngineName, this, &MainWidget::displayEngineName);

    // Initial Condition Preset
    auto presetLabel = new QLabel(tr("Initial Conditions Preset"));
    m_vLayout.addWidget(presetLabel);
    displayStyle(m_presetValue);
    m_vLayout.addWidget(&m_presetValue);
    connect(&m_updateUi, &UpdateUi::displayPresetName, this, &MainWidget::displayPresetName);

    // New
    auto newButton = new QPushButton(tr("New..."));
    newButton->setFocusPolicy(Qt::NoFocus);
    m_vLayout.addWidget(newButton);
    connect(newButton, &QPushButton::clicked, this, &MainWidget::showInitializerDialog);

    // Graph
    auto graphButton = new QPushButton(tr("Graph..."));
    graphButton->setFocusPolicy(Qt::NoFocus);
    m_vLayout.addWidget(graphButton);
    //connect(newButton, &QPushButton::clicked, this, &MainWidget::showInitializerDialog);

    updateStartButtonText(false);
}

void MainWidget::displayFrameNumber(const int num)
{
    m_frameNumberLCD.display(num);
    quint64 time = floor(m_simCondition.timePerFrame * (float)num);
    quint64 seconds = time % 60;
    quint64 remain = time / 60;
    quint64 minutes = remain % 60;
    remain /= 60;
    quint64 hours = remain % 24;
    remain /= 24;
    QString t = QString(tr("Day %1 %2:%3:%4"))
            .arg(remain)
            .arg(hours, 2, 10, QChar('0'))
            .arg(minutes, 2, 10, QChar('0'))
            .arg(seconds, 2, 10, QChar('0'));
    m_simTimeValue.setText(t);
}

void MainWidget::displayFPS(const int fps)
{
    m_fpsLCD.display(fps);
}

void MainWidget::updateStartButtonText(const bool setStop)
{
    if (setStop) {
        m_startButton.setText(tr("Stop"));
    } else {
        m_startButton.setText(tr("Start"));
    }
    m_frameAdvanceButton.setDisabled(setStop);
}

void MainWidget::displayStyle(QLCDNumber& lcd)
{
    lcd.setFrameStyle(QFrame::StyledPanel | QFrame::Plain);
    lcd.setSegmentStyle(QLCDNumber::Flat);
}

void MainWidget::displayStyle(QLabel& lbl)
{
    lbl.setFrameStyle(QFrame::StyledPanel | QFrame::Plain);
    lbl.setAlignment(Qt::AlignRight);
}

void MainWidget::displayModelScale(const float val)
{
    if (val <= 0.0f) {
        return;
    }
    bool state = m_scaleValue.blockSignals(true);
    m_scaleValue.setText(QString::number(1.0f / val));
    m_scaleValue.blockSignals(state);
}

void MainWidget::resetScaleSlider()
{
    bool state = m_scaleSlider.blockSignals(true);
    m_scaleSlider.setSliderPosition(UpdateUi::SCALE_SLIDER_CENTER);
    m_scaleSlider.blockSignals(state);
}

void MainWidget::displayTimePerFrame(const float time)
{
    m_simCondition.timePerFrame = time;
    m_timePerFrameValue.setText(QString::number(time));
}

void MainWidget::showInitializerDialog()
{
    if (!m_initializerDialog) {
        m_initializerDialog = new InitializerDialog(&m_updateUi, this);
        connect(m_initializerDialog, &InitializerDialog::accepted, this, &MainWidget::acceptInitializerDialog);
        connect(&m_updateUi, &UpdateUi::applyInitialConditions, this, &MainWidget::acceptInitializerDialog);
    }
    m_initializerDialog->setValues(m_simCondition);

    m_initializerDialog->show();
    m_initializerDialog->raise();
    m_initializerDialog->activateWindow();
}

void MainWidget::acceptInitializerDialog()
{
    m_simCondition = m_initializerDialog->simCondition();
    reset(m_simCondition);
}

void MainWidget::displayEngineName(const QString& name)
{
    m_engineValue.setText(name);
}

void MainWidget::reset(const bhs::SimCondition& sim)
{
    m_graphicWindows.reset(sim);
    resetScaleSlider();
    updateStartButtonText(false);
}

void MainWidget::displayPresetName(const QString& name)
{
    m_presetValue.setText(name);
}

void MainWidget::resetInitial()
{
    reset(m_simCondition);
}

void MainWidget::displayNumberOfParticles(const int num)
{
    m_simCondition.numberOfParticles = num;
    m_particleNumValue.setText(QString::number(num));
}
<commit_msg>graphButton enabled false<commit_after>#include "mainwidget.h"

MainWidget::MainWidget(QWidget* parent)
    : QWidget(parent)
    , m_graphicWindows(&m_updateUi)
    , m_hLayout(this)
    , m_frameNumberLCD(12)
    , m_fpsLCD(12)
    , m_frameAdvanceButton(tr("Frame Advance"))
{
    this->setWindowTitle(tr("Blackhole Simulator 2.0 beta"));

    auto container = QWidget::createWindowContainer(&m_graphicWindows);
    container->setFocusPolicy(Qt::StrongFocus);
    container->setFocus();
    container->setMinimumSize(QSize(200, 100));
    QSize screenSize = m_graphicWindows.screen()->size();
    container->setMaximumSize(screenSize);

    m_vLayout.setAlignment(Qt::AlignTop);
    m_hLayout.addWidget(container, Qt::AlignLeft);
    m_hLayout.addLayout(&m_vLayout);

    initUi();
}

void MainWidget::initUi()
{
    // FPS
    auto fpsLayout = new QHBoxLayout;
    auto fpsLabel = new QLabel(tr("FPS"));
    fpsLayout->addWidget(fpsLabel);
    displayStyle(m_fpsLCD);
    fpsLayout->addWidget(&m_fpsLCD);
    m_vLayout.addLayout(fpsLayout);
    connect(&m_updateUi, &UpdateUi::displayFps, this, &MainWidget::displayFPS);

    // Frames
    auto frameNumberLayout = new QHBoxLayout;
    auto frameNumberLabel = new QLabel(tr("Frames"));
    frameNumberLayout->addWidget(frameNumberLabel);
    displayStyle(m_frameNumberLCD);
    frameNumberLayout->addWidget(&m_frameNumberLCD);
    m_vLayout.addLayout(frameNumberLayout);
    connect(&m_updateUi, &UpdateUi::displayFrameNumber, this, &MainWidget::displayFrameNumber);

    // Time/Frame
    auto timePerFrameLayout = new QHBoxLayout;
    auto timePerFrameLabel = new QLabel(tr("Time/Frame (s)"));
    timePerFrameLayout->addWidget(timePerFrameLabel);
    displayStyle(m_timePerFrameValue);
    timePerFrameLayout->addWidget(&m_timePerFrameValue);
    m_vLayout.addLayout(timePerFrameLayout);
    connect(&m_updateUi, &UpdateUi::displayTimePerFrame, this, &MainWidget::displayTimePerFrame);

    // Simulation Time
    auto simTimeLayout = new QHBoxLayout;
    auto simTimeLabel = new QLabel(tr("Time"));
    simTimeLayout->addWidget(simTimeLabel);
    displayStyle(m_simTimeValue);
    simTimeLayout->addWidget(&m_simTimeValue);
    m_vLayout.addLayout(simTimeLayout);

    // Start Button
    m_startButton.setFocusPolicy(Qt::NoFocus);
    m_vLayout.addWidget(&m_startButton);
    connect(&m_startButton, &QPushButton::clicked, &m_graphicWindows, &GraphicWindow::startSim);
    connect(&m_updateUi, &UpdateUi::updateStartButtonText, this, &MainWidget::updateStartButtonText);

    // Frame Advance
    m_frameAdvanceButton.setFocusPolicy(Qt::NoFocus);
    m_vLayout.addWidget(&m_frameAdvanceButton);
    connect(&m_frameAdvanceButton, &QPushButton::clicked, &m_graphicWindows, &GraphicWindow::frameAdvance);

    // Reset
    auto resetBtn = new QPushButton(tr("Reset"));
    resetBtn->setFocusPolicy(Qt::NoFocus);
    m_vLayout.addWidget(resetBtn);
    connect(resetBtn, &QPushButton::clicked, this, &MainWidget::resetInitial);

    // Circle strafing
    auto circleStrafingCB = new QCheckBox(tr("Circle strafing"));
    circleStrafingCB->setFocusPolicy(Qt::NoFocus);
    circleStrafingCB->setChecked(false);
    m_vLayout.addWidget(circleStrafingCB);
    connect(circleStrafingCB, &QCheckBox::stateChanged, &m_graphicWindows, &GraphicWindow::circleStrafing);

    // Grid Lines
    auto gridLinesCB = new QCheckBox(tr("Grid Lines"));
    gridLinesCB->setChecked(true);
    gridLinesCB->setFocusPolicy(Qt::NoFocus);
    m_vLayout.addWidget(gridLinesCB);
    connect(gridLinesCB, &QCheckBox::stateChanged, &m_graphicWindows, &GraphicWindow::enableGridLines);

    // Line Type
    auto btnLineType = new QPushButton(tr("Line Type"));
    btnLineType->setFocusPolicy(Qt::NoFocus);
    m_vLayout.addWidget(btnLineType);
    connect(btnLineType, &QPushButton::clicked, &m_graphicWindows, &GraphicWindow::changeLinePosition);

    // Model Scale
    auto scaleLabel = new QLabel(tr("Model Scale (m):"));
    m_vLayout.addWidget(scaleLabel);

    m_scaleValue.setAlignment(Qt::AlignRight);
    m_vLayout.addWidget(&m_scaleValue);
    connect(&m_updateUi, &UpdateUi::displayModelScale, this, &MainWidget::displayModelScale);
    connect(&m_scaleValue, &QLineEdit::textChanged, &m_graphicWindows, &GraphicWindow::setModelScale);

    m_scaleSlider.setFocusPolicy(Qt::NoFocus);
    m_scaleSlider.setOrientation(Qt::Horizontal);
    m_scaleSlider.setMinimum(0);
    m_scaleSlider.setMaximum(UpdateUi::SCALE_SLIDER_CENTER * 2);
    m_scaleSlider.setSliderPosition(UpdateUi::SCALE_SLIDER_CENTER);
    m_vLayout.addWidget(&m_scaleSlider);
    connect(&m_scaleSlider, &QSlider::sliderMoved, &m_graphicWindows, &GraphicWindow::setModelScaleInt);

    // Number of particles
    auto particleNumLabel = new QLabel(tr("Number of particles"));
    m_vLayout.addWidget(particleNumLabel);
    displayStyle(m_particleNumValue);
    m_vLayout.addWidget(&m_particleNumValue);
    connect(&m_updateUi, &UpdateUi::displayNumberOfParticles, this, &MainWidget::displayNumberOfParticles);

    // Simulation Engine
    auto engineLabel = new QLabel(tr("Simulation Engine"));
    m_vLayout.addWidget(engineLabel);
    displayStyle(m_engineValue);
    m_vLayout.addWidget(&m_engineValue);
    connect(&m_updateUi, &UpdateUi::displayEngineName, this, &MainWidget::displayEngineName);

    // Initial Condition Preset
    auto presetLabel = new QLabel(tr("Initial Conditions Preset"));
    m_vLayout.addWidget(presetLabel);
    displayStyle(m_presetValue);
    m_vLayout.addWidget(&m_presetValue);
    connect(&m_updateUi, &UpdateUi::displayPresetName, this, &MainWidget::displayPresetName);

    // New
    auto newButton = new QPushButton(tr("New..."));
    newButton->setFocusPolicy(Qt::NoFocus);
    m_vLayout.addWidget(newButton);
    connect(newButton, &QPushButton::clicked, this, &MainWidget::showInitializerDialog);

    // Graph
    auto graphButton = new QPushButton(tr("Graph..."));
    graphButton->setEnabled(false);
    graphButton->setFocusPolicy(Qt::NoFocus);
    m_vLayout.addWidget(graphButton);
    //connect(newButton, &QPushButton::clicked, this, &MainWidget::showInitializerDialog);

    updateStartButtonText(false);
}

void MainWidget::displayFrameNumber(const int num)
{
    m_frameNumberLCD.display(num);
    quint64 time = floor(m_simCondition.timePerFrame * (float)num);
    quint64 seconds = time % 60;
    quint64 remain = time / 60;
    quint64 minutes = remain % 60;
    remain /= 60;
    quint64 hours = remain % 24;
    remain /= 24;
    QString t = QString(tr("Day %1 %2:%3:%4"))
            .arg(remain)
            .arg(hours, 2, 10, QChar('0'))
            .arg(minutes, 2, 10, QChar('0'))
            .arg(seconds, 2, 10, QChar('0'));
    m_simTimeValue.setText(t);
}

void MainWidget::displayFPS(const int fps)
{
    m_fpsLCD.display(fps);
}

void MainWidget::updateStartButtonText(const bool setStop)
{
    if (setStop) {
        m_startButton.setText(tr("Stop"));
    } else {
        m_startButton.setText(tr("Start"));
    }
    m_frameAdvanceButton.setDisabled(setStop);
}

void MainWidget::displayStyle(QLCDNumber& lcd)
{
    lcd.setFrameStyle(QFrame::StyledPanel | QFrame::Plain);
    lcd.setSegmentStyle(QLCDNumber::Flat);
}

void MainWidget::displayStyle(QLabel& lbl)
{
    lbl.setFrameStyle(QFrame::StyledPanel | QFrame::Plain);
    lbl.setAlignment(Qt::AlignRight);
}

void MainWidget::displayModelScale(const float val)
{
    if (val <= 0.0f) {
        return;
    }
    bool state = m_scaleValue.blockSignals(true);
    m_scaleValue.setText(QString::number(1.0f / val));
    m_scaleValue.blockSignals(state);
}

void MainWidget::resetScaleSlider()
{
    bool state = m_scaleSlider.blockSignals(true);
    m_scaleSlider.setSliderPosition(UpdateUi::SCALE_SLIDER_CENTER);
    m_scaleSlider.blockSignals(state);
}

void MainWidget::displayTimePerFrame(const float time)
{
    m_simCondition.timePerFrame = time;
    m_timePerFrameValue.setText(QString::number(time));
}

void MainWidget::showInitializerDialog()
{
    if (!m_initializerDialog) {
        m_initializerDialog = new InitializerDialog(&m_updateUi, this);
        connect(m_initializerDialog, &InitializerDialog::accepted, this, &MainWidget::acceptInitializerDialog);
        connect(&m_updateUi, &UpdateUi::applyInitialConditions, this, &MainWidget::acceptInitializerDialog);
    }
    m_initializerDialog->setValues(m_simCondition);

    m_initializerDialog->show();
    m_initializerDialog->raise();
    m_initializerDialog->activateWindow();
}

void MainWidget::acceptInitializerDialog()
{
    m_simCondition = m_initializerDialog->simCondition();
    reset(m_simCondition);
}

void MainWidget::displayEngineName(const QString& name)
{
    m_engineValue.setText(name);
}

void MainWidget::reset(const bhs::SimCondition& sim)
{
    m_graphicWindows.reset(sim);
    resetScaleSlider();
    updateStartButtonText(false);
}

void MainWidget::displayPresetName(const QString& name)
{
    m_presetValue.setText(name);
}

void MainWidget::resetInitial()
{
    reset(m_simCondition);
}

void MainWidget::displayNumberOfParticles(const int num)
{
    m_simCondition.numberOfParticles = num;
    m_particleNumValue.setText(QString::number(num));
}
<|endoftext|>


<commit_before>#include <QFileDialog>
#include <QFontDialog>
#include <QImageReader>
#include <QLabel>
#include <QMenu>
#include <QMessageBox>
#include <QSettings>
#include <QTimer>

#include "canvaswidget.h"
#include "mainwindow.h"
#include "ui_mainwindow.h"

const int maxRecentDocuments = 10;

MainWindow::MainWindow(QWidget* parent) :
  QMainWindow(parent),
  ui(new Ui::MainWindow)
{
  ui->setupUi(this);
  setAttribute(Qt::WA_QuitOnClose);
  setWindowState(windowState() | Qt::WindowMaximized);
  setWindowTitle(appName());

  modeActionGroup = new QActionGroup(this);
  openFileAction                    = new QAction(style()->standardIcon(QStyle::SP_DialogOpenButton), QString::fromUtf8("Открыть файл"),                              this);
  saveFileAction                    = new QAction(style()->standardIcon(QStyle::SP_DialogSaveButton), QString::fromUtf8("Сохранить файл"),                            this);
  toggleEtalonModeAction            = new QAction(QIcon(":/pictures/etalon.png"),                     QString::fromUtf8("Включить/выключить режим задания эталона"),  this);
  measureSegmentLengthAction        = new QAction(QIcon(":/pictures/segment_length.png"),             QString::fromUtf8("Измерение длин отрезков"),                   modeActionGroup);
  measurePolylineLengthAction       = new QAction(QIcon(":/pictures/polyline_length.png"),            QString::fromUtf8("Измерение длин кривых"),                     modeActionGroup);
  measureClosedPolylineLengthAction = new QAction(QIcon(":/pictures/closed_polyline_length.png"),     QString::fromUtf8("Измерение длин замкнутых кривых"),           modeActionGroup);
  measureRectangleAreaAction        = new QAction(QIcon(":/pictures/rectangle_area.png"),             QString::fromUtf8("Измерение площадей прямоугольников"),        modeActionGroup);
  measurePolygonAreaAction          = new QAction(QIcon(":/pictures/polygon_area.png"),               QString::fromUtf8("Измерение площадей многоугольников"),        modeActionGroup);
  toggleRulerAction                 = new QAction(QIcon(":/pictures/toggle_ruler.png"),               QString::fromUtf8("Показать/скрыть масштабную линейку"),        this);
  customizeInscriptionFontAction    = new QAction(QIcon(":/pictures/font.png"),                       QString::fromUtf8("Настроить шрифт подписей"),                  this);
  aboutAction                       = new QAction(QIcon(":/pictures/about.png"),                      QString::fromUtf8("О программе"),                               this);

  openRecentMenu = new QMenu(this);
  openFileAction->setMenu(openRecentMenu);
  saveFileAction->setEnabled(false);

  toggleEtalonModeAction->setCheckable(true);
  toggleEtalonModeAction->setChecked(true);

  foreach (QAction* action, modeActionGroup->actions())
    action->setCheckable(true);
  measureSegmentLengthAction->setChecked(true);

  toggleRulerAction->setCheckable(true);
  toggleRulerAction->setChecked(true);

  ui->mainToolBar->addAction(openFileAction);
  ui->mainToolBar->addAction(saveFileAction);
  ui->mainToolBar->addSeparator();
  ui->mainToolBar->addAction(toggleEtalonModeAction);
  ui->mainToolBar->addSeparator();
  ui->mainToolBar->addActions(modeActionGroup->actions());
  ui->mainToolBar->addSeparator();
  ui->mainToolBar->addAction(toggleRulerAction);
  ui->mainToolBar->addAction(customizeInscriptionFontAction);
  ui->mainToolBar->addSeparator();
  ui->mainToolBar->addAction(aboutAction);
  ui->mainToolBar->setIconSize(QSize(32, 32));
  ui->mainToolBar->setContextMenuPolicy(Qt::PreventContextMenu);

  QFont statusBarFont = ui->statusBar->font();
  int newSize = QFontInfo(statusBarFont).pointSize() * 1.5;  // In contrast to ``labelFont.pointSize()'' it always works
  statusBarFont.setPointSize(newSize);
  ui->statusBar->setFont(statusBarFont);
  scaleLabel  = new QLabel(this);
  statusLabel = new QLabel(this);
  ui->statusBar->addPermanentWidget(scaleLabel);
  ui->statusBar->addWidget(statusLabel);

  canvasWidget = 0;

  connect(openFileAction,                 SIGNAL(triggered()), this, SLOT(openFile()));
  connect(saveFileAction,                 SIGNAL(triggered()), this, SLOT(saveFile()));
  connect(customizeInscriptionFontAction, SIGNAL(triggered()), this, SLOT(customizeInscriptionFont()));
  connect(aboutAction,                    SIGNAL(triggered()), this, SLOT(showAbout()));

  connect(toggleEtalonModeAction, SIGNAL(toggled(bool)),       this, SLOT(toggleEtalonDefinition(bool)));
  connect(modeActionGroup,        SIGNAL(triggered(QAction*)), this, SLOT(updateMode(QAction*)));

  setDrawOptionsEnabled(false);
  loadAndApplySettings();
}

MainWindow::~MainWindow()
{
  saveSettings();
  delete ui;
}

QString MainWindow::companyName() const
{
  return "AMM Soft";
}

QString MainWindow::appName() const
{
  return "AreaMeasurement";
}

QList<int> MainWindow::appVersion() const
{
  // TODO: Don't forget to increment it!
  return QList<int>() << 1 << 0;
}

QString MainWindow::appVersionString() const
{
  QString versionString;
  foreach (int x, appVersion())
    versionString += QString::number(x) + '.';
  return versionString.left(versionString.length() - 1);
}

QFont MainWindow::getInscriptionFont() const
{
  return inscriptionFont;
}

void MainWindow::setMode(ShapeType newMode)
{
  if (canvasWidget)
    canvasWidget->setMode(newMode);
}

QString MainWindow::getImageFormatsFilter() const
{
  QList<QByteArray> supportedFormatsList__ = QImageReader::supportedImageFormats();
  QSet<QString> supportedFormatsSet;
  foreach (const QByteArray& format, supportedFormatsList__)
    supportedFormatsSet.insert(QString(format).toLower());
  QStringList supportedFormatsList(supportedFormatsSet.toList());
  qSort(supportedFormatsList);
  QString allFormatsString;
  QStringList singleFormatsList;
  foreach (const QString& lowerFormat, supportedFormatsList) {
    QString upperFormat = lowerFormat.toUpper();
    allFormatsString += QString("*.%1 ").arg(lowerFormat);
    if (upperFormat == "JPEG")
      singleFormatsList += QString::fromUtf8("Изображения JPEG (*.jpeg *.jpg)");
    else if (upperFormat != "JPG")
      singleFormatsList += QString::fromUtf8("Изображения %1 (*.%2)").arg(upperFormat).arg(lowerFormat);
  }
  allFormatsString = allFormatsString.trimmed();
  return QString::fromUtf8("Все изображения (%1);;").arg(allFormatsString) + singleFormatsList.join(";;");
}

void MainWindow::doOpenFile(const QString& filename)
{
  QPixmap image;
  if (!image.load(filename)) {
    QMessageBox::warning(this, appName(), QString::fromUtf8("Не могу открыть изображение «%1».").arg(filename));
    return;
  }

  openedFile = filename;
  recentFiles.removeAll(filename);
  recentFiles.prepend(filename);
  if (recentFiles.size() > maxRecentDocuments)
    recentFiles.erase(recentFiles.begin() + maxRecentDocuments, recentFiles.end());
  updateOpenRecentMenu();

  toggleEtalonModeAction->setChecked(true);
  measureSegmentLengthAction->setChecked(true);

  delete canvasWidget;
  canvasWidget = new CanvasWidget(image, this, ui->containingScrollArea, scaleLabel, statusLabel, this);
  ui->containingScrollArea->setWidget(canvasWidget);

  connect(toggleRulerAction, SIGNAL(toggled(bool)), canvasWidget, SLOT(toggleRuler(bool)));
  canvasWidget->toggleRuler(toggleRulerAction->isChecked());

  saveFileAction->setEnabled(true);
  saveSettings();
  setDrawOptionsEnabled(true);
}

void MainWindow::doSaveFile(const QString& filename)
{
  ASSERT_RETURN(canvasWidget);
  bool ok = canvasWidget->getModifiedImage().save(filename);
  if (ok)
    ui->statusBar->showMessage(QString::fromUtf8("Файл успешно сохранён"), 5000);
  else
    QMessageBox::warning(this, appName(), QString::fromUtf8("Не удалось записать файл «%1»!").arg(filename));
}

void MainWindow::loadSettings()
{
  QSettings settings(companyName(), appName());
  inscriptionFont = settings.value("Inscription Font", QFont()).value<QFont>();
  recentFiles.clear();
  int nRecent = settings.beginReadArray("Recent Documents");
  for (int i = 0; i < nRecent; ++i) {
    settings.setArrayIndex(i);
    recentFiles.append(settings.value("Filename").toString());
  }
  settings.endArray();
}

void MainWindow::loadAndApplySettings()
{
  loadSettings();
  updateOpenRecentMenu();
}

void MainWindow::saveSettings() const
{
  QSettings settings(companyName(), appName());
  settings.clear();
  settings.setValue("Inscription Font", inscriptionFont);
  settings.beginWriteArray("Recent Documents");
  for (int i = 0; i < recentFiles.size(); ++i) {
    settings.setArrayIndex(i);
    settings.setValue("Filename", recentFiles[i]);
  }
  settings.endArray();
}

void MainWindow::updateOpenRecentMenu()
{
  openRecentMenu->clear();
  foreach (const QString& file, recentFiles)
    openRecentMenu->addAction(file, this, SLOT(openRecentFile()));
  if (openRecentMenu->isEmpty()) {
    QAction* infoAction = new QAction(QString::fromUtf8("Список недавно открытых документов пуст"), openRecentMenu);
    infoAction->setEnabled(false);
    openRecentMenu->addAction(infoAction);
  }
}

void MainWindow::openFile()
{
  QString filename = QFileDialog::getOpenFileName(this, QString::fromUtf8("Открыть изображение — ") + appName(),
                                                  QString(), getImageFormatsFilter(), 0);
  if (!filename.isEmpty())
    doOpenFile(filename);
}

void MainWindow::openRecentFile()
{
  QAction* triggeredAction = qobject_cast<QAction*>(sender());
  ASSERT_RETURN(triggeredAction);
  doOpenFile(triggeredAction->text());
}

void MainWindow::saveFile()
{
  QString filename = QFileDialog::getSaveFileName(this, QString::fromUtf8("Сохранить изображение — ") + appName(),
                                                  openedFile, getImageFormatsFilter(), 0);
  if (!filename.isEmpty())
    doSaveFile(filename);
}

void MainWindow::setDrawOptionsEnabled(bool enabled)
{
  toggleEtalonModeAction->setEnabled(enabled);
  modeActionGroup       ->setEnabled(enabled);
  toggleRulerAction     ->setEnabled(enabled);
}

void MainWindow::updateMode(QAction* modeAction)
{
  if (modeAction == measureSegmentLengthAction)
    return setMode(SEGMENT);
  if (modeAction == measurePolylineLengthAction)
    return setMode(POLYLINE);
  if (modeAction == measureClosedPolylineLengthAction)
    return setMode(CLOSED_POLYLINE);
  if (modeAction == measureRectangleAreaAction)
    return setMode(RECTANGLE);
  if (modeAction == measurePolygonAreaAction)
    return setMode(POLYGON);
  ERROR_RETURN();
}

void MainWindow::toggleEtalonDefinition(bool isDefiningEtalon)
{
  ASSERT_RETURN(canvasWidget);
  canvasWidget->toggleEtalonDefinition(isDefiningEtalon);
  toggleEtalonModeAction->setChecked(isDefiningEtalon);
}

void MainWindow::customizeInscriptionFont()
{
  // TODO: Why does the dialog show wrong font for the first time?
  inscriptionFont = QFontDialog::getFont(0, inscriptionFont, this);
  if (canvasWidget)
    canvasWidget->update();
}

void MainWindow::showAbout()
{
  QString aboutText = QString::fromUtf8("Программа %1, версия %2.\n\n"
                                        "Приложение предназначено для измерения длин и площадей объектов на чертежах, картах и т.д.\n\n"
                                        "Автор — Матвеякин Андрей.\n\n"
                                        "Программа распространяется бесплатно по принципу «как есть»: автор не несёт ответственности за возможный ущерб, нанесённый в результате работы приложения."
                                        ).arg(appName()).arg(appVersionString());
  QMessageBox::about(this, QString::fromUtf8("О программе %1").arg(appName()), aboutText);
}
<commit_msg>Remove images that can't be opened from ``Recent files'' list<commit_after>#include <QFileDialog>
#include <QFontDialog>
#include <QImageReader>
#include <QLabel>
#include <QMenu>
#include <QMessageBox>
#include <QSettings>
#include <QTimer>

#include "canvaswidget.h"
#include "mainwindow.h"
#include "ui_mainwindow.h"

const int maxRecentDocuments = 10;

MainWindow::MainWindow(QWidget* parent) :
  QMainWindow(parent),
  ui(new Ui::MainWindow)
{
  ui->setupUi(this);
  setAttribute(Qt::WA_QuitOnClose);
  setWindowState(windowState() | Qt::WindowMaximized);
  setWindowTitle(appName());

  modeActionGroup = new QActionGroup(this);
  openFileAction                    = new QAction(style()->standardIcon(QStyle::SP_DialogOpenButton), QString::fromUtf8("Открыть файл"),                              this);
  saveFileAction                    = new QAction(style()->standardIcon(QStyle::SP_DialogSaveButton), QString::fromUtf8("Сохранить файл"),                            this);
  toggleEtalonModeAction            = new QAction(QIcon(":/pictures/etalon.png"),                     QString::fromUtf8("Включить/выключить режим задания эталона"),  this);
  measureSegmentLengthAction        = new QAction(QIcon(":/pictures/segment_length.png"),             QString::fromUtf8("Измерение длин отрезков"),                   modeActionGroup);
  measurePolylineLengthAction       = new QAction(QIcon(":/pictures/polyline_length.png"),            QString::fromUtf8("Измерение длин кривых"),                     modeActionGroup);
  measureClosedPolylineLengthAction = new QAction(QIcon(":/pictures/closed_polyline_length.png"),     QString::fromUtf8("Измерение длин замкнутых кривых"),           modeActionGroup);
  measureRectangleAreaAction        = new QAction(QIcon(":/pictures/rectangle_area.png"),             QString::fromUtf8("Измерение площадей прямоугольников"),        modeActionGroup);
  measurePolygonAreaAction          = new QAction(QIcon(":/pictures/polygon_area.png"),               QString::fromUtf8("Измерение площадей многоугольников"),        modeActionGroup);
  toggleRulerAction                 = new QAction(QIcon(":/pictures/toggle_ruler.png"),               QString::fromUtf8("Показать/скрыть масштабную линейку"),        this);
  customizeInscriptionFontAction    = new QAction(QIcon(":/pictures/font.png"),                       QString::fromUtf8("Настроить шрифт подписей"),                  this);
  aboutAction                       = new QAction(QIcon(":/pictures/about.png"),                      QString::fromUtf8("О программе"),                               this);

  openRecentMenu = new QMenu(this);
  openFileAction->setMenu(openRecentMenu);
  saveFileAction->setEnabled(false);

  toggleEtalonModeAction->setCheckable(true);
  toggleEtalonModeAction->setChecked(true);

  foreach (QAction* action, modeActionGroup->actions())
    action->setCheckable(true);
  measureSegmentLengthAction->setChecked(true);

  toggleRulerAction->setCheckable(true);
  toggleRulerAction->setChecked(true);

  ui->mainToolBar->addAction(openFileAction);
  ui->mainToolBar->addAction(saveFileAction);
  ui->mainToolBar->addSeparator();
  ui->mainToolBar->addAction(toggleEtalonModeAction);
  ui->mainToolBar->addSeparator();
  ui->mainToolBar->addActions(modeActionGroup->actions());
  ui->mainToolBar->addSeparator();
  ui->mainToolBar->addAction(toggleRulerAction);
  ui->mainToolBar->addAction(customizeInscriptionFontAction);
  ui->mainToolBar->addSeparator();
  ui->mainToolBar->addAction(aboutAction);
  ui->mainToolBar->setIconSize(QSize(32, 32));
  ui->mainToolBar->setContextMenuPolicy(Qt::PreventContextMenu);

  QFont statusBarFont = ui->statusBar->font();
  int newSize = QFontInfo(statusBarFont).pointSize() * 1.5;  // In contrast to ``labelFont.pointSize()'' it always works
  statusBarFont.setPointSize(newSize);
  ui->statusBar->setFont(statusBarFont);
  scaleLabel  = new QLabel(this);
  statusLabel = new QLabel(this);
  ui->statusBar->addPermanentWidget(scaleLabel);
  ui->statusBar->addWidget(statusLabel);

  canvasWidget = 0;

  connect(openFileAction,                 SIGNAL(triggered()), this, SLOT(openFile()));
  connect(saveFileAction,                 SIGNAL(triggered()), this, SLOT(saveFile()));
  connect(customizeInscriptionFontAction, SIGNAL(triggered()), this, SLOT(customizeInscriptionFont()));
  connect(aboutAction,                    SIGNAL(triggered()), this, SLOT(showAbout()));

  connect(toggleEtalonModeAction, SIGNAL(toggled(bool)),       this, SLOT(toggleEtalonDefinition(bool)));
  connect(modeActionGroup,        SIGNAL(triggered(QAction*)), this, SLOT(updateMode(QAction*)));

  setDrawOptionsEnabled(false);
  loadAndApplySettings();
}

MainWindow::~MainWindow()
{
  saveSettings();
  delete ui;
}

QString MainWindow::companyName() const
{
  return "AMM Soft";
}

QString MainWindow::appName() const
{
  return "AreaMeasurement";
}

QList<int> MainWindow::appVersion() const
{
  // TODO: Don't forget to increment it!
  return QList<int>() << 1 << 0;
}

QString MainWindow::appVersionString() const
{
  QString versionString;
  foreach (int x, appVersion())
    versionString += QString::number(x) + '.';
  return versionString.left(versionString.length() - 1);
}

QFont MainWindow::getInscriptionFont() const
{
  return inscriptionFont;
}

void MainWindow::setMode(ShapeType newMode)
{
  if (canvasWidget)
    canvasWidget->setMode(newMode);
}

QString MainWindow::getImageFormatsFilter() const
{
  QList<QByteArray> supportedFormatsList__ = QImageReader::supportedImageFormats();
  QSet<QString> supportedFormatsSet;
  foreach (const QByteArray& format, supportedFormatsList__)
    supportedFormatsSet.insert(QString(format).toLower());
  QStringList supportedFormatsList(supportedFormatsSet.toList());
  qSort(supportedFormatsList);
  QString allFormatsString;
  QStringList singleFormatsList;
  foreach (const QString& lowerFormat, supportedFormatsList) {
    QString upperFormat = lowerFormat.toUpper();
    allFormatsString += QString("*.%1 ").arg(lowerFormat);
    if (upperFormat == "JPEG")
      singleFormatsList += QString::fromUtf8("Изображения JPEG (*.jpeg *.jpg)");
    else if (upperFormat != "JPG")
      singleFormatsList += QString::fromUtf8("Изображения %1 (*.%2)").arg(upperFormat).arg(lowerFormat);
  }
  allFormatsString = allFormatsString.trimmed();
  return QString::fromUtf8("Все изображения (%1);;").arg(allFormatsString) + singleFormatsList.join(";;");
}

void MainWindow::doOpenFile(const QString& filename)
{
  recentFiles.removeAll(filename);

  QPixmap image;
  if (!image.load(filename)) {
    QMessageBox::warning(this, appName(), QString::fromUtf8("Не могу открыть изображение «%1».").arg(filename));
    updateOpenRecentMenu();
    return;
  }

  openedFile = filename;
  recentFiles.prepend(filename);
  if (recentFiles.size() > maxRecentDocuments)
    recentFiles.erase(recentFiles.begin() + maxRecentDocuments, recentFiles.end());
  updateOpenRecentMenu();

  toggleEtalonModeAction->setChecked(true);
  measureSegmentLengthAction->setChecked(true);

  delete canvasWidget;
  canvasWidget = new CanvasWidget(image, this, ui->containingScrollArea, scaleLabel, statusLabel, this);
  ui->containingScrollArea->setWidget(canvasWidget);

  connect(toggleRulerAction, SIGNAL(toggled(bool)), canvasWidget, SLOT(toggleRuler(bool)));
  canvasWidget->toggleRuler(toggleRulerAction->isChecked());

  saveFileAction->setEnabled(true);
  saveSettings();
  setDrawOptionsEnabled(true);
}

void MainWindow::doSaveFile(const QString& filename)
{
  ASSERT_RETURN(canvasWidget);
  bool ok = canvasWidget->getModifiedImage().save(filename);
  if (ok)
    ui->statusBar->showMessage(QString::fromUtf8("Файл успешно сохранён"), 5000);
  else
    QMessageBox::warning(this, appName(), QString::fromUtf8("Не удалось записать файл «%1»!").arg(filename));
}

void MainWindow::loadSettings()
{
  QSettings settings(companyName(), appName());
  inscriptionFont = settings.value("Inscription Font", QFont()).value<QFont>();
  recentFiles.clear();
  int nRecent = settings.beginReadArray("Recent Documents");
  for (int i = 0; i < nRecent; ++i) {
    settings.setArrayIndex(i);
    recentFiles.append(settings.value("Filename").toString());
  }
  settings.endArray();
}

void MainWindow::loadAndApplySettings()
{
  loadSettings();
  updateOpenRecentMenu();
}

void MainWindow::saveSettings() const
{
  QSettings settings(companyName(), appName());
  settings.clear();
  settings.setValue("Inscription Font", inscriptionFont);
  settings.beginWriteArray("Recent Documents");
  for (int i = 0; i < recentFiles.size(); ++i) {
    settings.setArrayIndex(i);
    settings.setValue("Filename", recentFiles[i]);
  }
  settings.endArray();
}

void MainWindow::updateOpenRecentMenu()
{
  openRecentMenu->clear();
  foreach (const QString& file, recentFiles)
    openRecentMenu->addAction(file, this, SLOT(openRecentFile()));
  if (openRecentMenu->isEmpty()) {
    QAction* infoAction = new QAction(QString::fromUtf8("Список недавно открытых документов пуст"), openRecentMenu);
    infoAction->setEnabled(false);
    openRecentMenu->addAction(infoAction);
  }
}

void MainWindow::openFile()
{
  QString filename = QFileDialog::getOpenFileName(this, QString::fromUtf8("Открыть изображение — ") + appName(),
                                                  QString(), getImageFormatsFilter(), 0);
  if (!filename.isEmpty())
    doOpenFile(filename);
}

void MainWindow::openRecentFile()
{
  QAction* triggeredAction = qobject_cast<QAction*>(sender());
  ASSERT_RETURN(triggeredAction);
  doOpenFile(triggeredAction->text());
}

void MainWindow::saveFile()
{
  QString filename = QFileDialog::getSaveFileName(this, QString::fromUtf8("Сохранить изображение — ") + appName(),
                                                  openedFile, getImageFormatsFilter(), 0);
  if (!filename.isEmpty())
    doSaveFile(filename);
}

void MainWindow::setDrawOptionsEnabled(bool enabled)
{
  toggleEtalonModeAction->setEnabled(enabled);
  modeActionGroup       ->setEnabled(enabled);
  toggleRulerAction     ->setEnabled(enabled);
}

void MainWindow::updateMode(QAction* modeAction)
{
  if (modeAction == measureSegmentLengthAction)
    return setMode(SEGMENT);
  if (modeAction == measurePolylineLengthAction)
    return setMode(POLYLINE);
  if (modeAction == measureClosedPolylineLengthAction)
    return setMode(CLOSED_POLYLINE);
  if (modeAction == measureRectangleAreaAction)
    return setMode(RECTANGLE);
  if (modeAction == measurePolygonAreaAction)
    return setMode(POLYGON);
  ERROR_RETURN();
}

void MainWindow::toggleEtalonDefinition(bool isDefiningEtalon)
{
  ASSERT_RETURN(canvasWidget);
  canvasWidget->toggleEtalonDefinition(isDefiningEtalon);
  toggleEtalonModeAction->setChecked(isDefiningEtalon);
}

void MainWindow::customizeInscriptionFont()
{
  // TODO: Why does the dialog show wrong font for the first time?
  inscriptionFont = QFontDialog::getFont(0, inscriptionFont, this);
  if (canvasWidget)
    canvasWidget->update();
}

void MainWindow::showAbout()
{
  QString aboutText = QString::fromUtf8("Программа %1, версия %2.\n\n"
                                        "Приложение предназначено для измерения длин и площадей объектов на чертежах, картах и т.д.\n\n"
                                        "Автор — Матвеякин Андрей.\n\n"
                                        "Программа распространяется бесплатно по принципу «как есть»: автор не несёт ответственности за возможный ущерб, нанесённый в результате работы приложения."
                                        ).arg(appName()).arg(appVersionString());
  QMessageBox::about(this, QString::fromUtf8("О программе %1").arg(appName()), aboutText);
}
<|endoftext|>