1
/* ipuz-barred.c
2
 *
3
 * Copyright 2023 Jonathan Blandford <jrb@gnome.org>
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Lesser General Public
7
 * License as published by the Free Software Foundation; either
8
 * version 2.1 of the License, or (at your option) any later version.
9
 *
10
 * This library is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Lesser General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Lesser General Public
16
 * License along with this library; if not, write to the Free Software
17
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18
 *
19
 * SPDX-License-Identifier: (LGPL-2.1-or-later OR MIT)
20
 */
21

            
22

            
23
#include "libipuz-config.h"
24
#include "ipuz-private.h"
25
#include <libipuz/ipuz-barred.h>
26

            
27

            
28
#define IPUZ_STYLE_HAS_BARRED_TOP(style) (ipuz_style_get_barred(style)|IPUZ_STYLE_SIDES_TOP)
29
#define IPUZ_STYLE_HAS_BARRED_LEFT(style) (ipuz_style_get_barred(style)|IPUZ_STYLE_SIDES_LEFT)
30
#define IPUZ_STYLE_HAS_BARRED_TOP_LEFT(style) ((ipuz_style_get_barred(style)& \
31
                                                (IPUZ_STYLE_SIDES_TOP|IPUZ_STYLE_SIDES_LEFT)) == \
32
                                               (IPUZ_STYLE_SIDES_TOP|IPUZ_STYLE_SIDES_LEFT))
33

            
34

            
35
static void                ipuz_barred_init                 (IPuzBarred         *self);
36
static void                ipuz_barred_class_init           (IPuzBarredClass    *klass);
37
static void                ipuz_barred_fixup                (IPuzPuzzle         *puzzle);
38
static void                ipuz_barred_fix_styles           (IPuzCrossword      *xword);
39
static const gchar *const *ipuz_barred_get_kind_str         (IPuzPuzzle         *puzzle);
40
static gboolean            ipuz_barred_clue_continues_up    (IPuzCrossword      *xword,
41
                                                             IPuzCellCoord       coord);
42
static gboolean            ipuz_barred_clue_continues_down  (IPuzCrossword      *xword,
43
                                                             IPuzCellCoord       coord);
44
static gboolean            ipuz_barred_clue_continues_left  (IPuzCrossword      *xword,
45
                                                             IPuzCellCoord       coord);
46
static gboolean            ipuz_barred_clue_continues_right (IPuzCrossword      *xword,
47
                                                             IPuzCellCoord       coord);
48
static void                ipuz_barred_mirror_cell          (IPuzCrossword      *self,
49
                                                             IPuzCellCoord       src_coord,
50
                                                             IPuzCellCoord       dest_coord,
51
                                                             IPuzSymmetry        symmetry,
52
                                                             IPuzSymmetryOffset  symmetry_offset);
53
static gboolean            ipuz_barred_check_mirror         (IPuzCrossword      *xword,
54
                                                             IPuzCellCoord       src_coord,
55
                                                             IPuzCellCoord       target_coord,
56
                                                             IPuzSymmetry        symmetry,
57
                                                             IPuzSymmetryOffset  symmetry_offset);
58

            
59

            
60
1
G_DEFINE_TYPE (IPuzBarred, ipuz_barred, IPUZ_TYPE_CROSSWORD);
61

            
62

            
63
static void
64
3
ipuz_barred_init (IPuzBarred *self)
65
{
66
  /* Pass */
67
3
}
68

            
69
static void
70
1
ipuz_barred_class_init (IPuzBarredClass *klass)
71
{
72
1
  IPuzPuzzleClass *puzzle_class = IPUZ_PUZZLE_CLASS (klass);
73
1
  IPuzCrosswordClass *crossword_class = IPUZ_CROSSWORD_CLASS (klass);
74

            
75
1
  puzzle_class->fixup = ipuz_barred_fixup;
76
1
  puzzle_class->get_kind_str = ipuz_barred_get_kind_str;
77
1
  crossword_class->fix_styles = ipuz_barred_fix_styles;
78
1
  crossword_class->clue_continues_up = ipuz_barred_clue_continues_up;
79
1
  crossword_class->clue_continues_down = ipuz_barred_clue_continues_down;
80
1
  crossword_class->clue_continues_left = ipuz_barred_clue_continues_left;
81
1
  crossword_class->clue_continues_right = ipuz_barred_clue_continues_right;
82
1
  crossword_class->mirror_cell = ipuz_barred_mirror_cell;
83
1
  crossword_class->check_mirror = ipuz_barred_check_mirror;
84
1
}
85

            
86
typedef struct
87
{
88
  gboolean t_count;
89
  gboolean l_count;
90
  gboolean tl_count;
91
} StyleFixupTuple;
92

            
93
static void
94
18
fixup_style_foreach (const gchar *style_name,
95
                     IPuzStyle   *style,
96
                     gpointer     user_data)
