~ubuntu-branches/ubuntu/maverick/zapping/maverick

« back to all changes in this revision

Viewing changes to plugins/teletext/view.c

  • Committer: Bazaar Package Importer
  • Author(s): Daniel Holbach
  • Date: 2005-03-08 23:19:08 UTC
  • mfrom: (2.1.1 sarge)
  • Revision ID: james.westby@ubuntu.com-20050308231908-oip7rfv6lcmo8c0e
Tags: 0.9.2-2ubuntu1
Rebuilt for Python transition (2.3 -> 2.4)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  Zapping TV viewer
 
3
 *
 
4
 *  Copyright (C) 2000, 2001, 2002 I�aki Garc�a Etxebarria
 
5
 *  Copyright (C) 2000, 2001, 2002, 2003, 2004 Michael H. Schimek
 
6
 *
 
7
 *  This program is free software; you can redistribute it and/or modify
 
8
 *  it under the terms of the GNU General Public License as published by
 
9
 *  the Free Software Foundation; either version 2 of the License, or
 
10
 *  (at your option) any later version.
 
11
 *
 
12
 *  This program is distributed in the hope that it will be useful,
 
13
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
 *  GNU General Public License for more details.
 
16
 *
 
17
 *  You should have received a copy of the GNU General Public License
 
18
 *  along with this program; if not, write to the Free Software
 
19
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
20
 */
 
21
 
 
22
/* $Id: view.c,v 1.13 2005/01/31 07:06:47 mschimek Exp $ */
 
23
 
 
24
#ifdef HAVE_CONFIG_H
 
25
#  include "config.h"
 
26
#endif
 
27
 
 
28
#include <gdk/gdkx.h>
 
29
 
 
30
#include "libvbi/exp-gfx.h"
 
31
#include "libvbi/exp-txt.h"
 
32
#include "src/zgconf.h"
 
33
#include "src/subtitle.h"
 
34
#include "src/remote.h"
 
35
#include "preferences.h"        /* teletext_foo_enum[] */
 
36
#include "export.h"
 
37
#include "search.h"
 
38
#include "main.h"
 
39
#include "view.h"
 
40
 
 
41
#define BLINK_CYCLE 300 /* ms */
 
42
 
 
43
#define GCONF_DIR "/apps/zapping/plugins/teletext"
 
44
 
 
45
enum {
 
46
  REQUEST_CHANGED,
 
47
  CHARSET_CHANGED,
 
48
  N_SIGNALS
 
49
};
 
50
 
 
51
static GObjectClass *           parent_class;
 
52
 
 
53
static guint                    signals[N_SIGNALS];
 
54
 
 
55
static GdkCursor *              cursor_normal;
 
56
static GdkCursor *              cursor_link;
 
57
static GdkCursor *              cursor_select;
 
58
 
 
59
static GdkAtom                  GA_CLIPBOARD            = GDK_NONE;
 
60
 
 
61
static vbi3_wst_level           teletext_level          = VBI3_WST_LEVEL_1p5;
 
62
static vbi3_charset_code        default_charset         = 0;
 
63
static GdkInterpType            interp_type             = GDK_INTERP_HYPER;
 
64
static gboolean                 rolling_header          = TRUE;
 
65
static gboolean                 live_clock              = TRUE;
 
66
static gboolean                 hex_pages               = TRUE;
 
67
static gint                     brightness              = 128;
 
68
static gint                     contrast                = 64;
 
69
static gint                     navigation              = 2;
 
70
static gboolean                 hyperlinks              = TRUE;
 
71
 
 
72
enum {
 
73
  TARGET_LAT1_STRING,
 
74
  TARGET_UTF8_STRING,
 
75
  TARGET_PIXMAP
 
76
};
 
77
 
 
78
static const GtkTargetEntry
 
79
clipboard_targets [] = {
 
80
  { "STRING",      0, TARGET_LAT1_STRING },
 
81
  { "UTF8_STRING", 0, TARGET_UTF8_STRING },
 
82
  { "PIXMAP",      0, TARGET_PIXMAP },
 
83
};
 
84
 
 
85
 
 
86
 
 
87
 
 
88
 
 
89
 
 
90
/*
 
91
        Scaling and drawing
 
92
*/
 
93
 
 
94
static void
 
95
draw_scaled_page_image          (TeletextView *         view,
 
96
                                 GdkDrawable *          drawable,
 
97
                                 GdkGC *                gc,
 
98
                                 gint                   src_x,
 
99
                                 gint                   src_y,
 
100
                                 gint                   dest_x,
 
101
                                 gint                   dest_y,
 
102
                                 gint                   width,
 
103
                                 gint                   height)
 
104
{
 
105
  gint sw;
 
106
  gint sh;
 
107
 
 
108
  if (!view->scaled_on)
 
109
    return;
 
110
 
 
111
  sw = gdk_pixbuf_get_width (view->scaled_on);
 
112
  sh = gdk_pixbuf_get_height (view->scaled_on);
 
113
 
 
114
  gdk_draw_pixbuf (/* dst */ drawable, gc,
 
115
                   /* src */ view->scaled_on,
 
116
                   src_x, src_y,
 
117
                   dest_x, dest_y,
 
118
                   MIN (width, sw),
 
119
                   MIN (height, sh),
 
120
                   GDK_RGB_DITHER_NORMAL,
 
121
                   /* dither offset */ src_x, src_y);
 
122
}
 
123
 
 
124
/* This whole patch stuff is overkill. It would probably suffice to create
 
125
   a view->scaled_off and just one "patch" for the header. */
 
126
 
 
127
#define CW 12
 
128
#define CH 10
 
129
 
 
130
static void
 
131
destroy_patch                   (struct ttx_patch *     p)
 
132
{
 
133
  g_assert (NULL != p);
 
134
 
 
135
  if (p->scaled_on)
 
136
    g_object_unref (G_OBJECT (p->scaled_on));
 
137
 
 
138
  if (p->scaled_off)
 
139
    g_object_unref (G_OBJECT (p->scaled_off));
 
140
 
 
141
  if (p->unscaled_on)
 
142
    g_object_unref (G_OBJECT (p->unscaled_on));
 
143
 
 
144
  if (p->unscaled_off)
 
145
    g_object_unref (G_OBJECT (p->unscaled_off));
 
146
 
 
147
  CLEAR (*p);
 
148
}
 
149
 
 
150
/* Creates p->scaled_on/off from p->unscaled_on/off.
 
151
   sw, sh: view->scaled_on size.
 
152
   uw, uh: view->unscaled_on size.
 
153
*/
 
154
static void
 
155
scale_patch                     (struct ttx_patch *     p,
 
156
                                 guint                  sw,
 
157
                                 guint                  sh,
 
158
                                 guint                  uw,
 
159
                                 guint                  uh)
 
160
{
 
161
  guint srcw;
 
162
  guint srch;
 
163
  guint dstw;
 
164
  guint dsth;
 
165
  gint n;
 
166
 
 
167
  g_assert (NULL != p);
 
168
 
 
169
  if (p->scaled_on)
 
170
    {
 
171
      g_object_unref (G_OBJECT (p->scaled_on));
 
172
      p->scaled_on = NULL;
 
173
    }
 
174
 
 
175
  if (p->scaled_off)
 
176
    {
 
177
      g_object_unref (G_OBJECT (p->scaled_off));
 
178
      p->scaled_off = NULL;
 
179
    }
 
180
 
 
181
  srch = p->height * CH + 10;
 
182
  dsth = (sh * srch + (uh >> 1)) / uh;
 
183
  n = (0 == p->row) ? 0 : 5;
 
184
  p->sy = dsth * n / srch;
 
185
  p->sh = ceil (dsth * (n + p->height * CH) / (double) srch) - p->sy;
 
186
  p->dy = p->sy + (int) floor (sh * p->row * CH / (double) uh
 
187
                               - dsth * n / (double) srch + .5);
 
188
 
 
189
  srcw = p->width * p->columns * CW + 10;
 
190
  dstw = (sw * srcw + (uw >> 1)) / uw;
 
191
  n = (0 == p->column) ? 0 : 5;
 
192
  p->sx = dstw * n / srcw;
 
193
  p->sw = ceil (dstw * (n + p->width * p->columns * CW)
 
194
                / (double) srcw) - p->sx;
 
195
  p->dx = p->sx + (int) floor (sw * p->column * CW / (double) uw
 
196
                               - dstw * n / (double) srcw + .5);
 
197
 
 
198
  if (dstw > 0 && dsth > 0)
 
199
    {
 
200
      p->scaled_on =
 
201
        z_pixbuf_scale_simple (p->unscaled_on,
 
202
                               (gint) dstw, (gint) dsth,
 
203
                               interp_type);
 
204
 
 
205
      if (p->flash)
 
206
        p->scaled_off =
 
207
          z_pixbuf_scale_simple (p->unscaled_off,
 
208
                                 (gint) dstw, (gint) dsth,
 
209
                                 interp_type);
 
210
 
 
211
      p->dirty = TRUE;
 
212
    }
 
213
}
 
214
 
 
215
static void
 
216
delete_patches                  (TeletextView *         view)
 
217
{
 
218
  struct ttx_patch *p;
 
219
  struct ttx_patch *end;
 
220
 
 
221
  end = view->patches + view->n_patches;
 
222
 
 
223
  for (p = view->patches; p < end; ++p)
 
224
    destroy_patch (p);
 
225
 
 
226
  g_free (view->patches);
 
227
 
 
228
  view->patches = NULL;
 
229
  view->n_patches = 0;
 
230
}
 
231
 
 
232
/* Copies dirty or flashing view->patches into view->scaled_on.
 
233
   draw: expose changed areas for later redrawing. */
 
234
static void
 
235
apply_patches                   (TeletextView *         view,
 
236
                                 gboolean               draw)
 
237
{
 
238
  GdkWindow *window;
 
239
  struct ttx_patch *p;
 
240
  struct ttx_patch *end;
 
241
  guint rows;
 
242
  guint columns;
 
243
 
 
244
  if (!view->pg)
 
245
    return;
 
246
 
 
247
  window = GTK_WIDGET (view)->window;
 
248
 
 
249
  rows = view->pg->rows;
 
250
  columns = view->pg->columns;
 
251
 
 
252
  end = view->patches + view->n_patches;
 
253
 
 
254
  for (p = view->patches; p < end; ++p)
 
255
    {
 
256
      GdkPixbuf *scaled;
 
257
 
 
258
      if (p->flash)
 
259
        {
 
260
          p->phase = (p->phase + 1) & 15;
 
261
 
 
262
          if (0 == p->phase)
 
263
            scaled = p->scaled_off;
 
264
          else if (4 == p->phase)
 
265
            scaled = p->scaled_on;
 
266
          else
 
267
            continue;
 
268
        }
 
269
      else
 
270
        {
 
271
          if (!p->dirty || !p->scaled_on)
 
272
            continue;
 
273
 
 
274
          p->dirty = FALSE;
 
275
 
 
276
          scaled = p->scaled_on;
 
277
        }
 
278
 
 
279
      /* Update the scaled version of the page */
 
280
      if (view->scaled_on)
 
281
        {
 
282
          z_pixbuf_copy_area (/* src */ scaled, p->sx, p->sy, p->sw, p->sh,
 
283
                              /* dst */ view->scaled_on, p->dx, p->dy);
 
284
 
 
285
          if (draw)
 
286
            gdk_window_clear_area_e (window, p->dx, p->dy, p->sw, p->sh);
 
287
        }
 
288
    }
 
289
}
 
290
 
 
291
static void
 
292
scale_patches                   (TeletextView *         view)
 
293
{
 
294
  struct ttx_patch *p;
 
295
  struct ttx_patch *end;
 
296
  guint sw;
 
297
  guint sh;
 
298
  guint uw;
 
299
  guint uh;
 
300
 
 
301
  if (!view->scaled_on)
 
302
    return;
 
303
 
 
304
  g_assert (NULL != view->unscaled_on);
 
305
 
 
306
  sw = gdk_pixbuf_get_width (view->scaled_on);
 
307
  sh = gdk_pixbuf_get_height (view->scaled_on);
 
308
 
 
309
  uw = gdk_pixbuf_get_width (view->unscaled_on);
 
310
  uh = gdk_pixbuf_get_height (view->unscaled_on);
 
311
 
 
312
  end = view->patches + view->n_patches;
 
313
 
 
314
  for (p = view->patches; p < end; ++p)
 
315
    scale_patch (p, sw, sh, uw, uh);
 
316
}
 
317
 
 
318
static void
 
319
add_patch                       (TeletextView *         view,
 
320
                                 guint                  column,
 
321
                                 guint                  row,
 
322
                                 guint                  columns,
 
323
                                 vbi3_size              size,
 
324
                                 gboolean               flash)
 
325
{
 
326
  struct ttx_patch *p;
 
327
  struct ttx_patch *end;
 
328
  gint pw, ph;
 
329
  gint ux, uy;
 
330
  guint endcol;
 
331
 
 
332
  g_assert (NULL != view->unscaled_on);
 
333
  g_assert (NULL != view->unscaled_off);
 
334
 
 
335
  end = view->patches + view->n_patches;
 
336
 
 
337
  endcol = column + columns;
 
338
 
 
339
  for (p = view->patches; p < end; ++p)
 
340
    if (p->row == row
 
341
        && p->column < endcol
 
342
        && (p->column + p->columns) > column)
 
343
      {
 
344
        /* Patches overlap, we replace the old one. */
 
345
        destroy_patch (p);
 
346
        break;
 
347
      }
 
348
 
 
349
  if (p >= end)
 
350
    {
 
351
      guint size;
 
352
 
 
353
      size = (view->n_patches + 1) * sizeof (*view->patches); 
 
354
      view->patches = g_realloc (view->patches, size);
 
355
 
 
356
      p = view->patches + view->n_patches;
 
357
      ++view->n_patches;
 
358
    }
 
359
 
 
360
  p->column             = column;
 
361
  p->row                = row;
 
362
  p->scaled_on          = NULL;
 
363
  p->scaled_off         = NULL;
 
364
  p->unscaled_off       = NULL;
 
365
  p->columns            = columns;
 
366
  p->phase              = 0;
 
367
  p->flash              = flash;
 
368
  p->dirty              = TRUE;
 
369
 
 
370
  switch (size)
 
371
    {
 
372
    case VBI3_DOUBLE_WIDTH:
 
373
      p->width = 2;
 
374
      p->height = 1;
 
375
      break;
 
376
 
 
377
    case VBI3_DOUBLE_HEIGHT:
 
378
      p->width = 1;
 
379
      p->height = 2;
 
380
      break;
 
381
 
 
382
    case VBI3_DOUBLE_SIZE:
 
383
      p->width = 2;
 
384
      p->height = 2;
 
385
      break;
 
386
 
 
387
    default:
 
388
      p->width = 1;
 
389
      p->height = 1;
 
390
      break;
 
391
    }
 
392
 
 
393
  ux = (0 == p->column) ? 0 : p->column * CW - 5;
 
394
  uy = (0 == p->row) ? 0 : p->row * CH - 5;
 
395
 
 
396
  pw = p->width * p->columns * CW + 10;
 
397
  ph = p->height * CH + 10;
 
398
 
 
399
  p->unscaled_on = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, pw, ph);
 
400
  g_assert (NULL != p->unscaled_on);
 
401
  z_pixbuf_copy_area (/* src */ view->unscaled_on, ux, uy, pw, ph,
 
402
                      /* dst */ p->unscaled_on, 0, 0);
 
403
 
 
404
  if (flash)
 
405
    {
 
406
      p->unscaled_off = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, pw, ph);
 
407
      g_assert (p->unscaled_off != NULL);
 
408
      z_pixbuf_copy_area (/* src */ view->unscaled_off, ux, uy, pw, ph,
 
409
                          /* dst */ p->unscaled_off, 0, 0);
 
410
    }
 
411
 
 
412
  if (view->scaled_on)
 
413
    {
 
414
      guint sw, sh;
 
415
      guint uw, uh;
 
416
 
 
417
      sw = gdk_pixbuf_get_width (view->scaled_on);
 
418
      sh = gdk_pixbuf_get_height (view->scaled_on);
 
419
 
 
420
      uw = gdk_pixbuf_get_width (view->unscaled_on);
 
421
      uh = gdk_pixbuf_get_height (view->unscaled_on);
 
422
 
 
423
      scale_patch (p, sw, sh, uw, uh);
 
424
    }
 
425
}
 
426
 
 
427
static void
 
428
build_patches                   (TeletextView *         view)
 
429
{
 
430
  vbi3_char *cp;
 
431
  guint row;
 
432
 
 
433
  delete_patches (view);
 
434
 
 
435
  if (!view->pg)
 
436
    return;
 
437
 
 
438
  cp = view->pg->text;
 
439
 
 
440
  for (row = 0; row < view->pg->rows; ++row)
 
441
    {
 
442
      guint col = 0;
 
443
 
 
444
      while (col < view->pg->columns)
 
445
        {
 
446
          if ((cp[col].attr & VBI3_FLASH)
 
447
              && cp[col].size <= VBI3_DOUBLE_SIZE)
 
448
            {
 
449
              guint n;
 
450
 
 
451
              for (n = 1; col + n < view->pg->columns; ++n)
 
452
                if (!(cp[col + n].attr & VBI3_FLASH)
 
453
                    || cp[col].size != cp[col + n].size)
 
454
                  break;
 
455
 
 
456
              add_patch (view, col, row, n, cp[col].size, /* flash */ TRUE);
 
457
 
 
458
              col += n;
 
459
            }
 
460
          else
 
461
            {
 
462
              ++col;
 
463
            }
 
464
        }
 
465
 
 
466
      cp += view->pg->columns;
 
467
    }
 
468
}
 
