1
/* ipuz-style.c
2
 *
3
 * Copyright 2022 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
#include "libipuz-config.h"
23
#include "ipuz-style.h"
24
#include <glib/gi18n-lib.h>
25

            
26

            
27
struct _IPuzStyle
28
{
29
  grefcount ref_count;
30
  gchar *style_name; /* Internal name of the style, for shared styles */
31

            
32
  IPuzStyleShape shapebg;
33
  gboolean highlight;
34
  gchar *named;
35
  gint border;
36
  IPuzStyleDivided divided;
37
  gchar *label;
38
  GHashTable *mark; /* Hashtable of strings indexed by IPUZ_STYLE_MARK_ */
39
  gchar *image_url;
40
  gchar *imagebg_url;
41
  gchar *bg_color;
42
  gchar *text_color;
43
  gchar *border_color;
44
  IPuzStyleSides barred;
45
  IPuzStyleSides dotted;
46
  IPuzStyleSides dashed;
47
  IPuzStyleSides lessthan;
48
  IPuzStyleSides greaterthan;
49
  IPuzStyleSides equal;
50
};
51

            
52

            
53
G_DEFINE_BOXED_TYPE (IPuzStyle, ipuz_style, ipuz_style_ref, ipuz_style_unref);
54

            
55

            
56
static IPuzStyleShape  parse_shapebg         (const char     *shapebg);
57
static IPuzStyleSides  parse_sides           (const char     *val);
58
static char           *sides_to_str          (IPuzStyleSides  sides);
59
static const char     *shapebg_to_str        (IPuzStyleShape  shapebg);
60
static char           *parse_color           (JsonNode       *node);
61
static void            ipuz_style_parse_mark (IPuzStyle      *style,
62
                                              JsonNode       *node);
63

            
64
IPuzStyle *
65
265
ipuz_style_new (void)
66
{
67
  IPuzStyle *style;
68

            
69
265
  style = (IPuzStyle *) g_new0 (IPuzStyle, 1);
70
265
  g_ref_count_init (&style->ref_count);
71

            
72
265
  return style;
73
}
74

            
75
static IPuzStyleSides
76
1416
sides_from_json (JsonObject *obj, const char *prop_name)
77
{
78
1416
  JsonNode *element = json_object_get_member (obj, prop_name);
79
1416
  if (element)
80
    {
81
17
      const gchar *val = json_node_get_string (element);
82
17
      return parse_sides (val);
83
    }
84
  else
85
    {
86
1399
      return 0;
87
    }
88
}
89

            
90
IPuzStyle *
91
236
ipuz_style_new_from_json (JsonNode *node)
92
{
93
  IPuzStyle *style;
94

            
95
236
  g_return_val_if_fail (node != NULL, NULL);
96

            
97
236
  style = ipuz_style_new ();
98

            
99
236
  if (JSON_NODE_HOLDS_OBJECT (node))
100
    {
101
      JsonObject *obj;
102
      JsonNode *element;
103

            
104
236
      obj = json_node_get_object (node);
105

            
106
236
      element = json_object_get_member (obj, "shapebg");
107
236
      if (element)
108
        {
109
17
          const gchar *shapebg = json_node_get_string (element);
110
17
          style->shapebg = parse_shapebg (shapebg);
111
        }
112

            
113
236
      element = json_object_get_member (obj, "highlight");
114
236
      if (element)
115
1
        style->highlight = json_node_get_boolean (element);
116

            
117
236
      element = json_object_get_member (obj, "named");
118
236
      if (element)
119
1
        style->named = g_strdup (json_node_get_string (element));
120

            
121
236
      element = json_object_get_member (obj, "border");
122
236
      if (element)
123
1
        style->border = json_node_get_int (element);
124

            
125
236
      element = json_object_get_member (obj, "divided");
126
236
      if (element)
127
        {
128
1
          const gchar *divided = json_node_get_string (element);
129

            
130
1
          if (g_strcmp0 (divided, "-") == 0)
131
1
            style->divided = IPUZ_STYLE_DIVIDED_HORIZ;
132
          else if (g_strcmp0 (divided, "|") == 0)
133
            style->divided = IPUZ_STYLE_DIVIDED_VERT;
134
          else if (g_strcmp0 (divided, "/") == 0)
135
            style->divided = IPUZ_STYLE_DIVIDED_UP_RIGHT;
136
          else if (g_strcmp0 (divided, "\\") == 0)
137
            style->divided = IPUZ_STYLE_DIVIDED_UP_LEFT;
138
          else if (g_strcmp0 (divided, "+") == 0)
139
            style->divided = IPUZ_STYLE_DIVIDED_PLUS;
140
          else if (g_strcmp0 (divided, "x") == 0)
141
            style->divided = IPUZ_STYLE_DIVIDED_CROSS;
142
          else
143
            style->divided = IPUZ_STYLE_DIVIDED_NONE;
144
        }
145

            
146
236
      element = json_object_get_member (obj, "label");
147
236
      if (element)
148
1
        style->label = g_strdup (json_node_get_string (element));
149

            
150
236
      element = json_object_get_member (obj, "mark");
151
236
      if (element)
152
206
        ipuz_style_parse_mark (style, element);
153

            
154
236
      element = json_object_get_member (obj, "image");
155
236
      if (element)
156
1
        style->image_url = g_strdup (json_node_get_string (element));
157

            
158
236
      element = json_object_get_member (obj, "imagebg");
159
236
      if (element)
160
1
        style->imagebg_url = g_strdup (json_node_get_string (element));
161

            
162
236
      element = json_object_get_member (obj, "color");
163
236
      if (element)
164
4
        style->bg_color = parse_color (element);
165

            
166
236
      element = json_object_get_member (obj, "colortext");
167
236
      if (element)
168
1
        style->text_color = parse_color (element);
169

            
170
236
      element = json_object_get_member (obj, "colorborder");
171
236
      if (element)
172
1
        style->border_color = parse_color (element);
173

            
174
236
      style->barred      = sides_from_json (obj, "barred");
175
236
      style->dotted      = sides_from_json (obj, "dotted");
176
236
      style->dashed      = sides_from_json (obj, "dashed");
177
236
      style->lessthan    = sides_from_json (obj, "lessthan");
178
236
      style->greaterthan = sides_from_json (obj, "greaterthan");
179
236
      style->equal       = sides_from_json (obj, "equal");
180
    }
181

            
182
236
  return style;
183
}
184

            
185
IPuzStyle *
186
269
ipuz_style_ref (IPuzStyle *style)
187
{
188
269
  g_return_val_if_fail (style != NULL, NULL);
189

            
190
269
  g_ref_count_inc (&style->ref_count);
191

            
192
269
  return style;
193
}
194

            
195
void
196
534
ipuz_style_unref (IPuzStyle *style)
197
{
198
534
  if (style == NULL)
199
    return;
200

            
201
534
  if (!g_ref_count_dec (&style->ref_count))
202
269
    return;
203

            
204
  /* Free */
205
265
  if (style->mark)
206
232
    g_hash_table_unref (style->mark);
207

            
208
265
  g_free (style->style_name);
209
265
  g_free (style->named);
210
265
  g_free (style->label);
211
265
  g_free (style->image_url);
212
265
  g_free (style->imagebg_url);
213
265
  g_free (style->bg_color);
214
265
  g_free (style->text_color);
215
265
  g_free (style->border_color);
216

            
217
265
  g_free (style);
218
}
219

            
220
gboolean
221
ipuz_style_equal (IPuzStyle *a,
222
                  IPuzStyle *b)