97
{
98
18
  StyleFixupTuple *fixup_tuple = user_data;
99

            
100
18
  if (!g_strcmp0 (style_name, IPUZ_BARRED_STYLE_TL) &&
101
4
      (IPUZ_STYLE_HAS_BARRED_TOP_LEFT (style)))
102
4
    fixup_tuple->tl_count = TRUE;
103
14
  else if (!g_strcmp0 (style_name, IPUZ_BARRED_STYLE_T) &&
104
4
           (IPUZ_STYLE_HAS_BARRED_TOP (style)))
105
4
    fixup_tuple->t_count = TRUE;
106
10
  else if (!g_strcmp0 (style_name, IPUZ_BARRED_STYLE_L) &&
107
4
           (IPUZ_STYLE_HAS_BARRED_LEFT (style)))
108
4
    fixup_tuple->l_count = TRUE;
109
18
}
110

            
111
static void
112
5
ensure_styles (IPuzBarred *barred)
113
{
114
5
  StyleFixupTuple fixup_tuple = {0, };
115

            
116
5
  ipuz_puzzle_style_foreach (IPUZ_PUZZLE (barred), fixup_style_foreach, &fixup_tuple);
117

            
118
5
  if (! fixup_tuple.tl_count)
119
    {
120
1
      g_autoptr (IPuzStyle) style = NULL;
121

            
122
1
      style = ipuz_style_new ();
123
1
      ipuz_style_set_style_name (style, IPUZ_BARRED_STYLE_TL);
124
1
      ipuz_style_set_barred (style, IPUZ_STYLE_SIDES_TOP | IPUZ_STYLE_SIDES_LEFT);
125
1
      ipuz_puzzle_set_style (IPUZ_PUZZLE (barred), IPUZ_BARRED_STYLE_TL, style);
126
    }
127
5
  if (! fixup_tuple.t_count)
128
    {
129
1
      g_autoptr (IPuzStyle) style = NULL;
130

            
131
1
      style = ipuz_style_new ();
132
1
      ipuz_style_set_style_name (style, IPUZ_BARRED_STYLE_T);
133
1
      ipuz_style_set_barred (style, IPUZ_STYLE_SIDES_TOP);
134
1
      ipuz_puzzle_set_style (IPUZ_PUZZLE (barred), IPUZ_BARRED_STYLE_T, style);
135
    }
136
5
  if (! fixup_tuple.l_count)
137
    {
138
1
      g_autoptr (IPuzStyle) style = NULL;
139

            
140
1
      style = ipuz_style_new ();
141
1
      ipuz_style_set_style_name (style, IPUZ_BARRED_STYLE_L);
142
1
      ipuz_style_set_barred (style, IPUZ_STYLE_SIDES_LEFT);
143
1
      ipuz_puzzle_set_style (IPUZ_PUZZLE (barred), IPUZ_BARRED_STYLE_L, style);
144
    }
145
5
}
146

            
147
static void
148
3
ipuz_barred_fixup (IPuzPuzzle *puzzle)
149
{
150
3
  IPUZ_PUZZLE_CLASS (ipuz_barred_parent_class)->fixup (puzzle);
151

            
152
3
  ensure_styles (IPUZ_BARRED (puzzle));
153
3
}
154

            
155
static const gchar *const *
156
ipuz_barred_get_kind_str (IPuzPuzzle *puzzle)
157
{
158
  static const char *kind_str[] =
159
    {
160
      "http://ipuz.org/crossword#1",
161
      "http://libipuz.org/barred#1",
162
      NULL
163
    };
164

            
165
  return kind_str;
166
}
167

            
168
static gboolean
169
144
ipuz_barred_clue_continues_up (IPuzCrossword *xword,
170
                               IPuzCellCoord  coord)
