~ubuntu-branches/ubuntu/utopic/gnome-mahjongg/utopic-proposed

« back to all changes in this revision

Viewing changes to src/gnome-mahjongg.vala

  • Committer: Package Import Robot
  • Author(s): Jeremy Bicha
  • Date: 2013-02-19 12:21:16 UTC
  • Revision ID: package-import@ubuntu.com-20130219122116-k1ufas8i5f34i8bp
Tags: upstream-3.7.5
ImportĀ upstreamĀ versionĀ 3.7.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
public class Mahjongg : Gtk.Application
 
2
{
 
3
    private Settings settings;
 
4
 
 
5
    private History history;
 
6
 
 
7
    private List<Map> maps = null;
 
8
 
 
9
    private Gtk.ApplicationWindow window;
 
10
    private int window_width;
 
11
    private int window_height;
 
12
    private bool is_fullscreen;
 
13
    private bool is_maximized;
 
14
 
 
15
    private GameView game_view;
 
16
    private Gtk.ToolButton pause_button;
 
17
    private Gtk.ToolItem status_item;
 
18
    private Gtk.Label moves_label;
 
19
    private Gtk.Label clock_label;
 
20
    private Gtk.Dialog? preferences_dialog = null;
 
21
 
 
22
    public Mahjongg ()
 
23
    {
 
24
        Object (application_id: "org.gnome.gnome-mahjongg", flags: ApplicationFlags.FLAGS_NONE);
 
25
 
 
26
        add_action_entries (action_entries, this);
 
27
        add_accelerator ("<Primary>n", "app.new-game", null);
 
28
        add_accelerator ("Pause", "app.pause", null);
 
29
        add_accelerator ("<Primary>h", "app.hint", null);
 
30
        add_accelerator ("<Primary>z", "app.undo", null);
 
31
        add_accelerator ("<Primary><Shift>z", "app.redo", null);
 
32
        add_accelerator ("F1", "app.help", null);
 
33
        add_accelerator ("<Primary>q", "app.quit", null);
 
34
    }
 
35
 
 
36
    protected override void startup ()
 
37
    {
 
38
        base.startup ();
 
39
 
 
40
        settings = new Settings ("org.gnome.gnome-mahjongg");
 
41
 
 
42
        load_maps ();
 
43
 
 
44
        history = new History (Path.build_filename (Environment.get_user_data_dir (), "gnome-mahjongg", "history"));
 
45
        history.load ();
 
46
 
 
47
        window = new Gtk.ApplicationWindow (this);
 
48
        window.title = _("Mahjongg");
 
49
        window.configure_event.connect (window_configure_event_cb);
 
50
        window.window_state_event.connect (window_state_event_cb);
 
51
        window.set_default_size (settings.get_int ("window-width"), settings.get_int ("window-height"));        
 
52
        if (settings.get_boolean ("window-is-fullscreen"))
 
53
            window.fullscreen ();
 
54
        else if (settings.get_boolean ("window-is-maximized"))
 
55
            window.maximize ();
 
56
 
 
57
        var status_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 10);
 
58
 
 
59
        var group_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
 
60
        var label = new Gtk.Label (_("Moves Left:"));
 
61
        group_box.pack_start (label, false, false, 0);
 
62
        var spacer = new Gtk.Label (" ");
 
63
        group_box.pack_start (spacer, false, false, 0);
 
64
        moves_label = new Gtk.Label ("");
 
65
        group_box.pack_start (moves_label, false, false, 0);
 
66
        status_box.pack_start (group_box, false, false, 0);
 
67
 
 
68
        clock_label = new Gtk.Label ("");
 
69
        status_box.pack_start (clock_label, false, false, 0);
 
70
        
 
71
        var vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
 
72
 
 
73
        /* Create the menus */
 
74
        var menu = new Menu ();
 
75
        var section = new Menu ();
 
76
        menu.append_section (null, section);
 
77
        section.append (_("_New Game"), "app.new-game");
 
78
        section.append (_("_Restart Game"), "app.restart-game");
 
79
        section.append (_("_Scores"), "app.scores");
 
80
        section.append (_("_Preferences"), "app.preferences");
 
81
        section = new Menu ();
 
82
        menu.append_section (null, section);
 
83
        section.append (_("_Help"), "app.help");
 
84
        section.append (_("_About"), "app.about");
 
85
        section = new Menu ();
 
86
        menu.append_section (null, section);
 
87
        section.append (_("_Quit"), "app.quit");
 
88
        set_app_menu (menu);
 
89
 
 
90
        game_view = new GameView ();
 
91
        game_view.button_press_event.connect (view_button_press_event);        
 
92
        game_view.set_size_request (600, 400);
 
93
 
 
94
        var toolbar = new Gtk.Toolbar ();
 
95
        toolbar.show_arrow = false;
 
96
        toolbar.get_style_context ().add_class (Gtk.STYLE_CLASS_PRIMARY_TOOLBAR);
 
97
 
 
98
        var new_game_button = new Gtk.ToolButton (null, _("_New"));
 
99
        new_game_button.use_underline = true;
 
100
        new_game_button.icon_name = "document-new";
 
101
        new_game_button.action_name = "app.new-game";
 
102
        new_game_button.show ();
 
103
        toolbar.insert (new_game_button, -1);
 
104
 
 
105
        var undo_button = new Gtk.ToolButton (null, _("_Undo Move"));
 
106
        undo_button.use_underline = true;
 
107
        undo_button.icon_name = "edit-undo";
 
108
        undo_button.action_name = "app.undo";
 
109
        undo_button.is_important = true;
 
110
        undo_button.show ();
 
111
        toolbar.insert (undo_button, -1);
 
112
 
 
113
        var redo_button = new Gtk.ToolButton (null, _("_Redo Move"));
 
114
        redo_button.use_underline = true;
 
115
        redo_button.icon_name = "edit-redo";
 
116
        redo_button.action_name = "app.redo";
 
117
        redo_button.is_important = true;
 
118
        redo_button.show ();
 
119
        toolbar.insert (redo_button, -1);
 
120
 
 
121
        var hint_button = new Gtk.ToolButton (null, _("Hint"));
 
122
        hint_button.use_underline = true;
 
123
        hint_button.icon_name = "dialog-information";
 
124
        hint_button.action_name = "app.hint";
 
125
        hint_button.is_important = true;
 
126
        hint_button.show ();
 
127
        toolbar.insert (hint_button, -1);
 
128
 
 
129
        pause_button = new Gtk.ToolButton (null, _("_Pause"));
 
130
        pause_button.icon_name = "media-playback-pause";
 
131
        pause_button.use_underline = true;
 
132
        pause_button.action_name = "app.pause";
 
133
        pause_button.is_important = true;
 
134
        pause_button.show ();
 
135
        toolbar.insert (pause_button, -1);
 
136
 
 
137
        var status_alignment = new Gtk.Alignment (1.0f, 0.5f, 0.0f, 0.0f);
 
138
        status_alignment.add (status_box);
 
139
 
 
140
        status_item = new Gtk.ToolItem ();
 
141
        status_item.set_expand (true);
 
142
        status_item.add (status_alignment);
 
143
 
 
144
        toolbar.insert (status_item, -1);
 
145
 
 
146
        vbox.pack_start (toolbar, false, false, 0);
 
147
        vbox.pack_start (game_view, true, true, 0);
 
148
 
 
149
        window.add (vbox);
 
150
        vbox.show_all ();
 
151
 
 
152
        settings.changed.connect (conf_value_changed_cb);
 
153
 
 
154
        new_game ();
 
155
 
 
156
        game_view.grab_focus ();
 
157
 
 
158
        conf_value_changed_cb (settings, "tileset");
 
159
        conf_value_changed_cb (settings, "bgcolour");
 
160
        tick_cb ();
 
161
    }
 
