gnome-nibbles.vala 40.5 KB
Newer Older
Iulian Radu's avatar
Iulian Radu committed
1 2
/* -*- Mode: vala; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * Gnome Nibbles: Gnome Worm Game
3
 * Copyright (C) 2015 Iulian-Gabriel Radu <iulian.radu67@gmail.com>
Iulian Radu's avatar
Iulian Radu committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

Iulian Radu's avatar
Iulian Radu committed
19 20
public class Nibbles : Gtk.Application
{
21
    /* Application and worm settings */
Iulian Radu's avatar
Iulian Radu committed
22 23
    private Settings settings;
    private Gee.ArrayList<Settings> worm_settings;
Iulian Radu's avatar
Iulian Radu committed
24

25 26
    /* Main window */
    private Gtk.ApplicationWindow window;
Iulian Radu's avatar
Iulian Radu committed
27 28 29 30 31
    private bool is_maximized;
    private bool is_tiled;
    private int window_width;
    private int window_height;

32
    private Gtk.Stack main_stack;
33
    private Gtk.Overlay overlay;
34 35 36

    /* HeaderBar */
    private Gtk.HeaderBar headerbar;
37
    private Gtk.Button new_game_button;
Iulian Radu's avatar
Iulian Radu committed
38
    private Gtk.Button pause_button;
39 40

    /* Pre-game screen widgets */
41
    private Gee.LinkedList<Gtk.ToggleButton> number_of_players_buttons;
42
    private Gee.LinkedList<Gtk.ToggleButton> number_of_ai_buttons;
43 44
    private Gtk.Button next_button;
    private Gtk.Button start_button;
Iulian Radu's avatar
Iulian Radu committed
45

Iulian Radu's avatar
Iulian Radu committed
46 47 48 49
    private Gtk.Box grids_box;
    private Gdk.Pixbuf arrow_pixbuf;
    private Gdk.Pixbuf arrow_key_pixbuf;

50 51 52 53 54
    /* Statusbar widgets */
    private Gtk.Stack statusbar_stack;
    private Gtk.Label countdown;
    private Scoreboard scoreboard;
    private Gdk.Pixbuf scoreboard_life;
55

Gabriel Ivascu's avatar
Gabriel Ivascu committed
56 57 58
    /* Preferences dialog */
    private PreferencesDialog preferences_dialog = null;

59
    /* Rendering of the game */
60
    private NibblesView? view;
61 62 63 64 65

    private Gtk.Box game_box;
    private Games.GridFrame frame;

    /* Game being played */
66
    private NibblesGame? game = null;
Iulian Radu's avatar
Iulian Radu committed
67

68 69 70
    /* Used for handling the game's scores */
    private Games.Scores.Context scores_context;
    private Gee.LinkedList<Games.Scores.Category> scorecats;
71

72
    /* HeaderBar actions */
Iulian Radu's avatar
Iulian Radu committed
73 74
    private SimpleAction new_game_action;
    private SimpleAction pause_action;
Iulian Radu's avatar
Iulian Radu committed
75
    private SimpleAction back_action;
Iulian Radu's avatar
Iulian Radu committed
76

77 78
    private uint countdown_id = 0;
    private const int COUNTDOWN_TIME = 3;
79
    private int seconds = 0;
80

Iulian Radu's avatar
Iulian Radu committed
81
    private const ActionEntry action_entries[] =
Iulian Radu's avatar
Iulian Radu committed
82
    {
83
        {"start-game", start_game_cb},
84
        {"new-game", new_game_cb},
Iulian Radu's avatar
Iulian Radu committed
85
        {"pause", pause_cb},
Gabriel Ivascu's avatar
Gabriel Ivascu committed
86
        {"preferences", preferences_cb},
87
        {"scores", scores_cb},
88
        {"help", help_cb},
Iulian Radu's avatar
Iulian Radu committed
89
        {"about", about_cb},
Iulian Radu's avatar
Iulian Radu committed
90 91 92
        {"quit", quit}
    };

93 94
    private const ActionEntry menu_entries[] =
    {
Iulian Radu's avatar
Iulian Radu committed
95
        {"show-new-game-screen", show_new_game_screen_cb},
Iulian Radu's avatar
Iulian Radu committed
96 97
        {"show-controls-screen", show_controls_screen_cb},
        {"back", back_cb}
98 99
    };

100
    private const OptionEntry[] option_entries =
101 102 103
    {
        { "version", 'v', 0, OptionArg.NONE, null,
        /* Help string for command line --version flag */
104
        N_("Show release version"), null },
105 106 107 108

        { null }
    };

Iulian Radu's avatar
Iulian Radu committed
109 110
    public Nibbles ()
    {
111
        Object (application_id: "org.gnome.Nibbles", flags: ApplicationFlags.FLAGS_NONE);
112

113 114 115
        add_main_option_entries (option_entries);
    }

Iulian Radu's avatar
Iulian Radu committed
116
    protected override int handle_local_options (VariantDict options)