171
{
172
  IPuzStyleSides sides;
173

            
174
144
  sides = ipuz_barred_get_cell_bars (IPUZ_BARRED (xword), coord);
175

            
176
144
  if (coord.row == 0)
177
12
    return FALSE;
178

            
179
132
  if (IPUZ_STYLE_SIDES_HAS_TOP (sides))
180
38
    return FALSE;
181

            
182
94
  return IPUZ_CROSSWORD_CLASS (ipuz_barred_parent_class)->clue_continues_up (xword, coord);
183
}
184

            
185
static gboolean
186
174
ipuz_barred_clue_continues_down (IPuzCrossword *xword,
187
                                 IPuzCellCoord  coord)
188
{
189
  IPuzStyleSides sides;
190
  guint height;
191

            
192
174
  sides = ipuz_barred_get_cell_bars (IPUZ_BARRED (xword), coord);
193
174
  height = ipuz_crossword_get_height (xword);
194

            
195
174
  if (coord.row == height - 1)
196
12
    return FALSE;
197

            
198
162
  if (IPUZ_STYLE_SIDES_HAS_BOTTOM (sides))
199
42
    return FALSE;
200

            
201
120
  return IPUZ_CROSSWORD_CLASS (ipuz_barred_parent_class)->clue_continues_down (xword, coord);
202
}
203

            
204
static gboolean
205
118
ipuz_barred_clue_continues_left (IPuzCrossword *xword,
206
                                 IPuzCellCoord  coord)
207
{
208
  IPuzStyleSides sides;
209

            
210
118
  sides = ipuz_barred_get_cell_bars (IPUZ_BARRED (xword), coord);
211

            
212
118
  if (coord.column == 0)
213
10
    return FALSE;
214

            
215
108
  if (IPUZ_STYLE_SIDES_HAS_LEFT (sides))
216
33
    return FALSE;
217

            
218
75
  return IPUZ_CROSSWORD_CLASS (ipuz_barred_parent_class)->clue_continues_left (xword, coord);
219
}
220

            
221
static gboolean
222
167
ipuz_barred_clue_continues_right (IPuzCrossword *xword,
223
                                  IPuzCellCoord  coord)
224
{
225
  IPuzStyleSides sides;
226
  guint width;
227

            
228
167
  sides = ipuz_barred_get_cell_bars (IPUZ_BARRED (xword), coord);
229
167
  width = ipuz_crossword_get_width (xword);
230

            
231
167
  if (coord.column == width - 1)
232
12
    return FALSE;
233

            
234
155
  if (IPUZ_STYLE_SIDES_HAS_RIGHT (sides))
235
38
    return FALSE;
236

            
237
117
  return IPUZ_CROSSWORD_CLASS (ipuz_barred_parent_class)->clue_continues_right (xword, coord);
238
}
239

            
240

            
241
/* This dense function will take sides, and rotate it according the to
242
   the symmetry / offset. Be careful touching it. */
243
static IPuzStyleSides
244
460
mirror_sides (IPuzStyleSides     sides,
245
              IPuzSymmetry       symmetry,
246
              IPuzSymmetryOffset symmetry_offset)