223
{
224
  if (a->mark)
225
    {
226
      GHashTableIter iter;
227
      gpointer key, value;
228

            
229
      if (b->mark == NULL)
230
        return FALSE;
231

            
232
      g_hash_table_iter_init (&iter, a->mark);
233

            
234
      while (g_hash_table_iter_next (&iter, &key, &value))
235
        {
236
          if (g_strcmp0 (g_hash_table_lookup (b->mark, key), value) != 0)
237
            return FALSE;
238
        }
239
    }
240
  else if (b->mark)
241
    {
242
      return FALSE;
243
    }
244

            
245
  return (!g_strcmp0 (a->style_name, b->style_name)
246
          && a->shapebg == b->shapebg
247
          && a->highlight == b->highlight
248
          && !g_strcmp0 (a->named, b->named)
249
          && a->border == b->border
250
          && a->divided == b->divided
251
          && !g_strcmp0 (a->label, b->label)
252
          && !g_strcmp0 (a->image_url, b->image_url)
253
          && !g_strcmp0 (a->imagebg_url, b->imagebg_url)
254
          && !g_strcmp0 (a->bg_color, b->bg_color)
255
          && !g_strcmp0 (a->text_color, b->text_color)
256
          && !g_strcmp0 (a->border_color, b->border_color)
257
          && a->barred == b->barred
258
          && a->dotted == b->dotted
259
          && a->dashed == b->dashed
260
          && a->lessthan == b->lessthan
261
          && a->greaterthan == b->greaterthan
262
          && a->equal == b->equal);
263
}
264

            
265
static void
266
26
mark_copy_func (gpointer    key,
267
                const char *mark_label,
268
                GHashTable *dest_marks)
269
{
270
26
  g_hash_table_insert (dest_marks, key, g_strdup (mark_label));
271
26
}
272

            
273
IPuzStyle *
274
449
ipuz_style_copy (IPuzStyle *style)
275
{
276
  IPuzStyle *new_style;
277

            
278
449
  if (style == NULL)
279
423
    return NULL;
280

            
281
26
  new_style = ipuz_style_new ();
282
26
  new_style->style_name = g_strdup (style->style_name);
283

            
284
26
  new_style->named = g_strdup (style->named);
285
26
  new_style->border = style->border;
286
26
  new_style->shapebg = style->shapebg;
287
26
  new_style->highlight = style->highlight;
288
26
  new_style->divided = style->divided;
289
26
  if (style->mark)
290
    {
291
26
      new_style->mark = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_free);
292
26
      g_hash_table_foreach (style->mark, (GHFunc) mark_copy_func, new_style->mark);
293
    }
294
26
  new_style->label = g_strdup (style->label);
295
26
  new_style->image_url = g_strdup (style->image_url);
296
26
  new_style->imagebg_url = g_strdup (style->imagebg_url);
297
26
  new_style->bg_color = g_strdup (style->bg_color);
298
26
  new_style->text_color = g_strdup (style->text_color);
