Create repository

This commit is contained in:
Loic Guegan 2022-02-23 18:11:55 +01:00
commit ce941c146a
127 changed files with 16162 additions and 0 deletions

143
src/game_tab/Game.cpp Normal file
View file

@ -0,0 +1,143 @@
#include "Game.hpp"
Game::Game() : current(NULL), moves(NULL) {
tags["White"] = "";
tags["Black"] = "";
initial_fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
board = "rnbqkbnrpppppppp PPPPPPPPRNBQKBNR";
}
Game::Game(std::string fen) : current(NULL), moves(NULL) {
tags["White"] = "";
tags["Black"] = "";
tags["FEN"] = fen;
initial_fen = fen;
board = chessarbiter::FENParser::Parse(fen).board;
}
Game::Game(HalfMove *m) {
moves = m;
current = m;
initial_fen = m->GetFen();
board = chessarbiter::FENParser::Parse(initial_fen).board;
}
std::string Game::GetBoard() { return (board); }
std::string Game::GetTag(std::string tagname) { return (tags[tagname]); }
void Game::SetTag(std::string tagname, std::string value) {
tags[tagname] = value;
}
bool Game::IsBlackToPlay() {
if (current == NULL) {
return (false);
}
return (!current->IsBlack);
}
void Game::DeleteTag(std::string tagname) { tags.erase(tagname); }
void Game::DeleteMove(HalfMove *m) {
if (moves == m) {
current = NULL;
moves = NULL;
delete m;
} else {
if (m != NULL) {
current = m->GetParent();
if (current != NULL) {
current->RemoveChild(m);
}
delete m;
}
}
}
HalfMove *Game::GetCurrentMove() { return (current); }
HalfMove *Game::GetMoves() { return (moves); }
void Game::PromoteMove(HalfMove *m) {
if (m != NULL) {
current = m;
m->Promote();
}
}
void Game::SetMoveAsMainline(HalfMove *m) {
if (m != NULL) {
current = m;
m->SetAsMainline();
}
}
bool Game::Play(std::string move) {
wxLogDebug("Playing move %s", move);
std::string fen = GetFen();
arbiter.Setup(fen);
if (arbiter.Play(move)) {
HalfMove *m = new HalfMove(arbiter.GetSAN(), arbiter.GetFEN());
char capture = arbiter.GetCapture();
if (capture != ' ') {
wxLogDebug("%c", capture);
m->SetCapture(capture);
}
if (current != NULL) {
current->AddMove(m);
} else if (moves != NULL) {
moves->AddVariation(m);
}
current = m;
if (moves == NULL) {
moves = m;
}
wxLogDebug("%s",GetPGN());
return (true);
}
return (false);
}
void Game::Previous() {
if (current != NULL) {
current = current->GetParent();
}
}
std::vector<std::string> Game::ListTags() {
std::vector<std::string> keys;
for (auto const &element : tags) {
keys.push_back(element.first);
}
return (keys);
}
void Game::Next() {
if (current != NULL) {
HalfMove *m = current->GetMainline();
if (m != NULL) {
current = m;
}
} else {
current = moves;
}
}
void Game::SetCurrent(HalfMove *m) { current = m; }
std::string Game::GetFen() {
if (current == NULL) {
return (initial_fen);
}
return (current->GetFen());
}
std::string Game::GetPGN() {
std::string pgn;
if (moves != NULL) {
for (auto const &element : tags) {
pgn += '[' + element.first + " \"" + element.second + "\"]\n";
}
pgn += moves->GetPGN();
}
return (pgn);
}

38
src/game_tab/Game.hpp Normal file
View file

@ -0,0 +1,38 @@
#pragma once
#include "ChessArbiter.hpp"
#include "HalfMove.hpp"
#include "ochess.hpp"
#include <unordered_map>
class Game {
std::string board;
std::string initial_fen;
std::unordered_map<std::string, std::string> tags;
HalfMove *moves;
HalfMove *current;
chessarbiter::ChessArbiter arbiter;
public:
Game();
Game(std::string fen);
Game(HalfMove *m);
std::string GetBoard();
std::string GetTag(std::string tagname);
void SetTag(std::string tagname, std::string value);
void DeleteTag(std::string tagname);
HalfMove *GetCurrentMove();
HalfMove *GetMoves();
std::string GetFen();
bool Play(std::string move);
bool IsBlackToPlay();
void Previous();
void Next();
void DeleteMove(HalfMove *m);
void PromoteMove(HalfMove *m);
void SetMoveAsMainline(HalfMove *m);
void SetCurrent(HalfMove *m);
std::vector<std::string> ListTags();
std::string GetPGN();
};

52
src/game_tab/GameTab.cpp Normal file
View file

@ -0,0 +1,52 @@
#include "GameTab.hpp"
#include <wx/clipbrd.h>
wxDEFINE_EVENT(GAME_CHANGE, wxCommandEvent);
GameTab::GameTab(wxFrame *parent, Game *game)
: wxPanel(parent), game(game), TabInfos(TabInfos::GAME) {
// Splitter
wxSplitterWindow *splitter = new wxSplitterWindow(this, wxID_ANY);
splitter->SetMinimumPaneSize(100);
// Panels
board_panel = new BoardPanel((wxFrame *)splitter, game);
editor_panel = new EditorPanel((wxFrame *)splitter, game);
splitter->SplitVertically(board_panel, editor_panel);
// Setup splitter
wxBoxSizer *topSizer = new wxBoxSizer(wxHORIZONTAL);
topSizer->Add(splitter, 1, wxEXPAND);
SetSizerAndFit(topSizer);
// Refresh panels
wxCommandEvent event(REFRESH_TAB_TITLE, GetId());
event.SetEventObject(this);
OnRefreshTabTitle(event);
board_panel->Notify();
editor_panel->Notify();
Bind(REFRESH_TAB_TITLE, &GameTab::OnRefreshTabTitle, this, wxID_ANY);
Bind(GAME_CHANGE, &GameTab::OnGameChange, this, wxID_ANY);
}
void GameTab::OnGameChange(wxCommandEvent &event) {
board_panel->Notify();
editor_panel->Notify();
}
void GameTab::OnRefreshTabTitle(wxCommandEvent &event) {
std::string white = game->GetTag("White");
std::string black = game->GetTag("Black");
if (white.size() == 0 && black.size() == 0) {
SetLabel("New Game");
} else {
SetLabel(white + "-" + black);
}
event.SetEventObject(this);
event.Skip();
}
void GameTab::ApplyPreferences() {
board_panel->ApplyPreferences();
}

28
src/game_tab/GameTab.hpp Normal file
View file

@ -0,0 +1,28 @@
#pragma once
#include "ChessArbiter.hpp"
#include "Game.hpp"
#include "HalfMove.hpp"
#include "board/BoardPanel.hpp"
#include "editor/EditorPanel.hpp"
#include "ochess.hpp"
#include <utility>
#include <wx/collpane.h>
#include <wx/splitter.h>
#include <wx/textctrl.h>
wxDECLARE_EVENT(REFRESH_TAB_TITLE, wxCommandEvent);
wxDECLARE_EVENT(GAME_CHANGE, wxCommandEvent);
class GameTab : public wxPanel, public TabInfos {
EditorPanel *editor_panel;
BoardPanel *board_panel;
Game *game;
void RefreshLabel();
void OnRefreshTabTitle(wxCommandEvent &event);
void OnGameChange(wxCommandEvent &event);
public:
GameTab(wxFrame *parent, Game *game);
void ApplyPreferences();
};

213
src/game_tab/HalfMove.cpp Normal file
View file