247
{
248
460
  switch (symmetry)
249
    {
250
    case IPUZ_SYMMETRY_NONE:
251
      return sides;
252
147
    case IPUZ_SYMMETRY_ROTATIONAL_HALF:
253
147
      return ipuz_style_sides_rotate_180 (sides);
254
301
    case IPUZ_SYMMETRY_ROTATIONAL_QUARTER:
255
301
      if (symmetry_offset == IPUZ_SYMMETRY_OFFSET_OPPOSITE)
256
3
        return ipuz_style_sides_rotate_180 (sides);
257
298
      else if (symmetry_offset == IPUZ_SYMMETRY_OFFSET_CW_ADJACENT)
258
149
        return ipuz_style_sides_rotate_rt (sides);
259
149
      else if (symmetry_offset == IPUZ_SYMMETRY_OFFSET_CCW_ADJACENT)
260
149
        return ipuz_style_sides_rotate_lt (sides);
261
      else
262
        g_assert_not_reached ();
263
      break;
264
4
    case IPUZ_SYMMETRY_HORIZONTAL:
265
4
      return ipuz_style_sides_flip_horiz (sides);
266
      break;
267
4
    case IPUZ_SYMMETRY_VERTICAL:
268
4
      return ipuz_style_sides_flip_vert (sides);
269
4
    case IPUZ_SYMMETRY_MIRRORED:
270
4
      if (symmetry_offset == IPUZ_SYMMETRY_OFFSET_OPPOSITE)
271
        return ipuz_style_sides_flip_vert (ipuz_style_sides_flip_horiz (sides));
272
4
      else if (symmetry_offset == IPUZ_SYMMETRY_OFFSET_CW_ADJACENT)
273
2
        return ipuz_style_sides_flip_horiz (sides);
274
2
      else if (symmetry_offset == IPUZ_SYMMETRY_OFFSET_CCW_ADJACENT)
275
2
        return ipuz_style_sides_flip_vert (sides);
276
      else
277
        g_assert_not_reached ();
278
      break;
279
    default:
280
      g_assert_not_reached ();
281
    }
282
}
283

            
284

            
285
static void
286
9
ipuz_barred_mirror_cell (IPuzCrossword      *xword,
287
                         IPuzCellCoord       src_coord,
288
                         IPuzCellCoord       dest_coord,
289
                         IPuzSymmetry        symmetry,
290
                         IPuzSymmetryOffset  symmetry_offset)
291
{
292
  IPuzStyleSides sides;
293
  IPuzStyleSides new_sides;
294

            
295
  /* Chain up to get the cell type correct */
296
9
  IPUZ_CROSSWORD_CLASS (ipuz_barred_parent_class)->mirror_cell (xword, src_coord, dest_coord, symmetry, symmetry_offset);
297

            
298
9
  sides = ipuz_barred_get_cell_bars (IPUZ_BARRED (xword), src_coord);
299
9
  new_sides = mirror_sides (sides, symmetry, symmetry_offset);
300

            
301
9
  ipuz_barred_set_cell_bars (IPUZ_BARRED (xword), dest_coord, new_sides);
302
9
}
303

            
304
static gboolean
305
451
ipuz_barred_check_mirror (IPuzCrossword      *xword,
306
                          IPuzCellCoord       src_coord,
307
                          IPuzCellCoord       target_coord,
308
                          IPuzSymmetry        symmetry,
309
                          IPuzSymmetryOffset  symmetry_offset)
310
{
311
  IPuzStyleSides src_sides, target_sides;
312

            
313
451
  if (!IPUZ_CROSSWORD_CLASS (ipuz_barred_parent_class)->check_mirror (xword, src_coord, target_coord, symmetry, symmetry_offset))
314
    return FALSE;
315

            
316
451
  src_sides = ipuz_barred_get_cell_bars (IPUZ_BARRED (xword), src_coord);
317
451
  target_sides = ipuz_barred_get_cell_bars (IPUZ_BARRED (xword), target_coord);
318

            
319
451
  return (target_sides == mirror_sides (src_sides, symmetry, symmetry_offset));
320
}
321

            
322
/**
323
 * Public Methods
324
 */
325

            
326
IPuzPuzzle *
327
ipuz_barred_new (void)
328
{
329
  IPuzPuzzle *barred;
330

            
331
  barred = g_object_new (IPUZ_TYPE_BARRED,
332
                         NULL);
333

            
334
  return barred;
335
}
336

            
337
/* Get the adjacent cell */
338
static IPuzCell *
339
6628
get_adjacent_cell (IPuzBarred    *self,
340
                   IPuzCellCoord  coord,
341
                   gint           row_offset,
342
                   gint           col_offset)