117 118 119 120 121 122 123 124 125 126
    {
        if (options.contains ("version"))
        {
            /* Not translated so can be easily parsed */
            stderr.printf ("gnome-nibbles %s\n", VERSION);
            return Posix.EXIT_SUCCESS;
        }

        /* Activate */
        return -1;
Iulian Radu's avatar
Iulian Radu committed
127 128 129 130 131 132
    }

    protected override void startup ()
    {
        base.startup ();

133 134 135 136 137 138 139 140
        unowned string[]? argv = null;
        GtkClutter.init (ref argv);

        Environment.set_prgname ("org.gnome.Nibbles");
        Environment.set_application_name (_("Nibbles"));

        Gtk.Window.set_default_icon_name ("gnome-nibbles");

Iulian Radu's avatar
Iulian Radu committed
141 142
        Gtk.Settings.get_default ().set ("gtk-application-prefer-dark-theme", true);

143 144 145 146
        var css_provider = new Gtk.CssProvider ();
        css_provider.load_from_resource ("/org/gnome/nibbles/ui/nibbles.css");
        Gtk.StyleContext.add_provider_for_screen (Gdk.Screen.get_default (), css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);

Iulian Radu's avatar
Iulian Radu committed
147
        add_action_entries (action_entries, this);
148
        add_action_entries (menu_entries, this);
Iulian Radu's avatar
Iulian Radu committed
149

Iulian Radu's avatar
Iulian Radu committed
150
        settings = new Settings ("org.gnome.nibbles");
Gabriel Ivascu's avatar
Gabriel Ivascu committed
151 152
        settings.changed.connect (settings_changed_cb);

Iulian Radu's avatar
Iulian Radu committed
153
        worm_settings = new Gee.ArrayList<Settings> ();
154
        for (int i = 0; i < NibblesGame.MAX_WORMS; i++)
Iulian Radu's avatar
Iulian Radu committed
155 156
        {
            var name = "org.gnome.nibbles.worm%d".printf(i);
Iulian Radu's avatar
Iulian Radu committed
157
            worm_settings.add (new Settings (name));
Gabriel Ivascu's avatar
Gabriel Ivascu committed
158
            worm_settings[i].changed.connect (worm_settings_changed_cb);
Iulian Radu's avatar
Iulian Radu committed
159
        }
Iulian Radu's avatar
Iulian Radu committed
160 161

        set_accels_for_action ("app.quit", {"<Primary>q"});
Iulian Radu's avatar
Iulian Radu committed
162
        set_accels_for_action ("app.back", {"Escape"});
163
        set_accels_for_action ("app.help", {"F1"});
Iulian Radu's avatar
Iulian Radu committed
164 165
        new_game_action = (SimpleAction) lookup_action ("new-game");
        pause_action = (SimpleAction) lookup_action ("pause");
Iulian Radu's avatar
Iulian Radu committed
166
        back_action = (SimpleAction) lookup_action ("back");
Iulian Radu's avatar
Iulian Radu committed
167

168
        var builder = new Gtk.Builder.from_resource ("/org/gnome/nibbles/ui/nibbles.ui");
Iulian Radu's avatar
Iulian Radu committed
169
        window = builder.get_object ("nibbles-window") as Gtk.ApplicationWindow;
Iulian Radu's avatar
Iulian Radu committed
170 171
        window.size_allocate.connect (size_allocate_cb);
        window.window_state_event.connect (window_state_event_cb);
172
        window.key_press_event.connect (key_press_event_cb);
Iulian Radu's avatar
Iulian Radu committed
173 174 175 176
        window.set_default_size (settings.get_int ("window-width"), settings.get_int ("window-height"));
        if (settings.get_boolean ("window-is-maximized"))
            window.maximize ();

177
        headerbar = (Gtk.HeaderBar) builder.get_object ("headerbar");
178
        overlay = (Gtk.Overlay) builder.get_object ("main_overlay");
179
        new_game_button = (Gtk.Button) builder.get_object ("new_game_button");
Iulian Radu's avatar
Iulian Radu committed
180
        pause_button = (Gtk.Button) builder.get_object ("pause_button");
181
        main_stack = (Gtk.Stack) builder.get_object ("main_stack");
Iulian Radu's avatar
Iulian Radu committed
182 183
        game_box = (Gtk.Box) builder.get_object ("game_box");
        statusbar_stack = (Gtk.Stack) builder.get_object ("statusbar_stack");
184
        countdown = (Gtk.Label) builder.get_object ("countdown");
185
        number_of_players_buttons = new Gee.LinkedList<Gtk.ToggleButton> ();
186
        for (int i = 0; i < NibblesGame.MAX_HUMANS; i++)
187 188 189 190 191
        {
            var button = (Gtk.ToggleButton) builder.get_object ("players%d".printf (i + 1));
            button.toggled.connect (change_number_of_players_cb);
            number_of_players_buttons.add (button);
        }
192 193 194 195 196 197 198
        number_of_ai_buttons = new Gee.LinkedList<Gtk.ToggleButton> ();
        for (int i = 0; i <= NibblesGame.MAX_AI; i++)
        {
            var button = (Gtk.ToggleButton) builder.get_object ("ai%d".printf (i));
            button.toggled.connect (change_number_of_ai_cb);
            number_of_ai_buttons.add (button);
        }
199 200
        next_button = (Gtk.Button) builder.get_object ("next_button");
        start_button = (Gtk.Button) builder.get_object ("start_button");
Iulian Radu's avatar
Iulian Radu committed
201
        grids_box = (Gtk.Box) builder.get_object ("grids_box");
Iulian Radu's avatar
Iulian Radu committed
202 203 204
        window.set_titlebar (headerbar);

        add_window (window);
205

206
        /* Create game */
207
        game = new NibblesGame (settings);
208
        game.log_score.connect (log_score_cb);
209
        game.level_completed.connect (level_completed_cb);
210
        game.notify["is-paused"].connect (() => {
Iulian Radu's avatar
Iulian Radu committed
211 212 213 214 215
            if (game.is_paused)
                statusbar_stack.set_visible_child_name ("paused");
            else
                statusbar_stack.set_visible_child_name ("scoreboard");
        });
216

217
        /* Create view */
218
        view = new NibblesView (game);
219
        view.configure_event.connect (configure_event_cb);
Iulian Radu's avatar
Iulian Radu committed
220
        view.is_muted = !settings.get_boolean ("sound");
221
        view.show ();
222 223

        frame = new Games.GridFrame (NibblesGame.WIDTH, NibblesGame.HEIGHT);
Iulian Radu's avatar
Iulian Radu committed
224 225
        game_box.pack_start (frame);

226
        /* Create scoreboard */
Iulian Radu's avatar
Iulian Radu committed
227 228 229 230
        scoreboard = new Scoreboard ();
        scoreboard_life = view.load_pixmap_file ("scoreboard-life.svg", 2 * game.tile_size, 2 * game.tile_size);
        scoreboard.show ();
        statusbar_stack.add_named (scoreboard, "scoreboard");
231 232 233 234

        frame.add (view);
        frame.show ();

235 236 237 238
        /* Number of worms */
        game.numhumans = settings.get_int ("players");
        game.numai = settings.get_int ("ai");

Iulian Radu's avatar
Iulian Radu committed
239 240 241 242
        /* Controls screen */
        arrow_pixbuf = view.load_pixmap_file ("arrow.svg", 5 * game.tile_size, 5 * game.tile_size);
        arrow_key_pixbuf = view.load_pixmap_file ("arrow-key.svg", 5 * game.tile_size, 5 * game.tile_size);

243
        /* Check whether to display the first run screen */
244 245 246 247
        var first_run = settings.get_boolean ("first-run");
        if (first_run)
            show_first_run_screen ();
        else
248
            show_new_game_screen_cb ();
249

250
        /* Create scores */
251
        create_scores ();
252 253

        window.show ();
Iulian Radu's avatar
Iulian Radu committed
254 255 256 257
    }

    protected override void activate ()
    {
258
        base.activate ();
259 260

        window.present ();
Iulian Radu's avatar
Iulian Radu committed
261 262 263 264 265 266 267
    }

    protected override void shutdown ()
    {
        settings.set_int ("window-width", window_width);
        settings.set_int ("window-height", window_height);
        settings.set_boolean ("window-is-maximized", is_maximized);
268
        game.save_properties (settings);
Iulian Radu's avatar
Iulian Radu committed
269 270

        base.shutdown ();
Iulian Radu's avatar
Iulian Radu committed
271 272
    }