162
 
 
163
    private bool window_configure_event_cb (Gdk.EventConfigure event)
 
164
    {
 
165
        if (!is_maximized && !is_fullscreen)
 
166
        {
 
167
            window_width = event.width;
 
168
            window_height = event.height;
 
169
        }
 
170
 
 
171
        return false;
 
172
    }
 
173
 
 
174
    private bool window_state_event_cb (Gdk.EventWindowState event)
 
175
    {
 
176
        if ((event.changed_mask & Gdk.WindowState.MAXIMIZED) != 0)
 
177
            is_maximized = (event.new_window_state & Gdk.WindowState.MAXIMIZED) != 0;
 
178
        if ((event.changed_mask & Gdk.WindowState.FULLSCREEN) != 0)
 
179
            is_fullscreen = (event.new_window_state & Gdk.WindowState.FULLSCREEN) != 0;
 
180
        return false;
 
181
    }
 
182
    
 
183
    protected override void shutdown ()
 
184
    {
 
185
        base.shutdown ();
 
186
 
 
187
        /* Save window state */
 
188
        settings.set_int ("window-width", window_width);
 
189
        settings.set_int ("window-height", window_height);
 
190
        settings.set_boolean ("window-is-maximized", is_maximized);
 
191
        settings.set_boolean ("window-is-fullscreen", is_fullscreen);
 
192
    }
 
193
 
 
194
    public override void activate ()
 
195
    {
 
196
        window.present ();
 
197
    }
 
198
 
 
199
    private void update_ui ()
 
