main.c 29.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* 
 * 
 * Copyright (C) 2009-2011 Colomban Wendling <ban@herbesfolles.org>
 *
 * 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/>.
 * 
 */
19

20
21
#include "config.h"

22
#include <stdarg.h>
23
24
#include <glib/gi18n.h>
#include <gtk/gtk.h>
25
26
27
#include <ovcc/ovcc.h>
#include <ovcc/ovccclient.h>

28
29
#include "utils.h"
#include "tiles.h"
30
#include "ovccgtk-board.h"
31
#include "ovccgtk-client.h"
32
#include "ovccgtk-connection-dialog.h"
33
#include "ovccgtk-player-list.h"
34

35

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#define GAME_TILES    "tiles.xml"
#define GAME_TILESET  "tileset.xml"


/* bad copy of the bot implemented in libovcc's test */

static gboolean
do_try_place_tile (OVCCBoard    *board,
                   OVCCPosition *pos,
                   OVCCTile     *tile,
                   gpointer      user_data)
{
  gsize     i;
  gboolean  keep_doing = TRUE;
  OVCCTile *new_tile = user_data;
  
  g_return_val_if_fail (new_tile != NULL, FALSE);
  
54
  for (i = 0; keep_doing && i < 4; i++) {
55
56
57
58
    OVCCPosition npos = *pos;
    int          r;
    
    switch (i) {
59
60
61
62
      case 0: npos.y--; break;
      case 1: npos.y++; break;
      case 2: npos.x--; break;
      case 3: npos.x++; break;
63
    }
64
    for (r = 0; keep_doing && r < 4; r++) {
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
      ovcc_tile_rotate (new_tile, 1);
      if (ovcc_board_add_tile_check (board, new_tile, &npos)) {
        ovcc_board_add_tile (board, new_tile, &npos);
        keep_doing = FALSE;
      }
    }
  }
  
  return keep_doing;
}

static gboolean
try_place_tile (OVCCBoard *board,
                OVCCTile  *tile)
{
  return ovcc_board_foreach (board, do_try_place_tile, tile) == FALSE;
}

static gboolean
test_board (OVCCBoard *board,
            OVCCStack *stack)
{
  OVCCTile *tile;
  gsize     n_success = 0;
  gsize     n_failed  = 0;
  
  while (NULL != (tile = ovcc_stack_pop (stack))) {
    if (! try_place_tile (board, tile)) {
      g_warning ("Failed to place tile %u", ovcc_tile_get_id (tile));
      n_failed ++;
    } else {
      n_success ++;
    }
98
    g_object_unref (tile);
99
100
101
102
103
104
105
106
107
  }
  
  g_debug ("%zu tiles over %zu placed (%.3u%%)",
           n_success, (n_success + n_failed),
           n_success * 100 / (n_success + n_failed));
  
  return n_failed == 0;
}

108
109
110
111
112
113
114
115
116
117
118
119
120
static gboolean
game_do_try_place_tile (OVCCBoard    *board,
                        OVCCPosition *pos,
                        OVCCTile     *tile,
                        gpointer      user_data)
{
  gsize     i;
  gboolean  keep_doing = TRUE;
  OVCCGame *game = user_data;
  OVCCTile *new_tile = ovcc_game_get_current_tile (game);
  
  g_return_val_if_fail (new_tile != NULL, FALSE);
  
121
  for (i = 0; keep_doing && i < 4; i++) {
122
123
124
125
    OVCCPosition npos = *pos;
    int          r;
    
    switch (i) {
126
127
128
129
      case 0: npos.y--; break;
      case 1: npos.y++; break;
      case 2: npos.x--; break;
      case 3: npos.x++; break;
130
    }
131
    for (r = 0; keep_doing && r < 4; r++) {
132
133
      if (ovcc_game_place_tile (game, ovcc_game_get_current_player (game), &npos)) {
        keep_doing = FALSE;
134
135
      } else {
        ovcc_tile_rotate (new_tile, 1);
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
      }
    }
  }
  
  return keep_doing;
}

static gboolean
game_try_place_tile (OVCCGame  *game,
                     OVCCTile  *tile)
{
  OVCCBoard *board;
  
  board = ovcc_game_get_board (game);
  return ovcc_board_foreach (board, game_do_try_place_tile, game) == FALSE;
}

153
154
155
156
157
158
struct _PlaceNextData {
  OVCCGame *game;
  guint     n_success;
  guint     n_failed;
};