@ -0,0 +1,213 @@
#include "HalfMove.hpp"
HalfMove::HalfMove(std::string move) : capture(' ') {
this->move = move;
fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
}
HalfMove::HalfMove(std::string move, std::string fen) : fen(fen), capture(' ') {
this->move = move;
}
HalfMove::~HalfMove() {
for (HalfMove *m : variations) {
delete m;
}
}
void HalfMove::AddVariation(HalfMove *m) {
m->IsBlack = this->IsBlack;
m->Number = this->Number;
HalfMove::variations.push_back(m);
cgeditor::CGEHalfMove::variations.push_back(m);
m->SetParent(this);
}
std::map<char, std::uint8_t> HalfMove::GetLineCaptures() {
std::map<char, std::uint8_t> captures;
HalfMove *m = this;
do {
char c = m->capture;
if (captures.find(c) != captures.end()) {
captures[c]++;
} else {
captures[c] = 1;
}
m = m->parent;
} while (m != NULL);
return (captures);
}
void HalfMove::SetCapture(char c) { capture = c; }
void HalfMove::AddMove(HalfMove *m) {
if (this->mainline == NULL) {
SetMainline(m);
} else {
if (mainline != NULL) {
mainline->AddVariation(m);
}
}
}
void HalfMove::SetMainline(HalfMove *m) {
if (!this->IsBlack) {
m->IsBlack = true;
m->Number = this->Number;
} else {
m->IsBlack = false;
m->Number = this->Number + 1;
}
HalfMove::mainline = m;
cgeditor::CGEHalfMove::MainLine = m;
if (m != NULL) {
m->SetParent(this);
}
}
void HalfMove::SetParent(HalfMove *m) {
HalfMove::parent = m;
CGEHalfMove::Parent = m;
}
void HalfMove::RemoveChild(HalfMove *m) {
std::uint32_t i = 0;
bool found = false;
for (i; i < HalfMove::variations.size(); i++) {
if (HalfMove::variations[i] == m) {
found = true;
break;
}
}
if (found) {
HalfMove::variations.erase(HalfMove::variations.begin() + i);
}
if (HalfMove::mainline == m) {
HalfMove::mainline = NULL;
}
cgeditor::CGEHalfMove::RemoveChild((CGEHalfMove *)m);
}
HalfMove *HalfMove::GetParent() { return (parent); }
HalfMove *HalfMove::GetRoot() {
HalfMove *m = this;
HalfMove *p = HalfMove::parent;
while (p != NULL) {
if (p->mainline != m) {
return (m);
}
m = p;
p = m->HalfMove::parent;
}
return (m);
}
void HalfMove::SetAsMainline() {
HalfMove *root = GetRoot();
HalfMove *lastRoot;
do {
lastRoot = root;
root->HalfMove::Promote();
root = GetRoot();
} while (root != lastRoot);
}
HalfMove *HalfMove::GetMainline() { return (mainline); }
HalfMove::HalfMove(pgnp::HalfMove *m, std::string initial_fen) {
chessarbiter::ChessArbiter arbiter;
arbiter.Setup(initial_fen);
arbiter.Play(arbiter.ParseSAN(m->move));
this->fen = arbiter.GetFEN();
this->move = m->move;
this->IsBlack = m->isBlack;
this->SetComment(m->comment);
this->Number = m->count;
if (m->MainLine != NULL) {
this->SetMainline(new HalfMove(m->MainLine, arbiter.GetFEN()));
}
for (pgnp::HalfMove *v : m->variations) {
arbiter.Setup(initial_fen);
arbiter.Play(arbiter.ParseSAN(v->move));
this->AddVariation(new HalfMove(v, arbiter.GetFEN()));
}
}
void HalfMove::SetFen(std::string fen) { this->fen = fen; }
void HalfMove::Promote() {
HalfMove *root = GetRoot();
if (root->parent != NULL) {
HalfMove *p = root->parent;
if (p->HalfMove::mainline != root) {
if (root->parent->HalfMove::parent != NULL) {
HalfMove *pp = root->parent->HalfMove::parent;
if (pp->HalfMove::mainline == p) {
pp->HalfMove::SetMainline(root);
} else {
pp->AddVariation(root);
pp->HalfMove::RemoveChild(p);
}
}
if (p->HalfMove::mainline == root) {
p->HalfMove::SetMainline(NULL);
} else {
p->HalfMove::RemoveChild(root);
}
root->AddVariation(p);
}
}
}
bool HalfMove::IsVariation() {
HalfMove *m = this;
HalfMove *p = HalfMove::parent;
while (p != NULL) {
if (p->mainline != m) {
return (true);
}
m = p;
p = m->HalfMove::parent;
}
return (false);
}
std::string HalfMove::GetFen() { return (fen); }
std::string HalfMove::GetPGN() { return (GetPGN(IsBlack)); }
std::string HalfMove::GetPGN(bool needDots) {
std::string part;
bool newNeedDots = false;
if (!IsBlack || needDots) {
part += std::to_string(Number) + ".";
if (needDots) {
part += "..";
}
}
part += move;
if (GetNbLineComment() > 0) {
part += " {";
part += GetComment();
part += "}";
newNeedDots = true;
}
if (variations.size() > 0) {
newNeedDots = true;
for (HalfMove *v : variations) {
part += " (";
part += v->GetPGN(IsBlack);
part += ")";
}
}
if (mainline != NULL) {
part += " " + mainline->GetPGN(newNeedDots);
}
return (part);
}

57
src/game_tab/HalfMove.hpp Normal file
View file

@ -0,0 +1,57 @@
#pragma once
#include "CGEditor.hpp"
#include "ChessArbiter.hpp"
#include "ochess.hpp"
#include "pgnp.hpp"
#include <map>
#include <vector>
/**
* @brief Create your custom half move class
*
* The implementation of the class should give you
* an overview of how to keep your move sync with the one of CGEditor
*
*/
class HalfMove : public cgeditor::CGEHalfMove {
HalfMove *parent = NULL;
HalfMove *mainline = NULL;
std::vector<HalfMove *> variations;
std::string fen;
char capture;
std::string GetPGN(bool needDots);
public:
HalfMove(std::string move);
HalfMove(std::string move, std::string fen);
HalfMove(pgnp::HalfMove *m, std::string initial_fen);
~HalfMove();
/// @brief Add variation to current move
void AddVariation(HalfMove *m);
/// @brief Remove the specified child from mainline and/or variations
void RemoveChild(HalfMove *m);
void AddMove(HalfMove *m);
/// @brief Set value of the mailine
void SetMainline(HalfMove *m);
/// @brief Set this move as mainline
void SetAsMainline();
/// @brief Promote the current move and submove
void Promote();
/// @brief Check if current half move is within a variation
bool IsVariation();
/// @brief Get the root of a variation
HalfMove *GetRoot();
/// @brief Get parent of the current move
HalfMove *GetParent();
HalfMove *GetMainline();
std::map<char, std::uint8_t> GetLineCaptures();
/// @brief Set parent of the current move
void SetParent(HalfMove *m);
std::string GetFen();
void SetFen(std::string fen);
void SetCapture(char c);
std::string GetPGN();
};

View file