299
26
  new_style->border_color = g_strdup (style->border_color);
300
26
  new_style->barred = style->barred;
301
26
  new_style->dotted = style->dotted;
302
26
  new_style->dashed = style->dashed;
303
26
  new_style->lessthan = style->lessthan;
304
26
  new_style->greaterthan = style->greaterthan;
305
26
  new_style->equal = style->equal;
306

            
307
26
  return new_style;
308
}
309

            
310
/**
311
 * ipuz_style_is_empty:
312
 * @style: An `IPuzStyle`
313
 *
314
 * Returns #true if style doesn't have anything set, and makes no
315
 * changes to the rendering of a cell it's associated with.
316
 *
317
 * Returns: #true if @style is empty
318
 **/
319
gboolean
320
125
ipuz_style_is_empty (IPuzStyle *style)
321
{
322
125
  g_return_val_if_fail (style != NULL, TRUE);
323

            
324
125
  return ((style->style_name == NULL)
325
          && (style->named == NULL)
326
          && (style->shapebg == IPUZ_STYLE_SHAPE_NONE)
327
          && (! style->highlight)
328
          && (style->named == NULL)
329
          && (style->border == 0)
330
          && (style->divided == IPUZ_STYLE_DIVIDED_NONE)
331
          && (style->label == NULL)
332
          && (style->mark == NULL)
333
          && (style->image_url == NULL)
334
          && (style->imagebg_url == NULL)
335
          && (style->bg_color == NULL)
336
          && (style->text_color == NULL)
337
          && (style->border_color == NULL)
338
          && (style->barred == 0)
339
          && (style->dotted == 0)
340
          && (style->dashed == 0)
341
          && (style->lessthan == 0)
342
          && (style->greaterthan == 0)
343
125
          && (style->equal == 0));
344
}
345

            
346
/* Internal function used by ipuz-barred.c */
347
gboolean
348
57
_ipuz_style_is_empty_except_bars (IPuzStyle *style)
349
{
350
57
  g_return_val_if_fail (style != NULL, TRUE);
351

            
352
57
  return ((style->style_name == NULL)
353
          && (style->named == NULL)
354
          && (style->shapebg == IPUZ_STYLE_SHAPE_NONE)
355
          && (! style->highlight)
356
          && (style->named == NULL)
357
          && (style->border == 0)
358
          && (style->divided == IPUZ_STYLE_DIVIDED_NONE)
359
          && (style->label == NULL)
360
          && (style->mark == NULL)
361
          && (style->image_url == NULL)
362
          && (style->imagebg_url == NULL)
363
          && (style->bg_color == NULL)
364
          && (style->text_color == NULL)
365
          && (style->border_color == NULL)
366
          && (style->barred != 0) /* The one difference between this and ipuz_style_is_empty */
367
          && (style->dotted == 0)
368
          && (style->dashed == 0)
369
          && (style->lessthan == 0)
370
          && (style->greaterthan == 0)
371
57
          && (style->equal == 0));
372
}
373

            
374
static void
375
26
build_mark_foreach_cb (IPuzStyleMark  mark,
376
                       const gchar   *label,
377
                       gpointer       user_data)
378

            
379
{
380
26
  JsonBuilder *builder = JSON_BUILDER (user_data);
381

            
382
26
  switch (mark)
383
    {
384
    case IPUZ_STYLE_MARK_TL:
385
      json_builder_set_member_name (builder, "TL");
386
      break;
387
    case IPUZ_STYLE_MARK_T:
388
      json_builder_set_member_name (builder, "T");
389
      break;
390
8
    case IPUZ_STYLE_MARK_TR:
391
8
      json_builder_set_member_name (builder, "TR");
392
8
      break;
393
    case IPUZ_STYLE_MARK_L:
394
      json_builder_set_member_name (builder, "L");
395
      break;
396
    case IPUZ_STYLE_MARK_C:
397
      json_builder_set_member_name (builder, "C");
398
      break;
399
    case IPUZ_STYLE_MARK_R:
400
      json_builder_set_member_name (builder, "R");
401
      break;
402
9
    case IPUZ_STYLE_MARK_BL:
403
9
      json_builder_set_member_name (builder, "BL");
404
9
      break;
405
    case IPUZ_STYLE_MARK_B:
406
      json_builder_set_member_name (builder, "B");
407
      break;
408
9
    case IPUZ_STYLE_MARK_BR:
409
9
      json_builder_set_member_name (builder, "BR");
410
9
      break;
411
    default:
412
      g_warning ("STYLE as a bitfield is not supported");
413
      json_builder_set_member_name (builder, "C");
414
      break;
415
    }
416
26
  json_builder_add_string_value (builder, label);
417
26
}
418

            
419
static void
420
156
sides_to_json (JsonBuilder *builder, const char *prop_name, IPuzStyleSides sides)
421
{
422
156
  if (sides != 0)
423
    {
424
      char *val = sides_to_str (sides);
425
      json_builder_set_member_name (builder, prop_name);
426
      json_builder_add_string_value (builder, val);
427
      g_free (val);
428
    }
429
156
}
430

            
431
void
432
26
ipuz_style_build (IPuzStyle   *style,
433
                  JsonBuilder *builder)