159
static gboolean
160
place_next (gpointer data)
161
{
162
163
  struct _PlaceNextData  *pdata = data;
  OVCCTile               *tile;
164
  
165
166
167
168
169
170
171
172
173
174
175
176
177
178
  tile = ovcc_game_get_current_tile (pdata->game);
  if (! tile) {
    g_debug ("%u tiles over %u placed (%.3u%%)",
             pdata->n_success, (pdata->n_success + pdata->n_failed),
             (pdata->n_success + pdata->n_failed) > 0
              ? pdata->n_success * 100 / (pdata->n_success + pdata->n_failed)
              : 0);
    return FALSE;
  }
  if (! game_try_place_tile (pdata->game, tile)) {
    g_warning ("Failed to place tile %u", ovcc_tile_get_id (tile));
    pdata->n_failed ++;
  } else {
    pdata->n_success ++;
179
180
  }
  
181
182
183
184
185
186
187
  return TRUE;
}

static void
test_game (OVCCGame *game)
{
  struct _PlaceNextData *pdata;
188
  
189
190
191
192
193
  pdata = g_malloc (sizeof *pdata);
  pdata->game = game;
  pdata->n_success = 0;
  pdata->n_failed = 0;
  g_timeout_add_full (G_PRIORITY_DEFAULT, 100, place_next, pdata, g_free);
194
195
}

196
197
static OVCCStack *
create_stack (void)
198
199
200
201
202
203
{
  gchar        *path;
  OVCCTilesDef *tiles;
  GFile        *tiles_file;
  OVCCTileSet  *set;
  GFile        *set_file;
204
  OVCCStack    *stack = NULL;
205
206
  GError       *err = NULL;
  
207
  tiles = ovcc_tiles_def_new ();
208
209
210
  path = data_get_path (GAME_TILES);
  tiles_file = g_file_new_for_path (path);
  g_free (path);
211
  if (! ovcc_tiles_def_load (tiles, tiles_file, &err)) {
212
213
214
    msg_error (_("Failed to load tiles: %s"), err->message);
    g_error_free (err);
  } else {
215
    set = ovcc_tile_set_new ();
216
217
218
    path = data_get_path (GAME_TILESET);
    set_file = g_file_new_for_path (path);
    g_free (path);
219
    if (! ovcc_tile_set_load (set, tiles, set_file, &err)) {
220
221
222
223
224
225
      msg_error (_("Failed to load tile set: %s"), err->message);
      g_error_free (err);
    } else {
      stack = ovcc_stack_new_from_tileset (set);
    }
    g_object_unref (set_file);
226
    g_object_unref (set);
227
228
  }
  g_object_unref (tiles_file);
229
  g_object_unref (tiles);
230
  
231
232
233
234
235
236
237
238
239
240
241
  return stack;
}

static OVCCBoard *
create_board (void)
{
  OVCCStack    *stack;
  OVCCBoard    *board = NULL;
  
  stack = create_stack ();
  if (stack) {
242
243
244
245
246
    OVCCTile *tile;
    
    tile = ovcc_stack_pop (stack);
    board = ovcc_board_new (tile);
    g_object_unref (tile);
247
248
    test_board (board, stack);
  }
249
  g_object_unref (stack);
250
  
251
252
253
  return board;
}

254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
static void
on_unplaceable_tile (OVCCGame *game,
                     OVCCTile *tile,
                     gpointer  data)
{
  g_warning ("Cannot place tile #%u, reshaking.", ovcc_tile_get_id (tile));
}

static OVCCGame *
create_game (void)
{
  gchar        *path;
  OVCCTilesDef *tiles;
  GFile        *tiles_file;
  OVCCTileSet  *set;
  GFile        *set_file;
  OVCCGame     *game = NULL;
  GError       *err = NULL;
  
273
  tiles = ovcc_tiles_def_new ();
274
275
276
  path = data_get_path (GAME_TILES);
  tiles_file = g_file_new_for_path (path);
  g_free (path);
277
  if (! ovcc_tiles_def_load (tiles, tiles_file, &err)) {
278
279
280
    msg_error (_("Failed to load tiles: %s"), err->message);
    g_error_free (err);
  } else {
281
    set = ovcc_tile_set_new ();
282
283
284
    path = data_get_path (GAME_TILESET);
    set_file = g_file_new_for_path (path);
    g_free (path);
285
    if (! ovcc_tile_set_load (set, tiles, set_file, &err)) {
286
287
288
      msg_error (_("Failed to load tile set: %s"), err->message);
      g_error_free (err);
    } else {
289
      game = ovcc_game_new (set, NULL);
290
291
292
293
294
295
      g_signal_connect (game, "unplaceable-tile",
                        G_CALLBACK (on_unplaceable_tile), NULL);
      ovcc_game_add_player (game, ovcc_player_new ("A"), NULL);
      ovcc_game_add_player (game, ovcc_player_new ("B"), NULL);
    }
    g_object_unref (set_file);
296
    g_object_unref (set);
297
298
  }
  g_object_unref (tiles_file);
299
  g_object_unref (tiles);
300
301
302
303
  
  return game;
}