@ -0,0 +1,294 @@
#include "BoardCanvas.hpp"
wxDEFINE_EVENT(PLAY_MOVE_EVENT, wxCommandEvent);
BoardCanvas::BoardCanvas(wxFrame *parent)
: wxPanel(parent), black_side(false), is_black_turn(true), frozen(false),
lock_square_size(false), t(new Theme()), t_captures(new Theme()) {
board = "rnbqkbnrpppppppp PPPPPPPPRNBQKBNR";
is_dragging = false;
valid_drag = false;
t_captures->ResizePieces(t->GetPiecesSizes() * CAPTURE_FACTOR);
SetClockTime(-1, -1, -1, false);
SetClockTime(-1, -1, -1, true);
ApplyPreferences();
}
BoardCanvas::BoardCanvas(wxFrame *parent, std::uint32_t square_width,
bool frozen)
: BoardCanvas(parent) {
t->ResizeSquaresAndPieces(square_width);
t_captures->ResizePieces(t->GetPiecesSizes() * CAPTURE_FACTOR);
this->frozen = true;
lock_square_size = true;
}
void BoardCanvas::OnPaint(wxPaintEvent &event) {
wxPaintDC dc(this);
REFRESH_MOUSE_LOCATION();
square_width = t->GetSquaresSizes();
canvas_size = dc.GetSize();
boardX = (canvas_size.x - (8 * square_width)) / 2;
boardY = (canvas_size.y - (8 * square_width)) / 2;
if (boardX > canvas_size.x)
boardX = 0;
if (boardY > canvas_size.y)
boardY = 0;
DrawBoard(dc);
}
void BoardCanvas::ApplyPreferences() {
if (t != NULL)
delete t;
if (t_captures != NULL)
delete t_captures;
t = new Theme();
t_captures = new Theme();
CONFIG_OPEN(config);
black_side = config->Read("board/black_by_default", false);
if (lock_square_size) {
t->ResizeSquaresAndPieces(square_width);
} else {
t->ResizeSquaresAndPieces(config->Read("board/square_size", 80));
}
t->SetSquareRadius(config->Read("board/corner_radius", 10));
t_captures->ResizePieces(t->GetPiecesSizes() * CAPTURE_FACTOR);
CONFIG_CLOSE(config);
Refresh();
}
void BoardCanvas::SetupBoard(std::string board, bool is_black_turn,
std::map<char, std::uint8_t> captures) {
this->board = board;
this->is_black_turn = is_black_turn;
this->captures = captures;
Refresh();
}
void BoardCanvas::DrawBoard(wxPaintDC &dc) {
std::uint32_t piece_width = t->GetPiecesSizes();
std::uint32_t centrer_offset = (square_width - piece_width) / 2;
bool DrawDraggingPiece = false;
char dp = 'p';
std::uint32_t dpx = 0, dpy = 0;
for (std::int8_t file = 7; file >= 0; file--) {
for (std::uint8_t rank = 0; rank <= 7; rank++) {
std::uint32_t x = boardX + (7 - file) * square_width;
std::uint32_t y = boardY + rank * square_width;
if ((file + rank) % 2 == 0) {
if (file == 0 && rank == 0) {
dc.DrawBitmap(*t->Get('1'), x, y, true);
} else if (file == 7 && rank == 7) {
dc.DrawBitmap(*t->Get('2'), x, y, true);
} else {
dc.DrawBitmap(*t->Get('s'), x, y, true);
}
} else {
if (file == 7 && rank == 0) {
dc.DrawBitmap(*t->Get('0'), x, y, true);
} else if (file == 0 && rank == 7) {
dc.DrawBitmap(*t->Get('3'), x, y, true);
} else {
dc.DrawBitmap(*t->Get('S'), x, y, true);
}
}
std::uint8_t prank = rank;
std::uint8_t pfile = file;
if (black_side) {
prank = 7 - rank;
pfile = 7 - file;
}
std::uint32_t px = x + centrer_offset;
std::uint32_t py = y + centrer_offset;
char piece = board[(7 - pfile) + 8 * prank];
if (is_dragging && (7 - pfile) == active_square.x &&
(7 - prank) == active_square.y) {
dp = piece;
dpx = px - (lastClickX - mouseX);
dpy = py - (lastClickY - mouseY);
DrawDraggingPiece = true;
continue;
}
if (piece != ' ') {
dc.DrawBitmap(*t->Get(piece), px, py, false);
}
}
}
// Draw badge
dc.SetPen(wxPen(*wxBLACK, 3));
std::uint32_t badgeY = boardY;
std::uint32_t badgeWidth = square_width / 2;
if (is_black_turn) {
dc.SetBrush(*wxBLACK_BRUSH);
if (black_side) {
badgeY = boardY + (8 * square_width) - badgeWidth;
}
} else {
dc.SetBrush(*wxWHITE_BRUSH);
if (!black_side) {
badgeY = boardY + (8 * square_width) - badgeWidth;
}
}
wxRect badge(boardX + (8 * square_width) + badgeWidth / 2, badgeY, badgeWidth,
badgeWidth);
dc.DrawRectangle(badge);
// Draw captures first for white then for black
std::uint32_t captures_size = t_captures->GetPiecesSizes();
std::uint8_t padding = 10;
std::uint32_t offsetX = 0;
std::uint32_t offsetY = -(captures_size + padding);
if (black_side) {
offsetY = 8 * square_width + padding;
}
for (char p : {'P', 'N', 'B', 'R', 'Q'}) {
if (captures.find(p) != captures.end()) {
for (std::uint8_t i = 0; i < captures[p]; i++) {
dc.DrawBitmap(*t_captures->Get(p), boardX + offsetX, boardY + offsetY);
offsetX += captures_size / 2;
}
offsetX += captures_size / 2;
}
}
offsetX = 0;
if (black_side) {
offsetY = -(captures_size + padding);
} else {
offsetY = 8 * square_width + padding;
}
for (char p : {'p', 'n', 'b', 'r', 'q'}) {
if (captures.find(p) != captures.end()) {
for (std::uint8_t i = 0; i < captures[p]; i++) {
dc.DrawBitmap(*t_captures->Get(p), boardX + offsetX, boardY + offsetY);
offsetX += captures_size / 2;
}
offsetX += captures_size / 2;
}
}
// Draw dragging piece
if (DrawDraggingPiece) {
dc.DrawBitmap(*t->Get(dp), dpx, dpy, false);
}
// Draw numbers
for (char l = 'a'; l < 'a' + 8; l++) {
dc.DrawText(wxString(l), wxPoint(boardX + l - 'a' * square_width,
boardY + 8 * square_width + 10));
}
// Draw Clocks
if (std::get<0>(black_time) >= 0) {
wxFont font = dc.GetFont();
ClockTime clock = black_side ? white_time : black_time;
wxString time =
wxString::Format("%ds", std::get<1>(clock), std::get<2>(clock));
if (std::get<0>(clock) > 0) {
time = wxString::Format("%d:%d", std::get<0>(clock), std::get<1>(clock));
} else if (std::get<1>(clock) > 0) {
time = wxString::Format("%d:%ds", std::get<1>(clock), std::get<2>(clock));
}
dc.SetFont(font.Scale(1.5).MakeBold());
wxCoord width, height;
dc.GetTextExtent(time, &width, &height);
dc.DrawText(time,
wxPoint(boardX + square_width * 8 - width, boardY - height));
clock = black_side ? black_time : white_time;
time = wxString::Format("%ds", std::get<1>(clock), std::get<2>(clock));
if (std::get<0>(clock) > 0) {
time = wxString::Format("%d:%d", std::get<0>(clock), std::get<1>(clock));
} else if (std::get<1>(clock) > 0) {
time = wxString::Format("%d:%ds", std::get<1>(clock), std::get<2>(clock));
}
dc.GetTextExtent(time, &width, &height);
dc.DrawText(time, wxPoint(boardX + square_width * 8 - width,
boardY + square_width * 8));
}
}
void BoardCanvas::MouseEvent(wxMouseEvent &event) {
if (!frozen) {
if (event.Dragging()) {
if (valid_drag) {
is_dragging = true;
Refresh();
}
} else {
if (is_dragging) {
is_dragging = false;
valid_drag = false;
// Handle drop
REFRESH_MOUSE_LOCATION();
INIT_CURRENT_SQUARE();
if (IsCurrentSquareValid) {
wxLogDebug("Drag end on square (%d,%d)", file, rank);
/// Play the move
wxCommandEvent event(PLAY_MOVE_EVENT, GetId());
event.SetEventObject(this);
std::string move = ((char)('a' + active_square.x)) +
std::to_string(+active_square.y + 1) +
((char)('a' + file)) + std::to_string(rank + 1);
event.SetString(move);
ProcessEvent(event);
}
}
if (event.LeftDown()) {
SetFocus();
REFRESH_MOUSE_LOCATION();
lastClickX = mouseX;
lastClickY = mouseY;
INIT_CURRENT_SQUARE();
if (IsCurrentSquareValid) {
active_square.x = file;
active_square.y = rank;
if (board[(7 - rank) * 8 + file] != ' ') {
wxLogDebug("Drag start on square (%d,%d)", file, rank);
valid_drag = true;
}
}
}
}
}
}
void BoardCanvas::Zoom(std::int32_t zoom) {
t->Zoom(zoom);
t_captures->ResizePieces(t->GetPiecesSizes() * CAPTURE_FACTOR);
Refresh();
}
void BoardCanvas::Swap() {
black_side = !black_side;
Refresh();
}
void BoardCanvas::OnKeyEvent(wxKeyEvent &event) {
if (event.GetKeyCode() == WXK_LEFT) {
wxCommandEvent previousEvent(PREVIOUS_MOVE_EVENT, GetId());
previousEvent.SetEventObject(this);
ProcessEvent(previousEvent);
} else if (event.GetKeyCode() == WXK_RIGHT) {
wxCommandEvent nextEvent(NEXT_MOVE_EVENT, GetId());
nextEvent.SetEventObject(this);
ProcessEvent(nextEvent);
}
}
void BoardCanvas::SetClockTime(short hours, short min, short sec,
bool IsBlack) {
if (IsBlack) {
black_time = std::make_tuple(hours, min, sec);
} else {
white_time = std::make_tuple(hours, min, sec);
}
}
wxBEGIN_EVENT_TABLE(BoardCanvas, wxPanel) EVT_PAINT(BoardCanvas::OnPaint)
EVT_MOUSE_EVENTS(BoardCanvas::MouseEvent)
EVT_CHAR_HOOK(BoardCanvas::OnKeyEvent) wxEND_EVENT_TABLE()