200
    {
 
201
        var pause_action = lookup_action ("pause") as SimpleAction;
 
202
        var hint_action = lookup_action ("hint") as SimpleAction;
 
203
        var undo_action = lookup_action ("undo") as SimpleAction;
 
204
        var redo_action = lookup_action ("redo") as SimpleAction;
 
205
 
 
206
        pause_action.set_enabled (game_view.game.started);
 
207
 
 
208
        if (game_view.game.paused)
 
209
        {
 
210
            hint_action.set_enabled (false);
 
211
            undo_action.set_enabled (false);
 
212
            redo_action.set_enabled (false);
 
213
        }
 
214
        else
 
215
        {
 
216
            hint_action.set_enabled (game_view.game.moves_left > 0);
 
217
            undo_action.set_enabled (game_view.game.can_undo);
 
218
            redo_action.set_enabled (game_view.game.can_redo);
 
219
        }
 
220
 
 
221
        moves_label.set_text ("%2u".printf (game_view.game.moves_left));
 
222
    }
 
223
 
 
224
    private void theme_changed_cb (Gtk.ComboBox widget)
 
225
    {
 
226
        Gtk.TreeIter iter;
 
227
        widget.get_active_iter (out iter);
 
228
        string theme;
 
229
        widget.model.get (iter, 1, out theme);
 
230
        settings.set_string ("tileset", theme);
 
231
    }
 
232
 
 
233
    private void conf_value_changed_cb (Settings settings, string key)
 
234
    {
 
235
        if (key == "tileset")
 
236
        {
 
237
            var theme = settings.get_string ("tileset");
 
238
            game_view.theme = Path.build_filename (DATA_DIRECTORY, "themes", theme);
 
239
        }
 
240
        else if (key == "bgcolour")
 
241
        {
 
242
            game_view.set_background (settings.get_string ("bgcolour"));
 
243
        }
 
244
        else if (key == "mapset")
 
245
        {
 
246
            /* Prompt user if already made a move */
 
247
            if (game_view.game.started)
 
248
            {
 
249
                var dialog = new Gtk.MessageDialog (window,
 
250
                                                    Gtk.DialogFlags.MODAL,
 
251
                                                    Gtk.MessageType.QUESTION,
 
252
                                                    Gtk.ButtonsType.NONE,
 
253
                                                    "%s", _("Do you want to start a new game with this map?"));
 
254
                dialog.format_secondary_text (_("If you continue playing the next game will use the new map."));
 
255
                dialog.add_buttons (_("_Continue playing"), Gtk.ResponseType.REJECT,
 
256
                                    _("Use _new map"), Gtk.ResponseType.ACCEPT,
 
257
                                    null);
 
258
                dialog.set_default_response (Gtk.ResponseType.ACCEPT);
 
259
                var response = dialog.run ();
 
260
                if (response == Gtk.ResponseType.ACCEPT)
 
261
                    new_game ();
 
262
                dialog.destroy ();
 
263
            }
 
264
            else
 
265
                new_game ();
 
266
        }
 
267
    }
 
268
 
 
269
    private bool view_button_press_event (Gtk.Widget widget, Gdk.EventButton event)
 
270
    {
 
271
        /* Cancel pause on click */
 
272
        if (game_view.game.paused)
 
273
        {
 
274
            game_view.game.paused = false;
 
275
            return true;
 
276
        }
 
277
 
 
278
        return false;
 
279
    }
 
280
 
 
281
    private void background_changed_cb (Gtk.ColorButton widget)
 
282
    {
 
283
        Gdk.RGBA colour;
 
284
        /* See https://bugzilla.gnome.org/show_bug.cgi?id=669386 */
 
285
        Gtk.color_button_get_rgba (widget, out colour);
 
286
        settings.set_string ("bgcolour", "#%04x%04x%04x".printf ((int) (colour.red * 65536 + 0.5), (int) (colour.green * 65536 + 0.5), (int) (colour.blue * 65536 + 0.5)));
 
287
    }
 
288
 
 
289
    private void map_changed_cb (Gtk.ComboBox widget)
 
290
    {
 
291
        settings.set_string ("mapset", maps.nth_data (widget.active).name);
 
292
    }
 
293
 
 
294
    private void moved_cb ()
 