469
 
 
470
static gboolean
 
471
vbi3_page_has_flash             (const vbi3_page *      pg)
 
472
{
 
473
  const vbi3_char *cp;
 
474
  const vbi3_char *end;
 
475
  guint attr;
 
476
 
 
477
  end = pg->text + pg->rows * pg->columns;
 
478
  attr = 0;
 
479
 
 
480
  for (cp = pg->text; cp < end; ++cp)
 
481
    attr |= cp->attr;
 
482
 
 
483
  return !!(attr & VBI3_FLASH);
 
484
}
 
485
 
 
486
static void
 
487
create_empty_image              (TeletextView *         view)
 
488
{
 
489
  gchar *filename;
 
490
  GdkPixbuf *pixbuf;
 
491
  gint sw;
 
492
  gint sh;
 
493
  double sx;
 
494
  double sy;
 
495
 
 
496
  if (!view->scaled_on)
 
497
    return;
 
498
 
 
499
  filename = g_strdup_printf ("%s/vt_loading%d.jpeg",
 
500
                              PACKAGE_PIXMAPS_DIR,
 
501
                              (rand () & 1) + 1);
 
502
 
 
503
  pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
 
504
 
 
505
  g_free (filename);
 
506
 
 
507
  if (!pixbuf)
 
508
    return;
 
509
 
 
510
  sw = gdk_pixbuf_get_width (view->scaled_on);
 
511
  sh = gdk_pixbuf_get_height (view->scaled_on);
 
512
 
 
513
  sx = sw / (double) gdk_pixbuf_get_width (pixbuf);
 
514
  sy = sh / (double) gdk_pixbuf_get_height (pixbuf);
 
515
 
 
516
  gdk_pixbuf_scale (/* src */ pixbuf,
 
517
                    /* dst */ view->scaled_on, 0, 0, sw, sh,
 
518
                    /* offset */ 0.0, 0.0,
 
519
                    /* scale */ sx, sy,
 
520
                    interp_type);
 
521
 
 
522
  g_object_unref (G_OBJECT (pixbuf));
 
523
 
 
524
  delete_patches (view);
 
525
}
 
526
 
 
527
static void
 
528
create_page_images_from_pg      (TeletextView *         view)
 
529
{
 
530
  vbi3_image_format format;
 
531
  vbi3_bool success;
 
532
 
 
533
  if (!view->pg)
 
534
    {
 
535
      create_empty_image (view);
 
536
      return;
 
537
    }
 
538
 
 
539
  g_assert (NULL != view->unscaled_on);
 
540
 
 
541
  CLEAR (format);
 
542
 
 
543
  format.width = gdk_pixbuf_get_width (view->unscaled_on);
 
544
  format.height = gdk_pixbuf_get_height (view->unscaled_on);
 
545
  format.pixfmt = VBI3_PIXFMT_RGBA24_LE;
 
546
  format.bytes_per_line = gdk_pixbuf_get_rowstride (view->unscaled_on);
 
547
  format.size = format.width * format.height * 4;
 
548
 
 
549
  success = vbi3_page_draw_teletext
 
550
    (view->pg,
 
551
     gdk_pixbuf_get_pixels (view->unscaled_on),
 
552
     &format,
 
553
     VBI3_BRIGHTNESS, brightness,
 
554
     VBI3_CONTRAST, contrast,
 
555
     VBI3_REVEAL, (vbi3_bool) view->reveal,
 
556
     VBI3_FLASH_ON, TRUE,
 
557
     VBI3_END);
 
558
 
 
559
  g_assert (success);
 
560
 
 
561
  if (view->scaled_on)
 
562
    {
 
563
      gint sw;
 
564
      gint sh;
 
565
      double sx;
 
566
      double sy;
 
567
 
 
568
      sw = gdk_pixbuf_get_width (view->scaled_on);
 
569
      sh = gdk_pixbuf_get_height (view->scaled_on);
 
570
  
 
571
      sx = sw / (double) format.width;
 
572
      sy = sh / (double) format.height;
 
573
 
 
574
      gdk_pixbuf_scale (/* src */ view->unscaled_on,
 
575
                        /* dst */ view->scaled_on, 0, 0, sw, sh,
 
576
                        /* offset */ 0.0, 0.0,
 
577
                        /* scale */ sx, sy,
 
578
                        interp_type);
 
579
    }
 
580
 
 
581
  if (vbi3_page_has_flash (view->pg))
 
582
    {
 
583
      success = vbi3_page_draw_teletext
 
584
        (view->pg,
 
585
         gdk_pixbuf_get_pixels (view->unscaled_off),
 
586
         &format,
 
587
         VBI3_BRIGHTNESS, brightness,
 
588
         VBI3_CONTRAST, contrast,
 
589
         VBI3_REVEAL, (vbi3_bool) view->reveal,
 
590
         VBI3_FLASH_ON, FALSE,
 
591
         VBI3_END);
 
592
 
 
593
      g_assert (success);
 
594
 
 
595
      build_patches (view);
 
596
    }
 
597
  else
 
598
    {
 
599
      delete_patches (view);
 
600
    }
 
601
}
 
602
 
 
603
static gboolean
 
604
resize_scaled_page_image        (TeletextView *         view,
 
605
                                 gint                   width,
 
606
                                 gint                   height)
 
607
{
 
608
  if (width <= 0 || height <= 0)
 
609
    return FALSE;
 
610
 
 
611
  if (!view->scaled_on
 
612
      || width != gdk_pixbuf_get_width (view->scaled_on)
 
613
      || height != gdk_pixbuf_get_height (view->scaled_on))
 
614
    {
 
615
      double sx;
 
616
      double sy;
 
617
 
 
618
      g_assert (NULL != view->unscaled_on);
 
619
 
 
620
      if (view->scaled_on)
 
621
        g_object_unref (G_OBJECT (view->scaled_on));
 
622
 
 
623
      view->scaled_on =
 
624
        gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height);
 
625
      g_assert (NULL != view->scaled_on);
 
626
 
 
627
      if (view->pg)
 
628
        {
 
629
          sx = width / (double) gdk_pixbuf_get_width (view->unscaled_on);
 
630
          sy = height / (double) gdk_pixbuf_get_height (view->unscaled_on);
 
631
 
 
632
          gdk_pixbuf_scale (/* src */ view->unscaled_on,
 
633
                            /* dst */ view->scaled_on, 0, 0, width, height,
 
634
                            /* offset */ 0.0, 0.0,
 
635
                            /* scale */ sx, sy,
 
636
                            interp_type);
 
637
 
 
638
          scale_patches (view);
 
639
        }
 
640
      else
 
641
        {
 
642
          /* Renders "loading" directly into view->scaled_on. */
 
643
          create_empty_image (view);
 
644
        }
 
645
    }
 
646
 
 
647
  return TRUE;
 
648
}
 
649
 
 
650
/*
 
651
        Browser history
 
652
*/
 
653
 
 
654
/* Note history.stack[top - 1] is the current page. */
 
655
 
 
656
static void
 
657
history_dump                    (TeletextView *         view)
 
658
{
 
659
  guint i;
 
660
 
 
661
  fprintf (stderr, "top=%u size=%u ",
 
662
           view->history.top,
 
663
           view->history.size);
 
664
 
 
665
  for (i = 0; i < view->history.size; ++i)
 
666
    fprintf (stderr, "%03x ",
 
667
             view->history.stack[i].pgno);
 
668
 
 
669
  fputc ('\n', stderr);
 
670
}
 
671
 
 
672
static void
 
673
history_update_gui              (TeletextView *         view)
 
674
{
 
675
  GtkAction *action;
 
676
 
 
677
  action = gtk_action_group_get_action (view->action_group, "HistoryBack");
 
678
  z_action_set_sensitive (action, view->history.top >= 2);
 
679
 
 
680
  action = gtk_action_group_get_action (view->action_group, "HistoryForward");
 
681
  z_action_set_sensitive (action, view->history.top < view->history.size);
 
682
}
 
683
 
 
684
/* Push *current page* onto history stack. Actually one should push
 
685
   the page being replaced, but things are simpler this way. */
 
686
static void
 
687
history_push                    (TeletextView *         view,
 
688
                                 const vbi3_network *   nk,
 
689
                                 vbi3_pgno              pgno,
 
690
                                 vbi3_subno             subno)
 
691
{
 
692
  guint top;
 
693
 
 
694
  top = view->history.top;
 
695
 
 
696
  if (NULL == nk)
 
697
    nk = &view->req.network;
 
698
 
 
699
  if (pgno < 0x100 || pgno > 0x899) 
 
700
    return;
 
701
 
 
702
  if (top > 0)
 
703
    {
 
704
      if (page_num_equal2 (&view->history.stack[top - 1],
 
705
                           nk, pgno, subno))
 
706
        return; /* already stacked */
 
707
 
 
708
      if (top >= N_ELEMENTS (view->history.stack))
 
709
        {
 
710
          top = N_ELEMENTS (view->history.stack) - 1;
 
711
 
 
712
          memmove (view->history.stack,
 
713
                   view->history.stack + 1,
 
714
                   top * sizeof (*view->history.stack));
 
715
        }
 
716
      else if (view->history.size > top)
 
717
        {
 
718
          if (page_num_equal2 (&view->history.stack[top],
 
719
                               nk, pgno, subno))
 
720
            {
 
721
              /* Apparently we move forward in history, no new page. */
 
722
              view->history.top = top + 1;
 
723
              history_update_gui (view);
 
724
              return;
 
725
            }
 
726
 
 
727
          /* Will discard future branch. */
 
728
        }
 
729
    }
 
730
 
 
731
  page_num_set (&view->history.stack[top], nk, pgno, subno);
 
732
 
 
733
  ++top;
 
734
 
 
735
  view->history.top = top;
 
736
  view->history.size = top;
 
737
 
 
738
  history_update_gui (view);
 
739
 
 
740
  if (0)
 
741
    history_dump (view);
 
742
}
 
743
 
 
744
static void
 
745
history_back_action             (GtkAction *            action _unused_,
 
746
                                 TeletextView *         view)
 
747
{
 
748
  guint top;
 
749
 
 
750
  top = view->history.top;
 
751
 
 
752
  if (top >= 2)
 
753
    {
 
754
      page_num *sp;
 
755
 
 
756
      view->history.top = top - 1;
 
757
      history_update_gui (view);
 
758
 
 
759
      sp = &view->history.stack[top - 2];
 
760
 
 
761
      teletext_view_load_page (view, &sp->network, sp->pgno, sp->subno);
 
762
    }
 
763
}
 
764
 
 
765
static PyObject *
 
766
py_ttx_history_prev             (PyObject *             self _unused_,
 
767
                                 PyObject *             args _unused_)
 
768
{
 
769
  TeletextView *view;
 
770
 
 
771
  if (!(view = teletext_view_from_widget (python_command_widget ())))
 
772
    py_return_true;
 
773
 
 
774
  history_back_action (NULL, view);
 
775
 
 
776
  py_return_true;
 
777
}
 
778
 
 
779
static void
 
780
history_forward_action          (GtkAction *            action _unused_,
 
781
                                 TeletextView *         view)
 
782
{
 
783
  guint top;
 
784
 
 
785
  top = view->history.top;
 
786
 
 
787
  if (top < view->history.size)
 
788
    {
 
789
      page_num *sp;
 
790
 
 
791
      view->history.top = top + 1;
 
792
      history_update_gui (view);
 
793
 
 
794
      sp = &view->history.stack[top];
 
795
 
 
796
      teletext_view_load_page (view, &sp->network, sp->pgno, sp->subno);
 
797
    }
 
798
}
 
799
 
 
800
static PyObject *
 
801
py_ttx_history_next             (PyObject *             self _unused_,
 
802
                                 PyObject *             args _unused_)
 
803
{
 
804
  TeletextView *view;
 
805
 
 
806
  if (!(view = teletext_view_from_widget (python_command_widget ())))
 
807
    py_return_true;
 
808
 
 
809
  history_forward_action (NULL, view);
 
810
 
 
811
  py_return_true;
 
812
}
 
813
 
 
814
/*
 
815
        Page rendering
 
816
*/
 
817
 
 
818
static void
 
819
update_cursor_shape             (TeletextView *         view)
 
820
{
 
821
  gint x;
 
822
  gint y;
 
823
  GdkModifierType mask;
 
824
  vbi3_link link;
 
825
  gchar *buffer;
 
826
  gboolean success;
 
827
 
 
828
  gdk_window_get_pointer (GTK_WIDGET (view)->window, &x, &y, &mask);
 
829
 
 
830
  link.type = VBI3_LINK_NONE;
 
831
 
 
832
  success = teletext_view_vbi3_link_from_pointer_position (view, &link, x, y);
 
833
 
 
834
  switch (link.type)
 
835
    {
 
836
    case VBI3_LINK_PAGE:
 
837
      buffer = g_strdup_printf (_(" Page %x"), link.pgno);
 
838
      goto show;
 
839
 
 
840
    case VBI3_LINK_SUBPAGE:
 
841
      buffer = g_strdup_printf (_(" Subpage %x"), link.subno & 0xFF);
 
842
      goto show;
 
843
 
 
844
    case VBI3_LINK_HTTP:
 
845
    case VBI3_LINK_FTP:
 
846
    case VBI3_LINK_EMAIL:
 
847
      buffer = g_strconcat (" ", link.url, NULL);
 
848
      goto show;
 
849
 
 
850
    show:
 
851
      if (!view->cursor_over_link)
 
852
        {
 
853
          view->cursor_over_link = TRUE;
 
854
 
 
855
          if (view->appbar)
 
856
            gnome_appbar_push (GNOME_APPBAR (view->appbar), buffer);
 
857
 
 
858
          gdk_window_set_cursor (GTK_WIDGET (view)->window, cursor_link);
 
859
        }
 
860
      else
 
861
        {
 
862
          if (view->appbar)
 
863
            gnome_appbar_set_status (GNOME_APPBAR (view->appbar), buffer);
 
864
        }
 
865
 
 
866
      g_free (buffer);
 
867
 
 
868
      break;
 
869
 
 
870
    default:
 
871
      if (view->cursor_over_link)
 
872
        {
 
873
          view->cursor_over_link = FALSE;
 
874
 
 
875
          if (view->appbar)
 
876
            gnome_appbar_pop (GNOME_APPBAR (view->appbar));
 
877
 
 
878
          gdk_window_set_cursor (GTK_WIDGET (view)->window, cursor_normal);
 
879
        }
 
880
 
 
881
      break;
 
882
    }
 
883
 
 
884
  if (success)
 
885
    vbi3_link_destroy (&link);
 
886
}
 
887
 
 
888
static gboolean
 
889
redraw_view                     (TeletextView *         view)
 
890
{
 
891
  GtkAction *action;
 
892
  GdkWindow *window;
 
893
  vbi3_page *pg;
 
894
  gint width;
 
895
  gint height;
 
896
 
 
897
  action = gtk_action_group_get_action (view->action_group, "Export");
 
898
  z_action_set_sensitive (action,
 
899
                          NULL != vbi3_export_info_enum (0)
 
900
                          && view->pg
 
901
                          && view->pg->pgno >= 0x100);
 
902
 
 
903
  if (view->selecting)
 
904
    return FALSE;
 
905
 
 
906
  create_page_images_from_pg (view);
 
907
 
 
908
  apply_patches (view, /* draw */ FALSE);
 
909
 
 
910
  if (!(window = GTK_WIDGET (view)->window))
 
911
    return FALSE;
 
912
 
 
913
  gdk_window_get_geometry (window,
 
914
                           /* x */ NULL,
 
915
                           /* y */ NULL,
 
916
                           &width,
 
917
                           &height,
 
918
                           /* depth */ NULL);
 
919
 
 
920
  /* XXX if we have rolling header and this is an old page from
 
921
     cache we'll show an old header. */
 
922
 
 
923
  /* Trigger expose event for later redrawing. */
 
924
  gdk_window_clear_area_e (window, 0, 0, width, height);
 
925
 
 
926
  if ((pg = view->pg))
 
927
    {
 
928
      if (view->toolbar)
 
929
        {
 
930
          if (view->freezed)
 
931
            teletext_toolbar_set_url (view->toolbar,
 
932
                                      pg->pgno,
 
933
                                      pg->subno);
 
934
          else
 
935
            teletext_toolbar_set_url (view->toolbar,
 
936
                                      view->req.pgno,
 
937
                                      view->req.subno);
 
938
        }
 
939
 
 
940
      /* Note does nothing if this page is already at TOS. */ 
 
941
      history_push (view, pg->network, pg->pgno, pg->subno);
 
942
    }
 
943
 
 
944
  update_cursor_shape (view);
 
945
  
 
946
  return TRUE;
 
947
}
 