343
{
344
6628
  if (coord.row == 0 && row_offset < 0)
345
149
    return NULL;
346
6479
  if (coord.column == 0 && col_offset < 0)
347
145
    return NULL;
348

            
349
6334
  coord.row += row_offset;
350
6334
  coord.column += col_offset;
351

            
352
6334
  return ipuz_crossword_get_cell (IPUZ_CROSSWORD (self), coord);
353
}
354

            
355
static IPuzStyleSides
356
6628
check_adjacent_cell_bars (IPuzBarred     *self,
357
                          IPuzCellCoord   coord,
358
                          gint            row_offset,
359
                          gint            col_offset,
360
                          IPuzStyleSides  side_to_check)
361
{
362
  IPuzCell *cell;
363
  IPuzStyle *style;
364
  IPuzStyleSides barred;
365

            
366
6628
  cell = get_adjacent_cell (self, coord, row_offset, col_offset);
367
6628
  if (cell == NULL)
368
573
    return 0;
369

            
370
6055
  style = ipuz_cell_get_style (cell);
371
6055
  if (style == NULL)
372
3068
    return 0;
373

            
374
2987
  barred = ipuz_style_get_barred (style);
375
2987
  if (barred & side_to_check)
376
868
    return ipuz_style_side_opposite (side_to_check);
377

            
378
2119
  return 0;
379
}
380

            
381

            
382
static IPuzStyleSides
383
match_side (IPuzStyleSides barred_sides,
384
            IPuzStyleSides side,
385
            IPuzSymmetry   symmetry)
386
{
387
  IPuzStyleSides opposite = ipuz_style_side_opposite (side);
388
  if (barred_sides & side)
389
    {
390
      if (!(barred_sides & opposite))
391
        barred_sides ^= opposite;
392
    }
393
  else
394
    {
395
      if (barred_sides & opposite)
396
        barred_sides ^= opposite;
397
    }
398
  return barred_sides;
399
}
400

            
401

            
402
/**
403
 * ipuz_barred_calculate_side_toggle:
404
 * @self: An IPuzBarred
405
 * @coord: The cell to be toggled
406
 * @side: The side to be toggled. Must not be a bitfield
407
 * @symmetry: The symmetry to respect
408
 *
409
 * Calculate the side of a cell after toggling one of its sides while
410
 * taking symmetry into account. For most of the cells on the board
411
 * this just toggles the side requested, but the center lines and
412
 * center square need to be handled separately.
413
 *
414
 * Returns:
415
 **/
416
IPuzStyleSides
417
ipuz_barred_calculate_side_toggle (IPuzBarred     *self,
418
                                   IPuzCellCoord   coord,
419
                                   IPuzStyleSides  side,
420
                                   IPuzSymmetry    symmetry)