295
    {
 
296
        update_ui ();
 
297
 
 
298
        if (game_view.game.complete)
 
299
        {
 
300
            var date = new DateTime.now_local ();
 
301
            var duration = (uint) (game_view.game.elapsed + 0.5);
 
302
            var entry = new HistoryEntry (date, game_view.game.map.score_name, duration);
 
303
            history.add (entry);
 
304
            history.save ();
 
305
 
 
306
            if (show_scores (entry, true) == Gtk.ResponseType.CLOSE)
 
307
                window.destroy ();
 
308
            else
 
309
                new_game ();
 
310
        }
 
311
        else if (!game_view.game.can_move)
 
312
        {
 
313
            var dialog = new Gtk.MessageDialog (window, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
 
314
                                                Gtk.MessageType.INFO,
 
315
                                                Gtk.ButtonsType.NONE,
 
316
                                                "%s", _("There are no more moves."));
 
317
            dialog.format_secondary_text (_("Each puzzle has at least one solution.  You can undo your moves and try and find the solution for a time penalty, restart this game or start an new one."));
 
318
            dialog.add_buttons (Gtk.Stock.UNDO, Gtk.ResponseType.REJECT,
 
319
                                _("_Restart"), Gtk.ResponseType.CANCEL,
 
320
                                _("_New game"), Gtk.ResponseType.ACCEPT);
 
321
 
 
322
            dialog.set_default_response (Gtk.ResponseType.ACCEPT);
 
323
            switch (dialog.run ())
 
324
            {
 
325
            case Gtk.ResponseType.REJECT:
 
326
                undo_cb ();
 
327
                break;
 
328
            case Gtk.ResponseType.CANCEL:
 
329
                restart_game ();
 
330
                break;
 
331
            default:
 
332
            case Gtk.ResponseType.ACCEPT:
 
333
                new_game ();
 
334
                break;
 
335
            }
 
336
            dialog.destroy ();
 
337
        }
 
338
    }
 
339
 
 
340
    private int show_scores (HistoryEntry? selected_entry = null, bool show_quit = false)
 
341
    {
 
342
        var dialog = new ScoreDialog (history, selected_entry, show_quit);
 
343
        dialog.modal = true;
 
344
        dialog.transient_for = window;
 
345
 
 
346
        var result = dialog.run ();
 
347
        dialog.destroy ();
 
348
 
 
349
        return result;
 
350
    }
 
351
 
 
352
    private void preferences_cb ()
 
353
    {
 
354
        if (preferences_dialog != null)
 
355
        {
 
356
            preferences_dialog.present ();
 
357
            return;
 
358
        }
 
359
 
 
360
        preferences_dialog = new Gtk.Dialog.with_buttons (_("Mahjongg Preferences"),
 
361
                                                   window,
 
362
                                                   Gtk.DialogFlags.DESTROY_WITH_PARENT,
 
363
                                                   Gtk.Stock.CLOSE,
 
364
                                                   Gtk.ResponseType.CLOSE, null);
 
365
        preferences_dialog.set_border_width (5);
 
366
        var dialog_content_area = (Gtk.Box) preferences_dialog.get_content_area ();
 
367
        dialog_content_area.set_spacing (2);
 
368
        preferences_dialog.set_resizable (false);
 
369
        preferences_dialog.set_default_response (Gtk.ResponseType.CLOSE);
 
370
        preferences_dialog.response.connect (preferences_dialog_response_cb);
 
371
 
 
372
        var grid = new Gtk.Grid ();
 
373
        grid.border_width = 5;
 
374
        grid.set_row_spacing (6);
 
375
        grid.set_column_spacing (18);
 
376
 
 
377
        var label = new Gtk.Label.with_mnemonic (_("_Theme:"));
 
378
        label.set_alignment (0, 0.5f);
 
379
        grid.attach (label, 0, 0, 1, 1);
 
380
 
 
381
        var themes = load_themes ();
 
382
        var theme_combo = new Gtk.ComboBox ();
 
383
        var theme_store = new Gtk.ListStore (2, typeof (string), typeof (string));
 
384
        theme_combo.model = theme_store;
 
385
        var renderer = new Gtk.CellRendererText ();
 
386
        theme_combo.pack_start (renderer, true);
 
387
        theme_combo.add_attribute (renderer, "text", 0);
 
388
        foreach (var theme in themes)
 
389
        {
 
390
            var tokens = theme.split (".", -1);
 
391
            var name = tokens[0];
 
392
 
 
393
            Gtk.TreeIter iter;
 
394
            theme_store.append (out iter);
 
395
            theme_store.set (iter, 0, name, 1, theme, -1);
 
396
 
 
397
            if (theme == settings.get_string ("tileset"))
 
398
                theme_combo.set_active_iter (iter);
 
399
        }
 
400
        theme_combo.changed.connect (theme_changed_cb);
 
401
        theme_combo.set_hexpand (true);
 
402
        grid.attach (theme_combo, 1, 0, 1, 1);
 
403
        label.set_mnemonic_widget (theme_combo);
 
404
 
 
405
        label = new Gtk.Label.with_mnemonic (_("_Layout:"));
 
406
        label.set_alignment (0, 0.5f);
 
407
        grid.attach (label, 0, 1, 1, 1);
 
408
 
 
409
        var map_combo = new Gtk.ComboBox ();
 
410
        var map_store = new Gtk.ListStore (2, typeof (string), typeof (string));
 
411
        map_combo.model = map_store;
 
412
        renderer = new Gtk.CellRendererText ();
 
413
        map_combo.pack_start (renderer, true);
 
414
        map_combo.add_attribute (renderer, "text", 0);
 
415
        foreach (var map in maps)
 
416
        {
 
417
            var display_name = dpgettext2 (null, "mahjongg map name", map.name);
 
418
 
 
419
            Gtk.TreeIter iter;
 
420
            map_store.append (out iter);
 
421
            map_store.set (iter, 0, display_name, 1, map, -1);
 
422
 
 
423
            if (settings.get_string ("mapset") == map.name)
 
424
                map_combo.set_active_iter (iter);
 
425
        }
 
426
        map_combo.changed.connect (map_changed_cb);
 
427
        map_combo.set_hexpand (true);
 
428
        grid.attach (map_combo, 1, 1, 1, 1);
 
429
        label.set_mnemonic_widget (map_combo);
 
430
 
 
431
        label = new Gtk.Label.with_mnemonic (_("_Background color:"));
 
432
        label.set_alignment (0, 0.5f);
 
433
        grid.attach (label, 0, 2, 1, 1);
 
434
 
 
435
        var widget = new Gtk.ColorButton ();
 
436
        widget.set_rgba (game_view.background_color);
 
437
        widget.color_set.connect (background_changed_cb);
 
438
        widget.set_hexpand (true);
 
439
        grid.attach (widget, 1, 2, 1, 1);
 
440
        label.set_mnemonic_widget (widget);
 
441
 
 
442
        dialog_content_area.pack_start (grid, true, true, 0);
 
443
 
 
444
        preferences_dialog.show_all ();
 
445
    }
 
