2 * Copyright (C) 2006-2008 Benjamin Otte <otte@gnome.org>
3 * 2007 Pekka Lampila <pekka.lampila@iki.fi>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301 USA
27 #include <pango/pangocairo.h>
29 #include "swfdec_text_field_movie.h"
30 #include "swfdec_as_context.h"
31 #include "swfdec_as_internal.h"
32 #include "swfdec_as_interpret.h"
33 #include "swfdec_as_strings.h"
34 #include "swfdec_debug.h"
35 #include "swfdec_internal.h"
36 #include "swfdec_player_internal.h"
37 #include "swfdec_renderer_internal.h"
38 #include "swfdec_resource.h"
39 #include "swfdec_sandbox.h"
40 #include "swfdec_text_format.h"
41 #include "swfdec_xml.h"
43 G_DEFINE_TYPE (SwfdecTextFieldMovie
, swfdec_text_field_movie
, SWFDEC_TYPE_ACTOR
)
47 #define BORDER_RIGHT 2
48 #define BORDER_BOTTOM 2
53 swfdec_text_field_movie_compute_layout_area (SwfdecTextFieldMovie
*text
)
57 tmpx
= round ((BORDER_LEFT
+ BORDER_RIGHT
) * SWFDEC_TWIPS_SCALE_FACTOR
* text
->to_layout
.xx
);
58 tmpy
= round ((BORDER_TOP
+ BORDER_BOTTOM
) * SWFDEC_TWIPS_SCALE_FACTOR
* text
->to_layout
.yy
);
59 text
->layout_area
= text
->stage_area
;
60 if (tmpx
>= text
->layout_area
.width
||
61 tmpy
>= text
->layout_area
.height
) {
65 text
->layout_area
.x
+= tmpx
/ 2;
66 text
->layout_area
.y
+= tmpy
/ 2;
67 text
->layout_area
.width
-= tmpx
;
68 text
->layout_area
.height
-= tmpy
;
71 /* NB: This signal can happen without a locked player */
73 swfdec_text_field_movie_update_area (SwfdecTextFieldMovie
*text
)
75 SwfdecMovie
*movie
= SWFDEC_MOVIE (text
);
76 cairo_matrix_t
*matrix
, translate
;
79 if (swfdec_player_is_locked (SWFDEC_PLAYER (swfdec_gc_object_get_context (text
))))
80 swfdec_movie_invalidate_next (movie
);
82 /* check if we indeed want to render */
83 matrix
= &text
->to_layout
;
84 swfdec_movie_local_to_global_matrix (movie
, matrix
);
85 cairo_matrix_multiply (matrix
, matrix
,
86 &SWFDEC_PLAYER (swfdec_gc_object_get_context (movie
))->priv
->global_to_stage
);
87 if (matrix
->xy
!= 0.0 || matrix
->yx
!= 0.0 ||
88 matrix
->xx
<= 0.0 || matrix
->yy
<= 0.0) {
89 swfdec_rectangle_init_empty (&text
->stage_area
);
90 swfdec_rectangle_init_empty (&text
->layout_area
);
96 cairo_matrix_transform_point (matrix
, &x
, &y
);
97 cairo_matrix_init_translate (&translate
, round (x
) - x
, round (y
) - y
);
98 cairo_matrix_multiply (matrix
, matrix
, &translate
);
99 text
->from_layout
= *matrix
;
100 if (cairo_matrix_invert (&text
->from_layout
)) {
101 SWFDEC_ERROR ("cannot invert to-layout matrix");
102 swfdec_rectangle_init_empty (&text
->stage_area
);
103 swfdec_rectangle_init_empty (&text
->layout_area
);
107 x
= text
->extents
.x0
;
108 y
= text
->extents
.y0
;
109 cairo_matrix_transform_point (matrix
, &x
, &y
);
110 text
->stage_area
.x
= x
;
111 text
->stage_area
.y
= y
;
112 x
= text
->extents
.x1
;
113 y
= text
->extents
.y1
;
114 cairo_matrix_transform_point (matrix
, &x
, &y
);
115 /* FIXME: floor, ceil or round? */
116 text
->stage_area
.width
= round (x
) - text
->stage_area
.x
;
117 text
->stage_area
.height
= round (y
) - text
->stage_area
.y
;
119 swfdec_text_field_movie_compute_layout_area (text
);
121 swfdec_text_layout_set_scale (text
->layout
, matrix
->yy
* SWFDEC_TWIPS_SCALE_FACTOR
);
122 swfdec_text_layout_set_wrap_width (text
->layout
, text
->layout_area
.width
);
126 swfdec_text_field_movie_autosize (SwfdecTextFieldMovie
*text
)
128 SwfdecMovie
*movie
= SWFDEC_MOVIE (text
);
129 double x0
, z0
, x1
, z1
; /* y0 and y1 are taken by math.h */
131 if (text
->auto_size
== SWFDEC_AUTO_SIZE_NONE
)
134 swfdec_text_field_movie_update_layout (text
);
135 x1
= text
->layout_width
;
136 z1
= text
->layout_height
;
137 cairo_matrix_transform_distance (&text
->from_layout
, &x1
, &z1
);
138 x1
+= (BORDER_LEFT
+ BORDER_RIGHT
) * SWFDEC_TWIPS_SCALE_FACTOR
;
139 z1
+= (BORDER_TOP
+ BORDER_BOTTOM
) * SWFDEC_TWIPS_SCALE_FACTOR
;
140 cairo_matrix_transform_distance (&movie
->inverse_matrix
, &x1
, &z1
);
141 x1
-= text
->extents
.x1
- text
->extents
.x0
;
142 z1
-= text
->extents
.y1
- text
->extents
.y0
;
144 /* when word wrapping is enabled, don't resize width */
145 if (swfdec_text_layout_get_word_wrap (text
->layout
))
148 if (x1
== 0 && z1
== 0)
154 switch (text
->auto_size
) {
155 case SWFDEC_AUTO_SIZE_LEFT
:
158 case SWFDEC_AUTO_SIZE_RIGHT
:
162 case SWFDEC_AUTO_SIZE_CENTER
:
166 case SWFDEC_AUTO_SIZE_NONE
:
169 g_assert_not_reached ();
173 swfdec_movie_invalidate_next (movie
);
174 swfdec_movie_queue_update (movie
, SWFDEC_MOVIE_INVALID_EXTENTS
);
176 text
->extents
.x0
+= x0
;
177 text
->extents
.y0
+= z0
;
178 text
->extents
.x1
+= x1
;
179 text
->extents
.y1
+= z1
;
181 swfdec_text_field_movie_update_area (text
);
185 swfdec_text_field_movie_update_extents (SwfdecMovie
*movie
,
188 SwfdecTextFieldMovie
*text
= SWFDEC_TEXT_FIELD_MOVIE (movie
);
190 swfdec_rect_union (extents
, extents
, &text
->extents
);
194 swfdec_text_field_movie_invalidate (SwfdecMovie
*movie
, const cairo_matrix_t
*matrix
, gboolean last
)
196 SwfdecTextFieldMovie
*text
= SWFDEC_TEXT_FIELD_MOVIE (movie
);
199 rect
.x0
= text
->stage_area
.x
;
200 rect
.y0
= text
->stage_area
.y
;
201 rect
.x1
= text
->stage_area
.x
+ text
->stage_area
.width
;
202 rect
.y1
= text
->stage_area
.y
+ text
->stage_area
.height
;
209 swfdec_rect_transform (&rect
, &rect
,
210 &SWFDEC_PLAYER (swfdec_gc_object_get_context (text
))->priv
->stage_to_global
);
211 swfdec_player_invalidate (SWFDEC_PLAYER (swfdec_gc_object_get_context (movie
)),
216 swfdec_text_field_movie_has_focus (SwfdecTextFieldMovie
*text
)
218 SwfdecPlayer
*player
= SWFDEC_PLAYER (swfdec_gc_object_get_context (text
));
220 return swfdec_player_has_focus (player
, SWFDEC_ACTOR (text
));
224 swfdec_text_field_movie_render (SwfdecMovie
*movie
, cairo_t
*cr
,
225 const SwfdecColorTransform
*ctrans
)
227 SwfdecTextFieldMovie
*text
= SWFDEC_TEXT_FIELD_MOVIE (movie
);
228 SwfdecRectangle
*area
;
232 /* textfields don't mask */
233 if (swfdec_color_transform_is_mask (ctrans
) ||
234 swfdec_rectangle_is_empty (&text
->stage_area
))
237 swfdec_renderer_reset_matrix (cr
);
239 if (text
->background
) {
240 cairo_rectangle (cr
, text
->stage_area
.x
, text
->stage_area
.y
,
241 text
->stage_area
.width
, text
->stage_area
.height
);
242 color
= swfdec_color_apply_transform (text
->background_color
, ctrans
);
243 swfdec_color_set_source (cr
, SWFDEC_COLOR_OPAQUE (color
));
247 cairo_rectangle (cr
, text
->stage_area
.x
+ 0.5, text
->stage_area
.y
+ 0.5,
248 text
->stage_area
.width
, text
->stage_area
.height
);
249 color
= swfdec_color_apply_transform (text
->border_color
, ctrans
);
250 swfdec_color_set_source (cr
, SWFDEC_COLOR_OPAQUE (color
));
251 cairo_set_line_width (cr
, 1.0);
252 cairo_set_operator (cr
, CAIRO_OPERATOR_OVER
);
256 if (swfdec_text_field_movie_has_focus (text
) &&
258 (text
->selectable
&& swfdec_text_buffer_has_selection (text
->text
)))) {
259 if (text
->background
) {
260 color
= swfdec_color_apply_transform (text
->background_color
, ctrans
);
261 color
= SWFDEC_COLOR_OPAQUE (color
);
263 color
= SWFDEC_COLOR_WHITE
;
269 area
= &text
->layout_area
;
270 /* Don't draw if out of clip */
271 cairo_clip_extents (cr
, &inval
.x0
, &inval
.y0
, &inval
.x1
, &inval
.y1
);
272 if (inval
.x1
<= area
->x
|| inval
.x0
>= area
->x
+ area
->width
||
273 inval
.y1
<= area
->y
|| inval
.y0
>= area
->y
+ area
->height
)
275 /* render the layout */
276 cairo_rectangle (cr
, area
->x
, area
->y
, area
->width
, area
->height
);
278 /* FIXME: This -1 is spacing? */
279 cairo_translate (cr
, (double) area
->x
- text
->hscroll
, area
->y
- 1);
280 swfdec_text_layout_render (text
->layout
, cr
, ctrans
,
281 text
->scroll
, area
->height
, color
);
285 swfdec_text_field_movie_dispose (GObject
*object
)
287 SwfdecTextFieldMovie
*text
;
289 text
= SWFDEC_TEXT_FIELD_MOVIE (object
);
291 if (text
->style_sheet
) {
292 if (SWFDEC_IS_STYLE_SHEET (text
->style_sheet
->relay
)) {
293 g_signal_handlers_disconnect_matched (text
->style_sheet
->relay
,
294 G_SIGNAL_MATCH_DATA
, 0, 0, NULL
, NULL
, text
);
296 text
->style_sheet
= NULL
;
300 g_signal_handlers_disconnect_matched (text
->text
, G_SIGNAL_MATCH_DATA
,
301 0, 0, NULL
, NULL
, text
);
302 g_object_unref (text
->text
);
306 g_object_unref (text
->layout
);
310 G_OBJECT_CLASS (swfdec_text_field_movie_parent_class
)->dispose (object
);
314 swfdec_text_field_movie_mark (SwfdecGcObject
*object
)
316 SwfdecTextFieldMovie
*text
;
318 text
= SWFDEC_TEXT_FIELD_MOVIE (object
);
320 if (text
->variable
!= NULL
)
321 swfdec_as_string_mark (text
->variable
);
323 swfdec_text_buffer_mark (text
->text
);
324 if (text
->style_sheet
!= NULL
)
325 swfdec_as_object_mark (text
->style_sheet
);
326 if (text
->style_sheet_input
!= NULL
)
327 swfdec_as_string_mark (text
->style_sheet_input
);
328 if (text
->restrict_
!= NULL
)
329 swfdec_as_string_mark (text
->restrict_
);
331 SWFDEC_GC_OBJECT_CLASS (swfdec_text_field_movie_parent_class
)->mark (object
);
335 swfdec_text_field_movie_emit_onScroller (SwfdecTextFieldMovie
*text
)
337 g_return_if_fail (SWFDEC_IS_TEXT_FIELD_MOVIE (text
));
339 if (!text
->onScroller_emitted
&& swfdec_movie_get_version (SWFDEC_MOVIE (text
)) > 6) {
340 swfdec_player_add_action (SWFDEC_PLAYER (swfdec_gc_object_get_context (text
)),
341 SWFDEC_ACTOR (text
), SWFDEC_EVENT_SCROLL
, 0, SWFDEC_PLAYER_ACTION_QUEUE_NORMAL
);
343 text
->onScroller_emitted
= TRUE
;
347 swfdec_text_field_movie_update_layout (SwfdecTextFieldMovie
*text
)
349 guint scroll_max
, lines_visible
, rows
, height
, max
;
350 gboolean scroll_changed
= FALSE
;
352 g_return_if_fail (SWFDEC_IS_TEXT_FIELD_MOVIE (text
));
354 text
->layout_width
= swfdec_text_layout_get_width (text
->layout
);
355 text
->layout_height
= swfdec_text_layout_get_height (text
->layout
);
357 height
= text
->layout_area
.height
;
358 rows
= swfdec_text_layout_get_n_rows (text
->layout
);
359 scroll_max
= rows
- swfdec_text_layout_get_visible_rows_end (text
->layout
, height
);
360 if (scroll_max
!= text
->scroll_max
) {
361 text
->scroll_max
= scroll_max
;
362 scroll_changed
= TRUE
;
364 if (scroll_max
< text
->scroll
) {
365 text
->scroll
= scroll_max
;
366 scroll_changed
= TRUE
;
368 lines_visible
= swfdec_text_layout_get_visible_rows (text
->layout
,
369 text
->scroll
, height
);
370 if (lines_visible
!= text
->lines_visible
) {
371 text
->lines_visible
= lines_visible
;
372 scroll_changed
= TRUE
;
375 max
= swfdec_text_field_movie_get_hscroll_max (text
);
376 if (text
->hscroll
> max
) {
378 scroll_changed
= TRUE
;
382 swfdec_text_field_movie_emit_onScroller (text
);
386 swfdec_text_field_movie_init_movie (SwfdecMovie
*movie
)
388 SwfdecTextFieldMovie
*text
= SWFDEC_TEXT_FIELD_MOVIE (movie
);
389 SwfdecTextField
*text_field
= SWFDEC_TEXT_FIELD (movie
->graphic
);
393 SwfdecAsObject
*array
;
394 gboolean needs_unuse
;
396 needs_unuse
= swfdec_sandbox_try_use (movie
->resource
->sandbox
);
398 cx
= swfdec_gc_object_get_context (movie
);
399 if (movie
->resource
->version
> 5) {
400 SwfdecAsObject
*o
= swfdec_as_relay_get_as_object (SWFDEC_AS_RELAY (movie
));
402 swfdec_text_field_movie_init_properties (cx
);
404 swfdec_as_object_set_constructor_by_name (o
,
405 SWFDEC_AS_STR_TextField
, NULL
);
407 /* create _listeners array containing self */
408 array
= swfdec_as_array_new (cx
);
409 SWFDEC_AS_VALUE_SET_MOVIE (&val
, movie
);
410 swfdec_as_array_push (array
, &val
);
411 SWFDEC_AS_VALUE_SET_OBJECT (&val
, array
);
412 swfdec_as_object_set_variable_and_flags (o
, SWFDEC_AS_STR__listeners
,
413 &val
, SWFDEC_AS_VARIABLE_HIDDEN
| SWFDEC_AS_VARIABLE_PERMANENT
);
416 text
->border_color
= SWFDEC_COLOR_COMBINE (0, 0, 0, 0);
417 text
->background_color
= SWFDEC_COLOR_COMBINE (255, 255, 255, 0);
420 text
->extents
= SWFDEC_GRAPHIC (text_field
)->extents
;
422 text
->html
= text_field
->html
;
423 text
->editable
= text_field
->editable
;
424 swfdec_text_layout_set_password (text
->layout
, text_field
->password
);
425 text
->max_chars
= text_field
->max_chars
;
426 text
->selectable
= text_field
->selectable
;
427 text
->embed_fonts
= text_field
->embed_fonts
;
428 swfdec_text_layout_set_word_wrap (text
->layout
, text_field
->word_wrap
);
429 text
->multiline
= text_field
->multiline
;
430 text
->auto_size
= text_field
->auto_size
;
431 text
->border
= text_field
->border
;
433 text
->text
->default_attributes
.color
= text_field
->color
;
434 text
->text
->default_attributes
.align
= text_field
->align
;
435 if (text_field
->font
!= NULL
) {
436 text
->text
->default_attributes
.font
=
437 swfdec_as_context_get_string (cx
, text_field
->font
);
439 text
->text
->default_attributes
.size
= text_field
->size
/ 20;
440 text
->text
->default_attributes
.left_margin
= text_field
->left_margin
/ 20;
441 text
->text
->default_attributes
.right_margin
= text_field
->right_margin
/ 20;
442 text
->text
->default_attributes
.indent
= text_field
->indent
/ 20;
443 text
->text
->default_attributes
.leading
= text_field
->leading
/ 20;
447 if (text_field
&& text_field
->input
!= NULL
) {
448 swfdec_text_field_movie_set_text (text
,
449 swfdec_as_context_get_string (cx
, text_field
->input
),
452 swfdec_text_field_movie_set_text (text
, SWFDEC_AS_STR_EMPTY
,
457 if (text_field
&& text_field
->variable
!= NULL
) {
458 swfdec_text_field_movie_set_listen_variable (text
,
459 swfdec_as_context_get_string (cx
, text_field
->variable
));
463 swfdec_sandbox_unuse (movie
->resource
->sandbox
);
467 g_signal_connect_swapped (parent
, "matrix-changed",
468 G_CALLBACK (swfdec_text_field_movie_update_area
), movie
);
469 parent
= parent
->parent
;
471 /* don't emit onScroller here, plz */
472 text
->onScroller_emitted
= TRUE
;
473 swfdec_text_field_movie_update_layout (text
);
474 if (swfdec_movie_get_version (movie
) > 6)
475 text
->onScroller_emitted
= FALSE
;
476 swfdec_text_field_movie_update_area (text
);
480 swfdec_text_field_movie_finish_movie (SwfdecMovie
*movie
)
482 SwfdecTextFieldMovie
*text
= SWFDEC_TEXT_FIELD_MOVIE (movie
);
485 parent
= SWFDEC_MOVIE (text
);
487 g_signal_handlers_disconnect_matched (parent
,
488 G_SIGNAL_MATCH_DATA
, 0, 0, NULL
, NULL
, text
);
489 parent
= parent
->parent
;
492 swfdec_text_field_movie_set_listen_variable (text
, NULL
);
496 swfdec_text_field_movie_iterate (SwfdecActor
*actor
)
498 SwfdecTextFieldMovie
*text
= SWFDEC_TEXT_FIELD_MOVIE (actor
);
500 while (text
->changed
) {
501 swfdec_player_add_action (SWFDEC_PLAYER (swfdec_gc_object_get_context (text
)),
502 SWFDEC_ACTOR (text
), SWFDEC_EVENT_CHANGED
, 0, SWFDEC_PLAYER_ACTION_QUEUE_NORMAL
);
506 if (text
->onScroller_emitted
&& swfdec_movie_get_version (SWFDEC_MOVIE (text
)) <= 6) {
507 swfdec_player_add_action (SWFDEC_PLAYER (swfdec_gc_object_get_context (text
)),
508 SWFDEC_ACTOR (text
), SWFDEC_EVENT_SCROLL
, 0, SWFDEC_PLAYER_ACTION_QUEUE_NORMAL
);
510 text
->onScroller_emitted
= FALSE
;
514 swfdec_text_field_movie_contains (SwfdecMovie
*movie
, double x
, double y
,
518 /* check for movies in a higher layer that react to events */
520 ret
= SWFDEC_MOVIE_CLASS (swfdec_text_field_movie_parent_class
)->contains (
522 if (ret
&& SWFDEC_IS_ACTOR (ret
) && swfdec_actor_get_mouse_events (SWFDEC_ACTOR (ret
)))
530 swfdec_text_field_movie_parse_listen_variable (SwfdecTextFieldMovie
*text
,
531 const char *variable
, SwfdecAsObject
**object
, const char **name
)
534 SwfdecAsObject
*parent
;
537 g_return_if_fail (SWFDEC_IS_TEXT_FIELD_MOVIE (text
));
538 g_return_if_fail (variable
!= NULL
);
539 g_return_if_fail (object
!= NULL
);
540 g_return_if_fail (name
!= NULL
);
545 if (SWFDEC_MOVIE (text
)->parent
== NULL
)
548 cx
= swfdec_gc_object_get_context (text
);
549 parent
= swfdec_as_relay_get_as_object (SWFDEC_AS_RELAY (SWFDEC_MOVIE (text
)->parent
));
551 p1
= strrchr (variable
, '.');
552 p2
= strrchr (variable
, ':');
553 if (p1
== NULL
&& p2
== NULL
) {
557 if (p1
== NULL
|| (p2
!= NULL
&& p2
> p1
))
559 if (strlen (p1
) == 1)
561 *object
= swfdec_action_lookup_object (cx
, parent
, variable
, p1
);
564 *name
= swfdec_as_context_get_string (cx
, p1
+ 1);
569 swfdec_text_field_movie_asfunction (SwfdecTextFieldMovie
*text
,
573 SwfdecAsObject
*object
;
577 g_return_if_fail (g_ascii_strncasecmp (url
, "asfunction:",
578 strlen ("asfunction:")) == 0);
580 cx
= swfdec_gc_object_get_context (text
);
582 parts
= g_strsplit (url
+ strlen ("asfunction:"), ",", 2);
583 if (parts
[0] == NULL
) {
584 SWFDEC_ERROR ("asfunction link without function name clicked");
589 swfdec_text_field_movie_parse_listen_variable (text
,
590 swfdec_as_context_get_string (cx
, parts
[0]), &object
, &name
);
592 if (object
== NULL
|| name
== NULL
) {
593 SWFDEC_ERROR ("Function in asfunction link not found: %s", parts
[0]);
598 swfdec_sandbox_use (SWFDEC_MOVIE (text
)->resource
->sandbox
);
599 if (parts
[1] != NULL
) {
601 SWFDEC_AS_VALUE_SET_STRING (&val
,
602 swfdec_as_context_get_string (cx
, parts
[1]));
603 swfdec_as_object_call (object
, name
, 1, &val
, NULL
);
605 swfdec_as_object_call (object
, name
, 0, NULL
, NULL
);
607 swfdec_sandbox_unuse (SWFDEC_MOVIE (text
)->resource
->sandbox
);
613 swfdec_text_field_movie_letter_clicked (SwfdecTextFieldMovie
*text
,
616 const SwfdecTextAttributes
*attr
;
618 g_return_if_fail (SWFDEC_IS_TEXT_FIELD_MOVIE (text
));
619 g_return_if_fail (index_
<= swfdec_text_buffer_get_length (text
->text
));
621 attr
= swfdec_text_buffer_get_attributes (text
->text
, index_
);
623 if (attr
->url
!= SWFDEC_AS_STR_EMPTY
) {
624 if (g_ascii_strncasecmp (attr
->url
, "asfunction:",
625 strlen ("asfunction:")) == 0) {
626 swfdec_text_field_movie_asfunction (text
, attr
->url
);
628 swfdec_player_launch (SWFDEC_PLAYER (swfdec_gc_object_get_context (text
)),
629 attr
->url
, attr
->target
, NULL
);
635 swfdec_text_field_movie_xy_to_index (SwfdecTextFieldMovie
*text
, double x
,
636 double y
, gsize
*index_
, gboolean
*hit
)
640 cairo_matrix_transform_point (&text
->to_layout
, &x
, &y
);
641 swfdec_text_layout_query_position (text
->layout
, text
->scroll
,
642 x
- text
->layout_area
.x
, y
- text
->layout_area
.y
, index_
, hit
, &trailing
);
648 static SwfdecMouseCursor
649 swfdec_text_field_movie_mouse_cursor (SwfdecActor
*actor
)
651 SwfdecTextFieldMovie
*text
= SWFDEC_TEXT_FIELD_MOVIE (actor
);
654 const SwfdecTextAttributes
*attr
;
657 swfdec_movie_get_mouse (SWFDEC_MOVIE (actor
), &x
, &y
);
659 swfdec_text_field_movie_xy_to_index (text
, x
, y
, &index_
, &hit
);
661 attr
= swfdec_text_buffer_get_attributes (text
->text
, index_
);
666 if (attr
!= NULL
&& attr
->url
!= SWFDEC_AS_STR_EMPTY
) {
667 return SWFDEC_MOUSE_CURSOR_CLICK
;
668 } else if (text
->selectable
) {
669 return SWFDEC_MOUSE_CURSOR_TEXT
;
671 return SWFDEC_MOUSE_CURSOR_NORMAL
;
676 swfdec_text_field_movie_mouse_events (SwfdecActor
*actor
)
678 SwfdecTextFieldMovie
*text
= SWFDEC_TEXT_FIELD_MOVIE (actor
);
680 return text
->selectable
;
684 swfdec_text_field_movie_mouse_press (SwfdecActor
*actor
, guint button
)
686 SwfdecTextFieldMovie
*text
= SWFDEC_TEXT_FIELD_MOVIE (actor
);
691 if (!text
->selectable
)
695 SWFDEC_FIXME ("implement popup menus, scrollwheel and middle mouse paste");
699 swfdec_movie_get_mouse (SWFDEC_MOVIE (actor
), &x
, &y
);
701 swfdec_text_field_movie_xy_to_index (text
, x
, y
, &index_
, &hit
);
704 text
->character_pressed
= index_
;
706 text
->character_pressed
= -1;
709 swfdec_player_grab_focus (SWFDEC_PLAYER (swfdec_gc_object_get_context (text
)), actor
);
711 text
->mouse_pressed
= TRUE
;
712 swfdec_text_buffer_set_cursor (text
->text
, index_
, index_
);
716 swfdec_text_field_movie_mouse_move (SwfdecActor
*actor
, double x
, double y
)
718 SwfdecTextFieldMovie
*text
= SWFDEC_TEXT_FIELD_MOVIE (actor
);
722 if (!text
->selectable
)
725 if (!text
->mouse_pressed
)
728 swfdec_text_field_movie_xy_to_index (text
, x
, y
, &index_
, NULL
);
730 swfdec_text_buffer_get_selection (text
->text
, &start
, &end
);
731 swfdec_text_buffer_set_cursor (text
->text
,
732 swfdec_text_buffer_get_cursor (text
->text
) == start
? end
: start
, index_
);
736 swfdec_text_field_movie_mouse_release (SwfdecActor
*actor
, guint button
)
738 SwfdecTextFieldMovie
*text
= SWFDEC_TEXT_FIELD_MOVIE (actor
);
743 if (!text
->selectable
)
747 SWFDEC_FIXME ("implement popup menus, scrollwheel and middle mouse paste");
751 swfdec_movie_get_mouse (SWFDEC_MOVIE (text
), &x
, &y
);
753 text
->mouse_pressed
= FALSE
;
755 swfdec_text_field_movie_xy_to_index (text
, x
, y
, &index_
, &hit
);
757 if (hit
&& text
->character_pressed
== index_
) {
758 swfdec_text_field_movie_letter_clicked (text
, text
->character_pressed
);
759 text
->character_pressed
= -1;
764 swfdec_text_field_movie_focus_in (SwfdecActor
*actor
)
766 SwfdecTextFieldMovie
*text
= SWFDEC_TEXT_FIELD_MOVIE (actor
);
768 swfdec_text_buffer_set_cursor (text
->text
, 0,
769 swfdec_text_buffer_get_length (text
->text
));
770 if (text
->editable
|| text
->selectable
)
771 swfdec_movie_invalidate_last (SWFDEC_MOVIE (actor
));
775 swfdec_text_field_movie_focus_out (SwfdecActor
*actor
)
777 SwfdecTextFieldMovie
*text
= SWFDEC_TEXT_FIELD_MOVIE (actor
);
779 if (text
->editable
|| text
->selectable
)
780 swfdec_movie_invalidate_last (SWFDEC_MOVIE (actor
));
784 swfdec_text_field_movie_key_press (SwfdecActor
*actor
, guint keycode
, guint character
)
786 SwfdecTextFieldMovie
*text
= SWFDEC_TEXT_FIELD_MOVIE (actor
);
791 #define BACKWARD(text, _index) ((_index) == 0 ? 0 : (gsize) (g_utf8_prev_char ((text) + (_index)) - (text)))
792 #define FORWARD(text, _index) ((text)[_index] == 0 ? (_index) : (gsize) (g_utf8_next_char ((text) + (_index)) - (text)))
797 string
= swfdec_text_buffer_get_text (text
->text
);
798 swfdec_text_buffer_get_selection (text
->text
, &start
, &end
);
801 case SWFDEC_KEY_LEFT
:
802 if (swfdec_text_buffer_has_selection (text
->text
)) {
803 swfdec_text_buffer_set_cursor (text
->text
, start
, start
);
805 start
= BACKWARD (string
, start
);
806 swfdec_text_buffer_set_cursor (text
->text
, start
, start
);
809 case SWFDEC_KEY_RIGHT
:
810 if (swfdec_text_buffer_has_selection (text
->text
)) {
811 swfdec_text_buffer_set_cursor (text
->text
, end
, end
);
813 start
= FORWARD (string
, start
);
814 swfdec_text_buffer_set_cursor (text
->text
, start
, start
);
817 case SWFDEC_KEY_BACKSPACE
:
818 if (!swfdec_text_buffer_has_selection (text
->text
)) {
819 start
= BACKWARD (string
, start
);
822 swfdec_text_buffer_delete_text (text
->text
, start
, end
- start
);
825 case SWFDEC_KEY_DELETE
:
826 if (!swfdec_text_buffer_has_selection (text
->text
)) {
827 end
= FORWARD (string
, end
);
830 swfdec_text_buffer_delete_text (text
->text
, start
, end
- start
);
839 len
= g_unichar_to_utf8 (character
, insert
);
843 swfdec_text_buffer_delete_text (text
->text
, start
, end
- start
);
844 swfdec_text_buffer_insert_text (text
->text
, start
, insert
);
849 swfdec_text_field_movie_key_release (SwfdecActor
*actor
, guint keycode
, guint character
)
851 //SwfdecTextFieldMovie *text = SWFDEC_TEXT_FIELD_MOVIE (actor);
855 swfdec_text_field_movie_property_get (SwfdecMovie
*movie
, guint prop_id
)
857 SwfdecTextFieldMovie
*text
= SWFDEC_TEXT_FIELD_MOVIE (movie
);
858 SwfdecAsContext
*cx
= swfdec_gc_object_get_context (text
);
862 case SWFDEC_MOVIE_PROPERTY_X
:
863 swfdec_text_field_movie_autosize (text
);
864 swfdec_movie_update (movie
);
865 d
= SWFDEC_TWIPS_TO_DOUBLE (movie
->matrix
.x0
+ text
->extents
.x0
);
866 return swfdec_as_value_from_number (cx
, d
);
867 case SWFDEC_MOVIE_PROPERTY_Y
:
868 swfdec_text_field_movie_autosize (text
);
869 swfdec_movie_update (movie
);
870 d
= SWFDEC_TWIPS_TO_DOUBLE (movie
->matrix
.y0
+ text
->extents
.y0
);
871 return swfdec_as_value_from_number (cx
, d
);
872 case SWFDEC_MOVIE_PROPERTY_WIDTH
:
873 case SWFDEC_MOVIE_PROPERTY_HEIGHT
:
874 swfdec_text_field_movie_autosize (text
);
880 return SWFDEC_MOVIE_CLASS (swfdec_text_field_movie_parent_class
)->property_get (movie
, prop_id
);
884 swfdec_text_field_movie_property_set (SwfdecMovie
*movie
, guint prop_id
,
887 SwfdecTextFieldMovie
*text
= SWFDEC_TEXT_FIELD_MOVIE (movie
);
888 SwfdecAsContext
*cx
= swfdec_gc_object_get_context (movie
);
892 case SWFDEC_MOVIE_PROPERTY_X
:
893 if (!swfdec_as_value_to_twips (swfdec_gc_object_get_context (movie
), &val
, FALSE
, &twips
))
895 movie
->modified
= TRUE
;
896 twips
-= text
->extents
.x0
;
897 if (twips
!= movie
->matrix
.x0
) {
898 swfdec_movie_begin_update_matrix (movie
);
899 movie
->matrix
.x0
= twips
;
900 swfdec_movie_end_update_matrix (movie
);
903 case SWFDEC_MOVIE_PROPERTY_Y
:
904 if (!swfdec_as_value_to_twips (swfdec_gc_object_get_context (movie
), &val
, FALSE
, &twips
))
906 movie
->modified
= TRUE
;
907 twips
-= text
->extents
.y0
;
908 if (twips
!= movie
->matrix
.y0
) {
909 swfdec_movie_begin_update_matrix (movie
);
910 movie
->matrix
.y0
= twips
;
911 swfdec_movie_end_update_matrix (movie
);
914 case SWFDEC_MOVIE_PROPERTY_WIDTH
:
915 if (swfdec_as_value_to_twips (cx
, &val
, TRUE
, &twips
)) {
916 movie
->modified
= TRUE
;
917 if (text
->extents
.x1
!= text
->extents
.x0
+ twips
) {
918 swfdec_movie_invalidate_next (movie
);
919 swfdec_movie_queue_update (movie
, SWFDEC_MOVIE_INVALID_EXTENTS
);
920 text
->extents
.x1
= text
->extents
.x0
+ twips
;
921 swfdec_text_field_movie_update_area (text
);
925 case SWFDEC_MOVIE_PROPERTY_HEIGHT
:
926 movie
->modified
= TRUE
;
927 if (swfdec_as_value_to_twips (cx
, &val
, TRUE
, &twips
)) {
928 movie
->modified
= TRUE
;
929 if (text
->extents
.y1
!= text
->extents
.y0
+ twips
) {
930 swfdec_movie_invalidate_next (movie
);
931 swfdec_movie_queue_update (movie
, SWFDEC_MOVIE_INVALID_EXTENTS
);
932 text
->extents
.y1
= text
->extents
.y0
+ twips
;
933 swfdec_text_field_movie_update_area (text
);
941 SWFDEC_MOVIE_CLASS (swfdec_text_field_movie_parent_class
)->property_set (movie
, prop_id
, val
);
945 swfdec_text_field_movie_class_init (SwfdecTextFieldMovieClass
* g_class
)
947 GObjectClass
*object_class
= G_OBJECT_CLASS (g_class
);
948 SwfdecGcObjectClass
*gc_class
= SWFDEC_GC_OBJECT_CLASS (g_class
);
949 SwfdecMovieClass
*movie_class
= SWFDEC_MOVIE_CLASS (g_class
);
950 SwfdecActorClass
*actor_class
= SWFDEC_ACTOR_CLASS (g_class
);
952 object_class
->dispose
= swfdec_text_field_movie_dispose
;
954 gc_class
->mark
= swfdec_text_field_movie_mark
;
956 movie_class
->init_movie
= swfdec_text_field_movie_init_movie
;
957 movie_class
->finish_movie
= swfdec_text_field_movie_finish_movie
;
958 movie_class
->update_extents
= swfdec_text_field_movie_update_extents
;
959 movie_class
->render
= swfdec_text_field_movie_render
;
960 movie_class
->invalidate
= swfdec_text_field_movie_invalidate
;
961 movie_class
->contains
= swfdec_text_field_movie_contains
;
962 movie_class
->property_get
= swfdec_text_field_movie_property_get
;
963 movie_class
->property_set
= swfdec_text_field_movie_property_set
;
965 actor_class
->mouse_cursor
= swfdec_text_field_movie_mouse_cursor
;
966 actor_class
->mouse_events
= swfdec_text_field_movie_mouse_events
;
967 actor_class
->mouse_press
= swfdec_text_field_movie_mouse_press
;
968 actor_class
->mouse_release
= swfdec_text_field_movie_mouse_release
;
969 actor_class
->mouse_move
= swfdec_text_field_movie_mouse_move
;
970 actor_class
->focus_in
= swfdec_text_field_movie_focus_in
;
971 actor_class
->focus_out
= swfdec_text_field_movie_focus_out
;
972 actor_class
->key_press
= swfdec_text_field_movie_key_press
;
973 actor_class
->key_release
= swfdec_text_field_movie_key_release
;
975 actor_class
->iterate_start
= swfdec_text_field_movie_iterate
;
979 swfdec_text_field_movie_text_changed (SwfdecTextBuffer
*buffer
,
980 SwfdecTextFieldMovie
*text
)
982 swfdec_movie_invalidate_last (SWFDEC_MOVIE (text
));
987 swfdec_text_field_movie_cursor_changed (SwfdecTextBuffer
*buffer
,
988 gulong start
, gulong end
, SwfdecTextFieldMovie
*text
)
990 swfdec_movie_invalidate_last (SWFDEC_MOVIE (text
));
991 /* FIXME: update selection */
995 swfdec_text_field_movie_init (SwfdecTextFieldMovie
*text
)
997 text
->text
= swfdec_text_buffer_new ();
998 g_signal_connect (text
->text
, "cursor-changed",
999 G_CALLBACK (swfdec_text_field_movie_cursor_changed
), text
);
1000 g_signal_connect (text
->text
, "text-changed",
1001 G_CALLBACK (swfdec_text_field_movie_text_changed
), text
);
1003 text
->layout
= swfdec_text_layout_new (text
->text
);
1005 text
->mouse_wheel_enabled
= TRUE
;
1006 text
->character_pressed
= -1;
1010 swfdec_text_field_movie_set_listen_variable_text (SwfdecTextFieldMovie
*text
,
1013 SwfdecAsObject
*object
;
1016 g_return_if_fail (SWFDEC_IS_TEXT_FIELD_MOVIE (text
));
1017 g_return_if_fail (text
->variable
!= NULL
);
1018 g_return_if_fail (value
!= NULL
);
1020 swfdec_text_field_movie_parse_listen_variable (text
, text
->variable
,
1022 if (object
!= NULL
) {
1024 SWFDEC_AS_VALUE_SET_STRING (&val
, value
);
1025 swfdec_as_object_set_variable (object
, name
, &val
);
1030 swfdec_text_field_movie_variable_listener_callback (gpointer data
,
1031 const char *name
, const SwfdecAsValue
*val
)
1033 SwfdecTextFieldMovie
*text
= SWFDEC_TEXT_FIELD_MOVIE (data
);
1035 swfdec_text_field_movie_set_text (text
,
1036 swfdec_as_value_to_string (swfdec_gc_object_get_context (text
), *val
), text
->html
);
1040 swfdec_text_field_movie_set_listen_variable (SwfdecTextFieldMovie
*text
,
1043 SwfdecAsObject
*object
;
1046 if (text
->variable
!= NULL
) {
1047 swfdec_text_field_movie_parse_listen_variable (text
, text
->variable
,
1049 if (object
!= NULL
&& object
->movie
) {
1050 swfdec_movie_remove_variable_listener (SWFDEC_MOVIE (object
->relay
),
1051 text
, name
, swfdec_text_field_movie_variable_listener_callback
);
1055 text
->variable
= value
;
1057 if (value
!= NULL
) {
1058 SwfdecTextField
*text_field
=
1059 SWFDEC_TEXT_FIELD (SWFDEC_MOVIE (text
)->graphic
);
1062 swfdec_text_field_movie_parse_listen_variable (text
, value
, &object
,
1064 if (object
!= NULL
&& swfdec_as_object_get_variable (object
, name
, &val
)) {
1065 swfdec_text_field_movie_set_text (text
,
1066 swfdec_as_value_to_string (swfdec_gc_object_get_context (text
), val
),
1068 } else if (text_field
!= NULL
&& text_field
->input
!= NULL
) {
1069 // Set to the original value from the tag, not current value
1070 const char *initial
= swfdec_as_context_get_string (
1071 swfdec_gc_object_get_context (text
), text_field
->input
);
1072 swfdec_text_field_movie_set_listen_variable_text (text
, initial
);
1073 // FIXME: html value correct here?
1074 swfdec_text_field_movie_set_text (text
, initial
, text_field
->html
);
1076 if (object
!= NULL
&& object
->movie
) {
1077 swfdec_movie_add_variable_listener (SWFDEC_MOVIE (object
->relay
),
1078 text
, name
, swfdec_text_field_movie_variable_listener_callback
);
1084 swfdec_text_field_movie_get_text (SwfdecTextFieldMovie
*text
)
1090 org
= swfdec_text_buffer_get_text (text
->text
);
1091 len
= swfdec_text_buffer_get_length (text
->text
);
1093 ret
= g_new (char, len
+ 1);
1096 while ((p
= strchr (org
, '\r'))) {
1097 memcpy (ret
+ filled
, org
, p
- org
);
1102 g_assert (len
>= filled
);
1103 memcpy (ret
+ filled
, org
, len
- filled
);
1106 /* change all \n to \r */
1108 while ((p
= strchr (p
, '\n')) != NULL
) {
1112 return swfdec_as_context_give_string (swfdec_gc_object_get_context (text
), ret
);
1116 swfdec_text_field_movie_set_text (SwfdecTextFieldMovie
*text
, const char *str
,
1121 g_return_if_fail (SWFDEC_IS_TEXT_FIELD_MOVIE (text
));
1122 g_return_if_fail (str
!= NULL
);
1124 /* Flash 7 resets to default style here */
1125 if (html
&& swfdec_gc_object_get_context (text
)->version
< 8)
1126 swfdec_text_buffer_reset_default_attributes (text
->text
);
1128 length
= swfdec_text_buffer_get_length (text
->text
);
1130 swfdec_text_buffer_delete_text (text
->text
, 0, length
);
1132 if (swfdec_gc_object_get_context (text
)->version
>= 7 &&
1133 text
->style_sheet
!= NULL
)
1135 text
->style_sheet_input
= str
;
1136 swfdec_text_field_movie_html_parse (text
, str
);
1140 text
->style_sheet_input
= NULL
;
1142 swfdec_text_field_movie_html_parse (text
, str
);
1145 s
= p
= g_strdup (str
);
1146 while ((p
= strchr (p
, '\r')))
1148 swfdec_text_buffer_insert_text (text
->text
, 0, s
);
1155 swfdec_text_field_movie_get_hscroll_max (SwfdecTextFieldMovie
*text
)
1157 g_return_val_if_fail (SWFDEC_IS_TEXT_FIELD_MOVIE (text
), 0);
1159 if ((guint
) text
->layout_area
.width
>= text
->layout_width
||
1160 swfdec_text_layout_get_word_wrap (text
->layout
))
1163 return text
->layout_width
- text
->layout_area
.width
;