273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
    private bool countdown_cb ()
    {
        seconds--;

        if (seconds == 0)
        {
            statusbar_stack.set_visible_child_name ("scoreboard");
            view.name_labels.hide ();

            game.add_bonus (true);
            game.start ();

            pause_action.set_enabled (true);
            back_action.set_enabled (true);

            countdown_id = 0;
            return Source.REMOVE;
        }

        countdown.set_label (seconds.to_string ());
        return Source.CONTINUE;
    }

296 297 298 299
    /*\
    * * Window events
    \*/

300 301 302 303 304 305 306 307 308 309
    /* The reason this event handler is found here (and not in nibbles-view.vala
     * which would be a more suitable place) is to avoid a weird behavior of having
     * your first key press ignored everytime by the start of a new level, thus
     * making your worm unresponsive to your command.
     */
    private bool key_press_event_cb (Gtk.Widget widget, Gdk.EventKey event)
    {
        return game.handle_keypress (event.keyval);
    }

Iulian Radu's avatar
Iulian Radu committed
310
    private void size_allocate_cb (Gtk.Allocation allocation)
Iulian Radu's avatar
Iulian Radu committed
311 312 313
    {
        if (is_maximized || is_tiled)
            return;
314
        window.get_size (out window_width, out window_height);
Iulian Radu's avatar
Iulian Radu committed
315 316 317 318 319 320 321 322 323 324 325 326
    }

    private bool window_state_event_cb (Gdk.EventWindowState event)
    {
        if ((event.changed_mask & Gdk.WindowState.MAXIMIZED) != 0)
            is_maximized = (event.new_window_state & Gdk.WindowState.MAXIMIZED) != 0;
        /* We don’t save this state, but track it for saving size allocation */
        if ((event.changed_mask & Gdk.WindowState.TILED) != 0)
            is_tiled = (event.new_window_state & Gdk.WindowState.TILED) != 0;
        return false;
    }

327
    private bool configure_event_cb (Gdk.EventConfigure event)
328 329 330 331 332 333
    {
        int tile_size, ts_x, ts_y;

        /* Compute the new tile size based on the size of the
         * drawing area, rounded down.
         */
Iulian Radu's avatar
Iulian Radu committed
334 335 336
        ts_x = event.width / NibblesGame.WIDTH;
        ts_y = event.height / NibblesGame.HEIGHT;
        if (ts_x * NibblesGame.WIDTH > event.width)
337
            ts_x--;
Iulian Radu's avatar
Iulian Radu committed
338
        if (ts_y * NibblesGame.HEIGHT > event.height)
339 340 341
            ts_y--;
        tile_size = int.min (ts_x, ts_y);

342 343 344
        if (tile_size == 0 || game.tile_size == 0)
            return true;

345
        if (game.tile_size != tile_size)
346
        {
Iulian Radu's avatar
Iulian Radu committed
347
            view.get_stage ().set_size (tile_size * NibblesGame.WIDTH, tile_size * NibblesGame.HEIGHT);
348 349

            view.board_rescale (tile_size);
350
            view.boni_rescale (tile_size);
351
            view.warps_rescale (tile_size);
Iulian Radu's avatar
Iulian Radu committed
352 353
            foreach (var worm in game.worms)
                worm.rescaled (tile_size);
354

355
            game.tile_size = tile_size;
356 357 358 359 360 361 362
        }

        return false;
    }

    private void start_game_cb ()
    {
363
        settings.set_boolean ("first-run", false);
364

365 366
        game.reset ();

367
        view.new_level (game.current_level);
368
        view.connect_worm_signals ();
369

370
        scoreboard.clear ();
371 372
        foreach (var worm in game.worms)
        {
Iulian Radu's avatar
Iulian Radu committed
373 374
            var color = game.worm_props.get (worm).color;
            scoreboard.register (worm, NibblesView.colorval_name (color), scoreboard_life);
375 376
            worm.notify["lives"].connect (scoreboard.update);
            worm.notify["score"].connect (scoreboard.update);
377
        }
378
        game.add_worms ();
Iulian Radu's avatar
Iulian Radu committed
379 380 381

        view.create_name_labels ();

382 383
        show_game_view ();

384 385 386
        start_game_with_countdown ();
    }

387
    private void start_game_with_countdown ()
388 389 390
    {
        statusbar_stack.set_visible_child_name ("countdown");

391
        new_game_action.set_enabled (true);
392

393 394
        seconds = COUNTDOWN_TIME;
        view.name_labels.show ();
Iulian Radu's avatar
Iulian Radu committed
395

396 397
        countdown.set_label (COUNTDOWN_TIME.to_string ());
        countdown_id = Timeout.add_seconds (1, countdown_cb);
398 399
    }

400
    private void restart_game ()
401 402 403 404 405 406 407
    {
        view.new_level (game.current_level);

        game.add_worms ();
        start_game_with_countdown ();
    }