View file

@ -0,0 +1,68 @@
#pragma once
#include "Theme.hpp"
#include "ochess.hpp"
#include <map>
#include <tuple>
#include <utility>
#include <vector>
#include <wx/artprov.h>
// Local events
wxDECLARE_EVENT(PLAY_MOVE_EVENT, wxCommandEvent);
// Foreign events
wxDECLARE_EVENT(PREVIOUS_MOVE_EVENT, wxCommandEvent);
wxDECLARE_EVENT(NEXT_MOVE_EVENT, wxCommandEvent);
#define REFRESH_MOUSE_LOCATION() \
{ \
const wxPoint pt = wxGetMousePosition(); \
mouseX = pt.x - this->GetScreenPosition().x; \
mouseY = pt.y - this->GetScreenPosition().y; \
}
#define INIT_CURRENT_SQUARE() \
std::uint32_t file = 7 - (mouseX - boardX) / square_width; \
std::uint32_t rank = (mouseY - boardY) / square_width; \
if (!black_side) { \
file = 7 - file; \
rank = 7 - rank; \
} \
bool IsCurrentSquareValid = file >= 0 && file <= 7 && rank >= 0 && rank <= 7;
#define MOUSE_ON(x, y, width, height) \
(mouseX >= (x) && mouseX <= ((x) + (width)) && mouseY >= (y) && \
mouseY <= ((y) + (height)))
#define CAPTURE_FACTOR 0.5
typedef std::tuple<short, short, short> ClockTime;
class BoardCanvas : public wxPanel {
Theme *t, *t_captures;
std::string board;
bool black_side, is_dragging, valid_drag, is_black_turn;
std::uint32_t boardX, boardY, square_width, mouseX, mouseY, lastClickX,
lastClickY;
wxSize canvas_size;
wxPoint active_square;
std::map<char, std::uint8_t> captures;
ClockTime black_time, white_time;
bool frozen,lock_square_size;
public:
BoardCanvas(wxFrame *parent);
BoardCanvas(wxFrame *parent,std::uint32_t square_width, bool frozen);
void ApplyPreferences();
void DrawBoard(wxPaintDC &dc);
void OnPaint(wxPaintEvent &event);
void OnKeyEvent(wxKeyEvent &event);
void MouseEvent(wxMouseEvent &event);
void Zoom(std::int32_t zoom);
void Swap();
void SetupBoard(std::string board, bool is_black_turn,
std::map<char, std::uint8_t> captures);
void SetClockTime(short hours, short min, short sec, bool IsBlack);
DECLARE_EVENT_TABLE()
};

View file

@ -0,0 +1,96 @@
#include "BoardPanel.hpp"
#include <wx/clipbrd.h>
BoardPanel::BoardPanel(wxFrame *parent, Game *game)
: wxPanel(parent), game(game) {
wxBoxSizer *board_panel_sizer = new wxBoxSizer(wxVERTICAL);
board_canvas = new BoardCanvas((wxFrame *)this);
board_panel_sizer->Add(board_canvas, 1, wxEXPAND);
// Left Panel buttons
wxBoxSizer *board_panel_button_sizer = new wxBoxSizer(wxHORIZONTAL);
board_panel_button_sizer->Add(
new wxBitmapButton(this, SWAP_BTN, LoadPNG("swap")), 0);
board_panel_button_sizer->Add(
new wxBitmapButton(this, ZOOM_IN_BTN, LoadPNG("zoomin")), 0);
board_panel_button_sizer->Add(
new wxBitmapButton(this, ZOOM_OUT_BTN, LoadPNG("zoomout")), 0);
board_panel_button_sizer->Add(new wxButton(this, COPY_FEN_BTN, L"Copy FEN"),
0, wxEXPAND);
board_panel_sizer->Add(board_panel_button_sizer, 0);
this->SetSizer(board_panel_sizer);
Bind(PLAY_MOVE_EVENT, &BoardPanel::OnPlay, this, wxID_ANY);
Bind(PREVIOUS_MOVE_EVENT, &BoardPanel::OnPreviousMove, this, wxID_ANY);
Bind(NEXT_MOVE_EVENT, &BoardPanel::OnNextMove, this, wxID_ANY);
Bind(wxEVT_BUTTON, &BoardPanel::OnSwap, this, SWAP_BTN);
Bind(wxEVT_BUTTON, &BoardPanel::OnZoomIn, this, ZOOM_IN_BTN);
Bind(wxEVT_BUTTON, &BoardPanel::OnZoomOut, this, ZOOM_OUT_BTN);
Bind(wxEVT_BUTTON, &BoardPanel::OnCopyFEN, this, COPY_FEN_BTN);
}
void BoardPanel::OnPreviousMove(wxCommandEvent &event) {
game->Previous();
Notify();
NotifyEditor();
}
void BoardPanel::OnZoomIn(wxCommandEvent &event) {
wxLogDebug("Clicked on zoom in");
board_canvas->Zoom(10);
}
void BoardPanel::OnZoomOut(wxCommandEvent &event) {
wxLogDebug("Clicked on zoom out");
board_canvas->Zoom(-10);
}
void BoardPanel::OnSwap(wxCommandEvent &event) {
wxLogDebug("Clicked on swap");
board_canvas->Swap();
}
void BoardPanel::OnNextMove(wxCommandEvent &event) {
wxLogDebug("Game tab received NEXT_MOVE_EVENT");
game->Next();
Notify();
NotifyEditor();
}
void BoardPanel::OnPlay(wxCommandEvent &event) {
wxLogDebug("Game tab received PLAY_MOVE_EVENT");
if (game->Play(event.GetString().ToStdString())) {
NotifyEditor();
}
Notify();
}
void BoardPanel::OnCopyFEN(wxCommandEvent &event) {
wxLogDebug("Clicked on copy fen");
if (wxTheClipboard->Open()) {
wxTheClipboard->SetData(new wxTextDataObject(game->GetFen()));
wxTheClipboard->Close();
}
}
void BoardPanel::Notify() {
std::string fen = game->GetFen();
std::map<char, std::uint8_t> captures;
HalfMove *m = game->GetCurrentMove();
if (m != NULL) {
captures = m->GetLineCaptures();
}
board_canvas->SetupBoard(chessarbiter::FENParser::Parse(fen).board,
game->IsBlackToPlay(), captures);
}
void BoardPanel::NotifyEditor() {
wxCommandEvent previousEvent(GAME_CHANGE, GetId());
previousEvent.SetEventObject(this);
ProcessEvent(previousEvent);
}
void BoardPanel::ApplyPreferences() {
board_canvas->ApplyPreferences();
}