304
/* end of bad game fake */
305

306
307
308
309
310





311
typedef struct _App App;
312
313
314

struct _App
{
315
  GtkWidget      *main_window;
316
  GtkWidget      *statusbar;
317
318
319
320
  GtkWidget      *tile_counter_label;
  GtkWidget      *current_tile_image;
  GtkWidget      *board_container;
  GtkWidget      *board;
321
  
322
  GtkWidget      *player_list;
323
  GtkWidget      *connection_dialog;
324
  GCancellable   *connection_cancel;
325
  
326
327
  OVCCGtkClient  *client;
  OVCCGame       *game;
328
329
330
331
332
333
334
335
336
  
  struct {
    struct {
      GtkAction  *new;
      GtkAction  *connect;
      GtkAction  *disconnect;
      GtkAction  *start;
      GtkAction  *rotate_tile_clockwise;
      GtkAction  *rotate_tile_counterclockwise;
337
      GtkAction  *finish_turn;
338
339
340
341
342
343
344
345
346
    } game;
    struct {
      GtkAction  *quit;
      GtkAction  *preferences;
    } app;
    struct {
      GtkAction  *about;
    } help;
  } actions;
347
348
};

349
350


351
352
353
354
355
356
357
358
359
360
361
362
363
static gint
show_message (GtkWindow      *parent,
              GtkMessageType  type,
              GtkButtonsType  buttons,
              const gchar    *secondary_text,
              const gchar    *primary_fmt,
              ...)
{
  gint          response;
  GtkWidget    *dialog;
  const gchar  *primary;
  va_list       ap;
  
364
365
  g_return_val_if_fail (parent == NULL || GTK_IS_WINDOW (parent), 0);
  g_return_val_if_fail (primary_fmt != NULL, 0);
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
  
  va_start (ap, primary_fmt);
  primary = g_strdup_vprintf (primary_fmt, ap);
  va_end (ap);
  dialog = g_object_new (GTK_TYPE_MESSAGE_DIALOG,
                         "message-type", type,
                         "buttons", buttons,
                         "text", primary,
                         "secondary-text", secondary_text,
                         NULL);
  
  if (parent != NULL) {
    gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
  }
  
  response = gtk_dialog_run (GTK_DIALOG (dialog));
  gtk_widget_destroy (dialog);
  
  return response;
}


388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
static void
app_set_status (App          *app,
                const gchar  *fmt,
                ...)
{
  gchar  *status;
  va_list ap;
  
  va_start (ap, fmt);
  status = g_strdup_vprintf (fmt, ap);
  va_end (ap);
  
  gtk_statusbar_pop (GTK_STATUSBAR (app->statusbar), 0);
  gtk_statusbar_push (GTK_STATUSBAR (app->statusbar), 0, status);
  g_free (status);
}


406
407
408
409
410
411
412
413
414
415
static gboolean
load_ui (GtkBuilder  *builder,
         const gchar *filename)
{
  GError   *err     = NULL;
  gboolean  loaded  = FALSE;
  gchar    *path    = data_get_path (filename);
  
  loaded = gtk_builder_add_from_file (builder, path, &err) > 0;
  if (! loaded) {
416
417
418
    show_message (NULL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
                  err->message, _("Failed to load UI file \"%s\""), path);
    g_error_free (err);
419
420
421
422
423
424
425
  }
  g_free (path);
  
  return loaded;
}


426
427
428
static void
update_tile_image (App *app)
{
429
  OVCCTile *tile = NULL;
430
431
  guint     tiles_left;
  gchar    *label;
432
  
433
434
435
436
437
438
439
440
441
  if (app->game) {
    tile = ovcc_game_get_current_tile (app->game);
    tiles_left = ovcc_game_get_n_tiles_left (app->game);
    label = g_strdup_printf (g_dngettext (NULL, "%u tile left",
                                                "%u tiles left", tiles_left),
                             tiles_left);
    gtk_label_set_text (GTK_LABEL (app->tile_counter_label), label);
    g_free (label);
  }
442
  if (tile) {
443
    GdkPixbuf *pix;
444
    
445
    pix = tile_get_pixbuf (tile, -1);
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
    gtk_image_set_from_pixbuf (GTK_IMAGE (app->current_tile_image), pix);
    g_object_unref (pix);
  } else {
    gtk_image_set_from_stock (GTK_IMAGE (app->current_tile_image),
                              GTK_STOCK_MISSING_IMAGE,
                              GTK_ICON_SIZE_LARGE_TOOLBAR);
  }
}