948
 
 
949
static void
 
950
redraw_all_views                (void)
 
951
{
 
952
  GList *p;
 
953
 
 
954
  for (p = g_list_first (teletext_views); p; p = p->next)
 
955
    {
 
956
      TeletextView *view = p->data;
 
957
 
 
958
      if (view->pg)
 
959
        redraw_view (view);
 
960
    }
 
961
}
 
962
 
 
963
static vbi3_page *
 
964
get_page                        (const vbi3_network *   nk,
 
965
                                 vbi3_pgno              pgno,
 
966
                                 vbi3_subno             subno,
 
967
                                 vbi3_charset_code      charset)
 
968
{
 
969
  vbi3_page *pg;
 
970
 
 
971
  if (nk && vbi3_network_is_anonymous (nk))
 
972
    nk = NULL; /* use currently received network */
 
973
 
 
974
  if ((int) charset >= 0)
 
975
    {
 
976
      pg = vbi3_teletext_decoder_get_page
 
977
        (td, nk, pgno, subno,
 
978
         VBI3_41_COLUMNS, TRUE, /* add_column, */
 
979
         /* VBI3_PANELS, FALSE, */
 
980
         VBI3_NAVIGATION, navigation,
 
981
         VBI3_HYPERLINKS, hyperlinks,
 
982
         /* VBI3_PDC_LINKS, TRUE, */
 
983
         VBI3_WST_LEVEL, teletext_level,
 
984
         VBI3_OVERRIDE_CHARSET_0, charset,
 
985
         VBI3_END);
 
986
    }
 
987
  else
 
988
    {
 
989
      pg = vbi3_teletext_decoder_get_page
 
990
        (td, nk, pgno, subno,
 
991
         VBI3_41_COLUMNS, TRUE, /* add_column, */
 
992
         /* VBI3_PANELS, FALSE, */
 
993
         VBI3_NAVIGATION, navigation,
 
994
         VBI3_HYPERLINKS, hyperlinks,
 
995
         /* VBI3_PDC_LINKS, TRUE, */
 
996
         VBI3_WST_LEVEL, teletext_level,
 
997
         VBI3_DEFAULT_CHARSET_0, default_charset,
 
998
         VBI3_END);
 
999
    }
 
1000
 
 
1001
  return pg;
 
1002
}
 
1003
 
 
1004
static void
 
1005
reformat_view                   (TeletextView *         view)
 
1006
{
 
1007
  vbi3_page *pg;
 
1008
 
 
1009
  if ((pg = get_page (view->pg->network,
 
1010
                      view->pg->pgno,
 
1011
                      view->pg->subno,
 
1012
                      view->charset)))
 
1013
    {
 
1014
      vbi3_page_unref (view->pg);
 
1015
      view->pg = pg;
 
1016
 
 
1017
      redraw_view (view);
 
1018
    }
 
1019
}
 
1020
 
 
1021
static void
 
1022
reformat_all_views              (void)
 
1023
{
 
1024
  GList *p;
 
1025
 
 
1026
  for (p = g_list_first (teletext_views); p; p = p->next)
 
1027
    {
 
1028
      TeletextView *view = p->data;
 
1029
 
 
1030
      if (view->selecting)
 
1031
        continue;
 
1032
 
 
1033
      if (view->freezed)
 
1034
        continue;
 
1035
 
 
1036
      if (view->pg)
 
1037
        reformat_view (view);
 
1038
    }
 
1039
}
 
1040
 
 
1041
static void
 
1042
update_header                   (TeletextView *         view,
 
1043
                                 const vbi3_event *     ev)
 
1044
{
 
1045
  vbi3_image_format format;
 
1046
  vbi3_page *pg;
 
1047
  vbi3_bool success;
 
1048
  guint column;
 
1049
  guint i;
 
1050
  uint8_t *buffer;
 
1051
 
 
1052
  if (!view->pg)
 
1053
    return;
 
1054
 
 
1055
  if (view->pg->pgno == view->req.pgno
 
1056
      || !rolling_header)
 
1057
    {
 
1058
      if (!live_clock)
 
1059
        return;
 
1060
 
 
1061
      column = 32; /* only clock */
 
1062
    }
 
1063
  else
 
1064
    {
 
1065
      if (!(ev->ev.ttx_page.flags & VBI3_SERIAL))
 
1066
        if (0 != ((ev->ev.ttx_page.pgno ^ view->req.pgno) & 0xF00))
 
1067
          return;
 
1068
 
 
1069
      column = 8; /* page number and clock */
 
1070
    }
 
1071
 
 
1072
  if ((int) view->charset >= 0)
 
1073
    {
 
1074
      pg = vbi3_teletext_decoder_get_page
 
1075
        (td,
 
1076
         ev->network,
 
1077
         ev->ev.ttx_page.pgno,
 
1078
         ev->ev.ttx_page.subno,
 
1079
         VBI3_41_COLUMNS, TRUE,
 
1080
         VBI3_HEADER_ONLY, TRUE,
 
1081
         VBI3_WST_LEVEL, VBI3_WST_LEVEL_1p5,
 
1082
         VBI3_OVERRIDE_CHARSET_0, view->charset,
 
1083
         VBI3_END);
 
1084
    } else {
 
1085
      pg = vbi3_teletext_decoder_get_page
 
1086
        (td,
 
1087
         ev->network,
 
1088
         ev->ev.ttx_page.pgno,
 
1089
         ev->ev.ttx_page.subno,
 
1090
         VBI3_41_COLUMNS, TRUE,
 
1091
         VBI3_HEADER_ONLY, TRUE,
 
1092
         VBI3_WST_LEVEL, VBI3_WST_LEVEL_1p5,
 
1093
         VBI3_DEFAULT_CHARSET_0, default_charset,
 
1094
         VBI3_END);
 
1095
    }
 
1096
 
 
1097
  if (!pg)
 
1098
    return;
 
1099
 
 
1100
  for (i = column; i < 40; ++i)
 
1101
    if (view->pg->text[i].unicode != pg->text[i].unicode)
 
1102
      break;
 
1103
 
 
1104
  if (i >= 40) {
 
1105
    vbi3_page_unref (pg);
 
1106
    return;
 
1107
  }
 
1108
 
 
1109
  /* Some networks put level 2.5 elements into the header, keep that. */
 
1110
  if (view->pg->pgno == view->req.pgno)
 
1111
    for (i = 32; i < 40; ++i)
 
1112
      {
 
1113
        guint unicode;
 
1114
 
 
1115
        unicode = pg->text[i].unicode;
 
1116
        pg->text[i] = view->pg->text[i];
 
1117
        pg->text[i].unicode = unicode;
 
1118
      }
 
1119
 
 
1120
  CLEAR (format);
 
1121
 
 
1122
  format.width = gdk_pixbuf_get_width (view->unscaled_on);
 
1123
  format.height = gdk_pixbuf_get_height (view->unscaled_on);
 
1124
  format.pixfmt = VBI3_PIXFMT_RGBA24_LE;
 
1125
  format.bytes_per_line = gdk_pixbuf_get_rowstride (view->unscaled_on);
 
1126
  format.size = format.width * format.height * 4;
 
1127
 
 
1128
  buffer = gdk_pixbuf_get_pixels (view->unscaled_on);
 
1129
  buffer += column * (CW * 4);
 
1130
 
 
1131
  success = vbi3_page_draw_teletext_region (pg,
 
1132
                                           buffer,
 
1133
                                           &format,
 
1134
                                           column,
 
1135
                                           /* row */ 0,
 
1136
                                           /* width */ 40 - column,
 
1137
                                           /* height */ 1,
 
1138
                                           VBI3_BRIGHTNESS, brightness,
 
1139
                                           VBI3_CONTRAST, contrast,
 
1140
                                           VBI3_REVEAL, TRUE,
 
1141
                                           VBI3_FLASH_ON, TRUE,
 
1142
                                           VBI3_END);
 
1143
  g_assert (success);
 
1144
 
 
1145
  add_patch (view,
 
1146
             column,
 
1147
             /* row */ 0,
 
1148
             /* columns */ 40 - column,
 
1149
             VBI3_NORMAL_SIZE,
 
1150
             /* flash */ FALSE);
 
1151
 
 
1152
  vbi3_page_unref (pg);
 
1153
}
 
1154
 
 
1155
static vbi3_bool
 
1156
view_vbi3_event_handler         (const vbi3_event *     ev,
 
1157
                                 void *                 user_data _unused_)
 
1158
{
 
1159
  GList *p;
 
1160
 
 
1161
  switch (ev->type) {
 
1162
  case VBI3_EVENT_NETWORK:
 
1163
    if (0)
 
1164
      {
 
1165
        _vbi3_network_dump (ev->network, stderr);
 
1166
        fputc ('\n', stderr);
 
1167
      }
 
1168
 
 
1169
    for (p = g_list_first (teletext_views); p; p = p->next)
 
1170
      {
 
1171
        TeletextView *view;
 
1172
 
 
1173
        view = (TeletextView *) p->data;
 
1174
 
 
1175
        if (!vbi3_network_is_anonymous (&view->req.network))
 
1176
          continue;
 
1177
 
 
1178
        if ((unsigned int) -1 != view->charset)
 
1179
          {
 
1180
            /* XXX should use default charset of the new network
 
1181
               from config if one exists. */
 
1182
            view->charset = -1;
 
1183
 
 
1184
            g_signal_emit (view, signals[CHARSET_CHANGED], 0);
 
1185
          }
 
1186
 
 
1187
        if (view->selecting)
 
1188
          continue;
 
1189
 
 
1190
        if (view->freezed)
 
1191
          continue;
 
1192
 
 
1193
        vbi3_page_unref (view->pg);
 
1194
 
 
1195
        /* Change to view of same page of the new network, such that
 
1196
           header updates match the rest of the page.  When the page
 
1197
           is not cached redraw_view() displays "loading". */
 
1198
        view->pg = get_page (ev->network,
 
1199
                             view->req.pgno,
 
1200
                             view->req.subno,
 
1201
                             view->charset);
 
1202
 
 
1203
        redraw_view (view);
 
1204
      }
 
1205
 
 
1206
    break;
 
1207
 
 
1208
  case VBI3_EVENT_TTX_PAGE:
 
1209
    for (p = g_list_first (teletext_views); p; p = p->next)
 
1210
      {
 
1211
        TeletextView *view;
 
1212
 
 
1213
        view = (TeletextView *) p->data;
 
1214
 
 
1215
        if (view->selecting)
 
1216
          continue;
 
1217
 
 
1218
        if (view->freezed)
 
1219
          continue;
 
1220
 
 
1221
        if (!vbi3_network_is_anonymous (&view->req.network)
 
1222
            && !vbi3_network_equal (&view->req.network, ev->network))
 
1223
          continue;
 
1224
 
 
1225
        if (ev->ev.ttx_page.pgno == view->req.pgno
 
1226
            && (VBI3_ANY_SUBNO == view->req.subno
 
1227
                || ev->ev.ttx_page.subno == view->req.subno))
 
1228
          {
 
1229
            vbi3_page *pg;
 
1230
 
 
1231
            if ((pg = get_page (ev->network,
 
1232
                                ev->ev.ttx_page.pgno,
 
1233
                                ev->ev.ttx_page.subno,
 
1234
                                view->charset)))
 
1235
              {
 
1236
                vbi3_page_unref (view->pg);
 
1237
                view->pg = pg;
 
1238
 
 
1239
                redraw_view (view);
 
1240
              }
 
1241
          }
 
1242
        else if (ev->ev.ttx_page.flags & VBI3_ROLL_HEADER)
 
1243
          {
 
1244
            update_header (view, ev);
 
1245
          }
 
1246
      }
 
1247
 
 
1248
    break;
 
1249
 
 
1250
  default:
 
1251
    break;
 
1252
  }
 
1253
 
 
1254
  return FALSE; /* pass on */
 
1255
}
 
1256
 
 
1257
static void
 
1258
set_hold                        (TeletextView *         view,
 
1259
                                 gboolean               hold)
 
1260
{
 
1261
  if (view->toolbar)
 
1262
    teletext_toolbar_set_hold (view->toolbar, hold);
 
1263
 
 
1264
  if (hold != view->hold)
 
1265
    {
 
1266
      const vbi3_page *pg;
 
1267
 
 
1268
      view->hold = hold;
 
1269
 
 
1270
      if ((pg = view->pg))
 
1271
        {
 
1272
          if (hold)
 
1273
            view->req.subno = pg->subno;
 
1274
          else
 
1275
            view->req.subno = VBI3_ANY_SUBNO;
 
1276
        }
 
1277
    }
 
1278
}
 
1279
 
 
1280
static void
 
1281
monitor_pgno                    (TeletextView *         view,
 
1282
                                 const vbi3_network *   nk,
 
1283
                                 vbi3_pgno              pgno,
 
1284
                                 vbi3_subno             subno,
 
1285
                                 vbi3_charset_code      charset)
 
1286
{
 
1287
  vbi3_page *pg;
 
1288
 
 
1289
  view->freezed = FALSE;
 
1290
 
 
1291
  if (!nk)
 
1292
    nk = &view->req.network;
 
1293
 
 
1294
  page_num_set (&view->req, nk, pgno, subno);
 
1295
 
 
1296
  g_signal_emit (view, signals[REQUEST_CHANGED], 0);
 
1297
 
 
1298
  pg = NULL;
 
1299
 
 
1300
  if (pgno >= 0x100 && pgno <= 0x899)
 
1301
    pg = get_page (nk, pgno, subno, charset);
 
1302
 
 
1303
  if (pg || !rolling_header)
 
1304
    {
 
1305
      vbi3_page_unref (view->pg);
 
1306
      view->pg = pg; /* can be NULL */
 
1307
    }
 
1308
 
 
1309
  redraw_view (view);
 
1310
}
 
1311
 
 
1312
static gboolean
 
1313
deferred_load_timeout           (gpointer               user_data)
 
1314
{
 
1315
  TeletextView *view = user_data;
 
1316
 
 
1317
  view->deferred.timeout_id = NO_SOURCE_ID;
 
1318
 
 
1319
  monitor_pgno (view,
 
1320
                &view->deferred.network,
 
1321
                view->deferred.pgno,
 
1322
                view->deferred.subno,
 
1323
                view->charset);
 
1324
 
 
1325
  view->deferred_load = FALSE;
 
1326
 
 
1327
  return FALSE; /* don't call again */
 
1328
}
 
1329
 
 
1330
void
 
1331
teletext_view_load_page         (TeletextView *         view,
 
1332
                                 const vbi3_network *   nk,
 
1333
                                 vbi3_pgno              pgno,
 
1334
                                 vbi3_subno             subno)
 
1335
{
 
1336
  view->hold = (VBI3_ANY_SUBNO != subno);
 
1337
  set_hold (view, view->hold);
 
1338
 
 
1339
  /* XXX this was intended to override a subtitle page character set
 
1340
     code, but on a second thought resetting on page change is strange. */
 
1341
  if (0)
 
1342
    if ((int) view->charset >= 0)
 
1343
      {
 
1344
        /* XXX actually we should query config for a
 
1345
           network-pgno-subno - charset pair, reset to default
 
1346
           only if none exists. */
 
1347
        view->charset = -1;
 
1348
        
 
1349
        g_signal_emit (view, signals[CHARSET_CHANGED], 0);
 
1350
      }
 
1351
 
 
1352
  if (view->toolbar)
 
1353
    teletext_toolbar_set_url (view->toolbar, pgno, subno);
 
1354
 
 
1355
  if (view->appbar)
 
1356
    {
 
1357
      gchar *buffer;
 
1358
 
 
1359
      if (pgno >= 0x100 && pgno <= 0x8FF)
 
1360
        {
 
1361
          if (0 == subno || VBI3_ANY_SUBNO == subno)
 
1362
            buffer = g_strdup_printf (_("Loading page %X..."), pgno);
 
1363
          else
 
1364
            buffer = g_strdup_printf (_("Loading page %X.%02X..."),
 
1365
                                      pgno, subno & 0x7F);
 
1366
        }
 
1367
      else
 
1368
        {
 
1369
          buffer = g_strdup_printf ("Invalid page %X.%X", pgno, subno);
 
1370
        }
 
1371
 
 
1372
      gnome_appbar_set_status (view->appbar, buffer);
 
1373
 
 
1374
      g_free (buffer);
 
1375
    }
 
1376
 
 
1377
  gtk_widget_grab_focus (GTK_WIDGET (view));
 
1378
 
 
1379
  if (nk)
 
1380
    network_set (&view->deferred.network, nk);
 
1381
  else
 
1382
    network_set (&view->deferred.network, &view->req.network);
 
1383
 
 
1384
  view->deferred.pgno = pgno;
 
1385
  view->deferred.subno = subno;
 
1386
 
 
1387
  if (view->deferred.timeout_id > 0)
 
1388
    g_source_remove (view->deferred.timeout_id);
 
1389
 
 
1390
  if (view->deferred_load)
 
1391
    {
 
1392
      view->deferred.timeout_id =
 
1393
        g_timeout_add (300, (GSourceFunc) deferred_load_timeout, view);
 
1394
    }
 
1395
  else
 
1396
    {
 
1397
      view->deferred.timeout_id = NO_SOURCE_ID;
 
1398
 
 
1399
      monitor_pgno (view, nk, pgno, subno, view->charset);
 
1400
    }
 
1401
 
 
1402
  z_update_gui ();
 
1403
}
 