434
{
435
26
  g_return_if_fail (style != NULL);
436

            
437
26
  json_builder_begin_object (builder);
438

            
439

            
440
26
  if (style->shapebg != IPUZ_STYLE_SHAPE_NONE)
441
    {
442
      json_builder_set_member_name (builder, "shapebg");
443
      json_builder_add_string_value (builder, shapebg_to_str (style->shapebg));
444
    }
445

            
446
  /* FIXME(parse): add highlight_set, or an enum */
447
26
  if (style->highlight)
448
    {
449
      json_builder_set_member_name (builder, "highlight");
450
      json_builder_add_boolean_value (builder, style->highlight);
451
    }
452

            
453
26
  if (style->named)
454
    {
455
      json_builder_set_member_name (builder, "named");
456
      json_builder_add_string_value (builder, style->named);
457
    }
458

            
459
  /* FIXME(build): border needs a border-set variable. 0 is a valid value */
460
26
  if (style->border != 0)
461
    {
462
      json_builder_set_member_name (builder, "border");
463
      json_builder_add_int_value (builder, style->border);
464
    }
465

            
466
26
  if (style->divided != IPUZ_STYLE_DIVIDED_NONE)
467
    {
468
      json_builder_set_member_name (builder, "divided");
469
      switch (style->divided)
470
        {
471
        case IPUZ_STYLE_DIVIDED_HORIZ:
472
          json_builder_add_string_value (builder, "-");
473
          break;
474
        case IPUZ_STYLE_DIVIDED_VERT:
475
          json_builder_add_string_value (builder, "|");
476
          break;
477
        case IPUZ_STYLE_DIVIDED_UP_RIGHT:
478
          json_builder_add_string_value (builder, "/");
479
          break;
480
        case IPUZ_STYLE_DIVIDED_UP_LEFT:
481
          json_builder_add_string_value (builder, "\\");
482
          break;
483
        case IPUZ_STYLE_DIVIDED_PLUS:
484
          json_builder_add_string_value (builder, "+");
485
          break;
486
        case IPUZ_STYLE_DIVIDED_CROSS:
487
          json_builder_add_string_value (builder, "X");
488
          break;
489
        default:
490
          g_warning ("unknown divided style");
491
          json_builder_add_string_value (builder, "?");
492
          break;
493
        }
494
    }
495

            
496
26
  if (style->label)
497
    {
498
      json_builder_set_member_name (builder, "label");
499
      json_builder_add_string_value (builder, style->label);
500
    }
501

            
502
26
  if (style->mark)
503
    {
504
26
      json_builder_set_member_name (builder, "mark");
505
26
      json_builder_begin_object (builder);
506
26
      ipuz_style_mark_foreach (style, build_mark_foreach_cb, builder);
507
26
      json_builder_end_object (builder);
508
    }
509

            
510
26
  if (style->imagebg_url)
511
    {
512
      json_builder_set_member_name (builder, "imagebg");
513
      json_builder_add_string_value (builder, style->imagebg_url);
514
    }
515

            
516
26
  if (style->image_url)
517
    {
518
      json_builder_set_member_name (builder, "label");
519
      json_builder_add_string_value (builder, style->image_url);
520

            
521
    }
522

            
523
26
  if (style->bg_color)
524
      {
525
        json_builder_set_member_name (builder, "color");
526
        json_builder_add_string_value (builder, style->bg_color);
527
      }
528

            
529
26
  if (style->text_color)
530
        {
531
          json_builder_set_member_name (builder, "colortext");
532
          json_builder_add_string_value (builder, style->text_color);
533
        }
534

            
535
26
  if (style->border_color)
536
    {
537
      json_builder_set_member_name (builder, "colorborder");
538
      json_builder_add_string_value (builder, style->border_color);
539
    }
540

            
541
26
  sides_to_json (builder, "barred",      style->barred);
542
26
  sides_to_json (builder, "dotted",      style->dotted);
543
26
  sides_to_json (builder, "dashed",      style->dashed);
544
26
  sides_to_json (builder, "lessthan",    style->lessthan);
545
26
  sides_to_json (builder, "greaterthan", style->greaterthan);
546
26
  sides_to_json (builder, "equal",       style->equal);
547

            
548
26
  json_builder_end_object (builder);
549
}
550

            
551
/* Getters and Setters */
552

            
553
const gchar *
554
4
ipuz_style_get_style_name (IPuzStyle *style)
555
{
556
4
  g_return_val_if_fail (style != NULL, NULL);
557

            
558
4
  return style->style_name;
559
}
560

            
561
void
562
18
ipuz_style_set_style_name (IPuzStyle   *style,
563
                           const gchar *style_name)
564
{
565
18
  g_return_if_fail (style != NULL);
566

            
567
18
  g_clear_pointer (&style->style_name, g_free);
568
18
  style->style_name = g_strdup (style_name);
569
}
570

            
571
const gchar *
572
1
ipuz_style_get_named (IPuzStyle *style)
573
{
574
1
  g_return_val_if_fail (style != NULL, NULL);
575

            
576
1
  return style->named;
577
}
578

            
579
void
580
ipuz_style_set_named (IPuzStyle   *style,
581
                      const gchar *named)