446
 
 
447
    private void preferences_dialog_response_cb (Gtk.Dialog dialog, int response)
 
448
    {
 
449
        preferences_dialog.destroy ();
 
450
        preferences_dialog = null;
 
451
    }
 
452
 
 
453
    private List<string> load_themes ()
 
454
    {
 
455
        List<string> themes = null;
 
456
 
 
457
        Dir dir;
 
458
        try
 
459
        {
 
460
            dir = Dir.open (Path.build_filename (DATA_DIRECTORY, "themes"));
 
461
        }
 
462
        catch (FileError e)
 
463
        {
 
464
            return themes;
 
465
        }
 
466
 
 
467
        while (true)
 
468
        {
 
469
            var s = dir.read_name ();
 
470
            if (s == null)
 
471
                break;
 
472
 
 
473
            if (s.has_suffix (".xpm") || s.has_suffix (".svg") || s.has_suffix (".gif") ||
 
474
                s.has_suffix (".png") || s.has_suffix (".jpg") || s.has_suffix (".xbm"))
 
475
                themes.append (s);
 
476
        }
 
477
 
 
478
        return themes;
 
479
    }
 
480
 
 
481
    private void hint_cb ()
 
482
    {
 
483
        var matches = game_view.game.find_matches (game_view.game.selected_tile);
 
484
        var n_matches = matches.length ();
 
485
 
 
486
        /* No match, just flash the selected tile */
 
487
        if (n_matches == 0)
 
488
        {
 
489
            if (game_view.game.selected_tile == null)
 
490
                return;
 
491
            game_view.game.set_hint (game_view.game.selected_tile, null);
 
492
        }
 
493
        else
 
494
        {
 
495
            var n = Random.int_range (0, (int) n_matches);
 
496
            var match = matches.nth_data (n);
 
497
            game_view.game.set_hint (match.tile0, match.tile1);
 
498
        }
 
499
 
 
500
        update_ui ();
 
501
    }
 
502
 
 
503
    private void about_cb ()
 