1404
 
 
1405
void
 
1406
teletext_view_show_page         (TeletextView *         view,
 
1407
                                 vbi3_page *            pg)
 
1408
{
 
1409
  if (NULL == pg)
 
1410
    return;
 
1411
 
 
1412
  view->hold = TRUE;
 
1413
  set_hold (view, view->hold);
 
1414
 
 
1415
  if (view->toolbar)
 
1416
    teletext_toolbar_set_url (view->toolbar, pg->pgno, pg->subno);
 
1417
 
 
1418
  if (view->appbar)
 
1419
    gnome_appbar_set_status (view->appbar, "");
 
1420
 
 
1421
  gtk_widget_grab_focus (GTK_WIDGET (view));
 
1422
 
 
1423
  if (view->deferred.timeout_id > 0)
 
1424
    g_source_remove (view->deferred.timeout_id);
 
1425
 
 
1426
  page_num_set (&view->req, pg->network, pg->pgno, pg->subno);
 
1427
 
 
1428
  g_signal_emit (view, signals[REQUEST_CHANGED], 0);
 
1429
 
 
1430
  if ((int) view->charset >= 0)
 
1431
    {
 
1432
      /* XXX actually we should query config for a
 
1433
         network-pgno-subno - charset pair, reset to default
 
1434
         only if none exists. */
 
1435
      view->charset = -1;
 
1436
 
 
1437
      g_signal_emit (view, signals[CHARSET_CHANGED], 0);
 
1438
    }
 
1439
 
 
1440
  vbi3_page_unref (view->pg);
 
1441
  view->pg = vbi3_page_ref (pg);
 
1442
 
 
1443
  view->freezed = TRUE;
 
1444
 
 
1445
  redraw_view (view);
 
1446
 
 
1447
  z_update_gui ();
 
1448
}
 
1449
 
 
1450
/*
 
1451
        User interface
 
1452
*/
 
1453
 
 
1454
TeletextView *
 
1455
teletext_view_from_widget       (GtkWidget *            widget)
 
1456
{
 
1457
  TeletextView *view;
 
1458
 
 
1459
  while (!(view = (TeletextView *)
 
1460
           g_object_get_data (G_OBJECT (widget), "TeletextView")))
 
1461
    {
 
1462
      if (!(widget = widget->parent))
 
1463
        return NULL;
 
1464
    }
 
1465
 
 
1466
  return view;
 
1467
}
 
1468
 
 
1469
static void
 
1470
set_transient_for               (GtkWindow *            window,
 
1471
                                 TeletextView *         view)
 
1472
{
 
1473
  GtkWidget *parent;
 
1474
 
 
1475
  parent = GTK_WIDGET (view);
 
1476
 
 
1477
  while (parent)
 
1478
    {
 
1479
      if (GTK_IS_WINDOW (parent))
 
1480
        {
 
1481
          gtk_window_set_transient_for (window, GTK_WINDOW (parent));
 
1482
          break;
 
1483
        }
 
1484
 
 
1485
      parent = parent->parent;
 
1486
    }
 
1487
}
 
1488
 
 
1489
/* Note on success you must vbi3_link_destroy. */
 
1490
gboolean
 
1491
teletext_view_vbi3_link_from_pointer_position
 
1492
                                (TeletextView *         view,
 
1493
                                 vbi3_link *            lk,
 
1494
                                 gint                   x,
 
1495
                                 gint                   y)
 
1496
{
 
1497
  GdkWindow *window;
 
1498
  const vbi3_page *pg;
 
1499
  gint width;
 
1500
  gint height;
 
1501
  guint row;
 
1502
  guint column;
 
1503
  const vbi3_char *ac;
 
1504
 
 
1505
  vbi3_link_init (lk);
 
1506
 
 
1507
  if (x < 0 || y < 0)
 
1508
    return FALSE;
 
1509
 
 
1510
  if (!(pg = view->pg))
 
1511
    return FALSE;
 
1512
 
 
1513
  if (!(window = GTK_WIDGET (view)->window))
 
1514
    return FALSE;
 
1515
 
 
1516
  gdk_window_get_geometry (window,
 
1517
                           /* x */ NULL,
 
1518
                           /* y */ NULL,
 
1519
                           &width,
 
1520
                           &height,
 
1521
                           /* depth */ NULL);
 
1522
 
 
1523
  if (width <= 0 || height <= 0)
 
1524
    return FALSE;
 
1525
 
 
1526
  column = (x * pg->columns) / width;
 
1527
  row = (y * pg->rows) / height;
 
1528
 
 
1529
  if (column >= pg->columns
 
1530
      || row >= pg->rows)
 
1531
    return FALSE;
 
1532
 
 
1533
  ac = pg->text + row * pg->columns + column;
 
1534
 
 
1535
  if (!(ac->attr & VBI3_LINK))
 
1536
    return FALSE;
 
1537
 
 
1538
  return vbi3_page_get_hyperlink (pg, lk, column, row);
 
1539
}
 
1540
 
 
1541
/*
 
1542
        Page commands
 
1543
*/
 
1544
 
 
1545
static gint
 
1546
decimal_subno                   (vbi3_subno             subno)
 
1547
{
 
1548
  if (0 == subno || (guint) subno > 0x99)
 
1549
    return -1; /* any */
 
1550
  else
 
1551
    return vbi3_bcd2dec (subno);
 
1552
}
 
1553
 
 
1554
static PyObject *
 
1555
py_ttx_hold                     (PyObject *             self _unused_,
 
1556
                                 PyObject *             args)
 
1557
{
 
1558
  TeletextView *view;
 
1559
  gint hold;
 
1560
 
 
1561
  if (!(view = teletext_view_from_widget (python_command_widget ())))
 
1562
    py_return_true;
 
1563
 
 
1564
  hold = -1;
 
1565
 
 
1566
  if (!ParseTuple (args, "|i", &hold))
 
1567
    g_error ("zapping.ttx_hold(|i)");
 
1568
 
 
1569
  if (hold < 0)
 
1570
    hold = !view->hold;
 
1571
  else
 
1572
    hold = !!hold;
 
1573
 
 
1574
  set_hold (view, hold);
 
1575
 
 
1576
  py_return_true;
 
1577
}
 
1578
 
 
1579
static PyObject *
 
1580
py_ttx_reveal                   (PyObject *             self _unused_,
 
1581
                                 PyObject *             args)
 
1582
{
 
1583
  TeletextView *view;
 
1584
  gint reveal;
 
1585
 
 
1586
  if (!(view = teletext_view_from_widget (python_command_widget ())))
 
1587
    py_return_true;
 
1588
 
 
1589
  reveal = -1;
 
1590
 
 
1591
  if (!ParseTuple (args, "|i", &reveal))
 
1592
    g_error ("zapping.ttx_reveal(|i)");
 
1593
 
 
1594
  if (reveal < 0)
 
1595
    reveal = !view->reveal;
 
1596
  else
 
1597
    reveal = !!reveal;
 
1598
 
 
1599
  if (view->toolbar)
 
1600
    teletext_toolbar_set_reveal (view->toolbar, reveal);
 
1601
 
 
1602
  view->reveal = reveal;
 
1603
 
 
1604
  if (view->pg)
 
1605
    redraw_view (view);
 
1606
 
 
1607
  py_return_true;
 
1608
}
 
1609
 
 
1610
static PyObject *
 
1611
py_ttx_open                     (PyObject *             self _unused_,
 
1612
                                 PyObject *             args)
 
1613
{
 
1614
  TeletextView *view;
 
1615
  int page;
 
1616
  int subpage;
 
1617
  vbi3_pgno pgno;
 
1618
  vbi3_subno subno;
 
1619
 
 
1620
  if (!(view = teletext_view_from_widget (python_command_widget ())))
 
1621
    py_return_true;
 
1622
 
 
1623
  page = 100;
 
1624
  subpage = -1;
 
1625
 
 
1626
  if (!ParseTuple (args, "|ii", &page, &subpage))
 
1627
    g_error ("zapping.ttx_open_new(|ii)");
 
1628
 
 
1629
  if (page >= 100 && page <= 899)
 
1630
    pgno = vbi3_dec2bcd (page);
 
1631
  else
 
1632
    py_return_false;
 
1633
 
 
1634
  if (subpage < 0)
 
1635
    subno = VBI3_ANY_SUBNO;
 
1636
  else if ((guint) subpage <= 99)
 
1637
    subno = vbi3_dec2bcd (subpage);
 
1638
  else
 
1639
    py_return_false;
 
1640
 
 
1641
  teletext_view_load_page (view, &view->req.network, pgno, subno);
 
1642
 
 
1643
  py_return_true;
 
1644
}
 
1645
 
 
1646
static PyObject *
 
1647
py_ttx_page_incr                (PyObject *             self _unused_,
 
1648
                                 PyObject *             args)
 
1649
{
 
1650
  TeletextView *view;
 
1651
  vbi3_pgno pgno;
 
1652
  gint value;
 
1653
 
 
1654
  if (!(view = teletext_view_from_widget (python_command_widget ())))
 
1655
    py_return_true;
 
1656
 
 
1657
  value = +1;
 
1658
 
 
1659
  if (!ParseTuple (args, "|i", &value))
 
1660
    g_error ("zapping.ttx_page_incr(|i)");
 
1661
 
 
1662
  if (abs (value) > 999)
 
1663
    py_return_false;
 
1664
 
 
1665
  if (value < 0)
 
1666
    value += 1000;
 
1667
 
 
1668
  pgno = vbi3_add_bcd (view->req.pgno, vbi3_dec2bcd (value)) & 0xFFF;
 
1669
 
 
1670
  if (pgno < 0x100)
 
1671
    pgno = 0x800 + (pgno & 0xFF);
 
1672
  else if (pgno > 0x899)
 
1673
    pgno = 0x100 + (pgno & 0xFF);
 
1674
 
 
1675
  teletext_view_load_page (view, &view->req.network, pgno, VBI3_ANY_SUBNO);
 
1676
 
 
1677
  py_return_true;
 
1678
}
 
1679
 
 
1680
static PyObject *
 
1681
py_ttx_subpage_incr             (PyObject *             self _unused_,
 
1682
                                 PyObject *             args)
 
1683
{
 
1684
  TeletextView *view;
 
1685
  vbi3_subno subno;
 
1686
  gint value;
 
1687
 
 
1688
  if (!(view = teletext_view_from_widget (python_command_widget ())))
 
1689
    py_return_true;
 
1690
 
 
1691
  value = +1;
 
1692
 
 
1693
  if (!ParseTuple (args, "|i", &value))
 
1694
    g_error ("zapping.ttx_subpage_incr(|i)");
 
1695
 
 
1696
  if (abs (value) > 99)
 
1697
    py_return_false;
 
1698
 
 
1699
  if (value < 0)
 
1700
    value += 100; /* XXX should use actual or anounced number of subp */
 
1701
 
 
1702
  subno = view->req.subno;
 
1703
  if (VBI3_ANY_SUBNO == view->req.subno)
 
1704
    {
 
1705
      subno = 0;
 
1706
      if (view->pg)
 
1707
        subno = view->pg->subno;
 
1708
    }
 
1709
 
 
1710
  subno = vbi3_add_bcd (subno, vbi3_dec2bcd (value)) & 0xFF;
 
1711
 
 
1712
  teletext_view_load_page (view, &view->req.network, view->req.pgno, subno);
 
1713
 
 
1714
  py_return_true;
 
1715
}
 
1716
 
 
1717
static vbi3_pgno
 
1718
default_home_pgno               (void)
 
1719
{
 
1720
  gint value;
 
1721
 
 
1722
  value = 100;
 
1723
  if (z_gconf_get_int (&value, GCONF_DIR "/home_page"))
 
1724
    value = SATURATE (value, 100, 899);
 
1725
 
 
1726
  return vbi3_dec2bcd (value);
 
1727
}
 
1728
 
 
1729
static void
 
1730
home_action                     (GtkAction *            action _unused_,
 
1731
                                 TeletextView *         view)
 
1732
{
 
1733
  const vbi3_link *lk;
 
1734
 
 
1735
  if (!view->pg)
 
1736
    return;
 
1737
 
 
1738
  lk = vbi3_page_get_home_link (view->pg);
 
1739
  if (!lk)
 
1740
    return;
 
1741
 
 
1742
  switch (lk->type)
 
1743
    {
 
1744
    case VBI3_LINK_PAGE:
 
1745
    case VBI3_LINK_SUBPAGE:
 
1746
      if (lk->pgno)
 
1747
        {
 
1748
          teletext_view_load_page (view, lk->network, lk->pgno, lk->subno);
 
1749
        }
 
1750
      else
 
1751
        {
 
1752
          teletext_view_load_page (view,
 
1753
                                   &view->req.network,
 
1754
                                   default_home_pgno (),
 
1755
                                   VBI3_ANY_SUBNO);
 
1756
        }
 
1757
 
 
1758
      break;
 
1759
 
 
1760
    default:
 
1761
      break;
 
1762
    }
 
1763
}
 
1764
 
 
1765
static PyObject *
 
1766
py_ttx_home                     (PyObject *             self _unused_,
 
1767
                                 PyObject *             args _unused_)
 
1768
{
 
1769
  TeletextView *view;
 
1770
 
 
1771
  if (!(view = teletext_view_from_widget (python_command_widget ())))
 
1772
    py_return_true;
 
1773
 
 
1774
  home_action (NULL, view);
 
1775
 
 
1776
  py_return_true;
 
1777
}
 
1778
 
 
1779
gboolean
 
1780
teletext_view_switch_network    (TeletextView *         view,
 
1781
                                 const vbi3_network *   nk)
 
1782
{
 
1783
  if ((unsigned int) -1 != view->charset)
 
1784
    {
 
1785
      view->charset = -1;
 
1786
 
 
1787
      g_signal_emit (view, signals[CHARSET_CHANGED], 0);
 
1788
    }
 
1789
 
 
1790
  teletext_view_load_page (view, nk, default_home_pgno (), VBI3_ANY_SUBNO);
 
1791
 
 
1792
  return TRUE;
 
1793
}
 
1794
 
 
1795
gboolean
 
1796
teletext_view_set_charset       (TeletextView *         view,
 
1797
                                 vbi3_charset_code      code)
 
1798
{
 
1799
  if (view->charset != code)
 
1800
    {
 
1801
      view->charset = code;
 
1802
 
 
1803
      g_signal_emit (view, signals[CHARSET_CHANGED], 0);
 
1804
 
 
1805
      reformat_view (view);
 
1806
    }
 
1807
 
 
1808
  return TRUE;
 
1809
}
 
1810
 
 
1811
 
 
1812
/*
 
1813
        Selection
 
1814
*/
 
1815
 
 
1816
/* Called when another application claims the selection
 
1817
   (i.e. sends new data to the clipboard). */
 
1818
static gboolean
 
1819
selection_clear_event           (GtkWidget *            widget,
 
1820
                                 GdkEventSelection *    event)
 
1821
{
 
1822
  TeletextView *view = TELETEXT_VIEW (widget);
 
1823
 
 
1824
  if (event->selection == GDK_SELECTION_PRIMARY)
 
1825
    view->select.in_selection = FALSE;
 
1826
  else if (event->selection == GA_CLIPBOARD)
 
1827
    view->select.in_clipboard = FALSE;
 
1828
 
 
1829
  return FALSE; /* pass on */
 
1830
}
 
1831
 
 
1832
/* Called when another application requests our selected data
 
1833
   (overriden GtkWidget method). */
 
1834
static void
 
1835
selection_get                   (GtkWidget *            widget,
 
1836
                                 GtkSelectionData *     selection_data,
 
1837
                                 guint                  info,
 
1838
                                 guint                  time _unused_)
 