View file

@ -0,0 +1,30 @@
#pragma once
#include "../Game.hpp"
#include "BoardCanvas.hpp"
#include "ochess.hpp"
// Foreign events
wxDECLARE_EVENT(GAME_CHANGE, wxCommandEvent);
enum { COPY_FEN_BTN = wxID_HIGHEST + 1, ZOOM_IN_BTN, ZOOM_OUT_BTN, SWAP_BTN };
class BoardPanel : public wxPanel {
Game *game;
BoardCanvas *board_canvas;
void NotifyEditor();
public:
BoardPanel(wxFrame *parent, Game *game);
void Notify();
void OnPlay(wxCommandEvent &event);
void OnGotoMove(wxCommandEvent &event);
void OnPreviousMove(wxCommandEvent &event);
void OnNextMove(wxCommandEvent &event);
void OnCopyFEN(wxCommandEvent &event);
void OnZoomIn(wxCommandEvent &event);
void OnZoomOut(wxCommandEvent &event);
void OnSwap(wxCommandEvent &event);
void OnRefreshBoard(wxCommandEvent &event);
void ApplyPreferences();
};

View file

@ -0,0 +1,184 @@
#include "Theme.hpp"
#include <wx/filename.h>
Theme::Theme() : square_radius(10) {
// Load config
CONFIG_OPEN(config);
std::string piece =
config->Read("board/theme/pieces/path", "default").ToStdString();
wxFileName piece_file(piece);
std::string square =
config->Read("board/theme/squares/path", "default").ToStdString();
wxFileName square_file(square);
CONFIG_CLOSE(config);
// Piece
if (piece == "default" || !piece_file.FileExists()) {
wxLogDebug("Loading piece skin from binres");
LoadPiecesSkin(LoadPNG("cburnett").ConvertToImage());
} else {
wxLogDebug("Loading piece skin: %s", piece);
LoadPiecesSkin(wxImage(piece, wxBITMAP_TYPE_PNG));
}
// Square
if (square == "default" || !square_file.FileExists()) {
wxLogDebug("Loading square skin from binres");
LoadSquaresSkin(LoadPNG("chesscom_8bits").ConvertToImage());
} else {
wxLogDebug("Loading square skin: %s", square);
LoadSquaresSkin(wxImage(square, wxBITMAP_TYPE_PNG));
}
}
Theme::Theme(std::string piece, std::string square) : square_radius(10) {
wxLogDebug("Loading piece skin: %s", piece);
LoadPiecesSkin(wxImage(piece, wxBITMAP_TYPE_PNG));
wxLogDebug("Loading square skin: %s", square);
LoadSquaresSkin(wxImage(square, wxBITMAP_TYPE_PNG));
}
Theme::~Theme() {
for (std::pair<char, wxBitmap *> c : skin_scaled) {
delete c.second;
}
}
std::uint8_t Theme::GetSquareRadius() { return (square_radius); }
void Theme::LoadPiecesSkin(wxImage iskin) {
if (iskin.GetWidth() != 2 * ELT_DIM || iskin.GetHeight() != 6 * ELT_DIM) {
throw "Invalid piece theme";
}
int offset = 0;
// Kings
skin['k'] = iskin.GetSubImage(wxRect(0, offset, ELT_DIM, ELT_DIM));
skin['K'] = iskin.GetSubImage(wxRect(ELT_DIM, offset, ELT_DIM, ELT_DIM));
// Queen
offset = ELT_DIM;
skin['q'] = iskin.GetSubImage(wxRect(0, offset, ELT_DIM, ELT_DIM));
skin['Q'] = iskin.GetSubImage(wxRect(ELT_DIM, offset, ELT_DIM, ELT_DIM));
// Rook
offset = ELT_DIM * 2;
skin['r'] = iskin.GetSubImage(wxRect(0, offset, ELT_DIM, ELT_DIM));
skin['R'] = iskin.GetSubImage(wxRect(ELT_DIM, offset, ELT_DIM, ELT_DIM));
// Bishop
offset = ELT_DIM * 3;
skin['b'] = iskin.GetSubImage(wxRect(0, offset, ELT_DIM, ELT_DIM));
skin['B'] = iskin.GetSubImage(wxRect(ELT_DIM, offset, ELT_DIM, ELT_DIM));
// Knight
offset = ELT_DIM * 4;
skin['n'] = iskin.GetSubImage(wxRect(0, offset, ELT_DIM, ELT_DIM));
skin['N'] = iskin.GetSubImage(wxRect(ELT_DIM, offset, ELT_DIM, ELT_DIM));
// Pawn
offset = ELT_DIM * 5;
skin['p'] = iskin.GetSubImage(wxRect(0, offset, ELT_DIM, ELT_DIM));
skin['P'] = iskin.GetSubImage(wxRect(ELT_DIM, offset, ELT_DIM, ELT_DIM));
// Update scaled version
ResizePieces(DEFAULT_SIZE * PIECE_SIZE_FACTOR);
}
void Theme::LoadSquaresSkin(wxImage iskin) {
if (iskin.GetWidth() != 2 * ELT_DIM || iskin.GetHeight() != ELT_DIM) {
throw "Invalid piece theme";
}
// Square
skin['s'] = iskin.GetSubImage(wxRect(0, 0, ELT_DIM, ELT_DIM));
skin['S'] = iskin.GetSubImage(wxRect(ELT_DIM, 0, ELT_DIM, ELT_DIM));
// Update scaled version
ResizeSquares(DEFAULT_SIZE);
}
wxBitmap *Theme::Get(char c) { return (skin_scaled[c]); }
void Theme::ResizePieces(std::uint32_t width) {
for (std::pair<char, wxImage> c : skin) {
if (c.first != 's' && c.first != 'S') {
if (skin_scaled.count(c.first))
delete skin_scaled[c.first];
skin_scaled[c.first] =
new wxBitmap(c.second.Scale(width, width, wxIMAGE_QUALITY_HIGH));
}
}
}
void Theme::ResizeSquaresAndPieces(std::uint32_t width) {
ResizeSquares(width);
ResizePieces(width * PIECE_SIZE_FACTOR);
}
void Theme::ResizeSquares(std::uint32_t width) {
if (skin_scaled.count('s'))
delete skin_scaled['s'];
skin_scaled['s'] =
new wxBitmap(skin['s'].Scale(width, width, wxIMAGE_QUALITY_HIGH));
if (skin_scaled.count('S'))
delete skin_scaled['S'];
skin_scaled['S'] =
new wxBitmap(skin['S'].Scale(width, width, wxIMAGE_QUALITY_HIGH));
skin_scaled['0'] = new wxBitmap(*skin_scaled['S']);
skin_scaled['1'] = new wxBitmap(*skin_scaled['s']);
skin_scaled['2'] = new wxBitmap(*skin_scaled['s']);
skin_scaled['3'] = new wxBitmap(*skin_scaled['S']);
skin_scaled['0']->SetMask(RoundedMask(width, 0));
skin_scaled['1']->SetMask(RoundedMask(width, 1));
skin_scaled['2']->SetMask(RoundedMask(width, 2));
skin_scaled['3']->SetMask(RoundedMask(width, 3));
}
void Theme::Zoom(int amount) {
double width = skin_scaled['s']->GetWidth() + amount;
ResizeSquares(std::max(width, 1.0));
ResizePieces(std::max(width * PIECE_SIZE_FACTOR, 1.0));
}
void Theme::SetSquareRadius(std::uint8_t radius) {
square_radius = radius;
Zoom(0); // Refresh scale
}
/**
* This will never fail since k entry always exists (cf constructor and
* ResizePieces)
*/
double Theme::GetPiecesSizes() { return (skin_scaled['k']->GetWidth()); }
/**
* This will never fail since s entry always exists (cf constructor and
* ResizeSquares)
*/
double Theme::GetSquaresSizes() { return (skin_scaled['s']->GetWidth()); }
wxMask *Theme::RoundedMask(std::uint32_t width, std::uint8_t corner) {
wxBitmap b(width, width, 1);
wxMemoryDC dc;
dc.SelectObject(b);
dc.SetPen(*wxBLACK_PEN);
dc.SetBrush(*wxBLACK_BRUSH);
dc.DrawRectangle(0, 0, width, width);
dc.SetBrush(*wxWHITE_BRUSH);
dc.SetPen(*wxWHITE_PEN);
dc.DrawRoundedRectangle(0, 0, width, width, square_radius);
dc.SetBrush(*wxWHITE_BRUSH);
dc.SetPen(*wxWHITE_PEN);
if (corner == 0) {
dc.DrawRectangle(0, width / 2, width, width);
dc.DrawRectangle(width / 2, 0, width, width);
} else if (corner == 1) {
dc.DrawRectangle(0, 0, width / 2, width);
dc.DrawRectangle(0, width / 2, width, width);
} else if (corner == 2) {
dc.DrawRectangle(0, 0, width, width / 2);
dc.DrawRectangle(width / 2, 0, width, width);
} else if (corner == 3) {
dc.DrawRectangle(0, 0, width / 2, width);
dc.DrawRectangle(0, 0, width, width / 2);
}
return (new wxMask(b));
}