static void
rotate_current_tile (App *app,
                     gint wise)
{
  OVCCTile *tile;
  
461
  tile = ovcc_game_get_current_tile (app->game);
462
463
464
465
466
467
  if (tile) {
    ovcc_tile_rotate (tile, wise);
  }
  update_tile_image (app);
}

468
469
470
471
472
473
474
475
static gboolean
app_leave (App *app)
{
  if (! app->client) {
    return TRUE;
  }
  
  if (app->game && ovcc_game_get_state (app->game) == OVCC_GAME_STATE_STARTED &&
476
      g_slist_length (ovcc_game_get_players (app->game)) > 1 &&
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
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
      show_message (GTK_WINDOW (app->main_window), GTK_MESSAGE_QUESTION,
                    GTK_BUTTONS_YES_NO,
                    _("The game is not finished and you are not the only "
                      "player on it. Do you really want to abort the game "
                      "and leave it?"),
                    _("Do you really want to leave?")) != GTK_RESPONSE_YES) {
    return FALSE;
  } else {
    GError *err = NULL;
    
    if (! ovccclient_client_leave (OVCCCLIENT_CLIENT (app->client), &err)) {
      app_set_status (app, "Ignoring disconnection error: %s", err->message);
      g_error_free (err);
    } else {
      app_set_status (app, "Disconnected!");
    }
    g_object_unref (app->client);
    app->client = NULL;
    app->game = NULL;
    
    gtk_widget_destroy (app->board);
    app->board = NULL;
    update_tile_image (app);
    
    gtk_action_set_sensitive (app->actions.game.disconnect, FALSE);
    gtk_action_set_sensitive (app->actions.game.start, FALSE);
    gtk_action_set_sensitive (app->actions.game.rotate_tile_clockwise, FALSE);
    gtk_action_set_sensitive (app->actions.game.rotate_tile_counterclockwise, FALSE);
    gtk_action_set_sensitive (app->actions.game.connect, TRUE);
    gtk_action_set_sensitive (app->actions.game.new, TRUE);
    
    return TRUE;
  }
}

static gboolean
app_quit (App *app)
{
  if (app_leave (app)) {
    gtk_main_quit ();
    
    return TRUE;
  } else {
    return FALSE;
  }
}

G_MODULE_EXPORT gboolean
525
526
527
main_window_delete_event_handler (GtkWidget *window,
                                  GdkEvent  *event,
                                  App       *app)
528
529
530
531
532
533
534
535
536
537
538
{
  return ! app_quit (app);
}

G_MODULE_EXPORT void
game_quit_activate_handler (GtkAction *action,
                            App       *app)
{
  app_quit (app);
}

539
G_MODULE_EXPORT void
540
541
rotate_tile_clockwise_activate_handler (GtkAction *action,
                                        App       *app)
542
{
543
  rotate_current_tile (app, 1);
544
545
546
}

G_MODULE_EXPORT void
547
548
rotate_tile_counterclockwise_activate_handler (GtkAction *action,
                                               App       *app)
549
{
550
  rotate_current_tile (app, -1);
551
552
}

553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
G_MODULE_EXPORT void
game_start_activate_handler (GtkAction *action,
                             App       *app)
{
  if (app->game && ovcc_game_get_state (app->game) == OVCC_GAME_STATE_STOPPED) {
    GError *err = NULL;
    
    if (! ovcc_game_start (app->game, &err)) {
      show_message (GTK_WINDOW (app->main_window), GTK_MESSAGE_WARNING,
                    GTK_BUTTONS_CLOSE, err->message, "Cannot start game");
      g_error_free (err);
    }
  }
}

568
static void
569
finish_turn (App *app)
570
{
571
  gtk_action_set_sensitive (app->actions.game.finish_turn, FALSE);
572
  g_signal_emit_by_name (app->game, "turn-finished");
573
574
575
  /* set board mode to tile for it to stop showing pawn placement infos  */
  ovccgtk_board_set_state (OVCCGTK_BOARD (app->board),
                           OVCCGTK_BOARD_STATE_TILE);
576
577
578
}