1839
{
 
1840
  TeletextView *view = TELETEXT_VIEW (widget);
 
1841
 
 
1842
  if ((selection_data->selection == GDK_SELECTION_PRIMARY
 
1843
       && view->select.in_selection)
 
1844
      || (selection_data->selection == GA_CLIPBOARD
 
1845
          && view->select.in_clipboard))
 
1846
    {
 
1847
      switch (info)
 
1848
        {
 
1849
        case TARGET_LAT1_STRING:
 
1850
        case TARGET_UTF8_STRING:
 
1851
          {
 
1852
            gint width;
 
1853
            gint height;
 
1854
            unsigned int size;
 
1855
            unsigned int actual;
 
1856
            char *buffer;
 
1857
 
 
1858
            width = view->select.column2 - view->select.column1 + 1;
 
1859
            height = view->select.row2 - view->select.row1 + 1;
 
1860
 
 
1861
            size = 25 * 64 * 4;
 
1862
            buffer = g_malloc (size);
 
1863
 
 
1864
            actual = vbi3_print_page_region
 
1865
              (view->select.pg,
 
1866
               buffer,
 
1867
               size,
 
1868
               (TARGET_LAT1_STRING == info) ? "ISO-8859-1" : "UTF-8",
 
1869
               NULL, 0, /* standard line separator */
 
1870
               view->select.column1,
 
1871
               view->select.row1,
 
1872
               width,
 
1873
               height,
 
1874
               VBI3_TABLE, (vbi3_bool) view->select.table_mode,
 
1875
               VBI3_RTL, (vbi3_bool) view->select.rtl_mode,
 
1876
               VBI3_REVEAL, (vbi3_bool) view->select.reveal,
 
1877
               VBI3_FLASH_ON, TRUE,
 
1878
               VBI3_END);
 
1879
 
 
1880
            if (actual > 0)
 
1881
              {
 
1882
                gtk_selection_data_set (selection_data,
 
1883
                                        GDK_SELECTION_TYPE_STRING, 8,
 
1884
                                        buffer, actual);
 
1885
              }
 
1886
 
 
1887
            g_free (buffer);
 
1888
 
 
1889
            break;
 
1890
          }
 
1891
 
 
1892
        case TARGET_PIXMAP:
 
1893
          {
 
1894
            const gint cell_width = 12; /* Teletext character cell size */
 
1895
            const gint cell_height = 10;
 
1896
            gint width;
 
1897
            gint height;
 
1898
            GdkPixmap *pixmap;
 
1899
            GdkPixbuf *pixbuf;
 
1900
            gint id[2];
 
1901
            vbi3_image_format format;
 
1902
            vbi3_bool success;
 
1903
 
 
1904
            /* Selection is open (eg. 25,5 - 15,6). */
 
1905
            if (view->select.column2 < view->select.column1)
 
1906
              break;
 
1907
 
 
1908
            width = view->select.column2 - view->select.column1 + 1;
 
1909
            height = view->select.row2 - view->select.row1 + 1;
 
1910
 
 
1911
            pixmap = gdk_pixmap_new (GTK_WIDGET (view)->window,
 
1912
                                     width * cell_width,
 
1913
                                     height * cell_height,
 
1914
                                     -1 /* same depth as window */);
 
1915
 
 
1916
            pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
 
1917
                                     /* has_alpha */ TRUE,
 
1918
                                     /* bits_per_sample */ 8,
 
1919
                                     width * cell_width,
 
1920
                                     height * cell_height);
 
1921
 
 
1922
            CLEAR (format);
 
1923
 
 
1924
            format.width = gdk_pixbuf_get_width (pixbuf);
 
1925
            format.height = gdk_pixbuf_get_height (pixbuf);
 
1926
            format.pixfmt = VBI3_PIXFMT_RGBA24_LE;
 
1927
            format.bytes_per_line = gdk_pixbuf_get_rowstride (pixbuf);
 
1928
            format.size = format.width * format.height * 4;
 
1929
 
 
1930
            success = vbi3_page_draw_teletext_region
 
1931
              (view->select.pg,
 
1932
               gdk_pixbuf_get_pixels (pixbuf),
 
1933
               &format,
 
1934
               view->select.column1,
 
1935
               view->select.row1,
 
1936
               width,
 
1937
               height,
 
1938
               VBI3_BRIGHTNESS, brightness,
 
1939
               VBI3_CONTRAST, contrast,
 
1940
               VBI3_REVEAL, (vbi3_bool) view->select.reveal,
 
1941
               VBI3_FLASH_ON, TRUE,
 
1942
               VBI3_END);
 
1943
 
 
1944
            g_assert (success);
 
1945
 
 
1946
            gdk_draw_pixbuf (pixmap,
 
1947
                             GTK_WIDGET (view)->style->white_gc,
 
1948
                             pixbuf,
 
1949
                             /* src */ 0, 0,
 
1950
                             /* dst */ 0, 0,
 
1951
                             gdk_pixbuf_get_width (pixbuf),
 
1952
                             gdk_pixbuf_get_height (pixbuf),
 
1953
                             GDK_RGB_DITHER_NORMAL,
 
1954
                             /* dither */ 0, 0);
 
1955
 
 
1956
            id[0] = GDK_WINDOW_XWINDOW (pixmap);
 
1957
 
 
1958
            gtk_selection_data_set (selection_data,
 
1959
                                    GDK_SELECTION_TYPE_PIXMAP, 32,
 
1960
                                    (char * ) id, 4);
 
1961
        
 
1962
            g_object_unref (pixbuf);
 
1963
 
 
1964
            break;
 
1965
          }
 
1966
 
 
1967
        default:
 
1968
          break;
 
1969
        }
 
1970
    }
 
1971
}
 
1972
 
 
1973
static __inline__ void
 
1974
select_positions                (TeletextView *         view,
 
1975
                                 gint                   x,
 
1976
                                 gint                   y,
 
1977
                                 gint *                 pcols,
 
1978
                                 gint *                 prows,
 
1979
                                 gint *                 scol,
 
1980
                                 gint *                 srow,
 
1981
                                 gint *                 ccol,
 
1982
                                 gint *                 crow)
 
1983
{
 
1984
  gint width, height;   /* window */
 
1985
  gint columns, rows;   /* page */
 
1986
 
 
1987
  gdk_window_get_geometry (GTK_WIDGET (view)->window,
 
1988
                           NULL, NULL,
 
1989
                           &width, &height,
 
1990
                           NULL);
 
1991
 
 
1992
  *pcols = columns = view->pg->columns;
 
1993
  *prows = rows = view->pg->rows;
 
1994
 
 
1995
  *scol = SATURATE ((view->select.start_x * columns) / width, 0, columns - 1);
 
1996
  *srow = SATURATE ((view->select.start_y * rows) / height, 0, rows - 1);
 
1997
 
 
1998
  *ccol = SATURATE ((x * columns) / width, 0, columns - 1);
 
1999
  *crow = SATURATE ((y * rows) / height, 0, rows - 1);
 
2000
}
 
2001
 
 
2002
static __inline__ gboolean
 
2003
is_hidden_row                   (TeletextView *         view,
 
2004
                                 gint                   row)
 
2005
{
 
2006
  vbi3_char *cp;
 
2007
  unsigned int column;
 
2008
 
 
2009
  if (row <= 0 || row >= 25)
 
2010
    return FALSE;
 
2011
 
 
2012
  cp = view->pg->text + row * view->pg->columns;
 
2013
  for (column = 0; column < view->pg->columns; ++column)
 
2014
    {
 
2015
      if (cp->size >= VBI3_OVER_BOTTOM)
 
2016
        return TRUE;
 
2017
      ++cp;
 
2018
    }
 
2019
 
 
2020
  return FALSE;
 
2021
}
 
2022
 
 
2023
/* Calculates a rectangle, scaling from text coordinates
 
2024
   to window coordinates. */
 
2025
static void
 
2026
scale_rect                      (GdkRectangle *         rect,
 
2027
                                 gint                   x1,
 
2028
                                 gint                   y1,
 
2029
                                 gint                   x2,
 
2030
                                 gint                   y2,
 
2031
                                 gint                   width,
 
2032
                                 gint                   height,
 
2033
                                 gint                   rows,
 
2034
                                 gint                   columns)
 
2035
{
 
2036
  gint ch = columns >> 1;
 
2037
  gint rh = rows >> 1;
 
2038
 
 
2039
  rect->x = (x1 * width + ch) / columns;
 
2040
  rect->y = (y1 * height + rh) / rows;
 
2041
  rect->width = ((x2 + 1) * width + ch) / columns - rect->x;
 
2042
  rect->height = ((y2 + 1) * height + rh) / rows - rect->y;
 
2043
}
 
2044
 
 
2045
/* Transforms the selected region from the first rectangle to the
 
2046
   second one, given in text coordinates. */
 
2047
static void
 
2048
select_transform                (TeletextView *         view,
 
2049
                                 gint                   sx1,
 
2050
                                 gint                   sy1,
 
2051
                                 gint                   sx2,
 
2052
                                 gint                   sy2,
 
2053
                                 gboolean               stable,
 
2054
                                 gint                   dx1,
 
2055
                                 gint                   dy1,
 
2056
                                 gint                   dx2,
 
2057
                                 gint                   dy2,
 
2058
                                 gboolean               dtable,
 
2059
                                 GdkRegion *            exposed)
 
2060
{
 
2061
  gint width, height; /* window */
 
2062
  gint columns, rows; /* page */
 
2063
  GdkRectangle rect;
 
2064
  GdkRegion *src_region;
 
2065
  GdkRegion *dst_region;
 
2066
 
 
2067
  gdk_window_get_geometry (GTK_WIDGET (view)->window,
 
2068
                           NULL, NULL,
 
2069
                           &width, &height,
 
2070
                           NULL);
 
2071
 
 
2072
  columns = view->pg->columns;
 
2073
  rows = view->pg->rows;
 
2074
 
 
2075
  gdk_gc_set_clip_origin (view->select.xor_gc, 0, 0);
 
2076
 
 
2077
  {
 
2078
    gboolean h1, h2, h3, h4;
 
2079
 
 
2080
    if (sy1 > sy2)
 
2081
      {
 
2082
        SWAP (sx1, sx2);
 
2083
        SWAP (sy1, sy2);
 
2084
      }
 
2085
 
 
2086
    h1 = is_hidden_row (view, sy1);
 
2087
    h2 = is_hidden_row (view, sy1 + 1);
 
2088
    h3 = is_hidden_row (view, sy2);
 
2089
    h4 = is_hidden_row (view, sy2 + 1);
 
2090
 
 
2091
    if (stable || sy1 == sy2 || ((sy2 - sy1) == 1 && h3))
 
2092
      {
 
2093
        if (sx1 > sx2)
 
2094
          SWAP (sx1, sx2);
 
2095
 
 
2096
        scale_rect (&rect,
 
2097
                    sx1, sy1 - h1,
 
2098
                    sx2, sy2 + h4,
 
2099
                    width, height,
 
2100
                    rows, columns);
 
2101
        src_region = gdk_region_rectangle (&rect);
 
2102
      }
 
2103
    else
 
2104
      {
 
2105
        scale_rect (&rect,
 
2106
                    sx1, sy1 - h1,
 
2107
                    columns - 1, sy1 + h2,
 
2108
                    width, height,
 
2109
                    rows, columns);
 
2110
        src_region = gdk_region_rectangle (&rect);
 
2111
 
 
2112
        scale_rect (&rect,
 
2113
                    0, sy2 - h3,
 
2114
                    sx2, sy2 + h4,
 
2115
                    width, height,
 
2116
                    rows, columns);
 
2117
        gdk_region_union_with_rect (src_region, &rect);
 
2118
 
 
2119
        sy1 += h2 + 1;
 
2120
        sy2 -= h3 + 1;
 
2121
 
 
2122
        if (sy2 >= sy1)
 
2123
          {
 
2124
            scale_rect (&rect,
 
2125
                        0, sy1,
 
2126
                        columns - 1, sy2,
 
2127
                        width, height,
 
2128
                        rows, columns);
 
2129
            gdk_region_union_with_rect (src_region, &rect);
 
2130
          }
 
2131
      }
 
2132
  }
 
2133
 
 
2134
  {
 
2135
    gboolean h1, h2, h3, h4;
 
2136
 
 
2137
    if (dy1 > dy2)
 
2138
      {
 
2139
        SWAP(dx1, dx2);
 
2140
        SWAP(dy1, dy2);
 
2141
      }
 
2142
 
 
2143
    h1 = is_hidden_row (view, dy1);
 
2144
    h2 = is_hidden_row (view, dy1 + 1);
 
2145
    h3 = is_hidden_row (view, dy2);
 
2146
    h4 = is_hidden_row (view, dy2 + 1);
 
2147
 
 
2148
    if (dtable || dy1 == dy2 || ((dy2 - dy1) == 1 && h3))
 
2149
      {
 
2150
        if (dx1 > dx2)
 
2151
          SWAP(dx1, dx2);
 
2152
 
 
2153
        view->select.column1 = dx1;
 
2154
        view->select.row1 = dy1 -= h1;
 
2155
        view->select.column2 = dx2;
 
2156
        view->select.row2 = dy2 += h4;
 
2157
 
 
2158
        scale_rect (&rect,
 
2159
                    dx1, dy1,
 
2160
                    dx2, dy2,
 
2161
                    width, height,
 
2162
                    rows, columns);
 
2163
        dst_region = gdk_region_rectangle (&rect);
 
2164
      }
 
2165
    else
 
2166
      {
 
2167
        scale_rect (&rect,
 
2168
                    dx1, dy1 - h1,
 
2169
                    columns - 1, dy1 + h2,
 
2170
                    width, height,
 
2171
                    rows, columns);
 
2172
        dst_region = gdk_region_rectangle (&rect);
 
2173
 
 
2174
        scale_rect (&rect,
 
2175
                    0, dy2 - h3,
 
2176
                    dx2, dy2 + h4,
 
2177
                    width, height,
 
2178
                    rows, columns);
 
2179
        gdk_region_union_with_rect (dst_region, &rect);
 
2180
 
 
2181
        view->select.column1 = dx1;
 
2182
        view->select.row1 = dy1 + h2;
 
2183
        view->select.column2 = dx2;
 
2184
        view->select.row2 = dy2 - h3;
 
2185
 
 
2186
        dy1 += h2 + 1;
 
2187
        dy2 -= h3 + 1;
 
2188
 
 
2189
        if (dy2 >= dy1)
 
2190
          {
 
2191
            scale_rect (&rect,
 
2192
                        0, dy1,
 
2193
                        columns - 1, dy2,
 
2194
                        width, height,
 
2195
                        rows, columns);
 
2196
            gdk_region_union_with_rect (dst_region, &rect);
 
2197
          }
 
2198
      }
 
2199
  }
 
2200
 
 
2201
  if (exposed)
 
2202
    gdk_region_subtract (src_region, exposed);
 
2203
 
 
2204
  gdk_region_xor (src_region, dst_region);
 
2205
 
 
2206
  gdk_region_destroy (dst_region);
 
2207
 
 
2208
  gdk_gc_set_clip_region (view->select.xor_gc, src_region);
 
2209
 
 
2210
  gdk_region_destroy (src_region);
 
2211
 
 
2212
  gdk_draw_rectangle (GTK_WIDGET (view)->window,
 
2213
                      view->select.xor_gc,
 
2214
                      TRUE,
 
2215
                      0, 0,
 
2216
                      width - 1, height - 1);
 
2217
 
 
2218
  gdk_gc_set_clip_rectangle (view->select.xor_gc, NULL);
 
2219
}
 
2220
 
 
2221
static void
 
2222
select_stop                     (TeletextView *         view)
 
2223
{
 
2224
  if (view->appbar)
 
2225
    gnome_appbar_pop (view->appbar);
 
2226
 
 
2227
  if (view->select.last_x != -1)
 
2228
    {
 
2229
      gint columns, rows; /* page */
 
2230
      gint scol, srow; /* start */
 
2231
      gint ccol, crow; /* current */
 
2232
      gint tcol1, trow1;
 
2233
      gint tcol2, trow2;
 
2234
 
 
2235
      select_positions (view,
 
2236
                        view->select.last_x,
 
2237
                        view->select.last_y,
 
2238
                        &columns, &rows,
 
2239
                        &scol, &srow,
 
2240
                        &ccol, &crow);
 
2241
 
 
2242
      /* Save because select_transform() will reset. */
 
2243
      tcol1 = view->select.column1;
 
2244
      trow1 = view->select.row1;
 
2245
      tcol2 = view->select.column2;
 
2246
      trow2 = view->select.row2;
 
2247
 
 
2248
      select_transform (view,
 
2249
                        scol, srow,     /* src1 */
 
2250
                        ccol, crow,     /* src2 */
 
2251
                        view->select.table_mode,
 
2252
                        columns, rows,  /* dst1 */
 
2253
                        columns, rows,  /* dst2 */
 
2254
                        view->select.table_mode,
 
2255
                        NULL);
 
2256
 
 
2257
      /* Copy selected text. */
 
2258
 
 
2259
      vbi3_page_unref (view->select.pg);
 
2260
      view->select.pg = vbi3_page_dup (view->pg);
 
2261
      g_assert (NULL != view->select.pg);
 
2262
 
 
2263
      view->select.column1 = tcol1;
 
2264
      view->select.row1    = trow1;
 
2265
      view->select.column2 = tcol2;
 
2266
      view->select.row2    = trow2;
 
2267
 
 
2268
      view->select.reveal  = view->reveal;
 
2269
 
 
2270
      if (!view->select.in_clipboard)
 
2271
        if (gtk_selection_owner_set (GTK_WIDGET (view),
 
2272
                                     GA_CLIPBOARD,
 
2273
                                     GDK_CURRENT_TIME))
 
2274
          view->select.in_clipboard = TRUE;
 
2275
 
 
2276
      if (!view->select.in_selection)
 
2277
        if (gtk_selection_owner_set (GTK_WIDGET (view),
 
2278
                                     GDK_SELECTION_PRIMARY,
 
2279
                                     GDK_CURRENT_TIME))
 
2280
          view->select.in_selection = TRUE;
 
2281
 
 
2282
      if (view->appbar)
 
2283
        gnome_appbar_set_status (view->appbar,
 
2284
                                 _("Selection copied to clipboard"));
 
2285
    }
 
2286
 
 
2287
  update_cursor_shape (view);
 
2288
 
 
2289
  view->selecting = FALSE;
 
2290
}
 