View file

@ -0,0 +1,37 @@
#pragma once
#include "ochess.hpp"
#include <string>
#include <unordered_map>
#define ELT_DIM 200
#define DEFAULT_SIZE 80
#define PIECE_SIZE_FACTOR 0.8 // Should be between 0 and 1
#define DEFAULT_PIECE_THEME "assets/pieces/cburnett.png"
#define DEFAULT_SQUARE_THEME "assets/boards/chesscom_8bits.png"
class Theme {
private:
std::unordered_map<char, wxImage> skin;
std::unordered_map<char, wxBitmap *> skin_scaled;
std::uint8_t square_radius;
wxMask *RoundedMask(std::uint32_t width, std::uint8_t corner);
public:
Theme();
Theme(std::string piece, std::string square);
~Theme();
void LoadPiecesSkin(wxImage skin);
void LoadSquaresSkin(wxImage iskin);
void ResizePieces(std::uint32_t width);
void ResizeSquares(std::uint32_t width);
void ResizeSquaresAndPieces(std::uint32_t width);
void SetSquareRadius(std::uint8_t radius);
std::uint8_t GetSquareRadius();
void Zoom(int amount);
double GetPiecesSizes();
double GetSquaresSizes();
wxBitmap *Get(char c);
};

View file

@ -0,0 +1,186 @@
#include "EditorCanvas.hpp"
EditorCanvas::EditorCanvas(wxFrame *parent)
: wxPanel(parent), NeedRedraw(false) {
hide_icon = LoadPNG("hide", wxSize(CGEditor::status.MoveIconWidth,
CGEditor::status.MoveIconWidth));
t.ResizePieces(CGEditor::status.MoveIconWidth);
}
void EditorCanvas::OnPaint(wxPaintEvent &event) {
wxPaintDC current_dc(this);
dc = &current_dc;
// Refresh canvas size
wxSize sz = GetClientSize();
CGEditor::status.CanvasWidth = sz.GetWidth();
CGEditor::status.CanvasHeight = sz.GetHeight();
CGEditor::status.UseMoveIcons =
true; // Piece image should be drawn before the move ?
const wxPoint pt = wxGetMousePosition();
CGEditor::status.MouseX = pt.x - this->GetScreenPosition().x;
CGEditor::status.MouseY = pt.y - this->GetScreenPosition().y;
CGEditor::Draw();
}
/**
* @brief Convenient fonction to center text
*
* @param e Element to center
* @return wxPoint The centered version of e according to wdWidget API
*/
wxPoint EditorCanvas::Middle(cgeditor::Element e) {
wxSize sz = dc->GetTextExtent(e.text);
return (wxPoint(e.x + (e.width - sz.GetWidth()) / 2,
e.y + (e.height - sz.GetHeight()) / 2));
}
void EditorCanvas::DrawElement(const cgeditor::Element &e) {
dc->SetPen(wxNullPen);
dc->SetBrush(*wxRED_BRUSH);
if (e.prop & cgeditor::Property::Rectangle) {
if (e.prop & cgeditor::Property::Scrollbarbg) {
dc->SetBrush(*wxCYAN_BRUSH);
} else if (e.prop & cgeditor::Property::Scrollbar) {
dc->SetBrush(*wxGREY_BRUSH);
} else if (e.prop & cgeditor::Property::Margin) {
dc->SetBrush(*wxLIGHT_GREY_BRUSH);
} else if (e.prop & cgeditor::Property::Button) {
if (e.prop & cgeditor::Property::On) {
dc->DrawBitmap(hide_icon, e.x, e.y);
return;
}
dc->SetBrush(*wxBLACK_BRUSH);
}
wxRect recToDraw(e.x, e.y, e.width, e.height);
dc->DrawRectangle(recToDraw);
} else if (e.prop & cgeditor::Property::Text ||
e.prop & cgeditor::Property::Image) {
if (e.prop & cgeditor::Property::Image) {
// Draw your pieces images instead
std::uint32_t y = Middle(e).y - CGEditor::status.MoveIconWidth / 2;
char p = 'P';
if (e.prop & cgeditor::Property::Knight) {
p = 'N';
} else if (e.prop & cgeditor::Property::Bishop) {
p = 'B';
} else if (e.prop & cgeditor::Property::Queen) {
p = 'Q';
} else if (e.prop & cgeditor::Property::King) {
p = 'K';
}
if (e.prop & cgeditor::Property::Black) {
p = std::tolower(p);
}
if (e.prop & cgeditor::Property::Current) {
wxRect recToDraw(e.x, e.y, e.width, e.height);
dc->SetBrush(*wxLIGHT_GREY_BRUSH);
dc->DrawRectangle(recToDraw);
}
dc->DrawBitmap(*t.Get(p), e.x, y);
} else if (e.prop & cgeditor::Property::Comment) {
wxRect recToDraw(e.x, e.y, e.width, e.height);
dc->SetBrush(*wxYELLOW_BRUSH);
dc->DrawRectangle(recToDraw);
dc->DrawText(wxString(e.text), wxPoint(e.x, e.y));
} else if (e.prop & cgeditor::Property::Menuitem) {
wxRect recToDraw(e.x, e.y, e.width, e.height);
dc->SetBrush(*wxLIGHT_GREY_BRUSH);
dc->DrawRectangle(recToDraw);
dc->DrawText(wxString(e.text), wxPoint(e.x, Middle(e).y));
} else {
if (e.prop & cgeditor::Property::Move) {
if (e.prop & cgeditor::Property::Current) {
wxRect recToDraw(e.x, e.y, e.width, e.height);
dc->SetBrush(*wxLIGHT_GREY_BRUSH);
dc->DrawRectangle(recToDraw);
}
if (CGEditor::status.UseMoveIcons) {
dc->DrawText(wxString(e.text), wxPoint(e.x, Middle(e).y));
} else {
dc->DrawText(wxString(e.text), Middle(e));
}
} else {
dc->DrawText(wxString(e.text), Middle(e));
}
}
}
}
void EditorCanvas::HandleEvent(const cgeditor::Event &e) {
wxLogDebug("Editor event!");
if (e.type == cgeditor::Event::Goto) {
wxCommandEvent event(GOTO_MOVE_EVENT, GetId());
event.SetEventObject(this);
event.SetClientData(e.move);
ProcessEvent(event);
} else if (e.type == cgeditor::Event::Delete) {
wxCommandEvent event(DELETE_MOVE_EVENT, GetId());
event.SetEventObject(this);
event.SetClientData(e.move);
ProcessEvent(event);
} else if (e.type == cgeditor::Event::Promote) {
wxCommandEvent event(PROMOTE_MOVE_EVENT, GetId());
event.SetEventObject(this);
event.SetClientData(e.move);
ProcessEvent(event);
} else if (e.type == cgeditor::Event::SetAsMainline) {
wxCommandEvent event(SET_AS_MAINLINE_EVENT, GetId());
event.SetEventObject(this);
event.SetClientData(e.move);
ProcessEvent(event);
}
}
void EditorCanvas::MouseEvent(wxMouseEvent &event) {
if (event.Dragging()) {
CGEditor::status.LeftClick = false;
CGEditor::status.IsDrag = true;
Refresh();
} else if (event.LeftDown()) {
SetFocus();
CGEditor::status.LeftClick = true;
Refresh();
} else if (event.RightDown()) {
SetFocus();
CGEditor::status.RightClick = true;
Refresh();
} else if (event.GetWheelRotation() != 0) {
SetFocus();
if (event.GetWheelRotation() < 0) {
CGEditor::status.EventVScroll = 50;
} else {
CGEditor::status.EventVScroll = -50;
}
Refresh();
}
// Should another draw of CGEditor be made?
if (NeedRedraw) {
Refresh();
NeedRedraw = false;
}
}
void EditorCanvas::SetMoves(HalfMove *moves, HalfMove *current) {
CGEditor::status.Moves = moves;
CGEditor::status.CurrentMove = current;
Refresh();
}
void EditorCanvas::OnKeyEvent(wxKeyEvent &event) {
if (event.GetKeyCode() == WXK_LEFT) {
wxCommandEvent previousEvent(PREVIOUS_MOVE_EVENT, GetId());
previousEvent.SetEventObject(this);
ProcessEvent(previousEvent);
} else if (event.GetKeyCode() == WXK_RIGHT) {
wxCommandEvent nextEvent(NEXT_MOVE_EVENT, GetId());
nextEvent.SetEventObject(this);
ProcessEvent(nextEvent);
}
}
wxBEGIN_EVENT_TABLE(EditorCanvas, wxPanel) EVT_PAINT(EditorCanvas::OnPaint)
EVT_MOUSE_EVENTS(EditorCanvas::MouseEvent)
EVT_CHAR_HOOK(EditorCanvas::OnKeyEvent) wxEND_EVENT_TABLE()