G_MODULE_EXPORT void
579
580
game_finish_turn_activate_handler (GtkAction *action,
                                   App       *app)
581
582
583
584
585
586
587
588
589
590
591
592
{
  finish_turn (app);
}

static void
on_pawn_add_request (OVCCGtkBoard        *board,
                     const OVCCPosition  *pos,
                     OVCCTileObject      *obj,
                     OVCCPawnKind         kind,
                     App                 *app)
{
  OVCCPlayer *player;
593
  
594
595
596
597
598
599
  g_debug ("Add pawn request @ [%d,%d]", pos->x, pos->y);
  
  player = ovccclient_client_get_player (OVCCCLIENT_CLIENT (app->client));
  if (ovcc_player_place_pawn (player, kind, obj, pos)) {
    g_debug ("OK, placed.");
    finish_turn (app);
600
601
602
  }
}

603
604
605
static void
on_tile_add_request (OVCCGtkBoard        *board,
                     const OVCCPosition  *pos,
606
                     App                 *app)
607
{
608
609
  OVCCTile *tile;
  
610
  g_debug ("Add request @ [%d,%d]", pos->x, pos->y);
611
  tile = ovcc_game_get_current_tile (app->game);
612
  if (tile) {
613
614
615
616
    OVCCPlayer *player;
    
    player = ovccclient_client_get_player (OVCCCLIENT_CLIENT (app->client));
    if (ovcc_player_place_tile (player, pos)) {
617
      g_debug ("OK, placed.");
618
619
      /* TODO: if there is no more pawns, we could finish our turn here (or
       * somewhere sensible) */
620
      gtk_action_set_sensitive (app->actions.game.finish_turn, TRUE);
621
622
    }
  } else {
623
624
    show_message (GTK_WINDOW (app->main_window), GTK_MESSAGE_INFO,
                  GTK_BUTTONS_CLOSE, NULL, "Game already finished!");
625
  }
626
627
}

Colomban Wendling's avatar
Colomban Wendling committed
628
static void
629
630
631
on_current_tile_changed (GObject     *object,
                         GParamSpec  *pspec,
                         App         *app)
Colomban Wendling's avatar
Colomban Wendling committed
632
633
634
635
636
{
  /* update current tile image */
  update_tile_image (app);
}

637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
static void
on_current_player_changed (OVCCGame    *game,
                           GParamSpec  *pspec,
                           App         *app)
{
  if (ovcc_game_get_state (game) == OVCC_GAME_STATE_STARTED) {
    OVCCPlayer *me;
    OVCCPlayer *player;
    
    me = ovccclient_client_get_player (OVCCCLIENT_CLIENT (app->client));
    player = ovcc_game_get_current_player (game);
    if (player != me) {
      app_set_status (app, _("It's the turn of %s"),
                      ovcc_player_get_nick (player));
    } else {
      app_set_status (app, _("It's your turn"));
    }
  }
}

static void
on_game_state_changed (OVCCGame    *game,
                       GParamSpec  *pspec,
                       App         *app)
{
662
  gboolean can_start = FALSE;
663
  gboolean started   = FALSE;
664
  
665
666
667
668
669
670
671
672
673
674
675
  switch (ovcc_game_get_state (game)) {
    case OVCC_GAME_STATE_ABORTED:
      app_set_status (app, _("Game aborted"));
      break;
    
    case OVCC_GAME_STATE_FINISHED:
      app_set_status (app, _("Game finished!"));
      break;
    
    case OVCC_GAME_STATE_STARTED:
      app_set_status (app, _("Game started!"));
676
      started = TRUE;
677
678
679
680
      break;
    
    case OVCC_GAME_STATE_STOPPED:
      app_set_status (app, _("Game stopped"));
681
      can_start = TRUE;
682
683
      break;
  }
684
  gtk_action_set_sensitive (app->actions.game.start, can_start);
685
686
687
  gtk_widget_set_sensitive (app->current_tile_image, started);
  gtk_action_set_sensitive (app->actions.game.rotate_tile_clockwise, started);
  gtk_action_set_sensitive (app->actions.game.rotate_tile_counterclockwise, started);
688
689
}

690
691
692
693
694
695
696
static void
game_pawn_placed_handler (OVCCGame       *game,
                          OVCCPawn       *pawn,
                          OVCCTileObject *obj,
                          OVCCPosition   *pos,
                          App            *app)
{
697
698
699
700
701
702
703
704
705
706
707
708
709
  /* for now just redraw the position */
  ovccgtk_board_queue_position_update (OVCCGTK_BOARD (app->board), pos);
}