408 409
    private void new_game_cb ()
    {
410 411 412 413 414 415
        if (countdown_id != 0)
        {
            Source.remove (countdown_id);
            countdown_id = 0;
        }

416 417
        if (game.is_running)
            game.stop ();
Iulian Radu's avatar
Iulian Radu committed
418

419 420 421 422
        var dialog = new Gtk.MessageDialog (window,
                                            Gtk.DialogFlags.MODAL,
                                            Gtk.MessageType.WARNING,
                                            Gtk.ButtonsType.OK_CANCEL,
423
                                            _("Are you sure you want to start a new game?"));
424 425 426
        dialog.secondary_text = _("If you start a new game, the current one will be lost.");

        var button = (Gtk.Button) dialog.get_widget_for_response (Gtk.ResponseType.OK);
427
        button.set_label (_("_New Game"));
428 429 430
        dialog.response.connect ((response_id) => {
            if (response_id == Gtk.ResponseType.OK)
                show_new_game_screen_cb ();
431 432 433
            if ((response_id == Gtk.ResponseType.CANCEL || response_id == Gtk.ResponseType.DELETE_EVENT)
                && !game.is_paused)
            {
434 435 436 437 438
                if (seconds == 0)
                    game.start ();
                else
                    countdown_id = Timeout.add_seconds (1, countdown_cb);

439 440
                view.grab_focus ();
            }
441 442 443 444 445 446 447

            dialog.destroy ();
        });

        dialog.show ();
    }

Iulian Radu's avatar
Iulian Radu committed
448 449 450 451 452
    private void pause_cb ()
    {
        if (game != null)
        {
            if (game.is_running)
453
            {
Iulian Radu's avatar
Iulian Radu committed
454
                game.pause ();
455 456
                pause_button.set_label (_("_Resume"));
            }
Iulian Radu's avatar
Iulian Radu committed
457
            else
458
            {
Iulian Radu's avatar
Iulian Radu committed
459
                game.unpause ();
460 461 462
                pause_button.set_label (_("_Pause"));
                view.grab_focus ();
            }
Iulian Radu's avatar
Iulian Radu committed
463 464 465
        }
    }

Gabriel Ivascu's avatar
Gabriel Ivascu committed
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
    /*\
    * * Settings changed events
    \*/

    private void settings_changed_cb (string key)
    {
        switch (key)
        {
            case "speed":
                game.speed = settings.get_int (key);
                break;
            case "sound":
                view.is_muted = !settings.get_boolean (key);
                break;
            case "fakes":
                game.fakes = settings.get_boolean (key);
                break;
        }
    }

    private void worm_settings_changed_cb (Settings changed_worm_settings, string key)
    {
        /* Empty worm properties means game has not started yet */
        if (game.worm_props.size == 0)
            return;

        var id = worm_settings.index_of (changed_worm_settings);

        if (id >= game.numworms)
            return;

        var worm = game.worms[id];
        var properties = game.worm_props.get (worm);

        switch (key)
        {
            case "color":
503
                properties.color = changed_worm_settings.get_enum ("color");
Gabriel Ivascu's avatar
Gabriel Ivascu committed
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521
                break;
            case "key-up":
                properties.up = changed_worm_settings.get_int ("key-up");
                break;
            case "key-down":
                properties.down = changed_worm_settings.get_int ("key-down");
                break;
            case "key-left":
                properties.left = changed_worm_settings.get_int ("key-left");
                break;
            case "key-right":
                properties.right = changed_worm_settings.get_int ("key-right");
                break;
        }

        game.worm_props.set (worm, properties);
    }

Iulian Radu's avatar
Iulian Radu committed
522 523 524 525
    /*\
    * * Switching the stack
    \*/

526
    private void show_first_run_screen ()
527
    {
528 529 530 531 532
        main_stack.set_visible_child_name ("first_run");
    }

    private void show_new_game_screen_cb ()
    {
533 534 535 536 537 538
        if (countdown_id != 0)
        {
            Source.remove (countdown_id);
            countdown_id = 0;
        }

539 540 541
        if (game.is_running)
            game.stop ();

542 543
        headerbar.set_title (_("Nibbles"));

Iulian Radu's avatar
Iulian Radu committed
544 545
        new_game_action.set_enabled (false);
        pause_action.set_enabled (false);
Iulian Radu's avatar
Iulian Radu committed
546
        back_action.set_enabled (true);
Iulian Radu's avatar
Iulian Radu committed
547

548
        new_game_button.hide ();
Iulian Radu's avatar
Iulian Radu committed
549
        pause_button.hide ();
550

551 552 553 554 555
        number_of_players_buttons[game.numhumans - 1].set_active (true);
        number_of_ai_buttons[game.numai].set_active (true);

        window.set_default (next_button);

556
        main_stack.set_transition_type (Gtk.StackTransitionType.NONE);
557
        main_stack.set_visible_child_name ("number_of_players");
558
        main_stack.set_transition_type (Gtk.StackTransitionType.SLIDE_UP);
559 560
    }

Iulian Radu's avatar
Iulian Radu committed
561 562
    private void show_controls_screen_cb ()
    {
563 564 565 566 567
        /* Save selected number of players before changing the screen */
        foreach (var button in number_of_players_buttons)
        {
            if (button.get_active ())
            {
568 569 570
                int numhumans = -1;
                button.get_label ().scanf ("_%d", &numhumans);
                game.numhumans = numhumans;
571 572 573

                /* Remember the option for the following runs */
                settings.set_int ("players", game.numhumans);
574 575 576 577 578 579 580 581 582 583 584 585
                break;
            }
        }

        /* Save selected number of computer players before changing the screen */
        foreach (var button in number_of_ai_buttons)
        {
            if (button.get_active ())
            {
                int numai = -1;
                button.get_label ().scanf ("_%d", &numai);
                game.numai = numai;
586 587 588

                /* Remember the option for the following runs */
                settings.set_int ("ai", game.numai);
589
                break;
590 591 592
            }
        }

Iulian Radu's avatar
Iulian Radu committed
593 594 595 596
        /* Create worms and load properties */
        game.create_worms ();
        game.load_worm_properties (worm_settings);

597 598 599
        foreach (var grid in grids_box.get_children ())
            grid.destroy ();

Iulian Radu's avatar
Iulian Radu committed
600 601 602 603 604 605 606 607
        foreach (var worm in game.worms)
        {
            if (worm.is_human)
            {
                var grid = new ControlsGrid (worm.id, game.worm_props.get (worm), arrow_pixbuf, arrow_key_pixbuf);
                grids_box.add (grid);
            }
        }
Iulian Radu's avatar
Iulian Radu committed
608

609 610
        window.set_default (start_button);

Iulian Radu's avatar
Iulian Radu committed
611 612 613
        main_stack.set_visible_child_name ("controls");
    }