View file

@ -0,0 +1,33 @@
#pragma once
#include "../HalfMove.hpp"
#include "CGEditor.hpp"
#include "ochess.hpp"
#include "../board/Theme.hpp"
// Foreign events
wxDECLARE_EVENT(GOTO_MOVE_EVENT, wxCommandEvent);
wxDECLARE_EVENT(PREVIOUS_MOVE_EVENT, wxCommandEvent);
wxDECLARE_EVENT(NEXT_MOVE_EVENT, wxCommandEvent);
wxDECLARE_EVENT(DELETE_MOVE_EVENT, wxCommandEvent);
wxDECLARE_EVENT(PROMOTE_MOVE_EVENT, wxCommandEvent);
wxDECLARE_EVENT(SET_AS_MAINLINE_EVENT, wxCommandEvent);
class EditorCanvas : public wxPanel, public cgeditor::CGEditor {
wxPaintDC *dc;
bool NeedRedraw;
wxPoint Middle(cgeditor::Element e);
wxBitmap hide_icon;
Theme t;
public:
EditorCanvas(wxFrame *parent);
void OnPaint(wxPaintEvent &event);
void MouseEvent(wxMouseEvent &event);
void DrawElement(const cgeditor::Element &e);
void HandleEvent(const cgeditor::Event &e);
void SetMoves(HalfMove *moves, HalfMove *current);
void OnKeyEvent(wxKeyEvent &event);
DECLARE_EVENT_TABLE()
};

View file