static void
game_pawn_removed_handler (OVCCGame       *game,
                           OVCCPawn       *pawn,
                           OVCCTileObject *obj,
                           OVCCPosition   *pos,
                           App            *app)
{
  /* for now just redraw the position */
  ovccgtk_board_queue_position_update (OVCCGTK_BOARD (app->board), pos);
710
711
}

712
713
714
715
716
717
718
static void
client_place_tile_handler (OVCCClientClient  *client,
                           OVCCTile          *tile,
                           App               *app)
{
  ovccgtk_board_set_state (OVCCGTK_BOARD (app->board),
                           OVCCGTK_BOARD_STATE_TILE);
719
  gtk_widget_set_sensitive (app->current_tile_image, TRUE);
720
721
  gtk_action_set_sensitive (app->actions.game.rotate_tile_clockwise, TRUE);
  gtk_action_set_sensitive (app->actions.game.rotate_tile_counterclockwise, TRUE);
722
723
724
725
726
727
728
729
730
}

static void
client_place_pawn_handler (OVCCClientClient  *client,
                           OVCCPosition      *pos,
                           App               *app)
{
  ovccgtk_board_set_state (OVCCGTK_BOARD (app->board),
                           OVCCGTK_BOARD_STATE_PAWN);
731
  gtk_widget_set_sensitive (app->current_tile_image, FALSE);
732
733
  gtk_action_set_sensitive (app->actions.game.rotate_tile_clockwise, FALSE);
  gtk_action_set_sensitive (app->actions.game.rotate_tile_counterclockwise, FALSE);
734
735
}

736
737
738
739
740
741
742
743
744

static void
client_join_ready (GObject       *object,
                   GAsyncResult  *result,
                   gpointer       data)
{
  GError *err = NULL;
  App    *app = data;
  
745
746
  g_object_unref (app->connection_cancel);
  app->connection_cancel = NULL;
747
  if (! ovccgtk_client_join_finish (result, &err)) {
748
749
750
751
752
753
754
755
756
757
758
    if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
      ovccgtk_connection_dialog_set_message (OVCCGTK_CONNECTION_DIALOG (app->connection_dialog),
                                             GTK_MESSAGE_INFO,
                                             _("Connection was cancelled"),
                                             NULL);
    } else {
      ovccgtk_connection_dialog_set_message (OVCCGTK_CONNECTION_DIALOG (app->connection_dialog),
                                             GTK_MESSAGE_WARNING,
                                             _("Failed to connect to server"),
                                             err->message);
    }
759
    g_error_free (err);
760
761
    g_object_unref (app->client);
    app->client = NULL;
762
763
764
    /* make sure we validate again since we have a validation rule on
     * app->client == NULL */
    ovccgtk_connection_dialog_validate (OVCCGTK_CONNECTION_DIALOG (app->connection_dialog));
765
    gtk_action_set_sensitive (app->actions.game.connect, TRUE);
766
  } else {
767
768
    /* hide the dialog we come from on success */
    gtk_widget_hide (app->connection_dialog);
769
770
    gtk_action_set_sensitive (app->actions.game.new, FALSE);
    gtk_action_set_sensitive (app->actions.game.disconnect, TRUE);
771
    app_set_status (app, _("Connected!"));
772
    
773
774
775
776
777
    g_signal_connect (app->client, "place-tile",
                      G_CALLBACK (client_place_tile_handler), app);
    g_signal_connect (app->client, "place-pawn",
                      G_CALLBACK (client_place_pawn_handler), app);
    
778
779
780
    app->game = ovccclient_client_get_game (OVCCCLIENT_CLIENT (app->client));
    g_signal_connect (app->game, "notify::current-tile",
                      G_CALLBACK (on_current_tile_changed), app);
781
782
783
784
    g_signal_connect (app->game, "notify::current-player",
                      G_CALLBACK (on_current_player_changed), app);
    g_signal_connect (app->game, "notify::state",
                      G_CALLBACK (on_game_state_changed), app);
785
786
    g_signal_connect (app->game, "pawn-placed",
                      G_CALLBACK (game_pawn_placed_handler), app);
787
788
    g_signal_connect (app->game, "pawn-removed",
                      G_CALLBACK (game_pawn_removed_handler), app);
789
    
790
791
792
793
794
    ovccgtk_player_list_set_game (OVCCGTK_PLAYER_LIST (app->player_list),
                                  app->game);
    ovccgtk_player_list_set_default_player (OVCCGTK_PLAYER_LIST (app->player_list),
                                            ovccclient_client_get_player (OVCCCLIENT_CLIENT (app->client)));
    
795
796
797
    app->board = ovccgtk_board_new (ovcc_game_get_board (app->game));
    g_signal_connect (app->board, "add-tile",
                      G_CALLBACK (on_tile_add_request), app);
798
799
    g_signal_connect (app->board, "add-pawn",
                      G_CALLBACK (on_pawn_add_request), app);
800
801
802
803
804
    gtk_container_add (GTK_CONTAINER (app->board_container), app->board);
    gtk_widget_show (app->board);
    
    update_tile_image (app);
    
805
    gtk_action_set_sensitive (app->actions.game.start, TRUE);
806
807
808
  }
}