582
{
583
  g_return_if_fail (style != NULL);
584

            
585
  g_clear_pointer (&style->named, g_free);
586
  style->named = g_strdup (named);
587
}
588

            
589
gint
590
1
ipuz_style_get_border (IPuzStyle *style)
591
{
592
1
  g_return_val_if_fail (style != NULL, 0);
593

            
594
1
  return style->border;
595
}
596

            
597
void
598
ipuz_style_set_border (IPuzStyle *style,
599
                       gint       border)
600
{
601
  g_return_if_fail (style != NULL);
602

            
603
  style->border = border;
604
}
605

            
606
IPuzStyleShape
607
6
ipuz_style_get_shapebg (IPuzStyle *style)
608
{
609
6
  g_return_val_if_fail (style != NULL, IPUZ_STYLE_SHAPE_NONE);
610

            
611
6
  return style->shapebg;
612
}
613

            
614
void
615
ipuz_style_set_shapebg (IPuzStyle      *style,
616
                        IPuzStyleShape  shapebg)
617
{
618
  g_return_if_fail (style != NULL);
619

            
620
  style->shapebg = shapebg;
621
}
622

            
623
gboolean
624
1
ipuz_style_get_highlight (IPuzStyle *style)
625
{
626
1
  g_return_val_if_fail (style != NULL, FALSE);
627

            
628
1
  return style->highlight;
629
}
630

            
631
void
632
ipuz_style_set_highlight (IPuzStyle *style,
633
                          gboolean   highlight)
634
{
635
  g_return_if_fail (style != NULL);
636

            
637
  style->highlight = !!highlight;
638
}
639

            
640
IPuzStyleDivided
641
1
ipuz_style_get_divided (IPuzStyle *style)
642
{
643
1
  g_return_val_if_fail (style != NULL, IPUZ_STYLE_DIVIDED_NONE);
644

            
645
1
  return style->divided;
646
}
647

            
648
void
649
ipuz_style_set_divided (IPuzStyle        *style,
650
                        IPuzStyleDivided  divided)
651
{
652
  g_return_if_fail (style != NULL);
653

            
654
  style->divided = divided;
655
}
656

            
657
typedef struct
658
{
659
  IPuzMarkFunc *func;
660
  gpointer user_data;
661
} ForeachTuple;
662

            
663

            
664
void
665
27
mark_foreach (gpointer key,
666
              gpointer value,
667
              gpointer user_data)
668
{
669
27
  ForeachTuple *tuple = (ForeachTuple *)user_data;
670

            
671
27
  (*tuple->func) (GPOINTER_TO_INT (key), (const char *) value, tuple->user_data);
672
27
}
673

            
674
void
675
27
ipuz_style_mark_foreach (IPuzStyle    *style,
676
                         IPuzMarkFunc  func,
677
                         gpointer      user_data)
678
{
679
  ForeachTuple tuple;
680

            
681
27
  g_return_if_fail (style != NULL);
682
27
  g_return_if_fail (func != NULL);
683

            
684
27
  if (style->mark)
685
    {
686
27
      tuple.func = &func;
687
27
      tuple.user_data = user_data;
688

            
689
27
      g_hash_table_foreach (style->mark, mark_foreach, &tuple);
690
    }
691
}
692

            
693
const gchar *
694
1
ipuz_style_get_label (IPuzStyle *style)
695
{
696
1
  g_return_val_if_fail (style != NULL, NULL);
697

            
698
1
  return style->label;
699
}
700

            
701
void
702
ipuz_style_set_label (IPuzStyle   *style,
703
                      const gchar *label)
704
{
705
  g_return_if_fail (style != NULL);
706

            
707
  g_clear_pointer (&style->label, g_free);
708
  style->label = g_strdup (label);
709
}
710

            
711
const gchar *
712
1
ipuz_style_get_image_url (IPuzStyle *style)
713
{
714
1
  g_return_val_if_fail (style != NULL, NULL);
715

            
716
1
  return style->image_url;
717
}
718

            
719
void
720
ipuz_style_set_image_url (IPuzStyle   *style,
721
                          const gchar *image_url)
722
{
723
  g_return_if_fail (style != NULL);
724

            
725
  g_clear_pointer (&style->image_url, g_free);
726
  style->image_url = g_strdup (image_url);
727
}
728

            
729
const gchar *
730
1
ipuz_style_get_imagebg_url (IPuzStyle *style)
731
{
732
1
  g_return_val_if_fail (style != NULL, NULL);
733

            
734
1
  return style->imagebg_url;
735
}
736

            
737
void
738
ipuz_style_set_imagebg_url (IPuzStyle   *style,
739
                            const gchar *imagebg_url)
740
{
741
  g_return_if_fail (style != NULL);
742

            
743
  g_clear_pointer (&style->imagebg_url, g_free);
744
  style->imagebg_url = g_strdup (imagebg_url);
745
}
746

            
747
const gchar *
748
1
ipuz_style_get_bg_color (IPuzStyle *style)
749
{
750
1
  g_return_val_if_fail (style != NULL, NULL);
751

            
752
1
  return style->bg_color;
753
}
754

            
755
void
756
ipuz_style_set_bg_color (IPuzStyle   *style,
757
                         const gchar *bg_color)