614 615
    private void show_game_view ()
    {
616 617 618 619 620
        /* FIXME: If there's a transition set, on Wayland, the ClutterEmbed
         * will show outside the game's window. Don't change the transition
         * type when that's no longer a problem.
         */
        main_stack.set_transition_type (Gtk.StackTransitionType.NONE);
621
        new_game_button.show ();
Iulian Radu's avatar
Iulian Radu committed
622
        pause_button.show ();
623

Iulian Radu's avatar
Iulian Radu committed
624 625
        back_action.set_enabled (false);

Michael Catanzaro's avatar
Michael Catanzaro committed
626
        headerbar.set_title (_("Level %d").printf (game.current_level));
Iulian Radu's avatar
Iulian Radu committed
627
        main_stack.set_visible_child_name ("game_box");
628 629

        main_stack.set_transition_type (Gtk.StackTransitionType.SLIDE_UP);
630 631
    }

Iulian Radu's avatar
Iulian Radu committed
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653
    private void back_cb ()
    {
        main_stack.set_transition_type (Gtk.StackTransitionType.SLIDE_DOWN);

        var child_name = main_stack.get_visible_child_name ();
        switch (child_name)
        {
            case "first_run":
                break;
            case "number_of_players":
                break;
            case "controls":
                main_stack.set_visible_child_name ("number_of_players");
                break;
            case "game_box":
                new_game_cb ();
                break;
        }

        main_stack.set_transition_type (Gtk.StackTransitionType.SLIDE_UP);
    }

654 655
    private void change_number_of_players_cb (Gtk.ToggleButton button)
    {
656
        foreach (var other_button in number_of_players_buttons)
657
        {
658 659 660 661 662 663 664 665 666 667 668 669 670
            if (button != other_button)
            {
                if (other_button.get_active ())
                {
                    /* We are blocking the signal to prevent another callback when setting the previous
                     * checked button to inactive
                     */
                    SignalHandler.block_matched (other_button, SignalMatchType.DATA, 0, 0, null, null, this);
                    other_button.set_active (false);
                    SignalHandler.unblock_matched (other_button, SignalMatchType.DATA, 0, 0, null, null, this);
                    break;
                }
            }
671
        }
672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702
        button.set_active (true);

        int numhumans = -1;
        button.get_label ().scanf ("_%d", &numhumans);

        int min_ai = 4 - numhumans;
        int max_ai = NibblesGame.MAX_WORMS - numhumans;
        for (int i = 0; i < min_ai; i++)
        {
            number_of_ai_buttons[i].hide ();
        }
        for (int i = min_ai; i <= max_ai; i++)
        {
            number_of_ai_buttons[i].show ();
        }
        for (int i = max_ai + 1; i < number_of_ai_buttons.size; i++)
        {
            number_of_ai_buttons[i].hide ();
        }

        if (numhumans == 4)
        {
            number_of_ai_buttons[0].show ();
        }

        number_of_ai_buttons[min_ai].set_active (true);
    }

    private void change_number_of_ai_cb (Gtk.ToggleButton button)
    {
        foreach (var other_button in number_of_ai_buttons)
703
        {
704
            if (button != other_button)
705
            {
706
                if (other_button.get_active ())
707
                {
708 709 710 711 712 713 714
                    /* We are blocking the signal to prevent another callback when setting the previous
                     * checked button to inactive
                     */
                    SignalHandler.block_matched (other_button, SignalMatchType.DATA, 0, 0, null, null, this);
                    other_button.set_active (false);
                    SignalHandler.unblock_matched (other_button, SignalMatchType.DATA, 0, 0, null, null, this);
                    break;
715 716 717
                }
            }
        }
718
        button.set_active (true);
719 720
    }

721 722 723 724
    /*\
    * * Scoring
    \*/

725
    private Games.Scores.Category? category_request (string key)
726 727 728 729 730 731
    {
        foreach (var cat in scorecats)
        {
            if (key == cat.key)
                return cat;
        }
732
        return null;
733 734
    }

735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
    private string? get_new_scores_key (string old_key)
    {
        switch (old_key)
        {
            case "1.0":
                return "fast";
            case "2.0":
                return "medium";
            case "3.0":
                return "slow";
            case "4.0":
                return "beginner";
            case "1.1":
                return "fast-fakes";
            case "2.1":
                return "medium-fakes";
            case "3.1":
                return "slow-fakes";
            case "4.1":
                return "beginner-fakes";
        }
        return null;
    }

759
    private void create_scores ()
760
    {
761
        scorecats = new Gee.LinkedList<Games.Scores.Category> ();
762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777
        /* Translators: Difficulty level displayed on the scores dialog */
        scorecats.add (new Games.Scores.Category ("beginner", _("Beginner")));
        /* Translators: Difficulty level displayed on the scores dialog */
        scorecats.add (new Games.Scores.Category ("slow", _("Slow")));
        /* Translators: Difficulty level displayed on the scores dialog */
        scorecats.add (new Games.Scores.Category ("medium", _("Medium")));
        /* Translators: Difficulty level displayed on the scores dialog */
        scorecats.add (new Games.Scores.Category ("fast", _("Fast")));
        /* Translators: Difficulty level with fake bonuses, displayed on the scores dialog */
        scorecats.add (new Games.Scores.Category ("beginner-fakes", _("Beginner with Fakes")));
        /* Translators: Difficulty level with fake bonuses, displayed on the scores dialog */
        scorecats.add (new Games.Scores.Category ("slow-fakes", _("Slow with Fakes")));
        /* Translators: Difficulty level with fake bonuses, displayed on the scores dialog */
        scorecats.add (new Games.Scores.Category ("medium-fakes", _("Medium with Fakes")));
        /* Translators: Difficulty level with fake bonuses, displayed on the scores dialog */
        scorecats.add (new Games.Scores.Category ("fast-fakes", _("Fast with Fakes")));
778

779 780 781 782 783 784
        scores_context = new Games.Scores.Context.with_importer (
            "gnome-nibbles",
            /* Displayed on the scores dialog, preceeding a difficulty. */
            _("Difficulty Level:"),
            window,
            category_request,
785
            Games.Scores.Style.POINTS_GREATER_IS_BETTER,
786
            new Games.Scores.DirectoryImporter.with_convert_func (get_new_scores_key));
787 788
    }