809
810
811
812
static void
connection_dialog_response_handler (GtkDialog  *dialog,
                                    gint        response,
                                    App        *app)
813
{
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
  OVCCGtkConnectionDialog *dlg = OVCCGTK_CONNECTION_DIALOG (dialog);
  
  switch (response) {
    case GTK_RESPONSE_ACCEPT:
    case GTK_RESPONSE_OK: {
      const gchar      *host;
      guint16           port;
      const gchar      *username;
      OVCCClientServer *server;
      
      host = ovccgtk_connection_dialog_get_hostname (dlg);
      port = ovccgtk_connection_dialog_get_port (dlg);
      username = ovccgtk_connection_dialog_get_username (dlg);
      
      /* FIXME! */
829
      gtk_action_set_sensitive (app->actions.game.connect, FALSE);
830
831
832
833
834
835
      gtk_dialog_set_response_sensitive (dialog, GTK_RESPONSE_OK, FALSE);
      ovccgtk_connection_dialog_set_message (dlg,
                                             GTK_MESSAGE_INFO,
                                             _("Connecting..."),
                                             NULL);
      
836
837
838
839
840
841
      if (app->connection_cancel) {
        g_warning ("connection cancellable isn't NULL, wtf?");
        g_object_unref (app->connection_cancel);
      }
      app->connection_cancel = g_cancellable_new ();
      
842
843
      server = ovccclient_server_new (host, port);
      app->client = ovccgtk_client_new (username);
844
845
      ovccgtk_client_join_async (app->client, server, app->connection_cancel,
                                 client_join_ready, app);
846
847
848
849
      g_object_unref (server);
    } break;
    
    default:
850
851
852
      if (app->connection_cancel) {
        g_cancellable_cancel (app->connection_cancel);
      }
853
      gtk_widget_hide (app->connection_dialog);
854
  }
855
856
857
}

static gboolean
858
859
connection_dialog_validate_handler (OVCCGtkConnectionDialog  *dialog,
                                    App                      *app)
860
{
861
862
863
864
865
866
867
868
869
  if (app->client != NULL) {
    ovccgtk_connection_dialog_set_message (dialog,
                                           GTK_MESSAGE_INFO,
                                           _("You are already connected to a server"),
                                           NULL);
    return FALSE;
  }
  
  return TRUE;
870
871
872
}

G_MODULE_EXPORT void
873
874
game_connect_activate_handler (GtkAction *action,
                               App       *app)
875
{
876
877
878
879
880
881
  OVCCGtkConnectionDialog *dialog = OVCCGTK_CONNECTION_DIALOG (app->connection_dialog);
  
  ovccgtk_connection_dialog_set_message (dialog,
                                         GTK_MESSAGE_INFO,
                                         _("Fill in the fields and hit Connect"),
                                         NULL);
882
  gtk_widget_show (app->connection_dialog);
883
  ovccgtk_connection_dialog_validate (dialog);
884
885
}

886
887
888
889
890
891
892
G_MODULE_EXPORT void
game_disconnect_activate_handler (GtkAction *action,
                                  App       *app)
{
  app_leave (app);
}

893
894
895
896
897
898
899
900
901
902
903
904
G_MODULE_EXPORT void
help_about_activate_handler (GtkAction *action,
                             App       *app)
{
  GtkWidget    *dialog;
  const gchar  *authors[] = {
    "Colomban Wendling <ban@herbesfolles.org>",
    NULL
  };
  
  dialog = g_object_new (GTK_TYPE_ABOUT_DIALOG,
                         "transient-for", app->main_window,
905
                         "logo-icon-name", "ovccgtk",
906
                         "program-name", _("OVCCGtk"),
907
908
                         "version", VERSION,
                         "comments", _("A GTK client for OVCC"),
909
910
                         "copyright", _("Copyright \xc2\xa9 2009-2013 Colomban Wendling"),
                         "license-type", GTK_LICENSE_GPL_3_0,
911
912
913
914
915
916
917
                         "translator-credits", _("translator-credits"),
                         "authors", authors,
                         NULL);
  gtk_dialog_run (GTK_DIALOG (dialog));
  gtk_widget_destroy (dialog);
}