758
{
759
  g_return_if_fail (style != NULL);
760

            
761
  g_clear_pointer (&style->bg_color, g_free);
762
  style->bg_color = g_strdup (bg_color);
763
}
764

            
765
const gchar *
766
1
ipuz_style_get_text_color (IPuzStyle *style)
767
{
768
1
  g_return_val_if_fail (style != NULL, NULL);
769

            
770
1
  return style->text_color;
771
}
772

            
773
void
774
ipuz_style_set_text_color (IPuzStyle   *style,
775
                           const gchar *text_color)
776
{
777
  g_return_if_fail (style != NULL);
778

            
779
  g_clear_pointer (&style->text_color, g_free);
780
  style->text_color = g_strdup (text_color);
781
}
782

            
783
const gchar *
784
1
ipuz_style_get_border_color (IPuzStyle *style)
785
{
786
1
  g_return_val_if_fail (style != NULL, NULL);
787

            
788
1
  return style->border_color;
789
}
790

            
791
void
792
ipuz_style_set_border_color (IPuzStyle   *style,
793
                             const gchar *border_color)
794
{
795
  g_return_if_fail (style != NULL);
796

            
797
  g_clear_pointer (&style->border_color, g_free);
798
  style->border_color = g_strdup (border_color);
799
}
800

            
801
IPuzStyleSides
802
3814
ipuz_style_get_barred (IPuzStyle *style)
803
{
804
3814
  g_return_val_if_fail (style != NULL, 0);
805

            
806
3814
  return style->barred;
807
}
808

            
809
void
810
60
ipuz_style_set_barred (IPuzStyle      *style,
811
                       IPuzStyleSides  barred)
812
{
813
60
  g_return_if_fail (style != NULL);
814

            
815
60
  style->barred = barred;
816
}
817

            
818
IPuzStyleSides
819
1
ipuz_style_get_dotted (IPuzStyle *style)
820
{
821
1
  g_return_val_if_fail (style != NULL, 0);
822

            
823
1
  return style->dotted;
824
}
825

            
826
void
827
ipuz_style_set_dotted (IPuzStyle      *style,
828
                       IPuzStyleSides  dotted)
829
{
830
  g_return_if_fail (style != NULL);
831

            
832
  style->dotted = dotted;
833
}
834

            
835
IPuzStyleSides
836
1
ipuz_style_get_dashed (IPuzStyle *style)
837
{
838
1
  g_return_val_if_fail (style != NULL, 0);
839

            
840
1
  return style->dashed;
841
}
842

            
843
void
844
ipuz_style_set_dashed (IPuzStyle      *style,
845
                       IPuzStyleSides  dashed)
846
{
847
  g_return_if_fail (style != NULL);
848

            
849
  style->dashed = dashed;
850
}
851

            
852
IPuzStyleSides
853
1
ipuz_style_get_lessthan (IPuzStyle *style)
854
{
855
1
  g_return_val_if_fail (style != NULL, 0);
856

            
857
1
  return style->lessthan;
858
}
859

            
860
void
861
ipuz_style_set_lessthan (IPuzStyle      *style,
862
                         IPuzStyleSides  lessthan)
863
{
864
  g_return_if_fail (style != NULL);
865

            
866
  style->lessthan = lessthan;
867
}
868

            
869
IPuzStyleSides
870
1
ipuz_style_get_greaterthan (IPuzStyle *style)
871
{
872
1
  g_return_val_if_fail (style != NULL, 0);
873

            
874
1
  return style->greaterthan;
875
}
876

            
877
void
878
ipuz_style_set_greaterthan (IPuzStyle      *style,
879
                            IPuzStyleSides  greaterthan)
880
{
881
  g_return_if_fail (style != NULL);
882

            
883
  style->greaterthan = greaterthan;
884
}
885

            
886
IPuzStyleSides
887
1
ipuz_style_get_equal (IPuzStyle *style)
888
{
889
1
  g_return_val_if_fail (style != NULL, 0);
890

            
891
1
  return style->equal;
892
}
893

            
894
void
895
ipuz_style_set_equal (IPuzStyle      *style,
896
                      IPuzStyleSides  equal)
897
{
898
  g_return_if_fail (style != NULL);
899

            
900
  style->equal = equal;
901
}
902

            
903
static char *
904
6
parse_color (JsonNode *node)
905
{
906
6
  GValue value = G_VALUE_INIT;
907
6
  gchar *retval = NULL;
908

            
909
6
  json_node_get_value (node, &value);
910
6
  if (G_VALUE_HOLDS_STRING (&value))
911
    {
912
6
      retval = g_value_dup_string (&value);
913
    }
914
  else if (G_VALUE_HOLDS_INT (&value) || G_VALUE_HOLDS_INT64 (&value))
915
    {
916
      int number = json_node_get_int (node);
917
      /* FIXME: pick a pallet */
918
      if (number == 0)
919
        retval = g_strdup ("black");
920
      else
921
        retval = NULL;
922
    }
923
6
  g_value_unset (&value);
924

            
925
6
  return retval;
926
}
927

            
928
static void
929
206
ipuz_style_parse_mark (IPuzStyle *style,
930
                       JsonNode  *node)
931
{
932
206
  if (style->mark == NULL)
933
206
    style->mark = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_free);
934

            
935
206
  if (JSON_NODE_HOLDS_OBJECT (node))