789
    private Games.Scores.Category get_scores_category (int speed, bool fakes)
790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819
    {
        string key = null;
        switch (speed)
        {
            case 1:
                key = "fast";
                break;
            case 2:
                key = "medium";
                break;
            case 3:
                key = "slow";
                break;
            case 4:
                key = "beginner";
                break;
        }

        if (fakes)
            key = key + "-fakes";

        foreach (var cat in scorecats)
        {
            if (key == cat.key)
                return cat;
        }

        return scorecats.first ();
    }

820
    private void log_score_cb (int score, int level_reached)
821
    {
822 823 824 825
        /* Disable these here to prevent the user clicking the buttons before the score is saved */
        new_game_action.set_enabled (false);
        pause_action.set_enabled (false);

826 827 828
        var scores = scores_context.get_high_scores (get_scores_category (game.speed, game.fakes));
        var lowest_high_score = (scores.size == 10 ? scores.last ().score : -1);

Iulian Radu's avatar
Iulian Radu committed
829
        if (game.numhumans != 1)
830
        {
831
            game_over (score, lowest_high_score, level_reached);
Iulian Radu's avatar
Iulian Radu committed
832
            return;
833
        }
Iulian Radu's avatar
Iulian Radu committed
834 835

        if (game.start_level != 1)
836
        {
837
            game_over (score, lowest_high_score, level_reached);
Iulian Radu's avatar
Iulian Radu committed
838
            return;
839
        }
840

841 842 843 844
        scores_context.add_score.begin (score,
                                        get_scores_category (game.speed, game.fakes),
                                        null,
                                        (object, result) => {
845
            try
846
            {
847
                scores_context.add_score.end (result);
848
            }
849 850
            catch (GLib.Error e)
            {
851
                warning ("Failed to add score: %s", e.message);
852 853
            }

854
            game_over (score, lowest_high_score, level_reached);
855
        });
856 857
    }

858 859
    private void scores_cb ()
    {
860 861 862 863 864 865 866
        var should_unpause = false;
        if (game.is_running)
        {
            pause_action.activate (null);
            should_unpause = true;
        }

867
        scores_context.run_dialog ();
868 869 870 871

        // Be quite careful about whether to unpause. Don't unpause if the game has not started.
        if (should_unpause)
            pause_action.activate (null);
872 873
    }

874
    private void level_completed_cb ()
875
    {
876 877 878
        if (game.current_level == NibblesGame.MAX_LEVEL)
            return;

879 880
        view.hide ();

881 882
        new_game_action.set_enabled (false);
        pause_action.set_enabled (false);
883

884 885
        // Translators: the %d is the number of the level that was completed.
        var label = new Gtk.Label (_("Level %d Completed!").printf (game.current_level));
886 887 888 889 890 891 892 893 894 895 896 897
        label.halign = Gtk.Align.CENTER;
        label.valign = Gtk.Align.START;
        label.set_margin_top (150);
        label.get_style_context ().add_class ("menu-title");
        label.show ();

        var button = new Gtk.Button.with_label (_("_Next Level"));
        button.set_use_underline (true);
        button.halign = Gtk.Align.CENTER;
        button.valign = Gtk.Align.END;
        button.set_margin_bottom (100);
        button.get_style_context ().add_class ("suggested-action");
898
        button.set_can_default (true);
899 900 901 902
        button.clicked.connect (() => {
            label.destroy ();
            button.destroy ();

Michael Catanzaro's avatar
Michael Catanzaro committed
903
            headerbar.set_title (_("Level %d").printf (game.current_level));
904

905 906
            view.show ();

907
            restart_game ();
908 909
        });

910 911 912 913
        overlay.add_overlay (label);
        overlay.add_overlay (button);

        overlay.show ();
914
        overlay.grab_default ();
915 916 917

        Timeout.add (500, () => {
            button.show ();
918
            button.grab_default ();
919 920 921

            return Source.REMOVE;
        });
922 923
    }

Gabriel Ivascu's avatar
Gabriel Ivascu committed
924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954
    private void preferences_cb ()
    {
        var should_unpause = false;
        if (game.is_running)
        {
            pause_action.activate (null);
            should_unpause = true;
        }

        if (preferences_dialog != null)
        {
            preferences_dialog.present ();

            if (should_unpause)
                pause_action.activate (null);

            return;
        }

        preferences_dialog = new PreferencesDialog (window, settings, worm_settings);

        preferences_dialog.destroy.connect (() => {
            preferences_dialog = null;

            if (should_unpause)
                pause_action.activate (null);
        });

        preferences_dialog.run ();
    }

955
    private void game_over (int score, long lowest_high_score, int level_reached)