504
    {
 
505
        string[] authors =
 
506
        {
 
507
            _("Main game:"),
 
508
            "Francisco Bustamante",
 
509
            "Max Watson",
 
510
            "Heinz Hempe",
 
511
            "Michael Meeks",
 
512
            "Philippe Chavin",
 
513
            "Callum McKenzie",
 
514
            "Robert Ancell",
 
515
            "",
 
516
            _("Maps:"),
 
517
            "Rexford Newbould",
 
518
            "Krzysztof Foltman",
 
519
            null
 
520
        };
 
521
 
 
522
        string[] artists =
 
523
        {
 
524
            _("Tiles:"),
 
525
            "Jonathan Buzzard",
 
526
            "Jim Evans",
 
527
            "Richard Hoelscher",
 
528
            "Gonzalo Odiard",
 
529
            "Max Watson",
 
530
            null
 
531
        };
 
532
 
 
533
        string[] documenters =
 
534
        {
 
535
            "Tiffany Antopolski",
 
536
            "Chris Beiser",
 
537
            null
 
538
        };
 
539
 
 
540
        var license = "Mahjongg 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 2 of the License, or (at your option) any later version.\n\nMahjongg 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.\n\nYou should have received a copy of the GNU General Public License along with Mahjongg; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA";
 
541
 
 
542
        Gtk.show_about_dialog (window,
 
543
                               "program-name", _("Mahjongg"),
 
544
                               "version", VERSION,
 
545
                               "comments",
 
546
                               _("A matching game played with Mahjongg tiles.\n\nMahjongg is a part of GNOME Games."),
 
547
                               "copyright", "Copyright \xc2\xa9 1998-2008 Free Software Foundation, Inc.",
 
548
                               "license", license,
 
549
                               "wrap-license", true,
 
550
                               "authors", authors,
 
551
                               "artists", artists,
 
552
                               "documenters", documenters,
 
553
                               "translator-credits", _("translator-credits"),
 
554
                               "logo-icon-name", "gnome-mahjongg",
 
555
                               "website", "http://www.gnome.org/projects/gnome-games",
 
556
                               "website-label", _("GNOME Games web site"),
 
557
                               null);
 
558
    }
 
559
 
 
560
    private void pause_cb ()
 
561
    {
 
562
        game_view.game.paused = !game_view.game.paused;
 
563
        game_view.game.set_hint (null, null);
 
564
        game_view.game.selected_tile = null;
 
565
        if (game_view.game.paused)
 
566
        {
 
567
            pause_button.icon_name = "media-playback-start";
 
568
            pause_button.label = _("Res_ume");
 
569
        }
 
570
        else
 
571
        {
 
572
            pause_button.icon_name = "media-playback-pause";
 
573
            pause_button.label = _("_Pause");
 
574
        }
 
575
 
 
576
        update_ui ();
 
577
    }
 
578
 
 
579
    private void scores_cb ()
 
580
    {
 
581
        show_scores ();
 
582
    }
 
583
 
 
584
    private void new_game_cb ()
 
585
    {
 
586
        new_game ();
 
587
    }
 
588
 
 
589
    private void restart_game_cb ()
 
590
    {
 
591
        game_view.game.reset ();
 
592
        game_view.queue_draw ();
 
593
    }
 
594
 
 
595
    private void quit_cb ()
 
596
    {
 
597
        window.destroy ();
 
598
    }
 
599
 
 
600
    private void redo_cb ()
 
601
    {
 
602
        if (game_view.game.paused)
 
603
            return;
 
604
 
 
605
        game_view.game.redo ();
 
606
        update_ui ();
 
607
    }
 
608
 
 
609
    private void undo_cb ()
 
610
    {
 
611
        game_view.game.undo ();
 
612
        update_ui ();
 
613
    }
 
614
 
 
615
    private void restart_game ()
 
616
    {
 
617
        game_view.game.reset ();
 
618
        update_ui ();
 
619
    }
 
620
 
 
621
    private void new_game ()
 
622
    {
 
623
        Map? map = null;
 
624
        foreach (var m in maps)
 
625
        {
 
626
            if (m.name == settings.get_string ("mapset"))
 
627
            {
 
628
                map = m;
 
629
                break;
 
630
            }
 
631
        }
 
632
        if (map == null)
 
633
            map = maps.nth_data (0);
 
634
 
 
635
        game_view.game = new Game (map);
 
636
        game_view.game.moved.connect (moved_cb);
 
637
        game_view.game.tick.connect (tick_cb);
 
638
 
 
639
        /* Set window title */
 
640
        var display_name = dpgettext2 (null, "mahjongg map name", game_view.game.map.name);
 
641
        /* Translators: This is the window title for Mahjongg which contains the map name, e.g. 'Mahjongg - Red Dragon' */
 
642
        window.set_title (_("Mahjongg - %s").printf (display_name));
 
643
 
 
644
        update_ui ();
 
645
    }
 
646
 
 
647
    private void tick_cb ()
 
648
    {
 
649
        var elapsed = 0;
 
650
        if (game_view.game != null)
 
651
            elapsed = (int) (game_view.game.elapsed + 0.5);
 
652
        var hours = elapsed / 3600;
 
653
        var minutes = (elapsed - hours * 3600) / 60;
 
654
        var seconds = elapsed - hours * 3600 - minutes * 60;
 
655
        clock_label.set_text ("%s: %02d:%02d:%02d".printf (_("Time"), hours, minutes, seconds));
 
656
    }
 
657
 
 
658
    private void help_cb ()
 
659
    {
 
660
        try
 
661
        {
 
662
            Gtk.show_uri (window.get_screen (), "help:gnome-mahjongg", Gtk.get_current_event_time ());
 
663
        }
 
664
        catch (Error e)
 
665
        {
 
666
            warning ("Failed to show help: %s", e.message);
 
667
        }
 
668
    }
 