936
    {
937
      const gchar *mark;
938
      JsonObject *obj;
939
      JsonNode *element;
940

            
941
206
      obj = json_node_get_object (node);
942

            
943
206
      element = json_object_get_member (obj, "TL");
944
206
      if (element)
945
        {
946
1
          mark = json_node_get_string (element);
947
1
          g_hash_table_insert (style->mark,
948
                               GINT_TO_POINTER (IPUZ_STYLE_MARK_TL),
949
1
                               g_strdup (mark));
950
          }
951
206
      element = json_object_get_member (obj, "T");
952
206
      if (element)
953
        {
954
          mark = json_node_get_string (element);
955
          g_hash_table_insert (style->mark,
956
                               GINT_TO_POINTER (IPUZ_STYLE_MARK_T),
957
                               g_strdup (mark));
958
          }
959
206
      element = json_object_get_member (obj, "TR");
960
206
      if (element)
961
        {
962
63
          mark = json_node_get_string (element);
963
63
          g_hash_table_insert (style->mark,
964
                               GINT_TO_POINTER (IPUZ_STYLE_MARK_TR),
965
63
                               g_strdup (mark));
966
          }
967
206
      element = json_object_get_member (obj, "L");
968
206
      if (element)
969
        {
970
          mark = json_node_get_string (element);
971
          g_hash_table_insert (style->mark,
972
                               GINT_TO_POINTER (IPUZ_STYLE_MARK_L),
973
                               g_strdup (mark));
974
          }
975
206
      element = json_object_get_member (obj, "C");
976
206
      if (element)
977
        {
978
          mark = json_node_get_string (element);
979
          g_hash_table_insert (style->mark,
980
                               GINT_TO_POINTER (IPUZ_STYLE_MARK_C),
981
                               g_strdup (mark));
982
          }
983
206
      element = json_object_get_member (obj, "R");
984
206
      if (element)
985
        {
986
          mark = json_node_get_string (element);
987
          g_hash_table_insert (style->mark,
988
                               GINT_TO_POINTER (IPUZ_STYLE_MARK_R),
989
                               g_strdup (mark));
990
          }
991
206
      element = json_object_get_member (obj, "BL");
992
206
      if (element)
993
        {
994
71
          mark = json_node_get_string (element);
995
71
          g_hash_table_insert (style->mark,
996
                               GINT_TO_POINTER (IPUZ_STYLE_MARK_BL),
997
71
                               g_strdup (mark));
998
          }
999
206
      element = json_object_get_member (obj, "B");
206
      if (element)
        {
          mark = json_node_get_string (element);
          g_hash_table_insert (style->mark,
                               GINT_TO_POINTER (IPUZ_STYLE_MARK_B),
                               g_strdup (mark));
        }
206
      element = json_object_get_member (obj, "BR");
206
      if (element)
        {
71
          mark = json_node_get_string (element);
71
          g_hash_table_insert (style->mark,
                               GINT_TO_POINTER (IPUZ_STYLE_MARK_BR),
71
                               g_strdup (mark));
        }
      }
  /* Make sure we actually added something */
206
  if (g_hash_table_size (style->mark) == 0)
    {
      g_hash_table_unref (style->mark);
      style->mark = NULL;
    }