2291
 
 
2292
static void
 
2293
select_update                   (TeletextView *         view,
 
2294
                                 gint                   x,
 
2295
                                 gint                   y,
 
2296
                                 guint                  state)
 
2297
{
 
2298
  gint columns, rows; /* page */
 
2299
  gint scol, srow; /* start */
 
2300
  gint ocol, orow; /* last */
 
2301
  gint ccol, crow; /* current */
 
2302
  gboolean table;
 
2303
 
 
2304
  select_positions (view,
 
2305
                    x, y,
 
2306
                    &columns, &rows,
 
2307
                    &scol, &srow,
 
2308
                    &ccol, &crow);
 
2309
 
 
2310
  table = !!(state & GDK_SHIFT_MASK);
 
2311
 
 
2312
  if (view->select.last_x == -1)
 
2313
    {
 
2314
      /* First motion. */
 
2315
      select_transform (view,
 
2316
                        columns, rows,  /* src1 */
 
2317
                        columns, rows,  /* src2 */
 
2318
                        view->select.table_mode,
 
2319
                        scol, srow,     /* dst1 */
 
2320
                        ccol, crow,     /* dst2 */
 
2321
                        table,
 
2322
                        NULL);
 
2323
    }
 
2324
  else
 
2325
    {
 
2326
      gint width, height;
 
2327
 
 
2328
      gdk_window_get_geometry (GTK_WIDGET (view)->window,
 
2329
                               NULL, NULL,
 
2330
                               &width, &height,
 
2331
                               NULL);
 
2332
 
 
2333
      ocol = (view->select.last_x * columns) / width;
 
2334
      ocol = SATURATE (ocol, 0, columns - 1);
 
2335
      orow = (view->select.last_y * rows) / height;
 
2336
      orow = SATURATE (orow, 0, rows - 1);
 
2337
 
 
2338
      select_transform (view,
 
2339
                        scol, srow,     /* src1 */      
 
2340
                        ocol, orow,     /* src2 */
 
2341
                        view->select.table_mode,
 
2342
                        scol, srow,     /* dst1 */
 
2343
                        ccol, crow,     /* dst2 */
 
2344
                        table,
 
2345
                        NULL);
 
2346
    }
 
2347
 
 
2348
  view->select.last_x = MAX (0, x);
 
2349
  view->select.last_y = y;
 
2350
 
 
2351
  view->select.table_mode = table;
 
2352
}
 
2353
 
 
2354
static void
 
2355
select_start                    (TeletextView *         view,
 
2356
                                 gint                   x,
 
2357
                                 gint                   y,
 
2358
                                 guint                  state)
 
2359
{
 
2360
  if (view->selecting || !view->pg)
 
2361
    return;
 
2362
 
 
2363
  if (view->pg->pgno < 0x100)
 
2364
    {
 
2365
      if (view->appbar)
 
2366
        gnome_appbar_set_status (view->appbar, _("No page loaded"));
 
2367
      return;
 
2368
    }
 
2369
 
 
2370
  if (view->cursor_over_link)
 
2371
    {
 
2372
      view->cursor_over_link = FALSE;
 
2373
      if (view->appbar)
 
2374
        gnome_appbar_pop (view->appbar);
 
2375
    }
 
2376
 
 
2377
  if (view->appbar)
 
2378
    gnome_appbar_push (view->appbar,
 
2379
                       _("Selecting - press Shift key for table mode"));
 
2380
 
 
2381
  gdk_window_set_cursor (GTK_WIDGET (view)->window, cursor_select);
 
2382
 
 
2383
  view->select.start_x = x;
 
2384
  view->select.start_y = y;
 
2385
 
 
2386
  view->select.last_x = -1; /* not yet, wait for move event */
 
2387
 
 
2388
  view->select.table_mode = !!(state & GDK_SHIFT_MASK);
 
2389
  view->select.rtl_mode = FALSE; /* XXX to do */
 
2390
 
 
2391
  view->selecting = TRUE;
 
2392
}
 
2393
 
 
2394
/*
 
2395
        Export
 
2396
*/
 
2397
 
 
2398
static void
 
2399
export_action                   (GtkAction *            action _unused_,
 
2400
                                 TeletextView *         view)
 
2401
{
 
2402
  extern gchar *zvbi_get_current_network_name (void); /* XXX */
 
2403
  GtkWidget *dialog;
 
2404
  gchar *name;
 
2405
 
 
2406
  g_assert (view->pg && view->pg->pgno >= 0x100);
 
2407
 
 
2408
  if ((name = zvbi_get_current_network_name ()))
 
2409
    {
 
2410
      guint i;
 
2411
 
 
2412
      for (i = 0; i < strlen (name); ++i)
 
2413
        if (!g_ascii_isalnum (name[i]))
 
2414
          name[i] = '_';
 
2415
 
 
2416
      dialog = export_dialog_new (view->pg, name, view->reveal);
 
2417
    }
 
2418
  else
 
2419
    {
 
2420
      dialog = export_dialog_new (view->pg, "Zapzilla", view->reveal);
 
2421
    }
 
2422
 
 
2423
  if (dialog)
 
2424
    {
 
2425
      set_transient_for (GTK_WINDOW (dialog), view);
 
2426
      gtk_widget_show_all (dialog);
 
2427
    }
 
2428
}
 
2429
 
 
2430
static PyObject *
 
2431
py_ttx_export                   (PyObject *             self _unused_,
 
2432
                                 PyObject *             args _unused_)
 
2433
{
 
2434
  TeletextView *view;
 
2435
 
 
2436
  if (!(view = teletext_view_from_widget (python_command_widget ())))
 
2437
    py_return_true;
 
2438
 
 
2439
  export_action (NULL, view);
 
2440
 
 
2441
  py_return_true;
 
2442
}
 
2443
 
 
2444
/*
 
2445
        Search
 
2446
*/
 
2447
 
 
2448
static void
 
2449
search_action                   (GtkAction *            action _unused_,
 
2450
                                 TeletextView *         view)
 
2451
{
 
2452
  GtkWidget *widget;
 
2453
 
 
2454
  if (view->search_dialog)
 
2455
    {
 
2456
      gtk_window_present (GTK_WINDOW (view->search_dialog));
 
2457
    }
 
2458
  else if ((widget = search_dialog_new (view)))
 
2459
    {
 
2460
      view->search_dialog = widget;
 
2461
 
 
2462
      g_signal_connect (G_OBJECT (widget), "destroy",
 
2463
                        G_CALLBACK (gtk_widget_destroyed),
 
2464
                        &view->search_dialog);
 
2465
 
 
2466
      set_transient_for (GTK_WINDOW (widget), view);
 
2467
 
 
2468
      gtk_widget_show_all (widget);
 
2469
    }
 
2470
}
 
2471
 
 
2472
static PyObject *
 
2473
py_ttx_search                   (PyObject *             self _unused_,
 
2474
                                 PyObject *             args _unused_)
 
2475
{
 
2476
  TeletextView *view;
 
2477
 
 
2478
  if (!(view = teletext_view_from_widget (python_command_widget ())))
 
2479
    py_return_true;
 
2480
 
 
2481
  search_action (NULL, view);
 
2482
 
 
2483
  py_return_true;
 
2484
}
 
2485
 
 
2486
/*
 
2487
        Popup Menu
 
2488
*/
 
2489
 
 
2490
static guint
 
2491
hotlist_menu_insert             (GtkMenuShell *         menu,
 
2492
                                 const vbi3_network *   nk,
 
2493
                                 gboolean               separator,
 
2494
                                 gint                   position)
 
2495
{
 
2496
  gboolean have_subtitle_index = FALSE;
 
2497
  gboolean have_now_and_next   = FALSE;
 
2498
  gboolean have_current_progr  = FALSE;
 
2499
  gboolean have_progr_index    = FALSE;
 
2500
  gboolean have_progr_schedule = FALSE;
 
2501
  gboolean have_progr_warning  = FALSE;
 
2502
  vbi3_pgno pgno;
 
2503
  guint count;
 
2504
 
 
2505
  if (!td)
 
2506
    return 0;
 
2507
 
 
2508
  count = 0;
 
2509
 
 
2510
  for (pgno = 0x100; pgno <= 0x899; pgno = vbi3_add_bcd (pgno, 0x001))
 
2511
    {
 
2512
      vbi3_ttx_page_stat ps;
 
2513
      gboolean new_window;
 
2514
      GtkWidget *menu_item;
 
2515
      gchar *buffer;
 
2516
 
 
2517
      ps.page_type = VBI3_UNKNOWN_PAGE;
 
2518
 
 
2519
      /* Error ignored. */
 
2520
      vbi3_teletext_decoder_get_ttx_page_stat (td, &ps, nk, pgno);
 
2521
 
 
2522
      new_window = TRUE;
 
2523
 
 
2524
      switch (ps.page_type)
 
2525
        {
 
2526
 
 
2527
#undef ONCE
 
2528
#define ONCE(b) if (b) continue; else b = TRUE;
 
2529
 
 
2530
        case VBI3_SUBTITLE_INDEX:
 
2531
          ONCE (have_subtitle_index);
 
2532
          menu_item = z_gtk_pixmap_menu_item_new (_("Subtitle index"),
 
2533
                                                  GTK_STOCK_INDEX);
 
2534
          break;
 
2535
 
 
2536
        case VBI3_NOW_AND_NEXT:
 
2537
          ONCE (have_now_and_next);
 
2538
          new_window = FALSE;
 
2539
          menu_item = z_gtk_pixmap_menu_item_new (_("Now and Next"),
 
2540
                                                  GTK_STOCK_JUSTIFY_FILL);
 
2541
          break;
 
2542
 
 
2543
        case VBI3_CURRENT_PROGR:
 
2544
          ONCE (have_current_progr);
 
2545
          menu_item = z_gtk_pixmap_menu_item_new (_("Current program"),
 
2546
                                                  GTK_STOCK_JUSTIFY_FILL);
 
2547
          break;
 
2548
 
 
2549
        case VBI3_PROGR_INDEX:
 
2550
          ONCE (have_progr_index);
 
2551
          menu_item = z_gtk_pixmap_menu_item_new (_("Program Index"),
 
2552
                                                  GTK_STOCK_INDEX);
 
2553
          break;
 
2554
 
 
2555
        case VBI3_PROGR_SCHEDULE:
 
2556
          ONCE (have_progr_schedule);
 
2557
          menu_item = z_gtk_pixmap_menu_item_new (_("Program Schedule"),
 
2558
                                                  "gnome-stock-timer");
 
2559
          break;
 
2560
 
 
2561
        case VBI3_PROGR_WARNING:
 
2562
          ONCE (have_progr_warning);
 
2563
          new_window = FALSE;
 
2564
          /* TRANSLATORS: Schedule changes and the like. */
 
2565
          menu_item = z_gtk_pixmap_menu_item_new (_("Program Warning"),
 
2566
                                                  "gnome-stock-mail");
 
2567
          break;
 
2568
 
 
2569
        default:
 
2570
          continue;
 
2571
        }
 
2572
 
 
2573
      if (separator)
 
2574
        {
 
2575
          GtkWidget *menu_item;
 
2576
 
 
2577
          menu_item = gtk_separator_menu_item_new ();
 
2578
          gtk_widget_show (menu_item);
 
2579
          gtk_menu_shell_insert (menu, menu_item, position);
 
2580
          if (position >= 0)
 
2581
            ++position;
 
2582
 
 
2583
          separator = FALSE;
 
2584
        }
 
2585
 
 
2586
      gtk_widget_show (menu_item);
 
2587
 
 
2588
      {
 
2589
        gchar buffer[32];
 
2590
 
 
2591
        g_snprintf (buffer, sizeof (buffer), "%x", pgno);
 
2592
        z_tooltip_set (menu_item, buffer);
 
2593
      }
 
2594
 
 
2595
      if (new_window)
 
2596
        buffer = g_strdup_printf ("zapping.ttx_open_new(%x, -1)", pgno);
 
2597
      else
 
2598
        buffer = g_strdup_printf ("zapping.ttx_open(%x, -1)", pgno);
 
2599
 
 
2600
      g_signal_connect (G_OBJECT (menu_item), "activate",
 
2601
                        G_CALLBACK (on_python_command1), buffer);
 
2602
      g_signal_connect_swapped (G_OBJECT (menu_item), "destroy",
 
2603
                                G_CALLBACK (g_free), buffer);
 
2604
 
 
2605
      gtk_menu_shell_insert (menu, menu_item, position);
 
2606
      if (position >= 0)
 
2607
        ++position;
 
2608
 
 
2609
      ++count;
 
2610
    }
 
2611
      
 
2612
  return count;
 
2613
}
 
2614
 
 
2615
guint
 
2616
ttxview_hotlist_menu_insert     (GtkMenuShell *         menu,
 
2617
                                 gboolean               separator,
 
2618
                                 gint                   position)
 
2619
{
 
2620
  return hotlist_menu_insert (menu, NULL, separator, position);
 
2621
}
 
2622
 
 
2623
static GnomeUIInfo
 
2624
popup_open_page_uiinfo [] = {
 
2625
  {
 
2626
    GNOME_APP_UI_ITEM, N_("_Open"), NULL,
 
2627
    G_CALLBACK (on_python_command1), NULL, NULL,
 
2628
    GNOME_APP_PIXMAP_NONE, NULL,
 
2629
    0, (GdkModifierType) 0, NULL
 
2630
  },
 
2631
  {
 
2632
    GNOME_APP_UI_ITEM, N_("Open in _New Window"), NULL, 
 
2633
    G_CALLBACK (on_python_command1), NULL, NULL,
 
2634
    GNOME_APP_PIXMAP_NONE, NULL,
 
2635
    0, (GdkModifierType) 0, NULL
 
2636
  },
 
2637
  GNOMEUIINFO_END
 
2638
};
 
2639
 
 
2640
static GnomeUIInfo
 
2641
popup_open_url_uiinfo [] = {
 
2642
  {
 
2643
    GNOME_APP_UI_ITEM, N_("_Open Link"), NULL,
 
2644
    G_CALLBACK (on_python_command1), NULL, NULL,
 
2645
    GNOME_APP_PIXMAP_NONE, NULL,
 
2646
    0, (GdkModifierType) 0, NULL
 
2647
  },
 
2648
  GNOMEUIINFO_END
 
2649
};
 
2650
 
 
2651
static GnomeUIInfo
 
2652
popup_page_uiinfo [] = {
 
2653
  {
 
2654
    GNOME_APP_UI_ITEM, N_("_New Window"), NULL, 
 
2655
    G_CALLBACK (on_python_command1), "zapping.ttx_open_new()", NULL,
 
2656
    GNOME_APP_PIXMAP_STOCK, GTK_STOCK_NEW,
 
2657
    0, (GdkModifierType) 0, NULL
 
2658
  },
 
2659
  {
 
2660
    GNOME_APP_UI_ITEM, N_("_Save as..."), NULL,
 
2661
    G_CALLBACK (on_python_command1), "zapping.ttx_export()", NULL,
 
2662
    GNOME_APP_PIXMAP_STOCK, GTK_STOCK_SAVE_AS,
 
2663
    0, (GdkModifierType) 0, NULL
 
2664
  },
 
2665
  {
 
2666
    GNOME_APP_UI_ITEM, N_("S_earch..."), NULL, 
 
2667
    G_CALLBACK (on_python_command1), "zapping.ttx_search()", NULL,
 
2668
    GNOME_APP_PIXMAP_STOCK, GTK_STOCK_FIND,
 
2669
    0, (GdkModifierType) 0, NULL
 
2670
  },
 
2671
  {
 
2672
    GNOME_APP_UI_ITEM, N_("S_ubtitles"), NULL,
 
2673
    NULL, NULL, NULL,
 
2674
    GNOME_APP_PIXMAP_STOCK, GTK_STOCK_INDEX,
 
2675
    0, (GdkModifierType) 0, NULL
 
2676
  },
 
2677
  {
 
2678
    GNOME_APP_UI_ITEM, N_("_Bookmarks"), NULL,
 
2679
    NULL, NULL, NULL,
 
2680
    GNOME_APP_PIXMAP_STOCK, GTK_STOCK_INDEX,
 
2681
    0, (GdkModifierType) 0, NULL
 
2682
  },
 
2683
  GNOMEUIINFO_END
 
2684
};
 
2685
 
 
2686
GtkWidget *
 
2687
teletext_view_popup_menu_new    (TeletextView *         view,
 
2688
                                 const vbi3_link *      lk,
 
2689
                                 gboolean               large)
 