669
 
 
670
    private const GLib.ActionEntry[] action_entries =
 
671
    {
 
672
        { "new-game",      new_game_cb     },
 
673
        { "undo",          undo_cb         },
 
674
        { "redo",          redo_cb         },
 
675
        { "hint",          hint_cb         },
 
676
        { "pause",         pause_cb        },
 
677
        { "restart-game",  restart_game_cb },
 
678
        { "scores",        scores_cb       },
 
679
        { "preferences",   preferences_cb  },
 
680
        { "help",          help_cb         },
 
681
        { "about",         about_cb        },
 
682
        { "quit",          quit_cb         }
 
683
    };
 
684
 
 
685
    private void load_maps ()
 
686
    {
 
687
        maps = null;
 
688
 
 
689
        /* Add the builtin map */
 
690
        maps.append (new Map.builtin ());
 
691
 
 
692
        Dir dir;
 
693
        try
 
694
        {
 
695
            dir = Dir.open (Path.build_filename (DATA_DIRECTORY, "maps"));
 
696
        }
 
697
        catch (FileError e)
 
698
        {
 
699
            return;
 
700
        }
 
701
        while (true)
 
702
        {
 
703
            var filename = dir.read_name ();
 
704
            if (filename == null)
 
705
                break;
 
706
 
 
707
            if (!filename.has_suffix (".map"))
 
708
                continue;
 
709
 
 
710
            var loader = new MapLoader ();
 
711
            var path = Path.build_filename (DATA_DIRECTORY, "maps", filename);
 
712
            try
 
713
            {
 
714
                loader.load (path);
 
715
            }
 
716
            catch (Error e)
 
717
            {
 
718
                warning ("Could not load map %s: %s\n", path, e.message);
 
719
                continue;
 
720
            }
 
721
            foreach (var map in loader.maps)
 
722
                maps.append (map);
 
723
        }
 
724
    }
 
725
 
 
726
    public static int main (string[] args)
 
727
    {
 
728
        Intl.setlocale (LocaleCategory.ALL, "");
 
729
        Intl.bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
 
730
        Intl.bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
 
731
        Intl.textdomain (GETTEXT_PACKAGE);
 
732
 
 
733
        Gtk.init (ref args);
 
734
 
 
735
        var context = new OptionContext ("");
 
736
        context.set_translation_domain (GETTEXT_PACKAGE);
 
737
        context.add_group (Gtk.get_option_group (true));
 
738
 
 
739
        try
 
740
        {
 
741
            context.parse (ref args);
 
742
        }
 
743
        catch (Error e)
 
744
        {
 
745
            stdout.printf ("%s\n", e.message);
 
746
            return Posix.EXIT_FAILURE;
 
747
        }
 
748
 
 
749
        Environment.set_application_name (_("Mahjongg"));
 
750
        Gtk.Window.set_default_icon_name ("gnome-mahjongg");
 
751
 
 
752
        var app = new Mahjongg ();
 
753
        var result = app.run ();
 
754
 
 
755
        Settings.sync();
 
756
 
 
757
        return result;
 
758
    }
 
759
}
 
760
 
 
761
public class ScoreDialog : Gtk.Dialog
 
762
{
 
763
    private History history;
 
764
    private HistoryEntry? selected_entry = null;
 
765
    private Gtk.ListStore size_model;
 
766
    private Gtk.ListStore score_model;
 
767
    private Gtk.ComboBox size_combo;
 
768
 
 
769
    public ScoreDialog (History history, HistoryEntry? selected_entry = null, bool show_quit = false)
 
770
    {
 
771
        this.history = history;
 
772
        history.entry_added.connect (entry_added_cb);
 
773
        this.selected_entry = selected_entry;
 
774
 
 
775
        if (show_quit)
 
776
        {
 
777
            add_button (Gtk.Stock.QUIT, Gtk.ResponseType.CLOSE);
 
778
            add_button (_("New Game"), Gtk.ResponseType.OK);
 
779
        }
 
780
        else
 
781
            add_button (Gtk.Stock.OK, Gtk.ResponseType.DELETE_EVENT);
 
782
        set_size_request (200, 300);
 
783
 
 
784
        var vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 5);
 
785
        vbox.border_width = 6;
 
786
        vbox.show ();
 
787
        get_content_area ().pack_start (vbox, true, true, 0);
 
788
 
 
789
        var hbox = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6);
 
790
        hbox.show ();
 
791
        vbox.pack_start (hbox, false, false, 0);
 
792
 
 
793
        var label = new Gtk.Label (_("Size:"));
 
794
        label.show ();
 
795
        hbox.pack_start (label, false, false, 0);
 