@ -0,0 +1,207 @@
#include "EditorPanel.hpp"
wxDEFINE_EVENT(GOTO_MOVE_EVENT, wxCommandEvent);
wxDEFINE_EVENT(DELETE_MOVE_EVENT, wxCommandEvent);
wxDEFINE_EVENT(PROMOTE_MOVE_EVENT, wxCommandEvent);
wxDEFINE_EVENT(SET_AS_MAINLINE_EVENT, wxCommandEvent);
wxDEFINE_EVENT(PREVIOUS_MOVE_EVENT, wxCommandEvent);
wxDEFINE_EVENT(NEXT_MOVE_EVENT, wxCommandEvent);
EditorPanel::EditorPanel(wxFrame *parent, Game *game)
: wxPanel(parent), game(game), selected_item(-1) {
// ----- Init -----
wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
wxNotebook *notebook = new wxNotebook(this, wxID_ANY);
//----- CGEditor Page -----
wxPanel *cgeditor_panel = new wxPanel(notebook, wxID_ANY);
wxBoxSizer *cgeditor_panel_sizer = new wxBoxSizer(wxVERTICAL);
editor_canvas = new EditorCanvas((wxFrame *)cgeditor_panel);
cgeditor_panel_sizer->Add(editor_canvas, 1, wxEXPAND);
// Comment box
wxStaticBox *commentBox =
new wxStaticBox(cgeditor_panel, wxID_ANY, "Move Comment");
wxBoxSizer *commentBoxSizer = new wxBoxSizer(wxVERTICAL);
comment_input = new wxTextCtrl(
commentBox, COMMENT_INPUT_BOX, wxEmptyString, // Use right ID
wxDefaultPosition, wxSize(0, 150), wxTE_MULTILINE);
commentBoxSizer->Add(comment_input, 0, wxEXPAND);
commentBox->SetSizer(commentBoxSizer);
cgeditor_panel_sizer->Add(commentBox, 0, wxEXPAND);
cgeditor_panel->SetSizer(cgeditor_panel_sizer);
//----- Tags Page -----
wxPanel *tag_panel = new wxPanel(notebook, wxID_ANY);
wxBoxSizer *tag_panel_sizer = new wxBoxSizer(wxVERTICAL);
wxPanel *tag_edit_panel = new wxPanel(tag_panel, wxID_ANY);
wxBoxSizer *tag_edit_panel_sizer = new wxBoxSizer(wxVERTICAL);
tagTextCtrl = new wxTextCtrl(tag_edit_panel, wxID_ANY);
tagTextCtrl->SetHint(wxString("Name"));
tag_edit_panel_sizer->Add(tagTextCtrl, 1, wxEXPAND);
valueTextCtrl = new wxTextCtrl(tag_edit_panel, wxID_ANY);
valueTextCtrl->SetHint(wxString("Value"));
tag_edit_panel_sizer->Add(valueTextCtrl, 1, wxEXPAND);
tag_edit_panel_sizer->Add(new wxButton(tag_edit_panel, UPDATE_BTN, L"Update"),
1, wxEXPAND);
tag_edit_panel->SetSizer(tag_edit_panel_sizer);
tag_panel_sizer->Add(tag_edit_panel, 0, wxEXPAND);
tags_list = new wxListCtrl(tag_panel, wxID_ANY, wxDefaultPosition,
wxDefaultSize, wxLC_REPORT);
tags_list->InsertColumn(0, L"Name");
tags_list->InsertColumn(1, L"Value", wxLIST_FORMAT_LEFT, 500);
RefreshTagsList();
tag_panel_sizer->Add(tags_list, 1, wxEXPAND);
delete_button = new wxButton(tag_panel, DELETE_BTN, L"Delete");
delete_button->Enable(false);
tag_panel_sizer->Add(delete_button, 0, wxEXPAND);
tag_panel->SetSizer(tag_panel_sizer);
//----- Notebook -----
notebook->AddPage(cgeditor_panel, L"Editor");
notebook->AddPage(tag_panel, L"Tags");
sizer->Add(notebook, 1, wxEXPAND);
//----- Sizer -----
this->SetSizer(sizer);
// Bind events
this->Bind(wxEVT_TEXT, &EditorPanel::OnCommentChange, this,
COMMENT_INPUT_BOX);
this->Bind(GOTO_MOVE_EVENT, &EditorPanel::OnGotoMove, this, wxID_ANY);
this->Bind(DELETE_MOVE_EVENT, &EditorPanel::OnMoveDelete, this, wxID_ANY);
this->Bind(PROMOTE_MOVE_EVENT, &EditorPanel::OnMovePromote, this, wxID_ANY);
this->Bind(SET_AS_MAINLINE_EVENT, &EditorPanel::OnMoveSetAsMainline, this,
wxID_ANY);
this->Bind(NEXT_MOVE_EVENT, &EditorPanel::OnNextMove, this, wxID_ANY);
this->Bind(PREVIOUS_MOVE_EVENT, &EditorPanel::OnPreviousMove, this, wxID_ANY);
this->Bind(wxEVT_LIST_ITEM_SELECTED, &EditorPanel::OnTagSelected, this,
wxID_ANY);
this->Bind(wxEVT_LIST_ITEM_DESELECTED, &EditorPanel::OnTagDeselected, this,
wxID_ANY);
this->Bind(wxEVT_BUTTON, &EditorPanel::OnApply, this, UPDATE_BTN);
this->Bind(wxEVT_BUTTON, &EditorPanel::OnDelete, this, DELETE_BTN);
}
void EditorPanel::OnTagSelected(wxListEvent &event) {
wxListItem item = event.GetItem();
std::string key = item.GetText().ToStdString();
tagTextCtrl->ChangeValue(key);
item.SetColumn(1);
tags_list->GetItem(item);
valueTextCtrl->ChangeValue(item.GetText().ToStdString());
selected_item = item.GetId();
delete_button->Enable(true);
}
void EditorPanel::OnTagDeselected(wxListEvent &event) {
selected_item = -1;
delete_button->Enable(false);
}
void EditorPanel::NotifyBoard() {
wxCommandEvent previousEvent(GAME_CHANGE, GetId());
previousEvent.SetEventObject(this);
ProcessEvent(previousEvent);
}
void EditorPanel::OnCommentChange(wxCommandEvent &event) {
wxLogDebug("EditorPanel: comment input change");
HalfMove *m = game->GetCurrentMove();
if (m != NULL) {
m->SetComment(event.GetString().ToStdString());
}
editor_canvas->Refresh();
}
void EditorPanel::OnApply(wxCommandEvent &event) {
std::string key = tagTextCtrl->GetValue().ToStdString();
if (key == "FEN") {
SHOW_DIALOG_ERROR("Editing the FEN tag is forbidden");
return;
}
if (key.size() > 0) {
std::string value = valueTextCtrl->GetValue().ToStdString();
game->SetTag(key, value);
RefreshTagsList();
wxCommandEvent event(REFRESH_TAB_TITLE, GetId());
event.SetEventObject(this);
ProcessEvent(event);
}
}
void EditorPanel::OnDelete(wxCommandEvent &event) {
if (selected_item >= 0) {
wxListItem item;
item.SetColumn(0);
item.SetId(selected_item);
tags_list->GetItem(item);
std::string key = item.GetText().ToStdString();
if (key != "FEN") {
game->DeleteTag(key);
selected_item = -1;
RefreshTagsList();
} else {
SHOW_DIALOG_ERROR("Deleting the FEN tag is forbidden.");
}
}
}
void EditorPanel::OnGotoMove(wxCommandEvent &event) {
wxLogDebug("EditorPanel: received GOTO_MOVE_EVENT");
game->SetCurrent((HalfMove *)event.GetClientData());
NotifyBoard();
editor_canvas->Refresh();
}
void EditorPanel::OnMoveDelete(wxCommandEvent &event) {
game->DeleteMove((HalfMove *)event.GetClientData());
NotifyBoard();
editor_canvas->Refresh();
}
void EditorPanel::OnMovePromote(wxCommandEvent &event) {
wxLogDebug("EditorPanel: promote move called");
game->PromoteMove((HalfMove *)event.GetClientData());
NotifyBoard();
editor_canvas->Refresh();
}
void EditorPanel::OnMoveSetAsMainline(wxCommandEvent &event) {
wxLogDebug("EditorPanel: set move as mainline called");
game->SetMoveAsMainline((HalfMove *)event.GetClientData());
NotifyBoard();
editor_canvas->Refresh();
}
void EditorPanel::Notify() {
HalfMove *m = game->GetCurrentMove();
if (m != NULL) {
comment_input->ChangeValue(
m->GetComment()); // ChangeValue do not raise events
}
editor_canvas->SetMoves(game->GetMoves(), m);
}
void EditorPanel::RefreshTagsList() {
tags_list->DeleteAllItems();
for (std::string s : game->ListTags()) {
long index = tags_list->InsertItem(0, s);
tags_list->SetItem(index, 1, game->GetTag(s));
if (s == "FEN") {
tags_list->SetItemBackgroundColour(index, wxColour(200, 200, 200));
}
}
}
void EditorPanel::OnPreviousMove(wxCommandEvent &event) {
game->Previous();
Notify();
NotifyBoard();
}
void EditorPanel::OnNextMove(wxCommandEvent &event) {
game->Next();
Notify();
NotifyBoard();
}

View file

@ -0,0 +1,44 @@
#include "../Game.hpp"
#include "EditorCanvas.hpp"
#include "ochess.hpp"
#include <wx/listctrl.h>
#include <wx/notebook.h>
// Local events
wxDECLARE_EVENT(GOTO_MOVE_EVENT, wxCommandEvent);
wxDECLARE_EVENT(DELETE_MOVE_EVENT, wxCommandEvent);
wxDECLARE_EVENT(PROMOTE_MOVE_EVENT, wxCommandEvent);
wxDECLARE_EVENT(SET_AS_MAINLINE_EVENT, wxCommandEvent);
wxDECLARE_EVENT(REFRESH_TAB_TITLE, wxCommandEvent);
// Foreign events
wxDECLARE_EVENT(GAME_CHANGE, wxCommandEvent);
enum { COMMENT_INPUT_BOX = wxID_HIGHEST + 100, UPDATE_BTN, DELETE_BTN };
class EditorPanel : public wxPanel {
Game *game;
EditorCanvas *editor_canvas;
wxTextCtrl *comment_input;
wxListCtrl *tags_list;
wxTextCtrl *tagTextCtrl, *valueTextCtrl;
wxButton *delete_button;
long selected_item;
public:
EditorPanel(wxFrame *parent, Game *game);
void NotifyBoard();
void Notify();
void OnCommentChange(wxCommandEvent &event);
void OnGotoMove(wxCommandEvent &event);
void OnMoveDelete(wxCommandEvent &event);
void OnMovePromote(wxCommandEvent &event);
void OnMoveSetAsMainline(wxCommandEvent &event);
void RefreshTagsList();
void OnTagSelected(wxListEvent &event);
void OnTagDeselected(wxListEvent &event);
void OnApply(wxCommandEvent &event);
void OnDelete(wxCommandEvent &event);
void OnPreviousMove(wxCommandEvent &event);
void OnNextMove(wxCommandEvent &event);
};