2690
{
 
2691
  GtkWidget *menu;
 
2692
  GtkWidget *widget;
 
2693
  gint pos;
 
2694
 
 
2695
  menu = gtk_menu_new ();
 
2696
  g_object_set_data (G_OBJECT (menu), "TeletextView", view);
 
2697
 
 
2698
  pos = 0;
 
2699
 
 
2700
  if (lk)
 
2701
    {
 
2702
      switch (lk->type)
 
2703
        {
 
2704
          gint subpage;
 
2705
 
 
2706
        case VBI3_LINK_PAGE:
 
2707
        case VBI3_LINK_SUBPAGE:
 
2708
          subpage = decimal_subno (lk->subno);
 
2709
 
 
2710
          popup_open_page_uiinfo[0].user_data =
 
2711
            g_strdup_printf ("zapping.ttx_open(%x, %d)",
 
2712
                             lk->pgno, subpage);
 
2713
          g_signal_connect_swapped (G_OBJECT (menu), "destroy",
 
2714
                                    G_CALLBACK (g_free),
 
2715
                                    popup_open_page_uiinfo[0].user_data);
 
2716
 
 
2717
          popup_open_page_uiinfo[1].user_data =
 
2718
            g_strdup_printf ("zapping.ttx_open_new(%x, %d)",
 
2719
                             lk->pgno, subpage);
 
2720
          g_signal_connect_swapped (G_OBJECT (menu), "destroy",
 
2721
                                    G_CALLBACK (g_free),
 
2722
                                    popup_open_page_uiinfo[1].user_data);
 
2723
 
 
2724
          gnome_app_fill_menu (GTK_MENU_SHELL (menu),
 
2725
                               popup_open_page_uiinfo,
 
2726
                               /* accel */ NULL,
 
2727
                               /* mnemo */ TRUE,
 
2728
                               pos);
 
2729
          return menu;
 
2730
 
 
2731
        case VBI3_LINK_HTTP:
 
2732
        case VBI3_LINK_FTP:
 
2733
        case VBI3_LINK_EMAIL:
 
2734
          popup_open_url_uiinfo[0].user_data = g_strdup (lk->url);
 
2735
          g_signal_connect_swapped (G_OBJECT (menu), "destroy",
 
2736
                                    G_CALLBACK (g_free),
 
2737
                                    popup_open_url_uiinfo[0].user_data);
 
2738
 
 
2739
          gnome_app_fill_menu (GTK_MENU_SHELL (menu), popup_open_url_uiinfo,
 
2740
                               /* accel */ NULL, /* mnemo */ TRUE, pos);
 
2741
          return menu;
 
2742
 
 
2743
        default:
 
2744
          break;
 
2745
        }
 
2746
    }
 
2747
 
 
2748
  gnome_app_fill_menu (GTK_MENU_SHELL (menu), popup_page_uiinfo,
 
2749
                       NULL, TRUE, pos);
 
2750
 
 
2751
  if (!vbi3_export_info_enum (0))
 
2752
    gtk_widget_set_sensitive (popup_page_uiinfo[1].widget, FALSE);
 
2753
 
 
2754
  if (large)
 
2755
    {
 
2756
      GtkWidget *subtitles_menu;
 
2757
 
 
2758
      widget = popup_page_uiinfo[3].widget; /* subtitles */
 
2759
 
 
2760
      if ((subtitles_menu = subtitle_menu_new ()))
 
2761
        gtk_menu_item_set_submenu (GTK_MENU_ITEM (widget), subtitles_menu);
 
2762
      else
 
2763
        gtk_widget_set_sensitive (widget, FALSE);
 
2764
 
 
2765
      widget = popup_page_uiinfo[4].widget; /* bookmarks */
 
2766
 
 
2767
      gtk_menu_item_set_submenu (GTK_MENU_ITEM (widget),
 
2768
                                 bookmarks_menu_new (view));
 
2769
 
 
2770
      ttxview_hotlist_menu_insert (GTK_MENU_SHELL (menu),
 
2771
                                   /* separator */ TRUE, APPEND);
 
2772
    }
 
2773
  else
 
2774
    {
 
2775
      widget = popup_page_uiinfo[3].widget; /* subtitles */
 
2776
      gtk_widget_set_sensitive (widget, FALSE);
 
2777
      gtk_widget_hide (widget);
 
2778
 
 
2779
      widget = popup_page_uiinfo[4].widget; /* bookmarks */
 
2780
      gtk_widget_set_sensitive (widget, FALSE);
 
2781
      gtk_widget_hide (widget);
 
2782
    }
 
2783
 
 
2784
  return menu;
 
2785
}
 
2786
 
 
2787
static gboolean
 
2788
blink_timeout                   (gpointer               user_data)
 
2789
{
 
2790
  TeletextView *view = user_data;
 
2791
 
 
2792
  apply_patches (view, /* draw */ TRUE);
 
2793
 
 
2794
  return TRUE;
 
2795
}
 
2796
 
 
2797
static void
 
2798
size_allocate                   (GtkWidget *            widget,
 
2799
                                 GtkAllocation *        allocation)
 
2800
{
 
2801
  TeletextView *view = TELETEXT_VIEW (widget);
 
2802
 
 
2803
  GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
 
2804
 
 
2805
  resize_scaled_page_image (view, allocation->width, allocation->height);
 
2806
}
 
2807
 
 
2808
static gboolean
 
2809
expose_event                    (GtkWidget *            widget,
 
2810
                                 GdkEventExpose *       event)
 
2811
{
 
2812
  TeletextView *view = TELETEXT_VIEW (widget);
 
2813
  GdkRegion *region;
 
2814
 
 
2815
  draw_scaled_page_image (view,
 
2816
                          widget->window,
 
2817
                          widget->style->white_gc,
 
2818
                          /* src */ event->area.x, event->area.y,
 
2819
                          /* dst */ event->area.x, event->area.y,
 
2820
                          event->area.width,
 
2821
                          event->area.height);
 
2822
 
 
2823
  if (view->selecting
 
2824
      && view->select.last_x != -1)
 
2825
    {
 
2826
      gint columns, rows; /* page */
 
2827
      gint scol, srow; /* start */
 
2828
      gint ccol, crow; /* current */
 
2829
 
 
2830
      select_positions (view,
 
2831
                        view->select.last_x,
 
2832
                        view->select.last_y,
 
2833
                        &columns, &rows,
 
2834
                        &scol, &srow,
 
2835
                        &ccol, &crow);
 
2836
 
 
2837
      region = gdk_region_rectangle (&event->area);
 
2838
 
 
2839
      select_transform (view,
 
2840
                        scol, srow,     /* src1 */
 
2841
                        ccol, crow,     /* src2 */
 
2842
                        view->select.table_mode,
 
2843
                        scol, srow,     /* dst2 */
 
2844
                        ccol, crow,     /* dst2 */
 
2845
                        view->select.table_mode,
 
2846
                        region);
 
2847
 
 
2848
      gdk_region_destroy (region);
 
2849
    }
 
2850
 
 
2851
  return TRUE;
 
2852
}
 
2853
 
 
2854
static gboolean
 
2855
motion_notify_event             (GtkWidget *            widget,
 
2856
                                 GdkEventMotion *       event)
 
2857
{
 
2858
  TeletextView *view = TELETEXT_VIEW (widget);
 
2859
 
 
2860
  if (view->selecting)
 
2861
    select_update (view, (int) event->x, (int) event->y, event->state);
 
2862
  else
 
2863
    update_cursor_shape (view);
 
2864
 
 
2865
  return FALSE;
 
2866
}
 
2867
 
 
2868
static gboolean
 
2869
button_release_event            (GtkWidget *            widget,
 
2870
                                 GdkEventButton *       event _unused_)
 
2871
{
 
2872
  TeletextView *view = TELETEXT_VIEW (widget);
 
2873
 
 
2874
  if (view->selecting)
 
2875
    select_stop (view);
 
2876
 
 
2877
  return FALSE; /* pass on */
 
2878
}
 
2879
 
 
2880
static gboolean
 
2881
button_press_event              (GtkWidget *            widget,
 
2882
                                 GdkEventButton *       event)
 
2883
{
 
2884
  TeletextView *view = TELETEXT_VIEW (widget);
 
2885
  vbi3_link lk;
 
2886
  gboolean success;
 
2887
 
 
2888
  switch (event->button)
 
2889
    {
 
2890
    case 1: /* left button */
 
2891
      if (event->state & (GDK_SHIFT_MASK |
 
2892
                          GDK_CONTROL_MASK |
 
2893
                          GDK_MOD1_MASK))
 
2894
        {
 
2895
          select_start (view,
 
2896
                        (gint) event->x,
 
2897
                        (gint) event->y,
 
2898
                        event->state);
 
2899
        }
 
2900
      else
 
2901
        {
 
2902
          success = teletext_view_vbi3_link_from_pointer_position
 
2903
            (view, &lk, (int) event->x, (int) event->y);
 
2904
 
 
2905
          if (success)
 
2906
            {
 
2907
              switch (lk.type)
 
2908
                {
 
2909
                case VBI3_LINK_PAGE:
 
2910
                case VBI3_LINK_SUBPAGE:
 
2911
                  teletext_view_load_page (view,lk.network, lk.pgno, lk.subno);
 
2912
                  break;
 
2913
 
 
2914
                case VBI3_LINK_HTTP:
 
2915
                case VBI3_LINK_FTP:
 
2916
                case VBI3_LINK_EMAIL:
 
2917
                  z_url_show (NULL, lk.url);
 
2918
                  break;
 
2919
              
 
2920
                default:
 
2921
                  select_start (view,
 
2922
                                (gint) event->x,
 
2923
                                (gint) event->y,
 
2924
                                event->state);
 
2925
                  break;
 
2926
                }
 
2927
 
 
2928
              vbi3_link_destroy (&lk);
 
2929
            }
 
2930
          else
 
2931
            {
 
2932
              select_start (view,
 
2933
                            (gint) event->x,
 
2934
                            (gint) event->y,
 
2935
                            event->state);
 
2936
            }
 
2937
        }
 
2938
 
 
2939
      return TRUE; /* handled */
 
2940
 
 
2941
    case 2: /* middle button, open link in new window */
 
2942
      success = teletext_view_vbi3_link_from_pointer_position
 
2943
        (view, &lk, (int) event->x, (int) event->y);
 
2944
 
 
2945
      if (success)
 
2946
        {
 
2947
          switch (lk.type)
 
2948
            {
 
2949
            case VBI3_LINK_PAGE:
 
2950
            case VBI3_LINK_SUBPAGE:
 
2951
              python_command_printf (widget,
 
2952
                                     "zapping.ttx_open_new(%x,%d)",
 
2953
                                     lk.pgno, decimal_subno (lk.subno));
 
2954
              vbi3_link_destroy (&lk);
 
2955
              return TRUE; /* handled */
 
2956
 
 
2957
            case VBI3_LINK_HTTP:
 
2958
            case VBI3_LINK_FTP:
 
2959
            case VBI3_LINK_EMAIL:
 
2960
              z_url_show (NULL, lk.url);
 
2961
              vbi3_link_destroy (&lk);
 
2962
              return TRUE; /* handled */
 
2963
 
 
2964
            default:
 
2965
              vbi3_link_destroy (&lk);
 
2966
              break;
 
2967
            }
 
2968
        }
 
2969
 
 
2970
      break;
 
2971
 
 
2972
    default:
 
2973
      break;
 
2974
    }
 
2975
 
 
2976
  return FALSE; /* pass on */
 
2977
}
 
2978
 
 
2979
/* Drawing area cannot focus, must be called by parent. */
 
2980
static gboolean
 
2981
teletext_view_on_key_press      (GtkWidget *            widget _unused_,
 
2982
                                 GdkEventKey *          event,
 
2983
                                 TeletextView *         view)
 
2984
{
 
2985
  guint digit;
 
2986
 
 
2987
  /* Loading a page takes time, possibly longer than the key
 
2988
     repeat period, and repeated keys will stack up. This kludge
 
2989
     effectively defers all loads until the key is released,
 
2990
     and discards all but the last load request. */
 
2991
  if (abs ((int) view->last_key_press_event_time - (int) event->time) < 100
 
2992
      || event->length > 1)
 
2993
    view->deferred_load = TRUE;
 
2994
 
 
2995
  view->last_key_press_event_time = event->time;
 
2996
 
 
2997
  digit = event->keyval - GDK_0;
 
2998
 
 
2999
  switch (event->keyval)
 
3000
    {
 
3001
    case GDK_a ... GDK_f:
 
3002
    case GDK_A ... GDK_F:
 
3003
      if (hex_pages)
 
3004
        {
 
3005
          digit = (event->keyval & 7) + 9;
 
3006
          goto page_number;
 
3007
        }
 
3008
 
 
3009
      break;
 
3010
 
 
3011
    case GDK_KP_0 ... GDK_KP_9:
 
3012
      digit = event->keyval - GDK_KP_0;
 
3013
      goto page_number;
 
3014
 
 
3015
    case GDK_0 ... GDK_9:
 
3016
    page_number:
 
3017
      if (event->state & (GDK_CONTROL_MASK |
 
3018
                          GDK_SHIFT_MASK |
 
3019
                          GDK_MOD1_MASK))
 
3020
        {
 
3021
          if (digit >= 1 && digit <= 8)
 
3022
            {
 
3023
              teletext_view_load_page (view,
 
3024
                                       NULL,
 
3025
                                       (vbi3_pgno) digit * 0x100,
 
3026
                                       VBI3_ANY_SUBNO);
 
3027
 
 
3028
              return TRUE; /* handled, don't pass on */
 
3029
            }
 
3030
 
 
3031
          break;
 
3032
        }
 
3033
 
 
3034
      if (view->entered_pgno >= 0x100)
 
3035
        view->entered_pgno = 0;
 
3036
 
 
3037
      view->entered_pgno = (view->entered_pgno << 4) + digit;
 
3038
 
 
3039
      if (view->entered_pgno >= 0x900)
 
3040
        view->entered_pgno ^= 0x800;
 
3041
 
 
3042
      if (view->entered_pgno >= 0x100)
 
3043
        {
 
3044
          teletext_view_load_page (view,
 
3045
                                   NULL,
 
3046
                                   view->entered_pgno,
 
3047
                                   VBI3_ANY_SUBNO);
 
3048
        }
 
3049
      else
 
3050
        {
 
3051
          /* view->freezed = TRUE; */
 
3052
 
 
3053
          if (view->toolbar)
 
3054
            teletext_toolbar_set_url (view->toolbar, view->entered_pgno, 0);
 
3055
        }
 
3056
 
 
3057
      return TRUE; /* handled */
 
3058
 
 
3059
    case GDK_S:
 
3060
      /* Menu is "Save As", plain "Save" might be confusing. Now Save As
 
3061
         has accelerator Ctrl-Shift-S, omitting the Shift might be
 
3062
         confusing. But we accept Ctrl-S (of plain Save) here. */
 
3063
      if (event->state & GDK_CONTROL_MASK)
 
3064
        {
 
3065
          python_command_printf (GTK_WIDGET (view), "zapping.ttx_export()");
 
3066
          return TRUE; /* handled */
 
3067
        }
 
3068
 
 
3069
      break;
 
3070
 
 
3071
    default:
 
3072
      break;
 
3073
    }
 
3074
 
 
3075
  return FALSE; /* pass on */
 
3076
}
 
3077
 
 
3078
static gboolean
 
3079
my_key_press                    (TeletextView *         view,
 
3080
                                 GdkEventKey *          event)
 
3081
{
 
3082
  return teletext_view_on_key_press (GTK_WIDGET (view), event, view);
 
3083
}
 
3084
 
 
3085
static void
 
3086
client_redraw                   (TeletextView *         view,
 
3087
                                 unsigned int           width,
 
3088
                                 unsigned int           height)
 
3089
{
 
3090
  resize_scaled_page_image (view, width, height);
 
3091
  draw_scaled_page_image (view,
 
3092
               GTK_WIDGET (view)->window,
 
3093
               GTK_WIDGET (view)->style->white_gc,
 
3094
               /* src */ 0, 0,
 
3095
               /* dst */ 0, 0,
 
3096
               width, height);
 
3097
}
 
3098
 
 
3099
static void
 
3100
instance_finalize               (GObject *              object)
 
3101
{
 
3102
  TeletextView *view = TELETEXT_VIEW (object);
 
3103
  GdkWindow *window;
 
3104
 
 
3105
  teletext_views = g_list_remove (teletext_views, view);
 
3106
 
 
3107
  if (view->search_dialog)
 
3108
    gtk_widget_destroy (view->search_dialog);
 
3109
 
 
3110
  if (view->blink_timeout_id > 0)
 
3111
    g_source_remove (view->blink_timeout_id);
 
3112
 
 
3113
  if (view->deferred.timeout_id > 0)
 
3114
    g_source_remove (view->deferred.timeout_id);
 
3115
 
 
3116
  g_object_unref (view->unscaled_on);
 
3117
  g_object_unref (view->unscaled_off);
 
3118
 
 
3119
  if (view->scaled_on)
 
3120
    g_object_unref (view->scaled_on);
 
3121
 
 
3122
  delete_patches (view);
 
3123
 
 
3124
 
 
3125
  g_object_unref (view->select.xor_gc);
 
3126
 
 
3127
  window = GTK_WIDGET (view)->window;
 
3128
 
 
3129
  if (view->select.in_clipboard)
 
3130
    {
 
3131
      if (gdk_selection_owner_get (GA_CLIPBOARD) == window)
 
3132
        gtk_selection_owner_set (NULL, GA_CLIPBOARD, GDK_CURRENT_TIME);
 
3133
    }
 
3134
 
 
3135
  if (view->select.in_selection)
 
3136
    {
 
3137
      if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == window)
 
3138
        gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY,
 
3139
                                 GDK_CURRENT_TIME);
 