206
}
struct shapebg_name {
  const char *shapebg;
  const char *display_name;
  IPuzStyleShape value;
};
static const struct shapebg_name shapebg_names[] = {
  { "none",           N_("None"),           IPUZ_STYLE_SHAPE_NONE },
  { "circle",         N_("Circle"),         IPUZ_STYLE_SHAPE_CIRCLE },
  { "arrow-left",     N_("Left Arrow"),     IPUZ_STYLE_SHAPE_ARROW_LEFT },
  { "arrow-right",    N_("Right Arrow"),    IPUZ_STYLE_SHAPE_ARROW_RIGHT },
  { "arrow-up",       N_("Up Arrow"),       IPUZ_STYLE_SHAPE_ARROW_UP },
  { "arrow-down",     N_("Down Arrow"),     IPUZ_STYLE_SHAPE_ARROW_DOWN },
  { "triangle-left",  N_("Left Triangle"),  IPUZ_STYLE_SHAPE_TRIANGLE_LEFT },
  { "triangle-right", N_("Right Triangle"), IPUZ_STYLE_SHAPE_TRIANGLE_RIGHT },
  { "triangle-up",    N_("Up Triangle"),    IPUZ_STYLE_SHAPE_TRIANGLE_UP },
  { "triangle-down",  N_("Down Triangle"),  IPUZ_STYLE_SHAPE_TRIANGLE_DOWN },
  { "diamond",        N_("Diamond"),        IPUZ_STYLE_SHAPE_DIAMOND },
  { "club",           N_("Club"),           IPUZ_STYLE_SHAPE_CLUB },
  { "heart",          N_("Heart"),          IPUZ_STYLE_SHAPE_HEART },
  { "spade",          N_("Spade"),          IPUZ_STYLE_SHAPE_SPADE },
  { "star",           N_("Star"),           IPUZ_STYLE_SHAPE_STAR },
  { "square",         N_("Square"),         IPUZ_STYLE_SHAPE_SQUARE },
  { "rhombus",        N_("Rhombus"),        IPUZ_STYLE_SHAPE_RHOMBUS },
  { "/",              N_("Slash"),          IPUZ_STYLE_SHAPE_SLASH },
  { "\\",             N_("Backslash"),      IPUZ_STYLE_SHAPE_BACKSLASH },
  { "X",              N_("X"),              IPUZ_STYLE_SHAPE_X },
};
static IPuzStyleShape
17
parse_shapebg (const char *shapebg)
{
  guint i;
87
  for (i = 0; i < G_N_ELEMENTS (shapebg_names); i++)
    {
86
      if (g_strcmp0 (shapebg, shapebg_names[i].shapebg) == 0)
16
        return shapebg_names[i].value;
    }
1
  return IPUZ_STYLE_SHAPE_NONE;
}
static const char *
shapebg_to_str (IPuzStyleShape shapebg)
{
  guint i;
  for (i = 0; i < G_N_ELEMENTS (shapebg_names); i++)
    {
      if (shapebg == shapebg_names[i].value)
        return shapebg_names[i].shapebg;
    }
  g_assert_not_reached ();
}
static IPuzStyleSides
17
parse_sides (const char *val)
{
17
  IPuzStyleSides sides = 0;
  const char *p;
41
  for (p = val; *p; p++)
    {
24
      switch (*p) {
9
      case 'T':
9
        sides |= IPUZ_STYLE_SIDES_TOP;
9
        break;
3
      case 'R':
3
        sides |= IPUZ_STYLE_SIDES_RIGHT;
3
        break;
3
      case 'B':
3
        sides |= IPUZ_STYLE_SIDES_BOTTOM;
3
        break;
9
      case 'L':
9
        sides |= IPUZ_STYLE_SIDES_LEFT;
9
        break;
      default:
        break;
      }
    }
17
  return sides;
}
static char *
sides_to_str (IPuzStyleSides sides)
{
  char val[5] = { 0 };
  gsize i = 0;
  if (sides & IPUZ_STYLE_SIDES_TOP)
    {
      val[i] = 'T';
      i++;
    }
  if (sides & IPUZ_STYLE_SIDES_RIGHT)
    {
      val[i] = 'R';
      i++;
    }
  if (sides & IPUZ_STYLE_SIDES_BOTTOM)
    {
      val[i] = 'B';
      i++;
    }
  if (sides & IPUZ_STYLE_SIDES_LEFT)
    {
      val[i] = 'L';
      i++;
    }
  g_assert (i < G_N_ELEMENTS(val));
  val[i] = 0;
  return g_strdup (val);
}
/* Public functions */
IPuzStyleSides
868
ipuz_style_side_opposite (IPuzStyleSides side)
{
868
  g_return_val_if_fail ((side == IPUZ_STYLE_SIDES_LEFT ||
                         side == IPUZ_STYLE_SIDES_RIGHT ||
                         side == IPUZ_STYLE_SIDES_TOP ||
                         side == IPUZ_STYLE_SIDES_BOTTOM), 0);
868
  if (side == IPUZ_STYLE_SIDES_LEFT)
435
    return IPUZ_STYLE_SIDES_RIGHT;
433
  else if (side == IPUZ_STYLE_SIDES_RIGHT)
    return IPUZ_STYLE_SIDES_LEFT;
433
  else if (side == IPUZ_STYLE_SIDES_TOP)
433
    return IPUZ_STYLE_SIDES_BOTTOM;
  else if (side == IPUZ_STYLE_SIDES_BOTTOM)
    return IPUZ_STYLE_SIDES_TOP;
  else
    g_assert_not_reached ();
}
static int
610
swap_bits (guint n,
           guint p1,
           guint p2)
{
610
  if (((n & (1 << p1)) >> p1) ^ ((n & (1 << p2)) >> p2))
  {
157
    n ^= 1 << p1;
157
    n ^= 1 << p2;
  }
610
  return n;
}
IPuzStyleSides
150
ipuz_style_sides_rotate_180 (IPuzStyleSides sides)
{
150
  guint n = (guint) sides << 2;
150
  n = swap_bits (n, 0, 4);
150
  n = swap_bits (n, 1, 5);
150
  return n;
}
IPuzStyleSides
149
ipuz_style_sides_rotate_rt (IPuzStyleSides sides)
{
149
  guint n = (guint) sides;
149
  return swap_bits (n << 1, 0, 4);
}
IPuzStyleSides
149
ipuz_style_sides_rotate_lt (IPuzStyleSides sides)
{
149
  guint n = (guint) sides;
149
  return (swap_bits (n, 0, 4)) >> 1;
}
IPuzStyleSides
6
ipuz_style_sides_flip_horiz (IPuzStyleSides sides)
{
6
  guint n = (guint) sides;
6
  return (swap_bits (n, 1, 3));
}
IPuzStyleSides
6
ipuz_style_sides_flip_vert (IPuzStyleSides sides)
{
6
  guint n = (guint) sides;
6
  return (swap_bits (n, 0, 2));
}
const gchar *
ipuz_style_shape_get_display_name (IPuzStyleShape shapebg)
{
  g_return_val_if_fail (shapebg >= IPUZ_STYLE_SHAPE_NONE, NULL);
  g_return_val_if_fail (shapebg <= IPUZ_STYLE_SHAPE_X, NULL);
  return _(shapebg_names [shapebg].display_name);
}