1 /**********************************************************************
2  *
3  *   FreeDoko a Doppelkopf-Game
4  *
5  *   Copyright (C) 2001 – 2018 by Diether Knof and Borg Enders
6  *
7  *   This program is free software; you can redistribute it and/or
8  *   modify it under the terms of the GNU General Public License as
9  *   published by the Free Software Foundation; either version 2 of
10  *   the License, or (at your option) any later version.
11  *
12  *   This program is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU General Public License for more details.
16  *   You can find this license in the file 'gpl.txt'.
17  *
18  *   You should have received a copy of the GNU General Public License
19  *   along with this program; if not, write to the Free Software
20  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  *   MA  02111-1307  USA
22  *
23  *  Contact:
24  *    Diether Knof dknof@posteo.de
25  *
26  *********************************************************************/
27 
28 #include "constants.h"
29 
30 #ifdef USE_UI_GTKMM
31 
32 #include "gameplay_actions.h"
33 
34 #include "ui.h"
35 
36 #include "../../party/party.h"
37 
38 #include "../../utils/string.h"
39 
40 #include <gtkmm/treeview.h>
41 
42 namespace UI_GTKMM_NS {
43 
44 /** constructor
45  **
46  ** @param    parent   parent object
47  **/
GameplayActions(Base * const parent)48 GameplayActions::GameplayActions(Base* const parent) :
49   Base(parent),
50   Gtk::TreeView()
51 {
52   this->init();
53 } // GameplayActions::GameplayActions(Base* parent)
54 
55 /** destruktor
56  **/
57 GameplayActions::~GameplayActions() = default;
58 
59 /** create all subelements
60  **/
61 void
init()62 GameplayActions::init()
63 {
64   this->list = Gtk::ListStore::create(this->model);
65   this->set_model(this->list);
66 
67   this->get_selection()->set_mode(Gtk::SELECTION_NONE);
68 
69   this->append_column(_("type"), this->model.type);
70   this->append_column(_("player"), this->model.player);
71   this->get_column_cell_renderer(1)->set_property("xalign", 0.5);
72   this->append_column(_("data"), this->model.data);
73 
74   for (auto c : this->get_columns()) {
75     c->set_cell_data_func(*c->get_first_cell(),
76                           sigc::mem_fun(*this, &GameplayActions::set_cell_color));
77   }
78   this->show_all_children();
79 
80   if (this->get_realized())
81     this->update();
82   else
83     this->signal_realize().connect(sigc::mem_fun(*this, &GameplayActions::update));
84 } // void GameplayActions::init()
85 
86 /** update (rewrite) all information
87  **
88  ** @todo   mark the winner gray
89  ** @todo   if there is no 'player_of' don't show the column
90  **/
91 void
update()92 GameplayActions::update()
93 {
94   if (!this->get_realized())
95     return ;
96 
97   this->list->clear();
98 
99   if (this->actions) {
100     unsigned trickno = 0;
101     for (unsigned a = 0; a < this->actions->size(); ++a) {
102       auto const& action = *((*this->actions)[a]);
103       auto row = *this->list->append();
104       row[this->model.type] = ::gettext(action.type());
105       unsigned const player = ::GameplayActions::player(action);
106       if (player == UINT_MAX)
107         row[this->model.player] = "";
108       else
109 #ifdef WORKAROUND
110         // in 'party_open()' 'BugReportReplay' first gets the party and then the ui
111         row[this->model.player]
112           = ::party->players()[player].name();
113 #else
114       row[this->model.player]
115         = this->ui->party().players()[player].name();
116 #endif
117       if (action.type() == ::GameplayActions::Type::trick_full) {
118         trickno += 1;
119         row[this->model.data]
120           = (String::to_string(trickno) + ": "
121              + action.data_translation());
122       } else {
123         row[this->model.data] = action.data_translation();
124       }
125       row[this->model.action] = &action;
126       row[this->model.no] = a;
127     } // for (a < this->actsion->size())
128   } // if (this->actions)
129 } // void GameplayActions::update()
130 
131 /** sets the actions
132  **
133  ** @param    actions   actions to display
134  **/
135 void
set_actions(vector<unique_ptr<::GameplayAction>> const & actions)136 GameplayActions::set_actions(vector<unique_ptr<::GameplayAction>> const& actions)
137 {
138   this->actions = &actions;
139   this->update();
140 } // void GameplayActions::set_actions(vector<unique_ptr<::GameplayAction>> actions)
141 
142 /** sets the discrepancy
143  **
144  ** @param    discrepancies   discrepancies of the actions to the gameplay
145  **/
146 void
set_discrepancies(vector<::GameplayActions::Discrepancy> const & discrepancies)147 GameplayActions::set_discrepancies(vector<::GameplayActions::Discrepancy>
148                                    const& discrepancies)
149 {
150   this->discrepancies = &discrepancies;
151   this->update();
152 } // void GameplayActions::set_discrepancies(vector<GameplayAction::Discrepancy> discrepancies)
153 
154 /** removes the actions
155  **/
156 void
remove_actions()157 GameplayActions::remove_actions()
158 {
159   this->actions = nullptr;
160   this->discrepancies = nullptr;
161   this->update();
162 } // void GameplayActions::remove_actions()
163 
164 /** sets the number of the current action
165  **
166  ** @param    current_action_no   new number
167  **/
168 void
set_current_action_no(unsigned const current_action_no)169 GameplayActions::set_current_action_no(unsigned const current_action_no)
170 {
171   this->current_action_no = current_action_no;
172 
173   if (!this->get_realized())
174     return ;
175 
176   // the redrawing is needed, so that the colors are updated
177   this->queue_draw();
178 
179   // just scroll to the current row (I do not know a simplier way)
180   if (this->current_action_no < this->get_model()->children().size())
181     this->scroll_to_row(this->get_model()->get_path(*(this->get_model()->get_iter(String::to_string(current_action_no)))));
182 } // void GameplayActions::set_current_action_no(unsigned current_action_no)
183 
184 /** the name of 'player' has changed
185  **
186  ** @param    player   the player with the changed name
187  **/
188 void
name_changed(Player const & player)189 GameplayActions::name_changed(Player const& player)
190 {
191   if (!this->get_realized())
192     return ;
193 
194   this->update();
195 } // void GameplayActions::name_changed(Player player)
196 
197 /** changes the color of the cell at 'iterator'
198  **
199  ** @param    cell_renderer   cell renderer to change
200  ** @param    iterator   row
201  **/
202 void
set_cell_color(Gtk::CellRenderer * const cell_renderer,Gtk::TreeModel::iterator const & iterator)203 GameplayActions::set_cell_color(Gtk::CellRenderer* const cell_renderer,
204                                 Gtk::TreeModel::iterator const& iterator)
205 {
206   auto const row = *iterator;
207   GameplayAction const* const action = row[this->model.action];
208   auto const action_no = row[this->model.no];
209 
210   pair<string, string> colors;
211   if (this->discrepancies) {
212     if (row[this->model.no] < this->discrepancies->size())
213       colors = GameplayActions::colors(action->type(),
214                                        (*this->discrepancies)[action_no]);
215     else
216       colors = GameplayActions::colors(action->type(),
217                                        ::GameplayActions::Discrepancy::future);
218 
219   } else { // if !(this->discrepancies)
220     colors = GameplayActions::colors(action->type(),
221                                      ::GameplayActions::Discrepancy::none);
222   } // if !(this->discrepancies)
223 
224   cell_renderer->set_property("foreground", colors.first);
225   cell_renderer->set_property("background", colors.second);
226   cell_renderer->set_property("cell_background", colors.second);
227 } // void GameplayActions::set_cell_color(Gtk::CellRenderer* cell_renderer, Gtk::TreeModel::iterator iterator)
228 
229 /** -> result
230  **
231  ** @param    action_type   the action type
232  ** @param    discrepancy   the discrepancy to the action
233  **
234  ** @return   pair of colors (foreground, background)
235  **/
236 pair<string, string>
colors(::GameplayActions::Type const action_type,::GameplayActions::Discrepancy const discrepancy)237 GameplayActions::colors(::GameplayActions::Type const action_type,
238                         ::GameplayActions::Discrepancy const discrepancy)
239 {
240   switch (action_type) {
241   case ::GameplayActions::Type::poverty_shift:
242   case ::GameplayActions::Type::poverty_accepted:
243   case ::GameplayActions::Type::poverty_returned:
244   case ::GameplayActions::Type::poverty_denied:
245   case ::GameplayActions::Type::poverty_denied_by_all:
246   case ::GameplayActions::Type::announcement:
247   case ::GameplayActions::Type::swines:
248   case ::GameplayActions::Type::hyperswines:
249   case ::GameplayActions::Type::marriage:
250   case ::GameplayActions::Type::genscher:
251     switch (discrepancy) {
252     case ::GameplayActions::Discrepancy::future:
253       return std::make_pair("black", "white");
254     case ::GameplayActions::Discrepancy::none:
255       return std::make_pair("black", "lightgreen");
256     case ::GameplayActions::Discrepancy::skipped:
257     case ::GameplayActions::Discrepancy::player:
258     case ::GameplayActions::Discrepancy::card:
259     case ::GameplayActions::Discrepancy::other:
260       return std::make_pair("black", "red");
261     } // switch (discrepancy)
262     break;
263 
264   case ::GameplayActions::Type::reservation:
265   case ::GameplayActions::Type::trick_open:
266   case ::GameplayActions::Type::trick_full:
267   case ::GameplayActions::Type::trick_taken:
268     switch (discrepancy) {
269     case ::GameplayActions::Discrepancy::future:
270       return std::make_pair("black", "lightgray");
271     case ::GameplayActions::Discrepancy::none:
272       return std::make_pair("white", "darkgreen");
273     case ::GameplayActions::Discrepancy::skipped:
274     case ::GameplayActions::Discrepancy::player:
275     case ::GameplayActions::Discrepancy::card:
276     case ::GameplayActions::Discrepancy::other:
277       return std::make_pair("white", "darkred");
278     } // switch (discrepancy)
279     break;
280 
281   case ::GameplayActions::Type::card_played:
282     switch (discrepancy) {
283     case ::GameplayActions::Discrepancy::future:
284       return std::make_pair("black", "white");
285     case ::GameplayActions::Discrepancy::none:
286       return std::make_pair("black", "green");
287     case ::GameplayActions::Discrepancy::skipped:
288     case ::GameplayActions::Discrepancy::player:
289     case ::GameplayActions::Discrepancy::card:
290     case ::GameplayActions::Discrepancy::other:
291       return std::make_pair("black", "red");
292     } // switch (discrepancy)
293     break;
294 
295   case ::GameplayActions::Type::check:
296     return std::make_pair("red", "blue");
297   case ::GameplayActions::Type::print:
298     return std::make_pair("black", "blue");
299   case ::GameplayActions::Type::quit:
300     return std::make_pair("red", "white");
301   }; // switch (action_type)
302 
303   return std::make_pair("black", "white");
304 } // static pair<string, string> GameplayActions::colors(GameplayActions::Type action_type, GameplayActions::Discrepancy discrepancy)
305 
306 } // namespace UI_GTKMM_NS
307 
308 #endif // #ifdef USE_UI_GTKMM
309