956
    {
957
        var is_high_score = (score > lowest_high_score);
958
        var is_game_won = (level_reached == NibblesGame.MAX_LEVEL + 1);
959

960
        var game_over_label = new Gtk.Label (is_game_won ? _("Congratulations!") : _("Game Over!"));
961 962
        game_over_label.halign = Gtk.Align.CENTER;
        game_over_label.valign = Gtk.Align.START;
963
        game_over_label.set_margin_top (150);
964 965 966
        game_over_label.get_style_context ().add_class ("menu-title");
        game_over_label.show ();

967 968 969 970 971 972 973
        var msg_label = new Gtk.Label (_("You have completed the game."));
        msg_label.halign = Gtk.Align.CENTER;
        msg_label.valign = Gtk.Align.START;
        msg_label.set_margin_top (window_height / 3);
        msg_label.get_style_context ().add_class ("menu-title");
        msg_label.show ();

974 975
        var score_string = ngettext ("%d Point", "%d Points", score);
        score_string = score_string.printf (score);
976
        var score_label = new Gtk.Label (@"<b>$(score_string)</b>");
977 978 979 980 981 982
        score_label.set_use_markup (true);
        score_label.halign = Gtk.Align.CENTER;
        score_label.valign = Gtk.Align.START;
        score_label.set_margin_top (window_height / 3 + 80);
        score_label.show ();

983
        var points_left = lowest_high_score - score;
984
        var points_left_label = new Gtk.Label (_("(%ld more points to reach the leaderboard)").printf (points_left));
985 986 987 988 989 990 991 992 993 994 995 996 997
        points_left_label.halign = Gtk.Align.CENTER;
        points_left_label.valign = Gtk.Align.START;
        points_left_label.set_margin_top (window_height / 3 + 100);
        points_left_label.show ();

        var button = new Gtk.Button.with_label (_("_Play Again"));
        button.set_use_underline (true);
        button.halign = Gtk.Align.CENTER;
        button.valign = Gtk.Align.END;
        button.set_margin_bottom (100);
        button.get_style_context ().add_class ("suggested-action");
        button.clicked.connect (() => {
            game_over_label.destroy ();
998 999
            score_label.destroy ();
            points_left_label.destroy ();
1000
            button.destroy ();
1001
            msg_label.destroy ();
1002

1003 1004 1005 1006
            new_game_action.set_enabled (true);
            pause_action.set_enabled (true);

            show_new_game_screen_cb ();
1007
        });
1008
        button.show ();
1009

1010
        overlay.add_overlay (game_over_label);
1011 1012
        if (is_game_won)
            overlay.add_overlay (msg_label);
1013 1014 1015 1016
        if (game.numhumans == 1)
            overlay.add_overlay (score_label);
        if (game.numhumans == 1 && !is_high_score)
            overlay.add_overlay (points_left_label);
1017 1018 1019 1020 1021
        overlay.add_overlay (button);

        button.grab_focus ();

        overlay.show ();
1022 1023
    }

1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035
    private void help_cb ()
    {
        try
        {
            Gtk.show_uri (window.get_screen (), "help:gnome-nibbles", Gtk.get_current_event_time ());
        }
        catch (Error e)
        {
            warning ("Unable to open help: %s", e.message);
        }
    }

Iulian Radu's avatar
Iulian Radu committed
1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059
    private void about_cb ()
    {
        const string authors[] = { "Sean MacIsaac",
                                   "Ian Peters",
                                   "Andreas Røsdal",
                                   "Guillaume Beland",
                                   "Iulian-Gabriel Radu",
                                   null };

        const string documenters[] = { "Kevin Breit",
                                       null };

        const string artists[] = { "Allan Day",
                                  null };

        Gtk.show_about_dialog (window,
                               "program-name", _("Nibbles"),
                               "logo-icon-name", "gnome-nibbles",
                               "version", VERSION,
                               "comments", _("A worm game for GNOME"),
                               "copyright",
                               "Copyright © 1999–2008 Sean MacIsaac, Ian Peters, Andreas Røsdal\n" +
                               "Copyright © 2009 Guillaume Beland\n" +
                               "Copyright © 2015 Iulian-Gabriel Radu",
1060
                               "license-type", Gtk.License.GPL_3_0,
Iulian Radu's avatar
Iulian Radu committed
1061 1062 1063 1064 1065 1066 1067 1068
                               "authors", authors,
                               "documenters", documenters,
                               "artists", artists,
                               "translator-credits", _("translator-credits"),
                               "website", "https://wiki.gnome.org/Apps/Nibbles/"
                               );
    }

Iulian Radu's avatar
Iulian Radu committed
1069 1070
    public static int main (string[] args)
    {
Arnaud Bonatti's avatar
Arnaud Bonatti committed
1071 1072 1073 1074 1075
        Intl.setlocale (LocaleCategory.ALL, "");
        Intl.bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
        Intl.bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
        Intl.textdomain (GETTEXT_PACKAGE);

Iulian Radu's avatar
Iulian Radu committed
1076 1077 1078
        return new Nibbles ().run (args);
    }
}
Iulian Radu's avatar
Iulian Radu committed
1079 1080

[GtkTemplate (ui = "/org/gnome/nibbles/ui/scoreboard.ui")]
1081
private class Scoreboard : Gtk.Box
Iulian Radu's avatar
Iulian Radu committed
1082
{
1083
    private Gee.HashMap<PlayerScoreBox, Worm> boxes;
Iulian Radu's avatar
Iulian Radu committed
1084 1085 1086

    public Scoreboard ()
    {
1087
        boxes = new Gee.HashMap<PlayerScoreBox, Worm> ();
Iulian Radu's avatar
Iulian Radu committed
1088 1089 1090 1091 1092 1093 1094
    }

    public void register (Worm worm, string color_name, Gdk.Pixbuf life_pixbuf)
    {
        var color = Pango.Color ();
        color.parse (color_name);

1095 1096 1097 1098
        /* FIXME: Consider changing this to "Worm %d"
         * It's set to "Player %d" for now to avoid a string change for 3.20.
         */
        var box = new PlayerScoreBox (_("Player %d").printf (worm.id + 1), color, worm.score, worm.lives, life_pixbuf);
1099
        boxes.set (box, worm);
Iulian Radu's avatar
Iulian Radu committed
1100 1101
        add (box);
    }
1102 1103 1104 1105 1106

    public void update ()
    {
        foreach (var entry in boxes.entries)
        {
1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119
            var box = entry.key;
            var worm = entry.value;

            box.update (worm.score, worm.lives);
        }
    }

    public void clear ()
    {
        foreach (var entry in boxes.entries)
        {
            var box = entry.key;
            box.destroy ();
1120
        }
1121
        boxes.clear ();
1122
    }
Iulian Radu's avatar
Iulian Radu committed
1123 1124 1125
}