3140
    }
 
3141
 
 
3142
  vbi3_page_unref (view->select.pg);
 
3143
  vbi3_page_unref (view->pg);
 
3144
 
 
3145
  vbi3_network_destroy (&view->req.network);
 
3146
  vbi3_network_destroy (&view->deferred.network);
 
3147
 
 
3148
  parent_class->finalize (object);
 
3149
}
 
3150
 
 
3151
static GtkActionEntry
 
3152
actions [] = {
 
3153
  { "GoSubmenu", NULL, N_("_Go"), NULL, NULL, NULL },
 
3154
  { "HistoryBack", GTK_STOCK_GO_BACK, NULL, NULL,
 
3155
    N_("Previous page in history"), G_CALLBACK (history_back_action) },
 
3156
  { "HistoryForward", GTK_STOCK_GO_FORWARD, NULL, NULL,
 
3157
    N_("Next page in history"), G_CALLBACK (history_forward_action) },
 
3158
  { "Home", GTK_STOCK_HOME, N_("Index"), NULL,
 
3159
    N_("Go to the index page"), G_CALLBACK (home_action) },
 
3160
  { "Search", GTK_STOCK_FIND, NULL, NULL,
 
3161
    N_("Search page memory"), G_CALLBACK (search_action) },
 
3162
  { "Export", GTK_STOCK_SAVE_AS, NULL, NULL,
 
3163
    N_("Save this page to a file"), G_CALLBACK (export_action) },
 
3164
};
 
3165
 
 
3166
/* We cannot initialize this until the widget (and its
 
3167
   parent GtkWindow) has a window. */
 
3168
static void
 
3169
realize                         (GtkWidget *            widget)
 
3170
{
 
3171
  TeletextView *view = TELETEXT_VIEW (widget);
 
3172
 
 
3173
  GTK_WIDGET_CLASS (parent_class)->realize (widget);
 
3174
 
 
3175
  /* No background, prevents flicker. */
 
3176
  gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
 
3177
 
 
3178
  view->select.xor_gc = gdk_gc_new (widget->window);
 
3179
  gdk_gc_set_function (view->select.xor_gc, GDK_INVERT);
 
3180
}
 
3181
 
 
3182
static int
 
3183
cur_pgno                        (TeletextView *         view)
 
3184
{
 
3185
  if (view->pg)
 
3186
    return view->pg->pgno;
 
3187
  else
 
3188
    return 0;
 
3189
}       
 
3190
 
 
3191
static void
 
3192
instance_init                   (GTypeInstance *        instance,
 
3193
                                 gpointer               g_class _unused_)
 
3194
{
 
3195
  TeletextView *view = (TeletextView *) instance;
 
3196
  GtkAction *action;
 
3197
  GtkWidget *widget;
 
3198
  gint uw, uh;
 
3199
 
 
3200
  view->action_group = gtk_action_group_new ("TeletextViewActions");
 
3201
#ifdef ENABLE_NLS
 
3202
  gtk_action_group_set_translation_domain (view->action_group,
 
3203
                                           GETTEXT_PACKAGE);
 
3204
#endif                                     
 
3205
  gtk_action_group_add_actions (view->action_group,
 
3206
                                actions, G_N_ELEMENTS (actions), view);
 
3207
 
 
3208
  action = gtk_action_group_get_action (view->action_group, "Export");
 
3209
  z_action_set_sensitive (action, NULL != vbi3_export_info_enum (0));
 
3210
 
 
3211
  vbi3_network_init (&view->req.network);
 
3212
 
 
3213
  view->charset = -1; /* automatic */
 
3214
 
 
3215
  history_update_gui (view);
 
3216
 
 
3217
  widget = GTK_WIDGET (view);
 
3218
 
 
3219
  gtk_widget_add_events (widget,
 
3220
                         GDK_EXPOSURE_MASK |            /* redraw */
 
3221
                         GDK_POINTER_MOTION_MASK |      /* cursor shape */
 
3222
                         GDK_BUTTON_PRESS_MASK |        /* links, selection */
 
3223
                         GDK_BUTTON_RELEASE_MASK |      /* selection */
 
3224
                         GDK_KEY_PRESS_MASK |           /* accelerators */
 
3225
                         GDK_STRUCTURE_MASK |           /* resize */
 
3226
                         0);
 
3227
 
 
3228
  /* Selection */
 
3229
 
 
3230
  gtk_selection_add_targets (widget, GDK_SELECTION_PRIMARY,
 
3231
                             clipboard_targets,
 
3232
                             N_ELEMENTS (clipboard_targets));
 
3233
 
 
3234
  gtk_selection_add_targets (widget, GA_CLIPBOARD,
 
3235
                             clipboard_targets,
 
3236
                             N_ELEMENTS (clipboard_targets));
 
3237
 
 
3238
  uw = 41 * CW;
 
3239
  uh = 25 * CH;
 
3240
 
 
3241
  view->unscaled_on = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, uw, uh);
 
3242
  view->unscaled_off = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, uw, uh);
 
3243
 
 
3244
  g_assert (view->unscaled_on != NULL);
 
3245
  g_assert (view->unscaled_off != NULL);
 
3246
 
 
3247
  create_page_images_from_pg (view);
 
3248
 
 
3249
  view->deferred_load = FALSE;
 
3250
  view->deferred.timeout_id = NO_SOURCE_ID;
 
3251
 
 
3252
  view->blink_timeout_id =
 
3253
    g_timeout_add (BLINK_CYCLE / 4, (GSourceFunc) blink_timeout, view);
 
3254
 
 
3255
  teletext_view_load_page (view,
 
3256
                           NULL,
 
3257
                           default_home_pgno (),
 
3258
                           VBI3_ANY_SUBNO);
 
3259
 
 
3260
  teletext_views = g_list_append (teletext_views, view);
 
3261
 
 
3262
  view->client_redraw = client_redraw;
 
3263
  view->key_press = my_key_press;
 
3264
  view->cur_pgno = cur_pgno;
 
3265
}
 
3266
 
 
3267
GtkWidget *
 
3268
teletext_view_new               (void)
 
3269
{
 
3270
  return GTK_WIDGET (g_object_new (TYPE_TELETEXT_VIEW, NULL));
 
3271
}
 
3272
 
 
3273
static void
 
3274
teletext_level_notify           (GConfClient *          client _unused_,
 
3275
                                 guint                  cnxn_id _unused_,
 
3276
                                 GConfEntry *           entry,
 
3277
                                 gpointer               user_data _unused_)
 
3278
{
 
3279
  if (entry->value)
 
3280
    {
 
3281
      const gchar *s;
 
3282
      gint enum_value;
 
3283
 
 
3284
      s = gconf_value_get_string (entry->value);
 
3285
      if (s && gconf_string_to_enum (teletext_level_enum, s, &enum_value))
 
3286
        {
 
3287
          teletext_level = (vbi3_wst_level) enum_value;
 
3288
          reformat_all_views ();
 
3289
        }
 
3290
    }
 
3291
}
 
3292
 
 
3293
static void
 
3294
default_charset_notify          (GConfClient *          client _unused_,
 
3295
                                 guint                  cnxn_id _unused_,
 
3296
                                 GConfEntry *           entry,
 
3297
                                 gpointer               user_data _unused_)
 
3298
{
 
3299
  if (entry->value)
 
3300
    {
 
3301
      const gchar *s;
 
3302
      gint enum_value;
 
3303
 
 
3304
      s = gconf_value_get_string (entry->value);
 
3305
      if (s && gconf_string_to_enum (teletext_charset_enum, s, &enum_value))
 
3306
        {
 
3307
          default_charset = enum_value;
 
3308
          reformat_all_views ();
 
3309
        }
 
3310
    }
 
3311
}
 
3312
 
 
3313
static void
 
3314
interp_type_notify              (GConfClient *          client _unused_,
 
3315
                                 guint                  cnxn_id _unused_,
 
3316
                                 GConfEntry *           entry,
 
3317
                                 gpointer               user_data _unused_)
 
3318
{
 
3319
  if (entry->value)
 
3320
    {
 
3321
      const gchar *s;
 
3322
      gint enum_value;
 
3323
 
 
3324
      s = gconf_value_get_string (entry->value);
 
3325
      if (s && gconf_string_to_enum (teletext_interp_enum, s, &enum_value))
 
3326
        {
 
3327
          interp_type = (GdkInterpType) enum_value;
 
3328
          redraw_all_views ();
 
3329
        }
 
3330
    }
 
3331
}
 
3332
 
 
3333
static void
 
3334
color_notify                    (GConfClient *          client _unused_,
 
3335
                                 guint                  cnxn_id _unused_,
 
3336
                                 GConfEntry *           entry _unused_,
 
3337
                                 gpointer               user_data _unused_)
 
3338
{
 
3339
  if (z_gconf_get_int (&brightness, GCONF_DIR "/view/brightness")
 
3340
      || z_gconf_get_int (&contrast, GCONF_DIR "/view/contrast"))
 
3341
    redraw_all_views ();
 
3342
}
 
3343
 
 
3344
GConfEnumStringPair
 
3345
navigation_enum [] = {
 
3346
  { 0, "disabled" },
 
3347
  { 1, "flof_top1" },
 
3348
  { 2, "flof_top2" },
 
3349
  { 0, NULL }
 
3350
};
 
3351
 
 
3352
static void
 
3353
navigation_notify               (GConfClient *          client _unused_,
 
3354
                                 guint                  cnxn_id _unused_,
 
3355
                                 GConfEntry *           entry,
 
3356
                                 gpointer               user_data _unused_)
 
3357
{
 
3358
  if (entry->value)
 
3359
    {
 
3360
      const gchar *s;
 
3361
      gint enum_value;
 
3362
 
 
3363
      s = gconf_value_get_string (entry->value);
 
3364
      if (s && gconf_string_to_enum (navigation_enum, s, &enum_value))
 
3365
        {
 
3366
          navigation = enum_value;
 
3367
          reformat_all_views ();
 
3368
        }
 
3369
    }
 
3370
}
 
3371
 
 
3372
static void
 
3373
class_init                      (gpointer               g_class,
 
3374
                                 gpointer               class_data _unused_)
 
3375
{
 
3376
  GObjectClass *object_class;
 
3377
  GtkWidgetClass *widget_class;
 
3378
  vbi3_bool success;
 
3379
 
 
3380
  object_class = G_OBJECT_CLASS (g_class);
 
3381
  widget_class = GTK_WIDGET_CLASS (g_class);
 
3382
  parent_class = g_type_class_peek_parent (g_class);
 
3383
 
 
3384
  object_class->finalize = instance_finalize;
 
3385
 
 
3386
  widget_class->realize                 = realize;
 
3387
  widget_class->size_allocate           = size_allocate;
 
3388
  widget_class->expose_event            = expose_event;
 
3389
  widget_class->button_press_event      = button_press_event;
 
3390
  widget_class->button_release_event    = button_release_event;
 
3391
  widget_class->motion_notify_event     = motion_notify_event;
 
3392
  widget_class->selection_clear_event   = selection_clear_event;
 
3393
  widget_class->selection_get           = selection_get;
 
3394
 
 
3395
  signals[REQUEST_CHANGED] =
 
3396
    g_signal_new ("z-request-changed",
 
3397
                  G_TYPE_FROM_CLASS (g_class),
 
3398
                  G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
 
3399
                  G_STRUCT_OFFSET (TeletextViewClass, request_changed),
 
3400
                  /* accumulator */ NULL, NULL,
 
3401
                  g_cclosure_marshal_VOID__VOID,
 
3402
                  /* return_type */ G_TYPE_NONE,
 
3403
                  /* n_params */ 0);
 
3404
 
 
3405
  signals[CHARSET_CHANGED] =
 
3406
    g_signal_new ("z-charset-changed",
 
3407
                  G_TYPE_FROM_CLASS (g_class),
 
3408
                  G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
 
3409
                  G_STRUCT_OFFSET (TeletextViewClass, charset_changed),
 
3410
                  /* accumulator */ NULL, NULL,
 
3411
                  g_cclosure_marshal_VOID__VOID,
 
3412
                  /* return_type */ G_TYPE_NONE,
 
3413
                  /* n_params */ 0);
 
3414
 
 
3415
  cursor_normal = gdk_cursor_new (GDK_LEFT_PTR);
 
3416
  cursor_link   = gdk_cursor_new (GDK_HAND2);
 
3417
  cursor_select = gdk_cursor_new (GDK_XTERM);
 
3418
 
 
3419
  GA_CLIPBOARD = gdk_atom_intern ("CLIPBOARD", FALSE);
 
3420
 
 
3421
  z_gconf_auto_update_bool (&rolling_header, GCONF_DIR "/view/rolling_header");
 
3422
  z_gconf_auto_update_bool (&live_clock, GCONF_DIR "/view/live_clock");
 
3423
 
 
3424
  /* Error ignored */
 
3425
  z_gconf_notify_add (GCONF_DIR "/level", teletext_level_notify, NULL);
 
3426
  z_gconf_notify_add (GCONF_DIR "/default_charset",
 
3427
                      default_charset_notify, NULL);
 
3428
  z_gconf_notify_add (GCONF_DIR "/view/interp_type", interp_type_notify, NULL);
 
3429
  z_gconf_notify_add (GCONF_DIR "/view/brightness", color_notify, NULL);
 
3430
  z_gconf_notify_add (GCONF_DIR "/view/contrast", color_notify, NULL);
 
3431
  z_gconf_notify_add (GCONF_DIR "/view/navigation", navigation_notify, NULL);
 
3432
 
 
3433
  cmd_register ("ttx_open", py_ttx_open, METH_VARARGS);
 
3434
  cmd_register ("ttx_page_incr", py_ttx_page_incr, METH_VARARGS,
 
3435
                ("Increment Teletext page number"),
 
3436
                "zapping.ttx_page_incr(+1)",
 
3437
                ("Decrement Teletext page number"),
 
3438
                "zapping.ttx_page_incr(-1)");
 
3439
  cmd_register ("ttx_subpage_incr", py_ttx_subpage_incr, METH_VARARGS,
 
3440
                ("Increment Teletext subpage number"),
 
3441
                "zapping.ttx_subpage_incr(+1)",
 
3442
                ("Decrement Teletext subpage number"),
 
3443
                "zapping.ttx_subpage_incr(-1)");
 
3444
  cmd_register ("ttx_home", py_ttx_home, METH_VARARGS,
 
3445
                ("Go to Teletext index page"), "zapping.ttx_home()");
 
3446
  cmd_register ("ttx_hold", py_ttx_hold, METH_VARARGS,
 
3447
                ("Hold Teletext subpage"), "zapping.ttx_hold()");
 
3448
  cmd_register ("ttx_reveal", py_ttx_reveal, METH_VARARGS,
 
3449
                ("Reveal concealed text"), "zapping.ttx_reveal()");
 
3450
  cmd_register ("ttx_history_prev", py_ttx_history_prev, METH_VARARGS,
 
3451
                ("Previous Teletext page in history"),
 
3452
                "zapping.ttx_history_prev()");
 
3453
  cmd_register ("ttx_history_next", py_ttx_history_next, METH_VARARGS,
 
3454
                ("Next Teletext page in history"),
 
3455
                "zapping.ttx_history_next()");
 
3456
  cmd_register ("ttx_search", py_ttx_search, METH_VARARGS,
 
3457
                ("Teletext search"), "zapping.ttx_search()");
 
3458
  cmd_register ("ttx_export", py_ttx_export, METH_VARARGS,
 
3459
                ("Teletext export"), "zapping.ttx_export()");
 
3460
 
 
3461
  /* Send all events to our main event handler. */
 
3462
  success = vbi3_teletext_decoder_add_event_handler
 
3463
    (td,
 
3464
     (VBI3_EVENT_NETWORK |
 
3465
      VBI3_EVENT_TTX_PAGE),
 
3466
     view_vbi3_event_handler, /* user_data */ NULL);
 
3467
 
 
3468
  g_assert (success);
 
3469
}
 
3470
 
 
3471
GType
 
3472
teletext_view_get_type          (void)
 
3473
{
 
3474
  static GType type = 0;
 
3475
  
 
3476
  if (!type)
 
3477
    {
 
3478
      GTypeInfo info;
 
3479
 
 
3480
      CLEAR (info);
 
3481
 
 
3482
      info.class_size = sizeof (TeletextViewClass);
 
3483
      info.class_init = class_init;
 
3484
      info.instance_size = sizeof (TeletextView);
 
3485
      info.instance_init = instance_init;
 
3486
 
 
3487
      type = g_type_register_static (GTK_TYPE_DRAWING_AREA,
 
3488
                                     "TeletextView",
 
3489
                                     &info, (GTypeFlags) 0);
 
3490
    }
 
3491
 
 
3492
  return type;
 
3493
}