796
 
 
797
        size_model = new Gtk.ListStore (2, typeof (string), typeof (string));
 
798
 
 
799
        size_combo = new Gtk.ComboBox ();
 
800
        size_combo.changed.connect (size_changed_cb);
 
801
        size_combo.model = size_model;
 
802
        var renderer = new Gtk.CellRendererText ();
 
803
        size_combo.pack_start (renderer, true);
 
804
        size_combo.add_attribute (renderer, "text", 0);
 
805
        size_combo.show ();
 
806
        hbox.pack_start (size_combo, true, true, 0);
 
807
 
 
808
        var scroll = new Gtk.ScrolledWindow (null, null);
 
809
        scroll.shadow_type = Gtk.ShadowType.ETCHED_IN;
 
810
        scroll.set_policy (Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
 
811
        scroll.show ();
 
812
        vbox.pack_start (scroll, true, true, 0);
 
813
 
 
814
        score_model = new Gtk.ListStore (3, typeof (string), typeof (string), typeof (int));
 
815
 
 
816
        var scores = new Gtk.TreeView ();
 
817
        renderer = new Gtk.CellRendererText ();
 
818
        scores.insert_column_with_attributes (-1, _("Date"), renderer, "text", 0, "weight", 2);
 
819
        renderer = new Gtk.CellRendererText ();
 
820
        renderer.xalign = 1.0f;
 
821
        scores.insert_column_with_attributes (-1, _("Time"), renderer, "text", 1, "weight", 2);
 
822
        scores.model = score_model;
 
823
        scores.show ();
 
824
        scroll.add (scores);
 
825
 
 
826
        foreach (var entry in history.entries)
 
827
            entry_added_cb (entry);
 
828
    }
 
829
 
 
830
    public void set_map (string name)
 
831
    {
 
832
        score_model.clear ();
 
833
 
 
834
        var entries = history.entries.copy ();
 
835
        entries.sort (compare_entries);
 
836
 
 
837
        foreach (var entry in entries)
 
838
        {
 
839
            if (entry.name != name)
 
840
                continue;
 
841
 
 
842
            var date_label = entry.date.format ("%d/%m/%Y");
 
843
 
 
844
            var time_label = "%us".printf (entry.duration);
 
845
            if (entry.duration >= 60)
 
846
                time_label = "%um %us".printf (entry.duration / 60, entry.duration % 60);
 
847
 
 
848
            int weight = Pango.Weight.NORMAL;
 
849
            if (entry == selected_entry)
 
850
                weight = Pango.Weight.BOLD;
 
851
 
 
852
            Gtk.TreeIter iter;
 
853
            score_model.append (out iter);
 
854
            score_model.set (iter, 0, date_label, 1, time_label, 2, weight);
 
855
        }
 
856
    }
 
857
 
 
858
    private static int compare_entries (HistoryEntry a, HistoryEntry b)
 
859
    {
 
860
        var d = strcmp (a.name, b.name);
 
861
        if (d != 0)
 
862
            return d;
 
863
        return a.date.compare (b.date);
 
864
    }
 
865
 
 
866
    private void size_changed_cb (Gtk.ComboBox combo)
 
867
    {
 
868
        Gtk.TreeIter iter;
 
869
        if (!combo.get_active_iter (out iter))
 
870
            return;
 
871
 
 
872
        string name;
 
873
        combo.model.get (iter, 1, out name);
 
874
        set_map (name);
 
875
    }
 
876
 
 
877
    private void entry_added_cb (HistoryEntry entry)
 
878
    {
 
879
        /* Ignore if already have an entry for this */
 
880
        Gtk.TreeIter iter;
 
881
        var have_size_entry = false;
 
882
        if (size_model.get_iter_first (out iter))
 
883
        {
 
884
            do
 
885
            {
 
886
                string name;
 
887
                size_model.get (iter, 1, out name);
 
888
                if (name == entry.name)
 
889
                {
 
890
                    have_size_entry = true;
 
891
                    break;
 
892
                }
 
893
            } while (size_model.iter_next (ref iter));
 
894
        }
 
895
 
 
896
        if (!have_size_entry)
 
897
        {
 
898
            var label = "%s".printf (entry.name);
 
899
 
 
900
            size_model.append (out iter);
 
901
            size_model.set (iter, 0, label, 1, entry.name);
 
902
    
 
903
            /* Select this entry if don't have any */
 
904
            if (size_combo.get_active () == -1)
 
905
                size_combo.set_active_iter (iter);
 
906
 
 
907
            /* Select this entry if the same category as the selected one */
 
908
            if (selected_entry != null && entry.name == selected_entry.name)
 
909
                size_combo.set_active_iter (iter);
 
910
        }
 
911
    }
 
912
}