[GtkTemplate (ui = "/org/gnome/nibbles/ui/player-score-box.ui")]
1126
private class PlayerScoreBox : Gtk.Box
Iulian Radu's avatar
Iulian Radu committed
1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143
{
    [GtkChild]
    private Gtk.Label name_label;
    [GtkChild]
    private Gtk.Label score_label;
    [GtkChild]
    private Gtk.Grid lives_grid;

    private Gee.LinkedList<Gtk.Image> life_images;

    public PlayerScoreBox (string name, Pango.Color color, int score, int lives_left, Gdk.Pixbuf life_pixbuf)
    {
        name_label.set_markup ("<span color=\"" + color.to_string () + "\">" + name + "</span>");
        score_label.set_label (score.to_string ());

        life_images = new Gee.LinkedList<Gtk.Image> ();

1144
        for (int i = 0; i < Worm.MAX_LIVES; i++)
Iulian Radu's avatar
Iulian Radu committed
1145 1146
        {
            var life = new Gtk.Image.from_pixbuf (life_pixbuf);
1147 1148 1149 1150
            life.show ();

            if (i >= Worm.STARTING_LIVES)
                life.set_opacity (0);
Iulian Radu's avatar
Iulian Radu committed
1151 1152

            life_images.add (life);
1153
            lives_grid.attach (life, i % 6, i / 6);
Iulian Radu's avatar
Iulian Radu committed
1154 1155
        }
    }
1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169

    public void update (int score, int lives_left)
    {
        update_score (score);
        update_lives (lives_left);
    }

    public void update_score (int score)
    {
        score_label.set_label (score.to_string ());
    }

    public void update_lives (int lives_left)
    {
1170
        /* Remove lost lives - if any */
1171 1172
        for (int i = life_images.size - 1; i >= lives_left; i--)
            life_images[i].set_opacity (0);
1173 1174

        /* Add new lives - if any */
1175 1176
        for (int i = 0; i < lives_left; i++)
            life_images[i].set_opacity (1);
1177
    }
Iulian Radu's avatar
Iulian Radu committed
1178
}
Iulian Radu's avatar
Iulian Radu committed
1179 1180

[GtkTemplate (ui = "/org/gnome/nibbles/ui/controls-grid.ui")]
1181
private class ControlsGrid : Gtk.Grid
Iulian Radu's avatar
Iulian Radu committed
1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214
{
    [GtkChild]
    private Gtk.Label name_label;
    [GtkChild]
    private Gtk.Image arrow_up;
    [GtkChild]
    private Gtk.Image arrow_down;
    [GtkChild]
    private Gtk.Image arrow_left;
    [GtkChild]
    private Gtk.Image arrow_right;
    [GtkChild]
    private Gtk.Overlay move_up;
    [GtkChild]
    private Gtk.Label move_up_label;
    [GtkChild]
    private Gtk.Overlay move_down;
    [GtkChild]
    private Gtk.Label move_down_label;
    [GtkChild]
    private Gtk.Overlay move_left;
    [GtkChild]
    private Gtk.Label move_left_label;
    [GtkChild]
    private Gtk.Overlay move_right;
    [GtkChild]
    private Gtk.Label move_right_label;

    public ControlsGrid (int worm_id, WormProperties worm_props, Gdk.Pixbuf arrow, Gdk.Pixbuf arrow_key)
    {
        var color = Pango.Color ();
        color.parse (NibblesView.colorval_name (worm_props.color));

1215 1216 1217
        /* Translators: the player's number, e.g. "Player 1" or "Player 2". */
        var player_id = _("Player %d").printf (worm_id + 1);
        name_label.set_markup (@"<b><span font-family=\"Sans\" color=\"$(color.to_string ())\">$(player_id)</span></b>");
Iulian Radu's avatar
Iulian Radu committed
1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229

        arrow_up.set_from_pixbuf (arrow.rotate_simple (Gdk.PixbufRotation.NONE));
        arrow_down.set_from_pixbuf (arrow.rotate_simple (Gdk.PixbufRotation.UPSIDEDOWN));
        arrow_left.set_from_pixbuf (arrow.rotate_simple (Gdk.PixbufRotation.COUNTERCLOCKWISE));
        arrow_right.set_from_pixbuf (arrow.rotate_simple (Gdk.PixbufRotation.CLOCKWISE));

        string upper_key;
        upper_key = Gdk.keyval_name (worm_props.up).up ();
        if (upper_key == "UP")
        {
            var rotated_pixbuf = arrow_key.rotate_simple (Gdk.PixbufRotation.NONE);
            move_up.add_overlay (new Gtk.Image.from_pixbuf (rotated_pixbuf));
Iulian Radu's avatar
Iulian Radu committed
1230
            move_up.show_all ();
Iulian Radu's avatar
Iulian Radu committed
1231 1232 1233 1234 1235 1236 1237 1238 1239
        }
        else
            move_up_label.set_markup (@"<b>$(upper_key)</b>");

        upper_key = Gdk.keyval_name (worm_props.down).up ();
        if (upper_key == "DOWN")
        {
            var rotated_pixbuf = arrow_key.rotate_simple (Gdk.PixbufRotation.UPSIDEDOWN);
            move_down.add_overlay (new Gtk.Image.from_pixbuf (rotated_pixbuf));
Iulian Radu's avatar
Iulian Radu committed
1240
            move_down.show_all ();
Iulian Radu's avatar
Iulian Radu committed
1241 1242 1243 1244 1245 1246 1247 1248 1249
        }
        else
            move_down_label.set_markup (@"<b>$(upper_key)</b>");

        upper_key = Gdk.keyval_name (worm_props.left).up ();
        if (upper_key == "LEFT")
        {
            var rotated_pixbuf = arrow_key.rotate_simple (Gdk.PixbufRotation.COUNTERCLOCKWISE);
            move_left.add_overlay (new Gtk.Image.from_pixbuf (rotated_pixbuf));
Iulian Radu's avatar
Iulian Radu committed
1250
            move_left.show_all ();
Iulian Radu's avatar
Iulian Radu committed
1251 1252 1253 1254 1255 1256 1257 1258 1259
        }
        else
            move_left_label.set_markup (@"<b>$(upper_key)</b>");

        upper_key = Gdk.keyval_name (worm_props.right).up ();
        if (upper_key == "RIGHT")
        {
            var rotated_pixbuf = arrow_key.rotate_simple (Gdk.PixbufRotation.CLOCKWISE);
            move_right.add_overlay (new Gtk.Image.from_pixbuf (rotated_pixbuf));
Iulian Radu's avatar
Iulian Radu committed
1260
            move_right.show_all ();
Iulian Radu's avatar
Iulian Radu committed
1261 1262 1263 1264 1265
        }
        else
            move_right_label.set_markup (@"<b>$(upper_key)</b>");
    }
}