4
* Copyright (C) 2000, 2001, 2002 I�aki Garc�a Etxebarria
5
* Copyright (C) 2000, 2001, 2002, 2003, 2004 Michael H. Schimek
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.
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.
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.
22
/* $Id: view.c,v 1.13 2005/01/31 07:06:47 mschimek Exp $ */
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[] */
41
#define BLINK_CYCLE 300 /* ms */
43
#define GCONF_DIR "/apps/zapping/plugins/teletext"
51
static GObjectClass * parent_class;
53
static guint signals[N_SIGNALS];
55
static GdkCursor * cursor_normal;
56
static GdkCursor * cursor_link;
57
static GdkCursor * cursor_select;
59
static GdkAtom GA_CLIPBOARD = GDK_NONE;
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;
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 },
95
draw_scaled_page_image (TeletextView * view,
96
GdkDrawable * drawable,
108
if (!view->scaled_on)
111
sw = gdk_pixbuf_get_width (view->scaled_on);
112
sh = gdk_pixbuf_get_height (view->scaled_on);
114
gdk_draw_pixbuf (/* dst */ drawable, gc,
115
/* src */ view->scaled_on,
120
GDK_RGB_DITHER_NORMAL,
121
/* dither offset */ src_x, src_y);
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. */
131
destroy_patch (struct ttx_patch * p)
133
g_assert (NULL != p);
136
g_object_unref (G_OBJECT (p->scaled_on));
139
g_object_unref (G_OBJECT (p->scaled_off));
142
g_object_unref (G_OBJECT (p->unscaled_on));
145
g_object_unref (G_OBJECT (p->unscaled_off));
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.
155
scale_patch (struct ttx_patch * p,
167
g_assert (NULL != p);
171
g_object_unref (G_OBJECT (p->scaled_on));
177
g_object_unref (G_OBJECT (p->scaled_off));
178
p->scaled_off = NULL;
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);
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);
198
if (dstw > 0 && dsth > 0)
201
z_pixbuf_scale_simple (p->unscaled_on,
202
(gint) dstw, (gint) dsth,
207
z_pixbuf_scale_simple (p->unscaled_off,
208
(gint) dstw, (gint) dsth,
216
delete_patches (TeletextView * view)
219
struct ttx_patch *end;
221
end = view->patches + view->n_patches;
223
for (p = view->patches; p < end; ++p)
226
g_free (view->patches);
228
view->patches = NULL;
232
/* Copies dirty or flashing view->patches into view->scaled_on.
233
draw: expose changed areas for later redrawing. */
235
apply_patches (TeletextView * view,
240
struct ttx_patch *end;
247
window = GTK_WIDGET (view)->window;
249
rows = view->pg->rows;
250
columns = view->pg->columns;
252
end = view->patches + view->n_patches;
254
for (p = view->patches; p < end; ++p)
260
p->phase = (p->phase + 1) & 15;
263
scaled = p->scaled_off;
264
else if (4 == p->phase)
265
scaled = p->scaled_on;
271
if (!p->dirty || !p->scaled_on)
276
scaled = p->scaled_on;
279
/* Update the scaled version of the page */
282
z_pixbuf_copy_area (/* src */ scaled, p->sx, p->sy, p->sw, p->sh,
283
/* dst */ view->scaled_on, p->dx, p->dy);
286
gdk_window_clear_area_e (window, p->dx, p->dy, p->sw, p->sh);
292
scale_patches (TeletextView * view)
295
struct ttx_patch *end;
301
if (!view->scaled_on)
304
g_assert (NULL != view->unscaled_on);
306
sw = gdk_pixbuf_get_width (view->scaled_on);
307
sh = gdk_pixbuf_get_height (view->scaled_on);
309
uw = gdk_pixbuf_get_width (view->unscaled_on);
310
uh = gdk_pixbuf_get_height (view->unscaled_on);
312
end = view->patches + view->n_patches;
314
for (p = view->patches; p < end; ++p)
315
scale_patch (p, sw, sh, uw, uh);
319
add_patch (TeletextView * view,
327
struct ttx_patch *end;
332
g_assert (NULL != view->unscaled_on);
333
g_assert (NULL != view->unscaled_off);
335
end = view->patches + view->n_patches;
337
endcol = column + columns;
339
for (p = view->patches; p < end; ++p)
341
&& p->column < endcol
342
&& (p->column + p->columns) > column)
344
/* Patches overlap, we replace the old one. */
353
size = (view->n_patches + 1) * sizeof (*view->patches);
354
view->patches = g_realloc (view->patches, size);
356
p = view->patches + view->n_patches;
363
p->scaled_off = NULL;
364
p->unscaled_off = NULL;
365
p->columns = columns;
372
case VBI3_DOUBLE_WIDTH:
377
case VBI3_DOUBLE_HEIGHT:
382
case VBI3_DOUBLE_SIZE:
393
ux = (0 == p->column) ? 0 : p->column * CW - 5;
394
uy = (0 == p->row) ? 0 : p->row * CH - 5;
396
pw = p->width * p->columns * CW + 10;
397
ph = p->height * CH + 10;
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);
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);
417
sw = gdk_pixbuf_get_width (view->scaled_on);
418
sh = gdk_pixbuf_get_height (view->scaled_on);
420
uw = gdk_pixbuf_get_width (view->unscaled_on);
421
uh = gdk_pixbuf_get_height (view->unscaled_on);
423
scale_patch (p, sw, sh, uw, uh);
428
build_patches (TeletextView * view)
433
delete_patches (view);
440
for (row = 0; row < view->pg->rows; ++row)
444
while (col < view->pg->columns)
446
if ((cp[col].attr & VBI3_FLASH)
447
&& cp[col].size <= VBI3_DOUBLE_SIZE)
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)
456
add_patch (view, col, row, n, cp[col].size, /* flash */ TRUE);
466
cp += view->pg->columns;
471
vbi3_page_has_flash (const vbi3_page * pg)
474
const vbi3_char *end;
477
end = pg->text + pg->rows * pg->columns;
480
for (cp = pg->text; cp < end; ++cp)
483
return !!(attr & VBI3_FLASH);
487
create_empty_image (TeletextView * view)
496
if (!view->scaled_on)
499
filename = g_strdup_printf ("%s/vt_loading%d.jpeg",
503
pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
510
sw = gdk_pixbuf_get_width (view->scaled_on);
511
sh = gdk_pixbuf_get_height (view->scaled_on);
513
sx = sw / (double) gdk_pixbuf_get_width (pixbuf);
514
sy = sh / (double) gdk_pixbuf_get_height (pixbuf);
516
gdk_pixbuf_scale (/* src */ pixbuf,
517
/* dst */ view->scaled_on, 0, 0, sw, sh,
518
/* offset */ 0.0, 0.0,
522
g_object_unref (G_OBJECT (pixbuf));
524
delete_patches (view);
528
create_page_images_from_pg (TeletextView * view)
530
vbi3_image_format format;
535
create_empty_image (view);
539
g_assert (NULL != view->unscaled_on);
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;
549
success = vbi3_page_draw_teletext
551
gdk_pixbuf_get_pixels (view->unscaled_on),
553
VBI3_BRIGHTNESS, brightness,
554
VBI3_CONTRAST, contrast,
555
VBI3_REVEAL, (vbi3_bool) view->reveal,
568
sw = gdk_pixbuf_get_width (view->scaled_on);
569
sh = gdk_pixbuf_get_height (view->scaled_on);
571
sx = sw / (double) format.width;
572
sy = sh / (double) format.height;
574
gdk_pixbuf_scale (/* src */ view->unscaled_on,
575
/* dst */ view->scaled_on, 0, 0, sw, sh,
576
/* offset */ 0.0, 0.0,
581
if (vbi3_page_has_flash (view->pg))
583
success = vbi3_page_draw_teletext
585
gdk_pixbuf_get_pixels (view->unscaled_off),
587
VBI3_BRIGHTNESS, brightness,
588
VBI3_CONTRAST, contrast,
589
VBI3_REVEAL, (vbi3_bool) view->reveal,
590
VBI3_FLASH_ON, FALSE,
595
build_patches (view);
599
delete_patches (view);
604
resize_scaled_page_image (TeletextView * view,
608
if (width <= 0 || height <= 0)
612
|| width != gdk_pixbuf_get_width (view->scaled_on)
613
|| height != gdk_pixbuf_get_height (view->scaled_on))
618
g_assert (NULL != view->unscaled_on);
621
g_object_unref (G_OBJECT (view->scaled_on));
624
gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height);
625
g_assert (NULL != view->scaled_on);
629
sx = width / (double) gdk_pixbuf_get_width (view->unscaled_on);
630
sy = height / (double) gdk_pixbuf_get_height (view->unscaled_on);
632
gdk_pixbuf_scale (/* src */ view->unscaled_on,
633
/* dst */ view->scaled_on, 0, 0, width, height,
634
/* offset */ 0.0, 0.0,
638
scale_patches (view);
642
/* Renders "loading" directly into view->scaled_on. */
643
create_empty_image (view);
654
/* Note history.stack[top - 1] is the current page. */
657
history_dump (TeletextView * view)
661
fprintf (stderr, "top=%u size=%u ",
665
for (i = 0; i < view->history.size; ++i)
666
fprintf (stderr, "%03x ",
667
view->history.stack[i].pgno);
669
fputc ('\n', stderr);
673
history_update_gui (TeletextView * view)
677
action = gtk_action_group_get_action (view->action_group, "HistoryBack");
678
z_action_set_sensitive (action, view->history.top >= 2);
680
action = gtk_action_group_get_action (view->action_group, "HistoryForward");
681
z_action_set_sensitive (action, view->history.top < view->history.size);
684
/* Push *current page* onto history stack. Actually one should push
685
the page being replaced, but things are simpler this way. */
687
history_push (TeletextView * view,
688
const vbi3_network * nk,
694
top = view->history.top;
697
nk = &view->req.network;
699
if (pgno < 0x100 || pgno > 0x899)
704
if (page_num_equal2 (&view->history.stack[top - 1],
706
return; /* already stacked */
708
if (top >= N_ELEMENTS (view->history.stack))
710
top = N_ELEMENTS (view->history.stack) - 1;
712
memmove (view->history.stack,
713
view->history.stack + 1,
714
top * sizeof (*view->history.stack));
716
else if (view->history.size > top)
718
if (page_num_equal2 (&view->history.stack[top],
721
/* Apparently we move forward in history, no new page. */
722
view->history.top = top + 1;
723
history_update_gui (view);
727
/* Will discard future branch. */
731
page_num_set (&view->history.stack[top], nk, pgno, subno);
735
view->history.top = top;
736
view->history.size = top;
738
history_update_gui (view);
745
history_back_action (GtkAction * action _unused_,
750
top = view->history.top;
756
view->history.top = top - 1;
757
history_update_gui (view);
759
sp = &view->history.stack[top - 2];
761
teletext_view_load_page (view, &sp->network, sp->pgno, sp->subno);
766
py_ttx_history_prev (PyObject * self _unused_,
767
PyObject * args _unused_)
771
if (!(view = teletext_view_from_widget (python_command_widget ())))
774
history_back_action (NULL, view);
780
history_forward_action (GtkAction * action _unused_,
785
top = view->history.top;
787
if (top < view->history.size)
791
view->history.top = top + 1;
792
history_update_gui (view);
794
sp = &view->history.stack[top];
796
teletext_view_load_page (view, &sp->network, sp->pgno, sp->subno);
801
py_ttx_history_next (PyObject * self _unused_,
802
PyObject * args _unused_)
806
if (!(view = teletext_view_from_widget (python_command_widget ())))
809
history_forward_action (NULL, view);
819
update_cursor_shape (TeletextView * view)
823
GdkModifierType mask;
828
gdk_window_get_pointer (GTK_WIDGET (view)->window, &x, &y, &mask);
830
link.type = VBI3_LINK_NONE;
832
success = teletext_view_vbi3_link_from_pointer_position (view, &link, x, y);
837
buffer = g_strdup_printf (_(" Page %x"), link.pgno);
840
case VBI3_LINK_SUBPAGE:
841
buffer = g_strdup_printf (_(" Subpage %x"), link.subno & 0xFF);
846
case VBI3_LINK_EMAIL:
847
buffer = g_strconcat (" ", link.url, NULL);
851
if (!view->cursor_over_link)
853
view->cursor_over_link = TRUE;
856
gnome_appbar_push (GNOME_APPBAR (view->appbar), buffer);
858
gdk_window_set_cursor (GTK_WIDGET (view)->window, cursor_link);
863
gnome_appbar_set_status (GNOME_APPBAR (view->appbar), buffer);
871
if (view->cursor_over_link)
873
view->cursor_over_link = FALSE;
876
gnome_appbar_pop (GNOME_APPBAR (view->appbar));
878
gdk_window_set_cursor (GTK_WIDGET (view)->window, cursor_normal);
885
vbi3_link_destroy (&link);
889
redraw_view (TeletextView * view)
897
action = gtk_action_group_get_action (view->action_group, "Export");
898
z_action_set_sensitive (action,
899
NULL != vbi3_export_info_enum (0)
901
&& view->pg->pgno >= 0x100);
906
create_page_images_from_pg (view);
908
apply_patches (view, /* draw */ FALSE);
910
if (!(window = GTK_WIDGET (view)->window))
913
gdk_window_get_geometry (window,
920
/* XXX if we have rolling header and this is an old page from
921
cache we'll show an old header. */
923
/* Trigger expose event for later redrawing. */
924
gdk_window_clear_area_e (window, 0, 0, width, height);
931
teletext_toolbar_set_url (view->toolbar,
935
teletext_toolbar_set_url (view->toolbar,
940
/* Note does nothing if this page is already at TOS. */
941
history_push (view, pg->network, pg->pgno, pg->subno);
944
update_cursor_shape (view);
950
redraw_all_views (void)
954
for (p = g_list_first (teletext_views); p; p = p->next)
956
TeletextView *view = p->data;
964
get_page (const vbi3_network * nk,
967
vbi3_charset_code charset)
971
if (nk && vbi3_network_is_anonymous (nk))
972
nk = NULL; /* use currently received network */
974
if ((int) charset >= 0)
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,
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,
1005
reformat_view (TeletextView * view)
1009
if ((pg = get_page (view->pg->network,
1014
vbi3_page_unref (view->pg);
1022
reformat_all_views (void)
1026
for (p = g_list_first (teletext_views); p; p = p->next)
1028
TeletextView *view = p->data;
1030
if (view->selecting)
1037
reformat_view (view);
1042
update_header (TeletextView * view,
1043
const vbi3_event * ev)
1045
vbi3_image_format format;
1055
if (view->pg->pgno == view->req.pgno
1061
column = 32; /* only clock */
1065
if (!(ev->ev.ttx_page.flags & VBI3_SERIAL))
1066
if (0 != ((ev->ev.ttx_page.pgno ^ view->req.pgno) & 0xF00))
1069
column = 8; /* page number and clock */
1072
if ((int) view->charset >= 0)
1074
pg = vbi3_teletext_decoder_get_page
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,
1085
pg = vbi3_teletext_decoder_get_page
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,
1100
for (i = column; i < 40; ++i)
1101
if (view->pg->text[i].unicode != pg->text[i].unicode)
1105
vbi3_page_unref (pg);
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)
1115
unicode = pg->text[i].unicode;
1116
pg->text[i] = view->pg->text[i];
1117
pg->text[i].unicode = unicode;
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;
1128
buffer = gdk_pixbuf_get_pixels (view->unscaled_on);
1129
buffer += column * (CW * 4);
1131
success = vbi3_page_draw_teletext_region (pg,
1136
/* width */ 40 - column,
1138
VBI3_BRIGHTNESS, brightness,
1139
VBI3_CONTRAST, contrast,
1141
VBI3_FLASH_ON, TRUE,
1148
/* columns */ 40 - column,
1152
vbi3_page_unref (pg);
1156
view_vbi3_event_handler (const vbi3_event * ev,
1157
void * user_data _unused_)
1162
case VBI3_EVENT_NETWORK:
1165
_vbi3_network_dump (ev->network, stderr);
1166
fputc ('\n', stderr);
1169
for (p = g_list_first (teletext_views); p; p = p->next)
1173
view = (TeletextView *) p->data;
1175
if (!vbi3_network_is_anonymous (&view->req.network))
1178
if ((unsigned int) -1 != view->charset)
1180
/* XXX should use default charset of the new network
1181
from config if one exists. */
1184
g_signal_emit (view, signals[CHARSET_CHANGED], 0);
1187
if (view->selecting)
1193
vbi3_page_unref (view->pg);
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,
1208
case VBI3_EVENT_TTX_PAGE:
1209
for (p = g_list_first (teletext_views); p; p = p->next)
1213
view = (TeletextView *) p->data;
1215
if (view->selecting)
1221
if (!vbi3_network_is_anonymous (&view->req.network)
1222
&& !vbi3_network_equal (&view->req.network, ev->network))
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))
1231
if ((pg = get_page (ev->network,
1232
ev->ev.ttx_page.pgno,
1233
ev->ev.ttx_page.subno,
1236
vbi3_page_unref (view->pg);
1242
else if (ev->ev.ttx_page.flags & VBI3_ROLL_HEADER)
1244
update_header (view, ev);
1254
return FALSE; /* pass on */
1258
set_hold (TeletextView * view,
1262
teletext_toolbar_set_hold (view->toolbar, hold);
1264
if (hold != view->hold)
1266
const vbi3_page *pg;
1270
if ((pg = view->pg))
1273
view->req.subno = pg->subno;
1275
view->req.subno = VBI3_ANY_SUBNO;
1281
monitor_pgno (TeletextView * view,
1282
const vbi3_network * nk,
1285
vbi3_charset_code charset)
1289
view->freezed = FALSE;
1292
nk = &view->req.network;
1294
page_num_set (&view->req, nk, pgno, subno);
1296
g_signal_emit (view, signals[REQUEST_CHANGED], 0);
1300
if (pgno >= 0x100 && pgno <= 0x899)
1301
pg = get_page (nk, pgno, subno, charset);
1303
if (pg || !rolling_header)
1305
vbi3_page_unref (view->pg);
1306
view->pg = pg; /* can be NULL */
1313
deferred_load_timeout (gpointer user_data)
1315
TeletextView *view = user_data;
1317
view->deferred.timeout_id = NO_SOURCE_ID;
1320
&view->deferred.network,
1321
view->deferred.pgno,
1322
view->deferred.subno,
1325
view->deferred_load = FALSE;
1327
return FALSE; /* don't call again */
1331
teletext_view_load_page (TeletextView * view,
1332
const vbi3_network * nk,
1336
view->hold = (VBI3_ANY_SUBNO != subno);
1337
set_hold (view, view->hold);
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. */
1342
if ((int) view->charset >= 0)
1344
/* XXX actually we should query config for a
1345
network-pgno-subno - charset pair, reset to default
1346
only if none exists. */
1349
g_signal_emit (view, signals[CHARSET_CHANGED], 0);
1353
teletext_toolbar_set_url (view->toolbar, pgno, subno);
1359
if (pgno >= 0x100 && pgno <= 0x8FF)
1361
if (0 == subno || VBI3_ANY_SUBNO == subno)
1362
buffer = g_strdup_printf (_("Loading page %X..."), pgno);
1364
buffer = g_strdup_printf (_("Loading page %X.%02X..."),
1365
pgno, subno & 0x7F);
1369
buffer = g_strdup_printf ("Invalid page %X.%X", pgno, subno);
1372
gnome_appbar_set_status (view->appbar, buffer);
1377
gtk_widget_grab_focus (GTK_WIDGET (view));
1380
network_set (&view->deferred.network, nk);
1382
network_set (&view->deferred.network, &view->req.network);
1384
view->deferred.pgno = pgno;
1385
view->deferred.subno = subno;
1387
if (view->deferred.timeout_id > 0)
1388
g_source_remove (view->deferred.timeout_id);
1390
if (view->deferred_load)
1392
view->deferred.timeout_id =
1393
g_timeout_add (300, (GSourceFunc) deferred_load_timeout, view);
1397
view->deferred.timeout_id = NO_SOURCE_ID;
1399
monitor_pgno (view, nk, pgno, subno, view->charset);
1406
teletext_view_show_page (TeletextView * view,
1413
set_hold (view, view->hold);
1416
teletext_toolbar_set_url (view->toolbar, pg->pgno, pg->subno);
1419
gnome_appbar_set_status (view->appbar, "");
1421
gtk_widget_grab_focus (GTK_WIDGET (view));
1423
if (view->deferred.timeout_id > 0)
1424
g_source_remove (view->deferred.timeout_id);
1426
page_num_set (&view->req, pg->network, pg->pgno, pg->subno);
1428
g_signal_emit (view, signals[REQUEST_CHANGED], 0);
1430
if ((int) view->charset >= 0)
1432
/* XXX actually we should query config for a
1433
network-pgno-subno - charset pair, reset to default
1434
only if none exists. */
1437
g_signal_emit (view, signals[CHARSET_CHANGED], 0);
1440
vbi3_page_unref (view->pg);
1441
view->pg = vbi3_page_ref (pg);
1443
view->freezed = TRUE;
1455
teletext_view_from_widget (GtkWidget * widget)
1459
while (!(view = (TeletextView *)
1460
g_object_get_data (G_OBJECT (widget), "TeletextView")))
1462
if (!(widget = widget->parent))
1470
set_transient_for (GtkWindow * window,
1471
TeletextView * view)
1475
parent = GTK_WIDGET (view);
1479
if (GTK_IS_WINDOW (parent))
1481
gtk_window_set_transient_for (window, GTK_WINDOW (parent));
1485
parent = parent->parent;
1489
/* Note on success you must vbi3_link_destroy. */
1491
teletext_view_vbi3_link_from_pointer_position
1492
(TeletextView * view,
1498
const vbi3_page *pg;
1503
const vbi3_char *ac;
1505
vbi3_link_init (lk);
1510
if (!(pg = view->pg))
1513
if (!(window = GTK_WIDGET (view)->window))
1516
gdk_window_get_geometry (window,
1523
if (width <= 0 || height <= 0)
1526
column = (x * pg->columns) / width;
1527
row = (y * pg->rows) / height;
1529
if (column >= pg->columns
1533
ac = pg->text + row * pg->columns + column;
1535
if (!(ac->attr & VBI3_LINK))
1538
return vbi3_page_get_hyperlink (pg, lk, column, row);
1546
decimal_subno (vbi3_subno subno)
1548
if (0 == subno || (guint) subno > 0x99)
1549
return -1; /* any */
1551
return vbi3_bcd2dec (subno);
1555
py_ttx_hold (PyObject * self _unused_,
1561
if (!(view = teletext_view_from_widget (python_command_widget ())))
1566
if (!ParseTuple (args, "|i", &hold))
1567
g_error ("zapping.ttx_hold(|i)");
1574
set_hold (view, hold);
1580
py_ttx_reveal (PyObject * self _unused_,
1586
if (!(view = teletext_view_from_widget (python_command_widget ())))
1591
if (!ParseTuple (args, "|i", &reveal))
1592
g_error ("zapping.ttx_reveal(|i)");
1595
reveal = !view->reveal;
1600
teletext_toolbar_set_reveal (view->toolbar, reveal);
1602
view->reveal = reveal;
1611
py_ttx_open (PyObject * self _unused_,
1620
if (!(view = teletext_view_from_widget (python_command_widget ())))
1626
if (!ParseTuple (args, "|ii", &page, &subpage))
1627
g_error ("zapping.ttx_open_new(|ii)");
1629
if (page >= 100 && page <= 899)
1630
pgno = vbi3_dec2bcd (page);
1635
subno = VBI3_ANY_SUBNO;
1636
else if ((guint) subpage <= 99)
1637
subno = vbi3_dec2bcd (subpage);
1641
teletext_view_load_page (view, &view->req.network, pgno, subno);
1647
py_ttx_page_incr (PyObject * self _unused_,
1654
if (!(view = teletext_view_from_widget (python_command_widget ())))
1659
if (!ParseTuple (args, "|i", &value))
1660
g_error ("zapping.ttx_page_incr(|i)");
1662
if (abs (value) > 999)
1668
pgno = vbi3_add_bcd (view->req.pgno, vbi3_dec2bcd (value)) & 0xFFF;
1671
pgno = 0x800 + (pgno & 0xFF);
1672
else if (pgno > 0x899)
1673
pgno = 0x100 + (pgno & 0xFF);
1675
teletext_view_load_page (view, &view->req.network, pgno, VBI3_ANY_SUBNO);
1681
py_ttx_subpage_incr (PyObject * self _unused_,
1688
if (!(view = teletext_view_from_widget (python_command_widget ())))
1693
if (!ParseTuple (args, "|i", &value))
1694
g_error ("zapping.ttx_subpage_incr(|i)");
1696
if (abs (value) > 99)
1700
value += 100; /* XXX should use actual or anounced number of subp */
1702
subno = view->req.subno;
1703
if (VBI3_ANY_SUBNO == view->req.subno)
1707
subno = view->pg->subno;
1710
subno = vbi3_add_bcd (subno, vbi3_dec2bcd (value)) & 0xFF;
1712
teletext_view_load_page (view, &view->req.network, view->req.pgno, subno);
1718
default_home_pgno (void)
1723
if (z_gconf_get_int (&value, GCONF_DIR "/home_page"))
1724
value = SATURATE (value, 100, 899);
1726
return vbi3_dec2bcd (value);
1730
home_action (GtkAction * action _unused_,
1731
TeletextView * view)
1733
const vbi3_link *lk;
1738
lk = vbi3_page_get_home_link (view->pg);
1744
case VBI3_LINK_PAGE:
1745
case VBI3_LINK_SUBPAGE:
1748
teletext_view_load_page (view, lk->network, lk->pgno, lk->subno);
1752
teletext_view_load_page (view,
1754
default_home_pgno (),
1766
py_ttx_home (PyObject * self _unused_,
1767
PyObject * args _unused_)
1771
if (!(view = teletext_view_from_widget (python_command_widget ())))
1774
home_action (NULL, view);
1780
teletext_view_switch_network (TeletextView * view,
1781
const vbi3_network * nk)
1783
if ((unsigned int) -1 != view->charset)
1787
g_signal_emit (view, signals[CHARSET_CHANGED], 0);
1790
teletext_view_load_page (view, nk, default_home_pgno (), VBI3_ANY_SUBNO);
1796
teletext_view_set_charset (TeletextView * view,
1797
vbi3_charset_code code)
1799
if (view->charset != code)
1801
view->charset = code;
1803
g_signal_emit (view, signals[CHARSET_CHANGED], 0);
1805
reformat_view (view);
1816
/* Called when another application claims the selection
1817
(i.e. sends new data to the clipboard). */
1819
selection_clear_event (GtkWidget * widget,
1820
GdkEventSelection * event)
1822
TeletextView *view = TELETEXT_VIEW (widget);
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;
1829
return FALSE; /* pass on */
1832
/* Called when another application requests our selected data
1833
(overriden GtkWidget method). */
1835
selection_get (GtkWidget * widget,
1836
GtkSelectionData * selection_data,
1838
guint time _unused_)
1840
TeletextView *view = TELETEXT_VIEW (widget);
1842
if ((selection_data->selection == GDK_SELECTION_PRIMARY
1843
&& view->select.in_selection)
1844
|| (selection_data->selection == GA_CLIPBOARD
1845
&& view->select.in_clipboard))
1849
case TARGET_LAT1_STRING:
1850
case TARGET_UTF8_STRING:
1855
unsigned int actual;
1858
width = view->select.column2 - view->select.column1 + 1;
1859
height = view->select.row2 - view->select.row1 + 1;
1862
buffer = g_malloc (size);
1864
actual = vbi3_print_page_region
1868
(TARGET_LAT1_STRING == info) ? "ISO-8859-1" : "UTF-8",
1869
NULL, 0, /* standard line separator */
1870
view->select.column1,
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,
1882
gtk_selection_data_set (selection_data,
1883
GDK_SELECTION_TYPE_STRING, 8,
1894
const gint cell_width = 12; /* Teletext character cell size */
1895
const gint cell_height = 10;
1901
vbi3_image_format format;
1904
/* Selection is open (eg. 25,5 - 15,6). */
1905
if (view->select.column2 < view->select.column1)
1908
width = view->select.column2 - view->select.column1 + 1;
1909
height = view->select.row2 - view->select.row1 + 1;
1911
pixmap = gdk_pixmap_new (GTK_WIDGET (view)->window,
1913
height * cell_height,
1914
-1 /* same depth as window */);
1916
pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
1917
/* has_alpha */ TRUE,
1918
/* bits_per_sample */ 8,
1920
height * cell_height);
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;
1930
success = vbi3_page_draw_teletext_region
1932
gdk_pixbuf_get_pixels (pixbuf),
1934
view->select.column1,
1938
VBI3_BRIGHTNESS, brightness,
1939
VBI3_CONTRAST, contrast,
1940
VBI3_REVEAL, (vbi3_bool) view->select.reveal,
1941
VBI3_FLASH_ON, TRUE,
1946
gdk_draw_pixbuf (pixmap,
1947
GTK_WIDGET (view)->style->white_gc,
1951
gdk_pixbuf_get_width (pixbuf),
1952
gdk_pixbuf_get_height (pixbuf),
1953
GDK_RGB_DITHER_NORMAL,
1956
id[0] = GDK_WINDOW_XWINDOW (pixmap);
1958
gtk_selection_data_set (selection_data,
1959
GDK_SELECTION_TYPE_PIXMAP, 32,
1962
g_object_unref (pixbuf);
1973
static __inline__ void
1974
select_positions (TeletextView * view,
1984
gint width, height; /* window */
1985
gint columns, rows; /* page */
1987
gdk_window_get_geometry (GTK_WIDGET (view)->window,
1992
*pcols = columns = view->pg->columns;
1993
*prows = rows = view->pg->rows;
1995
*scol = SATURATE ((view->select.start_x * columns) / width, 0, columns - 1);
1996
*srow = SATURATE ((view->select.start_y * rows) / height, 0, rows - 1);
1998
*ccol = SATURATE ((x * columns) / width, 0, columns - 1);
1999
*crow = SATURATE ((y * rows) / height, 0, rows - 1);
2002
static __inline__ gboolean
2003
is_hidden_row (TeletextView * view,
2007
unsigned int column;
2009
if (row <= 0 || row >= 25)
2012
cp = view->pg->text + row * view->pg->columns;
2013
for (column = 0; column < view->pg->columns; ++column)
2015
if (cp->size >= VBI3_OVER_BOTTOM)
2023
/* Calculates a rectangle, scaling from text coordinates
2024
to window coordinates. */
2026
scale_rect (GdkRectangle * rect,
2036
gint ch = columns >> 1;
2037
gint rh = rows >> 1;
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;
2045
/* Transforms the selected region from the first rectangle to the
2046
second one, given in text coordinates. */
2048
select_transform (TeletextView * view,
2059
GdkRegion * exposed)
2061
gint width, height; /* window */
2062
gint columns, rows; /* page */
2064
GdkRegion *src_region;
2065
GdkRegion *dst_region;
2067
gdk_window_get_geometry (GTK_WIDGET (view)->window,
2072
columns = view->pg->columns;
2073
rows = view->pg->rows;
2075
gdk_gc_set_clip_origin (view->select.xor_gc, 0, 0);
2078
gboolean h1, h2, h3, h4;
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);
2091
if (stable || sy1 == sy2 || ((sy2 - sy1) == 1 && h3))
2101
src_region = gdk_region_rectangle (&rect);
2107
columns - 1, sy1 + h2,
2110
src_region = gdk_region_rectangle (&rect);
2117
gdk_region_union_with_rect (src_region, &rect);
2129
gdk_region_union_with_rect (src_region, &rect);
2135
gboolean h1, h2, h3, h4;
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);
2148
if (dtable || dy1 == dy2 || ((dy2 - dy1) == 1 && h3))
2153
view->select.column1 = dx1;
2154
view->select.row1 = dy1 -= h1;
2155
view->select.column2 = dx2;
2156
view->select.row2 = dy2 += h4;
2163
dst_region = gdk_region_rectangle (&rect);
2169
columns - 1, dy1 + h2,
2172
dst_region = gdk_region_rectangle (&rect);
2179
gdk_region_union_with_rect (dst_region, &rect);
2181
view->select.column1 = dx1;
2182
view->select.row1 = dy1 + h2;
2183
view->select.column2 = dx2;
2184
view->select.row2 = dy2 - h3;
2196
gdk_region_union_with_rect (dst_region, &rect);
2202
gdk_region_subtract (src_region, exposed);
2204
gdk_region_xor (src_region, dst_region);
2206
gdk_region_destroy (dst_region);
2208
gdk_gc_set_clip_region (view->select.xor_gc, src_region);
2210
gdk_region_destroy (src_region);
2212
gdk_draw_rectangle (GTK_WIDGET (view)->window,
2213
view->select.xor_gc,
2216
width - 1, height - 1);
2218
gdk_gc_set_clip_rectangle (view->select.xor_gc, NULL);
2222
select_stop (TeletextView * view)
2225
gnome_appbar_pop (view->appbar);
2227
if (view->select.last_x != -1)
2229
gint columns, rows; /* page */
2230
gint scol, srow; /* start */
2231
gint ccol, crow; /* current */
2235
select_positions (view,
2236
view->select.last_x,
2237
view->select.last_y,
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;
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,
2257
/* Copy selected text. */
2259
vbi3_page_unref (view->select.pg);
2260
view->select.pg = vbi3_page_dup (view->pg);
2261
g_assert (NULL != view->select.pg);
2263
view->select.column1 = tcol1;
2264
view->select.row1 = trow1;
2265
view->select.column2 = tcol2;
2266
view->select.row2 = trow2;
2268
view->select.reveal = view->reveal;
2270
if (!view->select.in_clipboard)
2271
if (gtk_selection_owner_set (GTK_WIDGET (view),
2274
view->select.in_clipboard = TRUE;
2276
if (!view->select.in_selection)
2277
if (gtk_selection_owner_set (GTK_WIDGET (view),
2278
GDK_SELECTION_PRIMARY,
2280
view->select.in_selection = TRUE;
2283
gnome_appbar_set_status (view->appbar,
2284
_("Selection copied to clipboard"));
2287
update_cursor_shape (view);
2289
view->selecting = FALSE;
2293
select_update (TeletextView * view,
2298
gint columns, rows; /* page */
2299
gint scol, srow; /* start */
2300
gint ocol, orow; /* last */
2301
gint ccol, crow; /* current */
2304
select_positions (view,
2310
table = !!(state & GDK_SHIFT_MASK);
2312
if (view->select.last_x == -1)
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 */
2328
gdk_window_get_geometry (GTK_WIDGET (view)->window,
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);
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 */
2348
view->select.last_x = MAX (0, x);
2349
view->select.last_y = y;
2351
view->select.table_mode = table;
2355
select_start (TeletextView * view,
2360
if (view->selecting || !view->pg)
2363
if (view->pg->pgno < 0x100)
2366
gnome_appbar_set_status (view->appbar, _("No page loaded"));
2370
if (view->cursor_over_link)
2372
view->cursor_over_link = FALSE;
2374
gnome_appbar_pop (view->appbar);
2378
gnome_appbar_push (view->appbar,
2379
_("Selecting - press Shift key for table mode"));
2381
gdk_window_set_cursor (GTK_WIDGET (view)->window, cursor_select);
2383
view->select.start_x = x;
2384
view->select.start_y = y;
2386
view->select.last_x = -1; /* not yet, wait for move event */
2388
view->select.table_mode = !!(state & GDK_SHIFT_MASK);
2389
view->select.rtl_mode = FALSE; /* XXX to do */
2391
view->selecting = TRUE;
2399
export_action (GtkAction * action _unused_,
2400
TeletextView * view)
2402
extern gchar *zvbi_get_current_network_name (void); /* XXX */
2406
g_assert (view->pg && view->pg->pgno >= 0x100);
2408
if ((name = zvbi_get_current_network_name ()))
2412
for (i = 0; i < strlen (name); ++i)
2413
if (!g_ascii_isalnum (name[i]))
2416
dialog = export_dialog_new (view->pg, name, view->reveal);
2420
dialog = export_dialog_new (view->pg, "Zapzilla", view->reveal);
2425
set_transient_for (GTK_WINDOW (dialog), view);
2426
gtk_widget_show_all (dialog);
2431
py_ttx_export (PyObject * self _unused_,
2432
PyObject * args _unused_)
2436
if (!(view = teletext_view_from_widget (python_command_widget ())))
2439
export_action (NULL, view);
2449
search_action (GtkAction * action _unused_,
2450
TeletextView * view)
2454
if (view->search_dialog)
2456
gtk_window_present (GTK_WINDOW (view->search_dialog));
2458
else if ((widget = search_dialog_new (view)))
2460
view->search_dialog = widget;
2462
g_signal_connect (G_OBJECT (widget), "destroy",
2463
G_CALLBACK (gtk_widget_destroyed),
2464
&view->search_dialog);
2466
set_transient_for (GTK_WINDOW (widget), view);
2468
gtk_widget_show_all (widget);
2473
py_ttx_search (PyObject * self _unused_,
2474
PyObject * args _unused_)
2478
if (!(view = teletext_view_from_widget (python_command_widget ())))
2481
search_action (NULL, view);
2491
hotlist_menu_insert (GtkMenuShell * menu,
2492
const vbi3_network * nk,
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;
2510
for (pgno = 0x100; pgno <= 0x899; pgno = vbi3_add_bcd (pgno, 0x001))
2512
vbi3_ttx_page_stat ps;
2513
gboolean new_window;
2514
GtkWidget *menu_item;
2517
ps.page_type = VBI3_UNKNOWN_PAGE;
2519
/* Error ignored. */
2520
vbi3_teletext_decoder_get_ttx_page_stat (td, &ps, nk, pgno);
2524
switch (ps.page_type)
2528
#define ONCE(b) if (b) continue; else b = TRUE;
2530
case VBI3_SUBTITLE_INDEX:
2531
ONCE (have_subtitle_index);
2532
menu_item = z_gtk_pixmap_menu_item_new (_("Subtitle index"),
2536
case VBI3_NOW_AND_NEXT:
2537
ONCE (have_now_and_next);
2539
menu_item = z_gtk_pixmap_menu_item_new (_("Now and Next"),
2540
GTK_STOCK_JUSTIFY_FILL);
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);
2549
case VBI3_PROGR_INDEX:
2550
ONCE (have_progr_index);
2551
menu_item = z_gtk_pixmap_menu_item_new (_("Program Index"),
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");
2561
case VBI3_PROGR_WARNING:
2562
ONCE (have_progr_warning);
2564
/* TRANSLATORS: Schedule changes and the like. */
2565
menu_item = z_gtk_pixmap_menu_item_new (_("Program Warning"),
2566
"gnome-stock-mail");
2575
GtkWidget *menu_item;
2577
menu_item = gtk_separator_menu_item_new ();
2578
gtk_widget_show (menu_item);
2579
gtk_menu_shell_insert (menu, menu_item, position);
2586
gtk_widget_show (menu_item);
2591
g_snprintf (buffer, sizeof (buffer), "%x", pgno);
2592
z_tooltip_set (menu_item, buffer);
2596
buffer = g_strdup_printf ("zapping.ttx_open_new(%x, -1)", pgno);
2598
buffer = g_strdup_printf ("zapping.ttx_open(%x, -1)", pgno);
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);
2605
gtk_menu_shell_insert (menu, menu_item, position);
2616
ttxview_hotlist_menu_insert (GtkMenuShell * menu,
2620
return hotlist_menu_insert (menu, NULL, separator, position);
2624
popup_open_page_uiinfo [] = {
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
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
2641
popup_open_url_uiinfo [] = {
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
2652
popup_page_uiinfo [] = {
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
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
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
2672
GNOME_APP_UI_ITEM, N_("S_ubtitles"), NULL,
2674
GNOME_APP_PIXMAP_STOCK, GTK_STOCK_INDEX,
2675
0, (GdkModifierType) 0, NULL
2678
GNOME_APP_UI_ITEM, N_("_Bookmarks"), NULL,
2680
GNOME_APP_PIXMAP_STOCK, GTK_STOCK_INDEX,
2681
0, (GdkModifierType) 0, NULL
2687
teletext_view_popup_menu_new (TeletextView * view,
2688
const vbi3_link * lk,
2695
menu = gtk_menu_new ();
2696
g_object_set_data (G_OBJECT (menu), "TeletextView", view);
2706
case VBI3_LINK_PAGE:
2707
case VBI3_LINK_SUBPAGE:
2708
subpage = decimal_subno (lk->subno);
2710
popup_open_page_uiinfo[0].user_data =
2711
g_strdup_printf ("zapping.ttx_open(%x, %d)",
2713
g_signal_connect_swapped (G_OBJECT (menu), "destroy",
2714
G_CALLBACK (g_free),
2715
popup_open_page_uiinfo[0].user_data);
2717
popup_open_page_uiinfo[1].user_data =
2718
g_strdup_printf ("zapping.ttx_open_new(%x, %d)",
2720
g_signal_connect_swapped (G_OBJECT (menu), "destroy",
2721
G_CALLBACK (g_free),
2722
popup_open_page_uiinfo[1].user_data);
2724
gnome_app_fill_menu (GTK_MENU_SHELL (menu),
2725
popup_open_page_uiinfo,
2731
case VBI3_LINK_HTTP:
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);
2739
gnome_app_fill_menu (GTK_MENU_SHELL (menu), popup_open_url_uiinfo,
2740
/* accel */ NULL, /* mnemo */ TRUE, pos);
2748
gnome_app_fill_menu (GTK_MENU_SHELL (menu), popup_page_uiinfo,
2751
if (!vbi3_export_info_enum (0))
2752
gtk_widget_set_sensitive (popup_page_uiinfo[1].widget, FALSE);
2756
GtkWidget *subtitles_menu;
2758
widget = popup_page_uiinfo[3].widget; /* subtitles */
2760
if ((subtitles_menu = subtitle_menu_new ()))
2761
gtk_menu_item_set_submenu (GTK_MENU_ITEM (widget), subtitles_menu);
2763
gtk_widget_set_sensitive (widget, FALSE);
2765
widget = popup_page_uiinfo[4].widget; /* bookmarks */
2767
gtk_menu_item_set_submenu (GTK_MENU_ITEM (widget),
2768
bookmarks_menu_new (view));
2770
ttxview_hotlist_menu_insert (GTK_MENU_SHELL (menu),
2771
/* separator */ TRUE, APPEND);
2775
widget = popup_page_uiinfo[3].widget; /* subtitles */
2776
gtk_widget_set_sensitive (widget, FALSE);
2777
gtk_widget_hide (widget);
2779
widget = popup_page_uiinfo[4].widget; /* bookmarks */
2780
gtk_widget_set_sensitive (widget, FALSE);
2781
gtk_widget_hide (widget);
2788
blink_timeout (gpointer user_data)
2790
TeletextView *view = user_data;
2792
apply_patches (view, /* draw */ TRUE);
2798
size_allocate (GtkWidget * widget,
2799
GtkAllocation * allocation)
2801
TeletextView *view = TELETEXT_VIEW (widget);
2803
GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
2805
resize_scaled_page_image (view, allocation->width, allocation->height);
2809
expose_event (GtkWidget * widget,
2810
GdkEventExpose * event)
2812
TeletextView *view = TELETEXT_VIEW (widget);
2815
draw_scaled_page_image (view,
2817
widget->style->white_gc,
2818
/* src */ event->area.x, event->area.y,
2819
/* dst */ event->area.x, event->area.y,
2821
event->area.height);
2824
&& view->select.last_x != -1)
2826
gint columns, rows; /* page */
2827
gint scol, srow; /* start */
2828
gint ccol, crow; /* current */
2830
select_positions (view,
2831
view->select.last_x,
2832
view->select.last_y,
2837
region = gdk_region_rectangle (&event->area);
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,
2848
gdk_region_destroy (region);
2855
motion_notify_event (GtkWidget * widget,
2856
GdkEventMotion * event)
2858
TeletextView *view = TELETEXT_VIEW (widget);
2860
if (view->selecting)
2861
select_update (view, (int) event->x, (int) event->y, event->state);
2863
update_cursor_shape (view);
2869
button_release_event (GtkWidget * widget,
2870
GdkEventButton * event _unused_)
2872
TeletextView *view = TELETEXT_VIEW (widget);
2874
if (view->selecting)
2877
return FALSE; /* pass on */
2881
button_press_event (GtkWidget * widget,
2882
GdkEventButton * event)
2884
TeletextView *view = TELETEXT_VIEW (widget);
2888
switch (event->button)
2890
case 1: /* left button */
2891
if (event->state & (GDK_SHIFT_MASK |
2902
success = teletext_view_vbi3_link_from_pointer_position
2903
(view, &lk, (int) event->x, (int) event->y);
2909
case VBI3_LINK_PAGE:
2910
case VBI3_LINK_SUBPAGE:
2911
teletext_view_load_page (view,lk.network, lk.pgno, lk.subno);
2914
case VBI3_LINK_HTTP:
2916
case VBI3_LINK_EMAIL:
2917
z_url_show (NULL, lk.url);
2928
vbi3_link_destroy (&lk);
2939
return TRUE; /* handled */
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);
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 */
2957
case VBI3_LINK_HTTP:
2959
case VBI3_LINK_EMAIL:
2960
z_url_show (NULL, lk.url);
2961
vbi3_link_destroy (&lk);
2962
return TRUE; /* handled */
2965
vbi3_link_destroy (&lk);
2976
return FALSE; /* pass on */
2979
/* Drawing area cannot focus, must be called by parent. */
2981
teletext_view_on_key_press (GtkWidget * widget _unused_,
2982
GdkEventKey * event,
2983
TeletextView * view)
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;
2995
view->last_key_press_event_time = event->time;
2997
digit = event->keyval - GDK_0;
2999
switch (event->keyval)
3001
case GDK_a ... GDK_f:
3002
case GDK_A ... GDK_F:
3005
digit = (event->keyval & 7) + 9;
3011
case GDK_KP_0 ... GDK_KP_9:
3012
digit = event->keyval - GDK_KP_0;
3015
case GDK_0 ... GDK_9:
3017
if (event->state & (GDK_CONTROL_MASK |
3021
if (digit >= 1 && digit <= 8)
3023
teletext_view_load_page (view,
3025
(vbi3_pgno) digit * 0x100,
3028
return TRUE; /* handled, don't pass on */
3034
if (view->entered_pgno >= 0x100)
3035
view->entered_pgno = 0;
3037
view->entered_pgno = (view->entered_pgno << 4) + digit;
3039
if (view->entered_pgno >= 0x900)
3040
view->entered_pgno ^= 0x800;
3042
if (view->entered_pgno >= 0x100)
3044
teletext_view_load_page (view,
3051
/* view->freezed = TRUE; */
3054
teletext_toolbar_set_url (view->toolbar, view->entered_pgno, 0);
3057
return TRUE; /* handled */
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)
3065
python_command_printf (GTK_WIDGET (view), "zapping.ttx_export()");
3066
return TRUE; /* handled */
3075
return FALSE; /* pass on */
3079
my_key_press (TeletextView * view,
3080
GdkEventKey * event)
3082
return teletext_view_on_key_press (GTK_WIDGET (view), event, view);
3086
client_redraw (TeletextView * view,
3088
unsigned int height)
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,
3100
instance_finalize (GObject * object)
3102
TeletextView *view = TELETEXT_VIEW (object);
3105
teletext_views = g_list_remove (teletext_views, view);
3107
if (view->search_dialog)
3108
gtk_widget_destroy (view->search_dialog);
3110
if (view->blink_timeout_id > 0)
3111
g_source_remove (view->blink_timeout_id);
3113
if (view->deferred.timeout_id > 0)
3114
g_source_remove (view->deferred.timeout_id);
3116
g_object_unref (view->unscaled_on);
3117
g_object_unref (view->unscaled_off);
3119
if (view->scaled_on)
3120
g_object_unref (view->scaled_on);
3122
delete_patches (view);
3125
g_object_unref (view->select.xor_gc);
3127
window = GTK_WIDGET (view)->window;
3129
if (view->select.in_clipboard)
3131
if (gdk_selection_owner_get (GA_CLIPBOARD) == window)
3132
gtk_selection_owner_set (NULL, GA_CLIPBOARD, GDK_CURRENT_TIME);
3135
if (view->select.in_selection)
3137
if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == window)
3138
gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY,
3142
vbi3_page_unref (view->select.pg);
3143
vbi3_page_unref (view->pg);
3145
vbi3_network_destroy (&view->req.network);
3146
vbi3_network_destroy (&view->deferred.network);
3148
parent_class->finalize (object);
3151
static GtkActionEntry
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) },
3166
/* We cannot initialize this until the widget (and its
3167
parent GtkWindow) has a window. */
3169
realize (GtkWidget * widget)
3171
TeletextView *view = TELETEXT_VIEW (widget);
3173
GTK_WIDGET_CLASS (parent_class)->realize (widget);
3175
/* No background, prevents flicker. */
3176
gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
3178
view->select.xor_gc = gdk_gc_new (widget->window);
3179
gdk_gc_set_function (view->select.xor_gc, GDK_INVERT);
3183
cur_pgno (TeletextView * view)
3186
return view->pg->pgno;
3192
instance_init (GTypeInstance * instance,
3193
gpointer g_class _unused_)
3195
TeletextView *view = (TeletextView *) instance;
3200
view->action_group = gtk_action_group_new ("TeletextViewActions");
3202
gtk_action_group_set_translation_domain (view->action_group,
3205
gtk_action_group_add_actions (view->action_group,
3206
actions, G_N_ELEMENTS (actions), view);
3208
action = gtk_action_group_get_action (view->action_group, "Export");
3209
z_action_set_sensitive (action, NULL != vbi3_export_info_enum (0));
3211
vbi3_network_init (&view->req.network);
3213
view->charset = -1; /* automatic */
3215
history_update_gui (view);
3217
widget = GTK_WIDGET (view);
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 */
3230
gtk_selection_add_targets (widget, GDK_SELECTION_PRIMARY,
3232
N_ELEMENTS (clipboard_targets));
3234
gtk_selection_add_targets (widget, GA_CLIPBOARD,
3236
N_ELEMENTS (clipboard_targets));
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);
3244
g_assert (view->unscaled_on != NULL);
3245
g_assert (view->unscaled_off != NULL);
3247
create_page_images_from_pg (view);
3249
view->deferred_load = FALSE;
3250
view->deferred.timeout_id = NO_SOURCE_ID;
3252
view->blink_timeout_id =
3253
g_timeout_add (BLINK_CYCLE / 4, (GSourceFunc) blink_timeout, view);
3255
teletext_view_load_page (view,
3257
default_home_pgno (),
3260
teletext_views = g_list_append (teletext_views, view);
3262
view->client_redraw = client_redraw;
3263
view->key_press = my_key_press;
3264
view->cur_pgno = cur_pgno;
3268
teletext_view_new (void)
3270
return GTK_WIDGET (g_object_new (TYPE_TELETEXT_VIEW, NULL));
3274
teletext_level_notify (GConfClient * client _unused_,
3275
guint cnxn_id _unused_,
3277
gpointer user_data _unused_)
3284
s = gconf_value_get_string (entry->value);
3285
if (s && gconf_string_to_enum (teletext_level_enum, s, &enum_value))
3287
teletext_level = (vbi3_wst_level) enum_value;
3288
reformat_all_views ();
3294
default_charset_notify (GConfClient * client _unused_,
3295
guint cnxn_id _unused_,
3297
gpointer user_data _unused_)
3304
s = gconf_value_get_string (entry->value);
3305
if (s && gconf_string_to_enum (teletext_charset_enum, s, &enum_value))
3307
default_charset = enum_value;
3308
reformat_all_views ();
3314
interp_type_notify (GConfClient * client _unused_,
3315
guint cnxn_id _unused_,
3317
gpointer user_data _unused_)
3324
s = gconf_value_get_string (entry->value);
3325
if (s && gconf_string_to_enum (teletext_interp_enum, s, &enum_value))
3327
interp_type = (GdkInterpType) enum_value;
3328
redraw_all_views ();
3334
color_notify (GConfClient * client _unused_,
3335
guint cnxn_id _unused_,
3336
GConfEntry * entry _unused_,
3337
gpointer user_data _unused_)
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 ();
3345
navigation_enum [] = {
3353
navigation_notify (GConfClient * client _unused_,
3354
guint cnxn_id _unused_,
3356
gpointer user_data _unused_)
3363
s = gconf_value_get_string (entry->value);
3364
if (s && gconf_string_to_enum (navigation_enum, s, &enum_value))
3366
navigation = enum_value;
3367
reformat_all_views ();
3373
class_init (gpointer g_class,
3374
gpointer class_data _unused_)
3376
GObjectClass *object_class;
3377
GtkWidgetClass *widget_class;
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);
3384
object_class->finalize = instance_finalize;
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;
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,
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,
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);
3419
GA_CLIPBOARD = gdk_atom_intern ("CLIPBOARD", FALSE);
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");
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);
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()");
3461
/* Send all events to our main event handler. */
3462
success = vbi3_teletext_decoder_add_event_handler
3464
(VBI3_EVENT_NETWORK |
3465
VBI3_EVENT_TTX_PAGE),
3466
view_vbi3_event_handler, /* user_data */ NULL);
3472
teletext_view_get_type (void)
3474
static GType type = 0;
3482
info.class_size = sizeof (TeletextViewClass);
3483
info.class_init = class_init;
3484
info.instance_size = sizeof (TeletextView);
3485
info.instance_init = instance_init;
3487
type = g_type_register_static (GTK_TYPE_DRAWING_AREA,
3489
&info, (GTypeFlags) 0);