421
{
422
  guint width, height;
423
  IPuzStyleSides barred_sides;
424

            
425
  g_return_val_if_fail (IPUZ_IS_BARRED (self), 0);
426
  /* assert that we only have a single side we're toggling */
427
  g_return_val_if_fail ((side == IPUZ_STYLE_SIDES_LEFT ||
428
                         side == IPUZ_STYLE_SIDES_RIGHT ||
429
                         side == IPUZ_STYLE_SIDES_TOP ||
430
                         side == IPUZ_STYLE_SIDES_BOTTOM), 0);
431

            
432
  barred_sides = ipuz_barred_get_cell_bars (self, coord);
433
  width = ipuz_crossword_get_width (IPUZ_CROSSWORD (self));
434
  height = ipuz_crossword_get_height (IPUZ_CROSSWORD (self));
435

            
436
  barred_sides ^= side;
437

            
438
  /* Now, check the symmetry of the three areas of interest */
439

            
440
  /* Vertical center line.
441
   *
442
   * We update things iff we are mirrored/horizontal and if we're
443
   * adjusting a left or right side.
444
   */
445
  if ((width % 2 != 0) && (coord.column == width / 2) &&
446
      (symmetry == IPUZ_SYMMETRY_HORIZONTAL || symmetry == IPUZ_SYMMETRY_MIRRORED) &&
447
      (side & (IPUZ_STYLE_SIDES_LEFT | IPUZ_STYLE_SIDES_RIGHT)))
448
    {
449
      barred_sides = match_side (barred_sides, side, symmetry);
450
    }
451

            
452
  /* Horizontal center line.
453
   *
454
   * We update things iff we are mirrored/vertical and if we're
455
   * adjusting a top or bottom side.
456
   */
457
  if ((height % 2 != 0) && (coord.row == height / 2) &&
458
      (symmetry == IPUZ_SYMMETRY_VERTICAL || symmetry == IPUZ_SYMMETRY_MIRRORED) &&
459
      (side & (IPUZ_STYLE_SIDES_TOP | IPUZ_STYLE_SIDES_BOTTOM)))
460
    {
461
      barred_sides = match_side (barred_sides, side, symmetry);
462
    }
463

            
464
  /* Center box
465
   *
466
   * Handled as a special case for QUARTER
467
   */
468
  if ((width % 2 != 0) && (coord.column == width / 2) &&
469
      (height % 2 != 0) && (coord.row == height / 2) &&
470
      (symmetry == IPUZ_SYMMETRY_ROTATIONAL_QUARTER))
471
    {
472
      if (side & barred_sides)
473
        barred_sides = (IPUZ_STYLE_SIDES_TOP_LEFT | IPUZ_STYLE_SIDES_BOTTOM_RIGHT);
474
      else
475
        barred_sides = 0;
476
    }
477

            
478
  return barred_sides;
479
}
480

            
481

            
482
IPuzStyleSides
483
1657
ipuz_barred_get_cell_bars (IPuzBarred     *self,
484
                           IPuzCellCoord   coord)
485
{
486
1657
  IPuzStyleSides barred_sides = 0;
487
  IPuzCell *cell;
488
  IPuzStyle *style;
489

            
490
1657
  g_return_val_if_fail (IPUZ_IS_BARRED (self), 0);
491

            
492
1657
  cell = ipuz_crossword_get_cell (IPUZ_CROSSWORD (self), coord);
493
  /* coords needs to be within the board */
494
1657
  g_return_val_if_fail (cell != NULL, 0);
495

            
496
1657
  style = ipuz_cell_get_style (cell);
497
1657
  if (style)
498
812
    barred_sides = ipuz_style_get_barred (style);
499

            
500
1657
  barred_sides |= check_adjacent_cell_bars (self, coord, 0, 1, IPUZ_STYLE_SIDES_LEFT);
501
1657
  barred_sides |= check_adjacent_cell_bars (self, coord, 0, -1, IPUZ_STYLE_SIDES_RIGHT);
502
1657
  barred_sides |= check_adjacent_cell_bars (self, coord, 1, 0, IPUZ_STYLE_SIDES_TOP);
503
1657
  barred_sides |= check_adjacent_cell_bars (self, coord, -1, 0, IPUZ_STYLE_SIDES_BOTTOM);
504

            
505
1657
  return barred_sides;
506
}
507

            
508

            
509
static void
510
23
update_cell_style (IPuzCell       *cell,
511
                   IPuzStyleSides  sides,
512
                   IPuzStyle      *barred_style,
513
                   const gchar    *style_name)
514
{
515
  IPuzStyle *style;
516

            
517
23
  style = ipuz_cell_get_style (cell);
518

            
519
23
  if (style)
520
    {
521
4
      const gchar *style_name = ipuz_style_get_style_name (style);
522

            
523
5
      if (! g_strcmp0 (style_name, IPUZ_BARRED_STYLE_T) ||
524
1
          ! g_strcmp0 (style_name, IPUZ_BARRED_STYLE_L) ||
525
          ! g_strcmp0 (style_name, IPUZ_BARRED_STYLE_TL))
526
4
        ipuz_cell_set_style (cell, barred_style, style_name);
527
      /* FIXME(style-name): This misses the case where the puzzle has
528
       * a named style that's not one of the internal ones. In that
529
       * case, it will adjust the shared style to have the new sides,
530
       * which is almost certainly not what's wanted */
531
      else
532
        ipuz_style_set_barred (style, sides);
533

            
534
    }
535
  else
536
    {
537
19
      ipuz_cell_set_style (cell, barred_style, style_name);
538
    }
539
23
}
540

            
541
/* This expects ipuz_barred_fix_styles() to have been called.
542
 * Returns TRUE if a cell_bar is actually changed.
543
 */