918
919
920
921
922
923
static gboolean
ovcc_gtk (int     argc,
          char  **argv)
{
  GtkBuilder *builder;
  gboolean    success = FALSE;
924
925
926
927
928
  gchar      *icon_path;
  
  icon_path = data_get_path ("icons");
  gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (), icon_path);
  g_free (icon_path);
929
930
  
  builder = gtk_builder_new ();
931
  if (load_ui (builder, "ovccgtk-application.ui")) {
932
    GtkWidget  *players_cnt;
933
    GtkWidget  *board_cnt;
934
935
936
    App        *app;
    
    app = g_new0 (App, 1);
937
    
938
939
    gtk_builder_connect_signals (builder, app);
    app->main_window = GTK_WIDGET (gtk_builder_get_object (builder, "main-window"));
940
    app->statusbar = GTK_WIDGET (gtk_builder_get_object (builder, "main-statusbar"));
941
942
    board_cnt = GTK_WIDGET (gtk_builder_get_object (builder, "board-container"));
    players_cnt = GTK_WIDGET (gtk_builder_get_object (builder, "players-container"));
943
    app->current_tile_image = GTK_WIDGET (gtk_builder_get_object (builder, "current-tile-image"));
944
    app->tile_counter_label = GTK_WIDGET (gtk_builder_get_object (builder, "tiles-left-counter-label"));
945
946
947
948
    
    app->board_container = gtk_viewport_new (NULL, NULL);
    gtk_container_add (GTK_CONTAINER (board_cnt), app->board_container);
    
949
950
951
    app->player_list = ovccgtk_player_list_new ();
    gtk_container_add (GTK_CONTAINER (players_cnt), app->player_list);
    
952
953
    /* connection dialog */
    app->connection_dialog = ovccgtk_connection_dialog_new (GTK_WINDOW (app->main_window));
954
955
    g_signal_connect_after (app->connection_dialog, "response",
                            G_CALLBACK (connection_dialog_response_handler), app);
956
957
    g_signal_connect (app->connection_dialog, "validate",
                      G_CALLBACK (connection_dialog_validate_handler), app);
958
    
959
960
961
962
963
964
965
966
967
968
    /* Actions */
    #define GET_ACTION(b, n) GTK_ACTION (gtk_builder_get_object ((b), (n)))
    app->actions.app.quit = NULL;
    app->actions.app.preferences = GET_ACTION (builder, "edit-preferences");
    app->actions.game.new = GET_ACTION (builder, "game-new");
    app->actions.game.connect = GET_ACTION (builder, "game-connect");
    app->actions.game.disconnect = GET_ACTION (builder, "game-disconnect");
    app->actions.game.start = GET_ACTION (builder, "game-start");
    app->actions.game.rotate_tile_clockwise = GET_ACTION (builder, "tile-rotate-clockwise");
    app->actions.game.rotate_tile_counterclockwise = GET_ACTION (builder, "tile-rotate-counterclockwise");
969
    app->actions.game.finish_turn = GET_ACTION (builder, "game-finish-turn");
970
971
972
973
974
975
976
    app->actions.help.about = NULL;
    #undef GET_ACTION
    
    gtk_action_set_sensitive (app->actions.game.disconnect, FALSE);
    gtk_action_set_sensitive (app->actions.game.start, FALSE);
    gtk_action_set_sensitive (app->actions.game.rotate_tile_clockwise, FALSE);
    gtk_action_set_sensitive (app->actions.game.rotate_tile_counterclockwise, FALSE);
977
    gtk_action_set_sensitive (app->actions.game.finish_turn, FALSE);
978
    
979
980
    app_set_status (app, _("Welcome to OVCCGTK!"));
    
981
    gtk_widget_show_all (app->main_window);
982
983
    gtk_main ();
    
984
985
    g_free (app);
    
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
    success = TRUE;
  }
  
  return success;
}

int
main (int     argc,
      char  **argv)
{
  int rv = 1;
  
  gtk_init (&argc, &argv);
  rv = ovcc_gtk (argc, argv) ? 0 : 1;
  
  return rv;
}