544
gboolean
545
31
ipuz_barred_set_cell_bars (IPuzBarred     *self,
546
                           IPuzCellCoord   coord,
547
                           IPuzStyleSides  sides)
548
{
549
  guint width, height;
550
  IPuzStyle *t_style, *l_style, *tl_style;
551
  IPuzCell *cell;
552
31
  IPuzStyleSides old_sides = 0;
553

            
554
31
  g_return_val_if_fail (IPUZ_IS_BARRED (self), FALSE);
555

            
556
31
  width = ipuz_crossword_get_width (IPUZ_CROSSWORD (self));
557
31
  height = ipuz_crossword_get_height (IPUZ_CROSSWORD (self));
558

            
559
31
  t_style = ipuz_puzzle_get_style (IPUZ_PUZZLE (self), IPUZ_BARRED_STYLE_T);
560
31
  l_style = ipuz_puzzle_get_style (IPUZ_PUZZLE (self), IPUZ_BARRED_STYLE_L);
561
31
  tl_style = ipuz_puzzle_get_style (IPUZ_PUZZLE (self), IPUZ_BARRED_STYLE_TL);
562
31
  g_return_val_if_fail (t_style != NULL && l_style != NULL && tl_style != NULL, FALSE);
563

            
564
31
  cell = ipuz_crossword_get_cell (IPUZ_CROSSWORD (self), coord);
565
31
  if (cell == NULL)
566
    return FALSE;
567

            
568
31
  old_sides = ipuz_barred_get_cell_bars (self, coord);
569
31
  if (sides == old_sides)
570
    return FALSE;
571

            
572
  /* Set the right style for us */
573
31
  if (coord.column == 0 && IPUZ_STYLE_SIDES_HAS_LEFT (sides))
574
    sides ^= IPUZ_STYLE_SIDES_LEFT;
575

            
576
31
  if (coord.row == 0 && IPUZ_STYLE_SIDES_HAS_TOP (sides))
577
    sides ^= IPUZ_STYLE_SIDES_TOP;
578

            
579
31
  if (IPUZ_STYLE_SIDES_HAS_TOP (sides) && IPUZ_STYLE_SIDES_HAS_LEFT (sides))
580
3
    update_cell_style (cell, sides, tl_style, IPUZ_BARRED_STYLE_TL);
581
28
  else if (IPUZ_STYLE_SIDES_HAS_TOP (sides))
582
11
    update_cell_style (cell, sides, t_style, IPUZ_BARRED_STYLE_T);
583
17
  else if (IPUZ_STYLE_SIDES_HAS_LEFT (sides))
584
9
    update_cell_style (cell, sides, l_style, IPUZ_BARRED_STYLE_L);
585
  else
586
8
    ipuz_cell_set_style (cell, NULL, NULL);
587

            
588
31
  if (((sides ^ old_sides) & IPUZ_STYLE_SIDES_RIGHT) &&
589
9
      (coord.column + 1 < width))
590
    {
591
9
      IPuzCellCoord right_coord = coord;
592
      IPuzStyleSides right_sides;
593

            
594
9
      right_coord.column++;
595
9
      right_sides = ipuz_barred_get_cell_bars (self, right_coord);
596
9
      right_sides = right_sides ^ IPUZ_STYLE_SIDES_LEFT;
597
9
      ipuz_barred_set_cell_bars (self, right_coord, right_sides);
598
    }
599

            
600
31
  if (((sides ^ old_sides) & IPUZ_STYLE_SIDES_BOTTOM) &&
601
10
      (coord.row + 1 < height))
602
    {
603
10
      IPuzCellCoord bottom_coord = coord;
604
      IPuzStyleSides bottom_sides;
605

            
606
10
      bottom_coord.row++;
607
10
      bottom_sides = ipuz_barred_get_cell_bars (self, bottom_coord);
608
10
      bottom_sides = bottom_sides ^ IPUZ_STYLE_SIDES_TOP;
609
10
      ipuz_barred_set_cell_bars (self, bottom_coord, bottom_sides);
610
    }
611

            
612
31
  return (old_sides != ipuz_barred_get_cell_bars (self, coord));
613
}
614

            
615
static void
616
2
ipuz_barred_fix_styles (IPuzCrossword *xword)
617
{
618
  IPuzStyle *t_style, *l_style, *tl_style;
619
  IPuzCellCoord coord;
620
  IPuzBarred *self;
621

            
622
2
  g_return_if_fail (IPUZ_IS_BARRED (xword));
623
2
  self = IPUZ_BARRED (xword);
624

            
625
  /* Chain up to remove any empty styles */
626
2
  IPUZ_CROSSWORD_CLASS (ipuz_barred_parent_class)->fix_styles (xword);
627

            
628
  /* Make sure the "t", "l", and "tl" styles exist */
629
2
  ensure_styles (self);
630

            
631
2
  coord.column = ipuz_crossword_get_width (IPUZ_CROSSWORD (self));
632
2
  coord.row = ipuz_crossword_get_height (IPUZ_CROSSWORD (self));
633

            
634
  /* Too small */
635
2
  if (coord.column == 0 || coord.row == 0)
636
    return;
637

            
638
2
  t_style = ipuz_puzzle_get_style (IPUZ_PUZZLE (self), IPUZ_BARRED_STYLE_T);
639
2
  l_style = ipuz_puzzle_get_style (IPUZ_PUZZLE (self), IPUZ_BARRED_STYLE_L);
640
2
  tl_style = ipuz_puzzle_get_style (IPUZ_PUZZLE (self), IPUZ_BARRED_STYLE_TL);
641
2
  g_return_if_fail (t_style != NULL && l_style != NULL && tl_style != NULL);
642

            
643
  /* This loop is rough. It's a roundabout way of going from the lower
644
   * right to the upper-left with guints.
645
   */
646
  do
647
    {
648
24
      coord.row --;
649
24
      coord.column = ipuz_crossword_get_width (IPUZ_CROSSWORD (self));
650
      do
651
        {
652
          IPuzCell *cell;
653
          IPuzStyleSides sides;
654
          IPuzStyle *style;
655
288
          coord.column--;
656

            
657
288
          cell = ipuz_crossword_get_cell (IPUZ_CROSSWORD (self), coord);
658
288
          style = ipuz_cell_get_style (cell);
659

            
660
288
          if (style == NULL)
661
163
            continue;
662
125
          if (style == t_style || style == l_style || style == tl_style)
663
68
            continue;
664

            
665
57
          sides = ipuz_barred_get_cell_bars (self, coord);
666
57
          if (_ipuz_style_is_empty_except_bars (style))
667
            {
668
              /* We have a style that's not one of the three internal
669
               * styles but just has bars. we swap it out with our
670
               * internal style */
671
              if ((sides & IPUZ_STYLE_SIDES_TOP_LEFT) == IPUZ_STYLE_SIDES_TOP_LEFT)
672
                ipuz_cell_set_style (cell, tl_style, IPUZ_BARRED_STYLE_TL);
673
              else if (sides & IPUZ_STYLE_SIDES_TOP)
674
                ipuz_cell_set_style (cell, t_style, IPUZ_BARRED_STYLE_T);
675
              else if (sides & IPUZ_STYLE_SIDES_LEFT)
676
                ipuz_cell_set_style (cell, l_style, IPUZ_BARRED_STYLE_L);
677
              else
678
                ipuz_cell_set_style (cell, NULL, NULL);
679
            }
680
          else
681
            {
682
              /* We leave the style mostly alone. The one change we
683
               * want to make is to clear the bottom and right style
684
               * borders if they've been set for some reason.*/
685
57
              ipuz_style_set_barred (style, sides & IPUZ_STYLE_SIDES_TOP_LEFT);
686
            }
687
        }
688
288
      while (coord.column > 0);
689
    }
690
24
  while (coord.row > 0);
691
}