diff --git a/.gitignore b/.gitignore index 0ff441116..c3d05ec40 100644 --- a/.gitignore +++ b/.gitignore @@ -111,6 +111,7 @@ stamp-h1 /tests/interactive/test-easing /tests/interactive/test-interactive /tests/interactive/test-binding-pool +/tests/interactive/test-text-field /tests/interactive/redhand.png /tests/interactive/test-script.json /tests/conform/test-conformance @@ -158,6 +159,7 @@ stamp-h1 /clutter/x11/stamp-clutter-x11-enum-types.h /po/Makefile.in.in /po/POTFILES +/po/*.pot *.swn *.swo *.swp diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 4db7c9fe2..faa9480cc 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -64,7 +64,6 @@ source_h = \ $(srcdir)/clutter-container.h \ $(srcdir)/clutter-deprecated.h \ $(srcdir)/clutter-effect.h \ - $(srcdir)/clutter-entry.h \ $(srcdir)/clutter-event.h \ $(srcdir)/clutter-feature.h \ $(srcdir)/clutter-fixed.h \ @@ -72,7 +71,6 @@ source_h = \ $(srcdir)/clutter-group.h \ $(srcdir)/clutter-interval.h \ $(srcdir)/clutter-keysyms.h \ - $(srcdir)/clutter-label.h \ $(srcdir)/clutter-list-model.h \ $(srcdir)/clutter-main.h \ $(srcdir)/clutter-media.h \ @@ -87,6 +85,7 @@ source_h = \ $(srcdir)/clutter-stage.h \ $(srcdir)/clutter-stage-manager.h \ $(srcdir)/clutter-texture.h \ + $(srcdir)/clutter-text.h \ $(srcdir)/clutter-timeline.h \ $(srcdir)/clutter-timeout-pool.h \ $(srcdir)/clutter-types.h \ @@ -155,7 +154,6 @@ source_c = \ clutter-color.c \ clutter-container.c \ clutter-effect.c \ - clutter-entry.c \ clutter-enum-types.c \ clutter-event.c \ clutter-feature.c \ @@ -164,7 +162,6 @@ source_c = \ clutter-group.c \ clutter-id-pool.c \ clutter-interval.c \ - clutter-label.c \ clutter-list-model.c \ clutter-main.c \ clutter-marshal.c \ @@ -182,6 +179,7 @@ source_c = \ clutter-stage-manager.c \ clutter-stage-window.c \ clutter-texture.c \ + clutter-text.c \ clutter-timeline.c \ clutter-timeout-pool.c \ clutter-units.c \ diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index d804e41d2..7d616266e 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -221,7 +221,9 @@ struct _ClutterActorPrivate /* cached allocation is invalid (request has changed, probably) */ guint needs_allocation : 1; - guint has_clip : 1; + guint show_on_set_parent : 1; + guint has_clip : 1; + ClutterUnit clip[4]; /* Rotation angles */ @@ -260,7 +262,7 @@ struct _ClutterActorPrivate ShaderData *shader_data; - gboolean show_on_set_parent; + PangoContext *pango_context; }; enum @@ -772,6 +774,24 @@ clutter_actor_real_allocate (ClutterActor *self, g_object_thaw_notify (G_OBJECT (self)); } +/* like ClutterVertex, but using CoglFixed and with a w component */ +typedef struct { + CoglFixed x; + CoglFixed y; + CoglFixed z; + CoglFixed w; +} fixed_vertex_t; + +/* copies a fixed vertex into a ClutterVertex */ +static inline void +fixed_vertex_to_units (const fixed_vertex_t *f, + ClutterVertex *u) +{ + u->x = CLUTTER_UNITS_FROM_FIXED (f->x); + u->y = CLUTTER_UNITS_FROM_FIXED (f->y); + u->z = CLUTTER_UNITS_FROM_FIXED (f->z); +} + /* * Utility functions for manipulating transformation matrix * @@ -779,49 +799,114 @@ clutter_actor_real_allocate (ClutterActor *self, */ #define M(m,row,col) (m)[(col) * 4 + (row)] -/* Transform point (x,y,z) by matrix */ -static void -mtx_transform (ClutterFixed m[16], - ClutterFixed *x, ClutterFixed *y, ClutterFixed *z, - ClutterFixed *w) +/* Transforms a vertex using the passed matrix; vertex is + * an in-out parameter + */ +static inline void +mtx_transform (const ClutterFixed m[], + fixed_vertex_t *vertex) { - ClutterFixed _x, _y, _z, _w; - _x = *x; - _y = *y; - _z = *z; - _w = *w; + ClutterFixed _x, _y, _z, _w; - /* We care lot about precision here, so have to use QMUL */ - *x = COGL_FIXED_MUL (M (m, 0, 0), _x) - + COGL_FIXED_MUL (M (m, 0, 1), _y) - + COGL_FIXED_MUL (M (m, 0, 2), _z) - + COGL_FIXED_MUL (M (m, 0, 3), _w); + _x = vertex->x; + _y = vertex->y; + _z = vertex->z; + _w = vertex->w; - *y = COGL_FIXED_MUL (M (m, 1, 0), _x) - + COGL_FIXED_MUL (M (m, 1, 1), _y) - + COGL_FIXED_MUL (M (m, 1, 2), _z) - + COGL_FIXED_MUL (M (m, 1, 3), _w); + /* We care lot about precision here, so have to use MUL instead + * of FAST_MUL + */ + vertex->x = COGL_FIXED_MUL (M (m, 0, 0), _x) + + COGL_FIXED_MUL (M (m, 0, 1), _y) + + COGL_FIXED_MUL (M (m, 0, 2), _z) + + COGL_FIXED_MUL (M (m, 0, 3), _w); - *z = COGL_FIXED_MUL (M (m, 2, 0), _x) - + COGL_FIXED_MUL (M (m, 2, 1), _y) - + COGL_FIXED_MUL (M (m, 2, 2), _z) - + COGL_FIXED_MUL (M (m, 2, 3), _w); + vertex->y = COGL_FIXED_MUL (M (m, 1, 0), _x) + + COGL_FIXED_MUL (M (m, 1, 1), _y) + + COGL_FIXED_MUL (M (m, 1, 2), _z) + + COGL_FIXED_MUL (M (m, 1, 3), _w); - *w = COGL_FIXED_MUL (M (m, 3, 0), _x) - + COGL_FIXED_MUL (M (m, 3, 1), _y) - + COGL_FIXED_MUL (M (m, 3, 2), _z) - + COGL_FIXED_MUL (M (m, 3, 3), _w); + vertex->z = COGL_FIXED_MUL (M (m, 2, 0), _x) + + COGL_FIXED_MUL (M (m, 2, 1), _y) + + COGL_FIXED_MUL (M (m, 2, 2), _z) + + COGL_FIXED_MUL (M (m, 2, 3), _w); - /* Specially for Matthew: was going to put a comment here, but could not - * think of anything at all to say ;) - */ + vertex->w = COGL_FIXED_MUL (M (m, 3, 0), _x) + + COGL_FIXED_MUL (M (m, 3, 1), _y) + + COGL_FIXED_MUL (M (m, 3, 2), _z) + + COGL_FIXED_MUL (M (m, 3, 3), _w); + + /* Specially for Matthew: was going to put a comment here, but could not + * think of anything at all to say ;) + */ } #undef M +/* Help macros to scale from OpenGL <-1,1> coordinates system to our + * X-window based <0,window-size> coordinates + */ +#define MTX_GL_SCALE_X(x,w,v1,v2) (COGL_FIXED_MUL (((COGL_FIXED_DIV ((x), (w)) + COGL_FIXED_1) >> 1), (v1)) + (v2)) +#define MTX_GL_SCALE_Y(y,w,v1,v2) ((v1) - COGL_FIXED_MUL (((COGL_FIXED_DIV ((y), (w)) + COGL_FIXED_1) >> 1), (v1)) + (v2)) +#define MTX_GL_SCALE_Z(z,w,v1,v2) (MTX_GL_SCALE_X ((z), (w), (v1), (v2))) + +/* transforms a 4-tuple of coordinates using @matrix and + * places the result into a fixed @vertex + */ +static inline void +fixed_vertex_transform (const ClutterFixed matrix[], + ClutterFixed x, + ClutterFixed y, + ClutterFixed z, + ClutterFixed w, + fixed_vertex_t *vertex) +{ + fixed_vertex_t tmp = { 0, }; + + tmp.x = x; + tmp.y = y; + tmp.z = z; + tmp.w = w; + + mtx_transform (matrix, &tmp); + + *vertex = tmp; +} + +/* scales a fixed @vertex using @matrix and @viewport, and + * transforms the result into ClutterUnits, filling @vertex_p + */ +static inline void +fixed_vertex_scale (const ClutterFixed matrix[], + const fixed_vertex_t *vertex, + const ClutterFixed viewport[], + ClutterVertex *vertex_p) +{ + ClutterFixed v_x, v_y, v_width, v_height; + fixed_vertex_t tmp = { 0, }; + + tmp = *vertex; + + mtx_transform (matrix, &tmp); + + v_x = viewport[0]; + v_y = viewport[1]; + v_width = viewport[2]; + v_height = viewport[3]; + + tmp.x = MTX_GL_SCALE_X (tmp.x, tmp.w, v_width, v_x); + tmp.y = MTX_GL_SCALE_Y (tmp.y, tmp.w, v_height, v_y); + tmp.z = MTX_GL_SCALE_Z (tmp.z, tmp.w, v_width, v_x); + tmp.w = 0; + + fixed_vertex_to_units (&tmp, vertex_p); +} + /* Applies the transforms associated with this actor and its ancestors, * retrieves the resulting OpenGL modelview matrix, and uses the matrix * to transform the supplied point + * + * The point coordinates are in-out parameters */ static void clutter_actor_transform_point_relative (ClutterActor *actor, @@ -832,14 +917,33 @@ clutter_actor_transform_point_relative (ClutterActor *actor, ClutterUnit *w) { ClutterFixed mtx[16]; + fixed_vertex_t vertex = { 0, }; + + vertex.x = (x != NULL) ? CLUTTER_UNITS_TO_FIXED (*x) : 0; + vertex.y = (y != NULL) ? CLUTTER_UNITS_TO_FIXED (*y) : 0; + vertex.z = (z != NULL) ? CLUTTER_UNITS_TO_FIXED (*z) : 0; + vertex.w = (w != NULL) ? CLUTTER_UNITS_TO_FIXED (*w) : 0; cogl_push_matrix(); _clutter_actor_apply_modelview_transform_recursive (actor, ancestor); + cogl_get_modelview_matrix (mtx); - mtx_transform (mtx, x, y, z, w); + mtx_transform (mtx, &vertex); cogl_pop_matrix(); + + if (x) + *x = CLUTTER_UNITS_FROM_FIXED (vertex.x); + + if (y) + *y = CLUTTER_UNITS_FROM_FIXED (vertex.y); + + if (z) + *z = CLUTTER_UNITS_FROM_FIXED (vertex.z); + + if (w) + *w = CLUTTER_UNITS_FROM_FIXED (vertex.w); } /* Applies the transforms associated with this actor and its ancestors, @@ -854,22 +958,34 @@ clutter_actor_transform_point (ClutterActor *actor, ClutterUnit *w) { ClutterFixed mtx[16]; + fixed_vertex_t vertex = { 0, }; + + vertex.x = (x != NULL) ? CLUTTER_UNITS_TO_FIXED (*x) : 0; + vertex.y = (y != NULL) ? CLUTTER_UNITS_TO_FIXED (*y) : 0; + vertex.z = (z != NULL) ? CLUTTER_UNITS_TO_FIXED (*z) : 0; + vertex.w = (w != NULL) ? CLUTTER_UNITS_TO_FIXED (*w) : 0; cogl_push_matrix(); _clutter_actor_apply_modelview_transform_recursive (actor, NULL); + cogl_get_modelview_matrix (mtx); - mtx_transform (mtx, x, y, z, w); + mtx_transform (mtx, &vertex); cogl_pop_matrix(); -} -/* Help macros to scale from OpenGL <-1,1> coordinates system to our - * X-window based <0,window-size> coordinates - */ -#define MTX_GL_SCALE_X(x,w,v1,v2) (COGL_FIXED_MUL (((COGL_FIXED_DIV ((x), (w)) + COGL_FIXED_1) >> 1), (v1)) + (v2)) -#define MTX_GL_SCALE_Y(y,w,v1,v2) ((v1) - COGL_FIXED_MUL (((COGL_FIXED_DIV ((y), (w)) + COGL_FIXED_1) >> 1), (v1)) + (v2)) -#define MTX_GL_SCALE_Z(z,w,v1,v2) (MTX_GL_SCALE_X ((z), (w), (v1), (v2))) + if (x) + *x = CLUTTER_UNITS_FROM_FIXED (vertex.x); + + if (y) + *y = CLUTTER_UNITS_FROM_FIXED (vertex.y); + + if (z) + *z = CLUTTER_UNITS_FROM_FIXED (vertex.z); + + if (w) + *w = CLUTTER_UNITS_FROM_FIXED (vertex.w); +} /** * clutter_actor_apply_relative_transform_to_point: @@ -896,21 +1012,22 @@ clutter_actor_apply_relative_transform_to_point (ClutterActor *self, const ClutterVertex *point, ClutterVertex *vertex) { - ClutterVertex tmp = { 0, }; ClutterFixed v[4]; - ClutterFixed w = COGL_FIXED_1; + ClutterFixed x, y, z, w; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (ancestor == NULL || CLUTTER_IS_ACTOR (ancestor)); g_return_if_fail (point != NULL); g_return_if_fail (vertex != NULL); - tmp = *point; + x = CLUTTER_UNITS_TO_FIXED (vertex->x); + y = CLUTTER_UNITS_TO_FIXED (vertex->y); + z = CLUTTER_UNITS_TO_FIXED (vertex->z); + w = COGL_FIXED_1; /* First we tranform the point using the OpenGL modelview matrix */ clutter_actor_transform_point_relative (self, ancestor, - &tmp.x, &tmp.y, &tmp.z, - &w); + &x, &y, &z, &w); cogl_get_viewport (v); @@ -918,9 +1035,12 @@ clutter_actor_apply_relative_transform_to_point (ClutterActor *self, * The w[3] parameter should always be 1.0 here, so we ignore it; otherwise * we would have to divide the original verts with it. */ - vertex->x = COGL_FIXED_MUL ((tmp.x + COGL_FIXED_0_5), v[2]); - vertex->y = COGL_FIXED_MUL ((COGL_FIXED_0_5 - tmp.y), v[3]); - vertex->z = COGL_FIXED_MUL ((tmp.z + COGL_FIXED_0_5), v[2]); + vertex->x = + CLUTTER_UNITS_FROM_FIXED (COGL_FIXED_MUL ((x + COGL_FIXED_0_5), v[2])); + vertex->y = + CLUTTER_UNITS_FROM_FIXED (COGL_FIXED_MUL ((COGL_FIXED_0_5 - y), v[3])); + vertex->z = + CLUTTER_UNITS_FROM_FIXED (COGL_FIXED_MUL ((z + COGL_FIXED_0_5), v[2])); } /** @@ -940,30 +1060,35 @@ clutter_actor_apply_transform_to_point (ClutterActor *self, const ClutterVertex *point, ClutterVertex *vertex) { - ClutterVertex tmp = { 0, }; ClutterFixed mtx_p[16]; ClutterFixed v[4]; - ClutterFixed w = COGL_FIXED_1; + fixed_vertex_t tmp = { 0, }; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (point != NULL); g_return_if_fail (vertex != NULL); - tmp = *point; + tmp.x = CLUTTER_UNITS_TO_FIXED (vertex->x); + tmp.y = CLUTTER_UNITS_TO_FIXED (vertex->y); + tmp.z = CLUTTER_UNITS_TO_FIXED (vertex->z); + tmp.w = COGL_FIXED_1; /* First we tranform the point using the OpenGL modelview matrix */ - clutter_actor_transform_point (self, &tmp.x, &tmp.y, &tmp.z, &w); + clutter_actor_transform_point (self, &tmp.x, &tmp.y, &tmp.z, &tmp.w); cogl_get_projection_matrix (mtx_p); cogl_get_viewport (v); /* Now, transform it again with the projection matrix */ - mtx_transform (mtx_p, &tmp.x, &tmp.y, &tmp.z, &w); + mtx_transform (mtx_p, &tmp); /* Finaly translate from OpenGL coords to window coords */ - vertex->x = MTX_GL_SCALE_X (tmp.x, w, v[2], v[0]); - vertex->y = MTX_GL_SCALE_Y (tmp.y, w, v[3], v[1]); - vertex->z = MTX_GL_SCALE_Z (tmp.z, w, v[2], v[0]); + vertex->x = + CLUTTER_UNITS_FROM_FIXED (MTX_GL_SCALE_X (tmp.x, tmp.w, v[2], v[0])); + vertex->y = + CLUTTER_UNITS_FROM_FIXED (MTX_GL_SCALE_Y (tmp.y, tmp.w, v[3], v[1])); + vertex->z = + CLUTTER_UNITS_FROM_FIXED (MTX_GL_SCALE_Z (tmp.z, tmp.w, v[2], v[0])); } /* Recursively tranform supplied vertices with the tranform for the current @@ -971,66 +1096,27 @@ clutter_actor_apply_transform_to_point (ClutterActor *self, * for all the vertices in one go). */ static void -clutter_actor_transform_vertices_relative (ClutterActor *self, - ClutterActor *ancestor, - ClutterVertex verts[4], - ClutterFixed w[4]) +clutter_actor_transform_vertices_relative (ClutterActor *self, + ClutterActor *ancestor, + fixed_vertex_t vertices[]) { - ClutterFixed mtx[16]; - ClutterFixed _x, _y, _z, _w; + ClutterActorPrivate *priv = self->priv; + ClutterFixed mtx[16]; + ClutterFixed width, height; + + width = CLUTTER_UNITS_TO_FIXED (priv->allocation.x2 - priv->allocation.x1); + height = CLUTTER_UNITS_TO_FIXED (priv->allocation.y2 - priv->allocation.y1); cogl_push_matrix(); _clutter_actor_apply_modelview_transform_recursive (self, ancestor); + cogl_get_modelview_matrix (mtx); - _x = 0; - _y = 0; - _z = 0; - _w = COGL_FIXED_1; - - mtx_transform (mtx, &_x, &_y, &_z, &_w); - - verts[0].x = _x; - verts[0].y = _y; - verts[0].z = _z; - w[0] = _w; - - _x = self->priv->allocation.x2 - self->priv->allocation.x1; - _y = 0; - _z = 0; - _w = COGL_FIXED_1; - - mtx_transform (mtx, &_x, &_y, &_z, &_w); - - verts[1].x = _x; - verts[1].y = _y; - verts[1].z = _z; - w[1] = _w; - - _x = 0; - _y = self->priv->allocation.y2 - self->priv->allocation.y1; - _z = 0; - _w = COGL_FIXED_1; - - mtx_transform (mtx, &_x, &_y, &_z, &_w); - - verts[2].x = _x; - verts[2].y = _y; - verts[2].z = _z; - w[2] = _w; - - _x = self->priv->allocation.x2 - self->priv->allocation.x1; - _y = self->priv->allocation.y2 - self->priv->allocation.y1; - _z = 0; - _w = COGL_FIXED_1; - - mtx_transform (mtx, &_x, &_y, &_z, &_w); - - verts[3].x = _x; - verts[3].y = _y; - verts[3].z = _z; - w[3] = _w; + fixed_vertex_transform (mtx, 0, 0, 0, COGL_FIXED_1, &vertices[0]); + fixed_vertex_transform (mtx, width, 0, 0, COGL_FIXED_1, &vertices[1]); + fixed_vertex_transform (mtx, 0, height, 0, COGL_FIXED_1, &vertices[2]); + fixed_vertex_transform (mtx, width, height, 0, COGL_FIXED_1, &vertices[3]); cogl_pop_matrix(); } @@ -1045,12 +1131,15 @@ clutter_actor_transform_and_project_box (ClutterActor *self, const ClutterActorBox *box, ClutterVertex verts[4]) { - ClutterActor *stage; - ClutterFixed mtx[16]; - ClutterFixed mtx_p[16]; - ClutterFixed _x, _y, _z, _w; - ClutterFixed w[4]; - ClutterFixed v[4]; + ClutterActor *stage; + ClutterFixed mtx[16]; + ClutterFixed mtx_p[16]; + ClutterFixed v[4]; + ClutterFixed width, height; + fixed_vertex_t vertices[4]; + + width = CLUTTER_UNITS_TO_FIXED (box->x2 - box->x1); + height = CLUTTER_UNITS_TO_FIXED (box->y2 - box->y1); /* We essentially have to dupe some code from clutter_redraw() here * to make sure GL Matrices etc are initialised if we're called and we @@ -1071,102 +1160,25 @@ clutter_actor_transform_and_project_box (ClutterActor *self, _clutter_stage_maybe_setup_viewport (CLUTTER_STAGE (stage)); cogl_push_matrix(); + _clutter_actor_apply_modelview_transform_recursive (self, NULL); cogl_get_modelview_matrix (mtx); - _x = 0; - _y = 0; - _z = 0; - _w = COGL_FIXED_1; - - mtx_transform (mtx, &_x, &_y, &_z, &_w); - - verts[0].x = _x; - verts[0].y = _y; - verts[0].z = _z; - w[0] = _w; - - _x = box->x2 - box->x1; - _y = 0; - _z = 0; - _w = COGL_FIXED_1; - - mtx_transform (mtx, &_x, &_y, &_z, &_w); - - verts[1].x = _x; - verts[1].y = _y; - verts[1].z = _z; - w[1] = _w; - - _x = 0; - _y = box->y2 - box->y1; - _z = 0; - _w = COGL_FIXED_1; - - mtx_transform (mtx, &_x, &_y, &_z, &_w); - - verts[2].x = _x; - verts[2].y = _y; - verts[2].z = _z; - w[2] = _w; - - _x = box->x2 - box->x1; - _y = box->y2 - box->y1; - _z = 0; - _w = COGL_FIXED_1; - - mtx_transform (mtx, &_x, &_y, &_z, &_w); - - verts[3].x = _x; - verts[3].y = _y; - verts[3].z = _z; - w[3] = _w; + fixed_vertex_transform (mtx, 0, 0, 0, COGL_FIXED_1, &vertices[0]); + fixed_vertex_transform (mtx, width, 0, 0, COGL_FIXED_1, &vertices[1]); + fixed_vertex_transform (mtx, 0, height, 0, COGL_FIXED_1, &vertices[2]); + fixed_vertex_transform (mtx, width, height, 0, COGL_FIXED_1, &vertices[3]); cogl_pop_matrix(); cogl_get_projection_matrix (mtx_p); cogl_get_viewport (v); - mtx_transform (mtx_p, - &verts[0].x, - &verts[0].y, - &verts[0].z, - &w[0]); - - verts[0].x = MTX_GL_SCALE_X (verts[0].x, w[0], v[2], v[0]); - verts[0].y = MTX_GL_SCALE_Y (verts[0].y, w[0], v[3], v[1]); - verts[0].z = MTX_GL_SCALE_Z (verts[0].z, w[0], v[2], v[0]); - - mtx_transform (mtx_p, - &verts[1].x, - &verts[1].y, - &verts[1].z, - &w[1]); - - verts[1].x = MTX_GL_SCALE_X (verts[1].x, w[1], v[2], v[0]); - verts[1].y = MTX_GL_SCALE_Y (verts[1].y, w[1], v[3], v[1]); - verts[1].z = MTX_GL_SCALE_Z (verts[1].z, w[1], v[2], v[0]); - - mtx_transform (mtx_p, - &verts[2].x, - &verts[2].y, - &verts[2].z, - &w[2]); - - verts[2].x = MTX_GL_SCALE_X (verts[2].x, w[2], v[2], v[0]); - verts[2].y = MTX_GL_SCALE_Y (verts[2].y, w[2], v[3], v[1]); - verts[2].z = MTX_GL_SCALE_Z (verts[2].z, w[2], v[2], v[0]); - - mtx_transform (mtx_p, - &verts[3].x, - &verts[3].y, - &verts[3].z, - &w[3]); - - verts[3].x = MTX_GL_SCALE_X (verts[3].x, w[3], v[2], v[0]); - verts[3].y = MTX_GL_SCALE_Y (verts[3].y, w[3], v[3], v[1]); - verts[3].z = MTX_GL_SCALE_Z (verts[3].z, w[3], v[2], v[0]); + fixed_vertex_scale (mtx_p, &vertices[0], v, &verts[0]); + fixed_vertex_scale (mtx_p, &vertices[1], v, &verts[1]); + fixed_vertex_scale (mtx_p, &vertices[2], v, &verts[2]); + fixed_vertex_scale (mtx_p, &vertices[3], v, &verts[3]); } /** @@ -1181,10 +1193,10 @@ clutter_actor_transform_and_project_box (ClutterActor *self, * actor in the plane of @ancestor. The returned vertices relate to * the #ClutterActorBox coordinates as follows: * - * v[0] contains (x1, y1) - * v[1] contains (x2, y1) - * v[2] contains (x1, y2) - * v[3] contains (x2, y2) + * @verts[0] contains (x1, y1) + * @verts[1] contains (x2, y1) + * @verts[2] contains (x1, y2) + * @verts[3] contains (x2, y2) * * * If @ancestor is %NULL the ancestor will be the #ClutterStage. In @@ -1197,12 +1209,13 @@ clutter_actor_transform_and_project_box (ClutterActor *self, void clutter_actor_get_allocation_vertices (ClutterActor *self, ClutterActor *ancestor, - ClutterVertex verts[4]) + ClutterVertex verts[]) { - ClutterFixed v[4]; - ClutterFixed w[4]; - ClutterActorPrivate *priv; - ClutterActor *stage; + ClutterActorPrivate *priv; + ClutterActor *stage; + ClutterFixed v[4]; + fixed_vertex_t vertices[4]; + fixed_vertex_t tmp = { 0, }; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (ancestor == NULL || CLUTTER_IS_ACTOR (ancestor)); @@ -1234,28 +1247,33 @@ clutter_actor_get_allocation_vertices (ClutterActor *self, if (priv->needs_allocation) _clutter_stage_maybe_relayout (stage); - clutter_actor_transform_vertices_relative (self, ancestor, verts, w); + clutter_actor_transform_vertices_relative (self, ancestor, vertices); + cogl_get_viewport (v); /* - * The w[3] parameter should always be 1.0 here, so we ignore it; otherwise - * we would have to devide the original verts with it. + * The w[3] parameter should always be 1.0 here, so we ignore it; + * otherwise we would have to divide the original verts with it. */ - verts[0].x = COGL_FIXED_MUL ((verts[0].x + COGL_FIXED_0_5), v[2]); - verts[0].y = COGL_FIXED_MUL ((COGL_FIXED_0_5 - verts[0].y), v[3]); - verts[0].z = COGL_FIXED_MUL ((verts[0].z + COGL_FIXED_0_5), v[2]); + tmp.x = COGL_FIXED_MUL ((vertices[0].x + COGL_FIXED_0_5), v[2]); + tmp.y = COGL_FIXED_MUL ((COGL_FIXED_0_5 - vertices[0].y), v[3]); + tmp.z = COGL_FIXED_MUL ((vertices[0].z + COGL_FIXED_0_5), v[2]); + fixed_vertex_to_units (&tmp, &verts[0]); - verts[1].x = COGL_FIXED_MUL ((verts[1].x + COGL_FIXED_0_5), v[2]); - verts[1].y = COGL_FIXED_MUL ((COGL_FIXED_0_5 - verts[1].y), v[3]); - verts[1].z = COGL_FIXED_MUL ((verts[1].z + COGL_FIXED_0_5), v[2]); + tmp.x = COGL_FIXED_MUL ((vertices[1].x + COGL_FIXED_0_5), v[2]); + tmp.y = COGL_FIXED_MUL ((COGL_FIXED_0_5 - vertices[1].y), v[3]); + tmp.z = COGL_FIXED_MUL ((vertices[1].z + COGL_FIXED_0_5), v[2]); + fixed_vertex_to_units (&tmp, &verts[1]); - verts[2].x = COGL_FIXED_MUL ((verts[2].x + COGL_FIXED_0_5), v[2]); - verts[2].y = COGL_FIXED_MUL ((COGL_FIXED_0_5 - verts[2].y), v[3]); - verts[2].z = COGL_FIXED_MUL ((verts[2].z + COGL_FIXED_0_5), v[2]); + tmp.x = COGL_FIXED_MUL ((vertices[2].x + COGL_FIXED_0_5), v[2]); + tmp.y = COGL_FIXED_MUL ((COGL_FIXED_0_5 - vertices[2].y), v[3]); + tmp.z = COGL_FIXED_MUL ((vertices[2].z + COGL_FIXED_0_5), v[2]); + fixed_vertex_to_units (&tmp, &verts[2]); - verts[3].x = COGL_FIXED_MUL ((verts[3].x + COGL_FIXED_0_5), v[2]); - verts[3].y = COGL_FIXED_MUL ((COGL_FIXED_0_5 - verts[3].y), v[3]); - verts[3].z = COGL_FIXED_MUL ((verts[3].z + COGL_FIXED_0_5), v[2]); + tmp.x = COGL_FIXED_MUL ((vertices[3].x + COGL_FIXED_0_5), v[2]); + tmp.y = COGL_FIXED_MUL ((COGL_FIXED_0_5 - vertices[3].y), v[3]); + tmp.z = COGL_FIXED_MUL ((vertices[3].z + COGL_FIXED_0_5), v[2]); + fixed_vertex_to_units (&tmp, &verts[3]); } /** @@ -1941,6 +1959,12 @@ clutter_actor_dispose (GObject *object) destroy_shader_data (self); + if (priv->pango_context) + { + g_object_unref (priv->pango_context); + priv->pango_context = NULL; + } + g_signal_emit (self, actor_signals[DESTROY], 0); G_OBJECT_CLASS (clutter_actor_parent_class)->dispose (object); @@ -6842,17 +6866,18 @@ clutter_scriptable_iface_init (ClutterScriptableIface *iface) * Since: 0.6 */ gboolean -clutter_actor_transform_stage_point (ClutterActor *self, - ClutterUnit x, - ClutterUnit y, - ClutterUnit *x_out, - ClutterUnit *y_out) +clutter_actor_transform_stage_point (ClutterActor *self, + ClutterUnit x, + ClutterUnit y, + ClutterUnit *x_out, + ClutterUnit *y_out) { ClutterVertex v[4]; ClutterFixed ST[3][3]; ClutterFixed RQ[3][3]; int du, dv, xi, yi; - ClutterFixed xf, yf, wf, px, py, det; + ClutterUnit px, py; + ClutterFixed xf, yf, wf, det; ClutterActorPrivate *priv; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); @@ -6865,11 +6890,12 @@ clutter_actor_transform_stage_point (ClutterActor *self, * * http://www.cs.cmu.edu/~ph/texfund/texfund.pdf * - * and the sample implementaion at http://www.cs.cmu.edu/~ph/src/texfund/. + * and the sample implementation at http://www.cs.cmu.edu/~ph/src/texfund/. * - * Our texture is a rectangle with origin [0,0], so we are mapping from quad - * to rectangle only, which significantly simplifies things; the function - * calls have been unrolled, and most of the math is done in fixed point. + * Our texture is a rectangle with origin [0, 0], so we are mapping from + * quad to rectangle only, which significantly simplifies things; the + * function calls have been unrolled, and most of the math is done in fixed + * point. */ clutter_actor_get_abs_allocation_vertices (self, v); @@ -6886,9 +6912,11 @@ clutter_actor_transform_stage_point (ClutterActor *self, #define FP2FX COGL_FIXED_FROM_FLOAT #define FX2FP COGL_FIXED_TO_DOUBLE +#define UX2FP CLUTTER_UNITS_TO_FLOAT +#define UX2FX CLUTTER_UNITS_TO_FIXED #define FP2INT CLUTTER_FLOAT_TO_INT -#define DET2X(a,b,c,d) (COGL_FIXED_MUL (a, d) - COGL_FIXED_MUL (b, c)) -#define DET2FP(a,b,c,d) (a*d - b*c) +#define DET2X(a,b,c,d) (COGL_FIXED_MUL ((a), (d)) - COGL_FIXED_MUL ((b), (c))) +#define DET2FP(a,b,c,d) ((a) * (d) - (b) * (c)) /* * First, find mapping from unit uv square to xy quadrilateral; this @@ -6900,20 +6928,21 @@ clutter_actor_transform_stage_point (ClutterActor *self, py = v[0].y - v[1].y + v[3].y - v[2].y; if (!px && !py) - { /* affine transform */ - RQ[0][0] = v[1].x - v[0].x; - RQ[1][0] = v[3].x - v[1].x; - RQ[2][0] = v[0].x; - RQ[0][1] = v[1].y - v[0].y; - RQ[1][1] = v[3].y - v[1].y; - RQ[2][1] = v[0].y; + { + /* affine transform */ + RQ[0][0] = UX2FX (v[1].x - v[0].x); + RQ[1][0] = UX2FX (v[3].x - v[1].x); + RQ[2][0] = UX2FX (v[0].x); + RQ[0][1] = UX2FX (v[1].y - v[0].y); + RQ[1][1] = UX2FX (v[3].y - v[1].y); + RQ[2][1] = UX2FX (v[0].y); RQ[0][2] = 0; RQ[1][2] = 0; RQ[2][2] = COGL_FIXED_1; } else - { /* - * projective transform + { + /* projective transform * * Must do this in floating point, as the del value can overflow the * range of ClutterFixed for large actors. @@ -6923,13 +6952,12 @@ clutter_actor_transform_stage_point (ClutterActor *self, */ double dx1, dx2, dy1, dy2, del; - dx1 = FX2FP (v[1].x - v[3].x); - dx2 = FX2FP (v[2].x - v[3].x); - dy1 = FX2FP (v[1].y - v[3].y); - dy2 = FX2FP (v[2].y - v[3].y); - - del = DET2FP (dx1,dx2, dy1,dy2); + dx1 = UX2FP (v[1].x - v[3].x); + dx2 = UX2FP (v[2].x - v[3].x); + dy1 = UX2FP (v[1].y - v[3].y); + dy2 = UX2FP (v[2].y - v[3].y); + del = DET2FP (dx1, dx2, dy1, dy2); if (!del) return FALSE; @@ -6937,16 +6965,20 @@ clutter_actor_transform_stage_point (ClutterActor *self, * The division here needs to be done in floating point for * precisions reasons. */ - RQ[0][2] = FP2FX (DET2FP (FX2FP(px),dx2, FX2FP(py),dy2) / del); - RQ[1][2] = FP2FX (DET2FP (dx1,FX2FP(px), dy1,FX2FP(py)) / del); - RQ[1][2] = FP2FX (DET2FP(dx1,FX2FP(px), dy1,FX2FP(py))/del); + RQ[0][2] = FP2FX (DET2FP (UX2FP (px), dx2, UX2FP (py), dy2) / del); + RQ[1][2] = FP2FX (DET2FP (dx1, UX2FP (px), dy1, UX2FP (py)) / del); + RQ[1][2] = FP2FX (DET2FP (dx1, UX2FP (px), dy1, UX2FP (py)) / del); RQ[2][2] = COGL_FIXED_1; - RQ[0][0] = v[1].x - v[0].x + COGL_FIXED_MUL (RQ[0][2], v[1].x); - RQ[1][0] = v[2].x - v[0].x + COGL_FIXED_MUL (RQ[1][2], v[2].x); - RQ[2][0] = v[0].x; - RQ[0][1] = v[1].y - v[0].y + COGL_FIXED_MUL (RQ[0][2], v[1].y); - RQ[1][1] = v[2].y - v[0].y + COGL_FIXED_MUL (RQ[1][2], v[2].y); - RQ[2][1] = v[0].y; + RQ[0][0] = UX2FX (v[1].x - v[0].x) + + COGL_FIXED_MUL (RQ[0][2], UX2FX (v[1].x)); + RQ[1][0] = UX2FX (v[2].x - v[0].x) + + COGL_FIXED_MUL (RQ[1][2], UX2FX (v[2].x)); + RQ[2][0] = UX2FX (v[0].x); + RQ[0][1] = UX2FX (v[1].y - v[0].y) + + COGL_FIXED_MUL (RQ[0][2], UX2FX (v[1].y)); + RQ[1][1] = UX2FX (v[2].y - v[0].y) + + COGL_FIXED_MUL (RQ[1][2], UX2FX (v[2].y)); + RQ[2][1] = UX2FX (v[0].y); } /* @@ -6985,24 +7017,27 @@ clutter_actor_transform_stage_point (ClutterActor *self, return FALSE; /* - * Now transform our point with the ST matrix; the notional w coordiance - * is 1, hence the last part is simply added. + * Now transform our point with the ST matrix; the notional w + * coordinate is 1, hence the last part is simply added. */ xi = CLUTTER_UNITS_TO_DEVICE (x); yi = CLUTTER_UNITS_TO_DEVICE (y); - xf = xi*ST[0][0] + yi*ST[1][0] + ST[2][0]; - yf = xi*ST[0][1] + yi*ST[1][1] + ST[2][1]; - wf = xi*ST[0][2] + yi*ST[1][2] + ST[2][2]; + xf = xi * ST[0][0] + yi * ST[1][0] + ST[2][0]; + yf = xi * ST[0][1] + yi * ST[1][1] + ST[2][1]; + wf = xi * ST[0][2] + yi * ST[1][2] + ST[2][2]; /* * The division needs to be done in floating point for precision reasons. */ if (x_out) *x_out = CLUTTER_UNITS_FROM_FLOAT (FX2FP (xf) / FX2FP (wf)); + if (y_out) *y_out = CLUTTER_UNITS_FROM_FLOAT (FX2FP (yf) / FX2FP (wf)); +#undef UX2FX +#undef UX2FP #undef FP2FX #undef FX2FP #undef FP2INT @@ -7197,11 +7232,11 @@ clutter_actor_set_shader (ClutterActor *self, g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); g_return_val_if_fail (shader == NULL || CLUTTER_IS_SHADER (shader), FALSE); - /* if shader passed in is NULL we destroy the shader */ - if (shader == NULL) - { - destroy_shader_data (self); - } + if (shader) + g_object_ref (shader); + else + /* if shader passed in is NULL we destroy the shader */ + destroy_shader_data (self); actor_priv = self->priv; shader_data = actor_priv->shader_data; @@ -7215,15 +7250,9 @@ clutter_actor_set_shader (ClutterActor *self, shader_value_free); } if (shader_data->shader) - { - g_object_unref (shader_data->shader); - shader_data->shader = NULL; - } + g_object_unref (shader_data->shader); - if (shader) - { - shader_data->shader = g_object_ref (shader); - } + shader_data->shader = shader; if (CLUTTER_ACTOR_IS_VISIBLE (self)) clutter_actor_queue_redraw (self); @@ -7570,3 +7599,99 @@ clutter_actor_allocate_preferred_size (ClutterActor *self, clutter_actor_allocate (self, &actor_box, absolute_origin_changed); } + +/** + * clutter_actor_grab_key_focus: + * @self: a #ClutterActor + * + * Sets the key focus of the #ClutterStage including @self + * to this #ClutterActor. + * + * Since: 1.0 + */ +void +clutter_actor_grab_key_focus (ClutterActor *self) +{ + ClutterActor *parent; + + g_return_if_fail (CLUTTER_IS_ACTOR (self)); + + parent = clutter_actor_get_parent (self); + if (!parent) + return; + + parent = clutter_actor_get_stage (self); + if (parent && CLUTTER_IS_STAGE (parent)) + clutter_stage_set_key_focus (CLUTTER_STAGE (parent), self); +} + +/** + * clutter_actor_get_pango_context: + * @self: a #ClutterActor + * + * Retrieves the #PangoContext for @self. The actor's #PangoContext + * is already configured using the appropriate font map, resolution + * and font options. + * + * Unlike clutter_actor_create_pango_context(), this context is owend + * by the #ClutterActor and it will be updated each time the options + * stored by the #ClutterBackend change. + * + * You can use the returned #PangoContext to create a #PangoLayout + * and render text using cogl_pango_render_layout() to reuse the + * glyphs cache also used by Clutter. + * + * Return value: the #PangoContext for a #ClutterActor. The returned + * #PangoContext is owned by the actor and should not be unreferenced + * by the application code + * + * Since: 1.0 + */ +PangoContext * +clutter_actor_get_pango_context (ClutterActor *self) +{ + ClutterActorPrivate *priv; + ClutterMainContext *ctx; + + g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); + + priv = self->priv; + + if (priv->pango_context) + return priv->pango_context; + + ctx = CLUTTER_CONTEXT (); + priv->pango_context = _clutter_context_get_pango_context (ctx); + g_object_ref (priv->pango_context); + + return priv->pango_context; +} + +/** + * clutter_actor_create_pango_context: + * @self: a #ClutterActor + * + * Creates a #PangoContext for the given actor. The #PangoContext + * is already configured using the appropriate font map, resolution + * and font options. + * + * See also clutter_actor_get_pango_context(). + * + * Return value: the newly created #PangoContext. Use g_object_ref() + * on the returned value to deallocate its resources + * + * Since: 1.0 + */ +PangoContext * +clutter_actor_create_pango_context (ClutterActor *self) +{ + ClutterMainContext *ctx; + PangoContext *retval; + + g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); + + ctx = CLUTTER_CONTEXT (); + retval = _clutter_context_create_pango_context (ctx); + + return retval; +} diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h index e964a9ef0..af962caf1 100644 --- a/clutter/clutter-actor.h +++ b/clutter/clutter-actor.h @@ -31,6 +31,8 @@ /* clutter-actor.h */ #include +#include + #include #include #include @@ -420,6 +422,8 @@ void clutter_actor_set_opacity (ClutterActor guint8 opacity); guint8 clutter_actor_get_opacity (ClutterActor *self); guint8 clutter_actor_get_paint_opacity (ClutterActor *self); +gboolean clutter_actor_get_paint_visibility (ClutterActor *self); + void clutter_actor_set_name (ClutterActor *self, const gchar *name); @@ -545,21 +549,24 @@ gboolean clutter_actor_is_rotated (ClutterActor *self); gboolean clutter_actor_is_scaled (ClutterActor *self); gboolean clutter_actor_should_pick_paint (ClutterActor *self); -void clutter_actor_box_get_from_vertices (ClutterVertex vtx[4], - ClutterActorBox *box); +void clutter_actor_box_get_from_vertices (ClutterVertex vtx[4], + ClutterActorBox *box); -void clutter_actor_get_abs_allocation_vertices (ClutterActor *self, - ClutterVertex verts[4]); +void clutter_actor_get_abs_allocation_vertices (ClutterActor *self, + ClutterVertex verts[4]); -void clutter_actor_apply_transform_to_point (ClutterActor *self, - const ClutterVertex *point, - ClutterVertex *vertex); -void clutter_actor_apply_relative_transform_to_point (ClutterActor *self, - ClutterActor *ancestor, - const ClutterVertex *point, - ClutterVertex *vertex); +void clutter_actor_apply_transform_to_point (ClutterActor *self, + const ClutterVertex *point, + ClutterVertex *vertex); +void clutter_actor_apply_relative_transform_to_point (ClutterActor *self, + ClutterActor *ancestor, + const ClutterVertex *point, + ClutterVertex *vertex); -gboolean clutter_actor_get_paint_visibility (ClutterActor *self); +void clutter_actor_grab_key_focus (ClutterActor *self); + +PangoContext *clutter_actor_get_pango_context (ClutterActor *self); +PangoContext *clutter_actor_create_pango_context (ClutterActor *self); G_END_DECLS diff --git a/clutter/clutter-alpha.c b/clutter/clutter-alpha.c index 2114fe38e..dc1dddeba 100644 --- a/clutter/clutter-alpha.c +++ b/clutter/clutter-alpha.c @@ -395,6 +395,9 @@ clutter_alpha_set_timeline (ClutterAlpha *alpha, priv = alpha->priv; + if (priv->timeline == timeline) + return; + if (priv->timeline) { g_signal_handlers_disconnect_by_func (priv->timeline, @@ -565,13 +568,13 @@ clutter_alpha_set_mode (ClutterAlpha *alpha, priv = alpha->priv; - priv->mode = mode; - /* sanity check to avoid getting an out of sync enum/function mapping */ g_assert (animation_modes[mode].mode == mode); if (G_LIKELY (animation_modes[mode].func != NULL)) clutter_alpha_set_func (alpha, animation_modes[mode].func, NULL, NULL); + priv->mode = mode; + g_object_notify (G_OBJECT (alpha), "mode"); } diff --git a/clutter/clutter-animation.c b/clutter/clutter-animation.c index 83845057b..eae81250d 100644 --- a/clutter/clutter-animation.c +++ b/clutter/clutter-animation.c @@ -739,6 +739,8 @@ clutter_animation_set_actor (ClutterAnimation *animation, priv = animation->priv; + g_object_ref (actor); + if (priv->actor) { g_object_weak_unref (G_OBJECT (animation), @@ -750,7 +752,7 @@ clutter_animation_set_actor (ClutterAnimation *animation, g_object_unref (priv->actor); } - priv->actor = g_object_ref (actor); + priv->actor = actor; g_object_weak_ref (G_OBJECT (animation), on_animation_weak_notify, priv->actor); @@ -780,12 +782,11 @@ clutter_animation_get_actor (ClutterAnimation *animation) } static inline void -clutter_animation_set_mode_internal (ClutterAnimation *animation) +clutter_animation_set_mode_internal (ClutterAnimation *animation, + ClutterAlpha *alpha) { ClutterAnimationPrivate *priv = animation->priv; - ClutterAlpha *alpha; - alpha = clutter_animation_get_alpha (animation); if (alpha) clutter_alpha_set_mode (alpha, priv->mode); } @@ -810,7 +811,7 @@ clutter_animation_set_mode (ClutterAnimation *animation, priv = animation->priv; priv->mode = mode; - clutter_animation_set_mode_internal (animation); + clutter_animation_set_mode_internal (animation, priv->alpha); g_object_notify (G_OBJECT (animation), "mode"); } @@ -968,6 +969,9 @@ clutter_animation_set_timeline (ClutterAnimation *animation, priv = animation->priv; + if (timeline && priv->timeline == timeline) + return; + g_object_freeze_notify (G_OBJECT (animation)); if (priv->timeline) @@ -1048,6 +1052,19 @@ clutter_animation_set_alpha (ClutterAnimation *animation, priv = animation->priv; + if (!alpha) + { + ClutterTimeline *timeline; + + timeline = clutter_animation_get_timeline (animation); + + alpha = clutter_alpha_new (); + clutter_alpha_set_timeline (alpha, timeline); + clutter_animation_set_mode_internal (animation, alpha); + } + + g_object_ref_sink (alpha); + if (priv->alpha) { if (priv->alpha_notify_id) @@ -1058,18 +1075,7 @@ clutter_animation_set_alpha (ClutterAnimation *animation, priv->alpha = NULL; } - if (!alpha) - { - ClutterTimeline *timeline; - - timeline = clutter_animation_get_timeline (animation); - - alpha = clutter_alpha_new (); - clutter_alpha_set_timeline (alpha, timeline); - clutter_animation_set_mode_internal (animation); - } - - priv->alpha = g_object_ref_sink (alpha); + priv->alpha = alpha; priv->alpha_notify_id = g_signal_connect (alpha, "notify::alpha", diff --git a/clutter/clutter-backend.c b/clutter/clutter-backend.c index 9a599c16b..4c60d40ff 100644 --- a/clutter/clutter-backend.c +++ b/clutter/clutter-backend.c @@ -44,10 +44,13 @@ #include "clutter-backend.h" #include "clutter-debug.h" #include "clutter-fixed.h" +#include "clutter-marshal.h" #include "clutter-private.h" G_DEFINE_ABSTRACT_TYPE (ClutterBackend, clutter_backend, G_TYPE_OBJECT); +#define DEFAULT_FONT_NAME "Sans 10" + #define CLUTTER_BACKEND_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_BACKEND, ClutterBackendPrivate)) @@ -60,11 +63,24 @@ struct _ClutterBackendPrivate ClutterFixed resolution; cairo_font_options_t *font_options; + + gchar *font_name; }; +enum +{ + RESOLUTION_CHANGED, + FONT_CHANGED, + + LAST_SIGNAL +}; + +static guint backend_signals[LAST_SIGNAL] = { 0, }; + static void clutter_backend_dispose (GObject *gobject) { + ClutterBackendPrivate *priv = CLUTTER_BACKEND (gobject)->priv; ClutterMainContext *clutter_context; clutter_context = clutter_context_get_default (); @@ -76,6 +92,8 @@ clutter_backend_dispose (GObject *gobject) clutter_context->events_queue = NULL; } + g_free (priv->font_name); + clutter_backend_set_font_options (CLUTTER_BACKEND (gobject), NULL); G_OBJECT_CLASS (clutter_backend_parent_class)->dispose (gobject); @@ -89,6 +107,24 @@ clutter_backend_class_init (ClutterBackendClass *klass) gobject_class->dispose = clutter_backend_dispose; g_type_class_add_private (gobject_class, sizeof (ClutterBackendPrivate)); + + backend_signals[RESOLUTION_CHANGED] = + g_signal_new (I_("resolution-changed"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterBackendClass, resolution_changed), + NULL, NULL, + clutter_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + backend_signals[FONT_CHANGED] = + g_signal_new (I_("font-changed"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterBackendClass, font_changed), + NULL, NULL, + clutter_marshal_VOID__VOID, + G_TYPE_NONE, 0); } static void @@ -388,8 +424,9 @@ clutter_backend_set_resolution (ClutterBackend *backend, priv->resolution = fixed_dpi; if (CLUTTER_CONTEXT ()->font_map) - cogl_pango_font_map_set_resolution (CLUTTER_CONTEXT ()->font_map, - COGL_FIXED_TO_FLOAT (fixed_dpi)); + cogl_pango_font_map_set_resolution (CLUTTER_CONTEXT ()->font_map, dpi); + + g_signal_emit (backend, backend_signals[RESOLUTION_CHANGED], 0); } /** @@ -445,6 +482,8 @@ clutter_backend_set_font_options (ClutterBackend *backend, priv->font_options = cairo_font_options_copy (options); else priv->font_options = NULL; + + g_signal_emit (backend, backend_signals[FONT_CHANGED], 0); } } @@ -482,3 +521,63 @@ clutter_backend_get_font_options (ClutterBackend *backend) return priv->font_options; } +/** + * clutter_backend_set_font_name: + * @backend: a #ClutterBackend + * @font_name: the name of the font + * + * Sets the default font to be used by Clutter. The @font_name string + * must either be %NULL, which means that the font name from the + * default #ClutterBackend will be used; or be something that can + * be parsed by the pango_font_description_from_string() function. + * + * Since: 1.0 + */ +void +clutter_backend_set_font_name (ClutterBackend *backend, + const gchar *font_name) +{ + ClutterBackendPrivate *priv; + + g_return_if_fail (CLUTTER_IS_BACKEND (backend)); + + priv = backend->priv; + + g_free (priv->font_name); + + if (font_name == NULL || *font_name == '\0') + priv->font_name = g_strdup (DEFAULT_FONT_NAME); + else + priv->font_name = g_strdup (font_name); + + g_signal_emit (backend, backend_signals[FONT_CHANGED], 0); +} + +/** + * clutter_backend_get_font_name: + * @backend: a #ClutterBackend + * + * Retrieves the default font name as set by + * clutter_backend_set_font_name(). + * + * Return value: the font name for the backend. The returned string is + * owned by the #ClutterBackend and should never be modified or freed + * + * Since: 1.0 + */ +G_CONST_RETURN gchar * +clutter_backend_get_font_name (ClutterBackend *backend) +{ + ClutterBackendPrivate *priv; + + g_return_val_if_fail (CLUTTER_IS_BACKEND (backend), NULL); + + priv = backend->priv; + + if (G_LIKELY (priv->font_name)) + return priv->font_name; + + priv->font_name = g_strdup (DEFAULT_FONT_NAME); + + return priv->font_name; +} diff --git a/clutter/clutter-backend.h b/clutter/clutter-backend.h index 27f49c46f..fda1d3413 100644 --- a/clutter/clutter-backend.h +++ b/clutter/clutter-backend.h @@ -30,6 +30,8 @@ #include #include +#include + #include #include #include @@ -77,6 +79,10 @@ struct _ClutterBackendClass ClutterStage *stage); void (* ensure_context) (ClutterBackend *backend, ClutterStage *stage); + + /* signals */ + void (* resolution_changed) (ClutterBackend *backend); + void (* font_changed) (ClutterBackend *backend); }; GType clutter_backend_get_type (void) G_GNUC_CONST; @@ -95,6 +101,9 @@ guint clutter_backend_get_double_click_distance (ClutterBackend void clutter_backend_set_font_options (ClutterBackend *backend, cairo_font_options_t *options); cairo_font_options_t *clutter_backend_get_font_options (ClutterBackend *backend); +void clutter_backend_set_font_name (ClutterBackend *backend, + const gchar *font_name); +G_CONST_RETURN gchar *clutter_backend_get_font_name (ClutterBackend *backend); G_END_DECLS diff --git a/clutter/clutter-behaviour-rotate.c b/clutter/clutter-behaviour-rotate.c index 1a62fee56..3e13c6dab 100644 --- a/clutter/clutter-behaviour-rotate.c +++ b/clutter/clutter-behaviour-rotate.c @@ -619,7 +619,7 @@ clutter_behaviour_rotate_get_boundsx (ClutterBehaviourRotate *rotate, priv = rotate->priv; - if (angle_start); + if (angle_start) *angle_start = priv->angle_start; if (angle_end) diff --git a/clutter/clutter-behaviour.c b/clutter/clutter-behaviour.c index 11da23d35..006be1bda 100644 --- a/clutter/clutter-behaviour.c +++ b/clutter/clutter-behaviour.c @@ -570,6 +570,9 @@ clutter_behaviour_set_alpha (ClutterBehaviour *behave, priv = behave->priv; + if (alpha) + g_object_ref_sink (alpha); + if (priv->notify_id) { CLUTTER_NOTE (BEHAVIOUR, "removing previous notify-id (%d)", @@ -590,7 +593,6 @@ clutter_behaviour_set_alpha (ClutterBehaviour *behave, if (alpha) { priv->alpha = alpha; - g_object_ref_sink (priv->alpha); priv->notify_id = g_signal_connect (priv->alpha, "notify::alpha", G_CALLBACK(notify_cb), diff --git a/clutter/clutter-binding-pool.c b/clutter/clutter-binding-pool.c index 8b567b28d..78a483f72 100644 --- a/clutter/clutter-binding-pool.c +++ b/clutter/clutter-binding-pool.c @@ -476,10 +476,129 @@ clutter_binding_pool_install_closure (ClutterBindingPool *pool, g_hash_table_insert (pool->entries_hash, entry, entry); } -gchar ** -clutter_binding_pool_list_actions (ClutterBindingPool *pool) +/** + * clutter_binding_pool_override_action: + * @pool: a #ClutterBindingPool + * @key_val: key symbol + * @modifiers: bitmask of modifiers + * @callback: function to be called when the action is activated + * @data: data to be passed to @callback + * @notify: function to be called when the action is removed + * from the pool + * + * Allows overriding the action for @key_val and @modifiers inside a + * #ClutterBindingPool. See clutter_binding_pool_install_action(). + * + * When an action has been activated using clutter_binding_pool_activate() + * the passed @callback will be invoked (with @data). + * + * Actions can be blocked with clutter_binding_pool_block_action() + * and then unblocked using clutter_binding_pool_unblock_action(). + * + * Since: 1.0 + */ +void +clutter_binding_pool_override_action (ClutterBindingPool *pool, + guint key_val, + ClutterModifierType modifiers, + GCallback callback, + gpointer data, + GDestroyNotify notify) { - return NULL; + ClutterBindingEntry *entry; + GClosure *closure; + + g_return_if_fail (pool != NULL); + g_return_if_fail (key_val != 0); + g_return_if_fail (callback != NULL); + + entry = binding_pool_lookup_entry (pool, key_val, modifiers); + if (G_UNLIKELY (entry == NULL)) + { + g_warning ("There is no action for the given key symbol " + "of %d (modifiers: %d) installed inside the " + "binding pool.", + key_val, modifiers); + return; + } + + if (entry->closure) + { + g_closure_unref (entry->closure); + entry->closure = NULL; + } + + closure = g_cclosure_new (callback, data, (GClosureNotify) notify); + entry->closure = g_closure_ref (closure); + g_closure_sink (closure); + + if (G_CLOSURE_NEEDS_MARSHAL (closure)) + { + GClosureMarshal marshal; + + marshal = clutter_marshal_BOOLEAN__STRING_UINT_ENUM; + g_closure_set_marshal (closure, marshal); + } +} + +/** + * clutter_binding_pool_override_closure: + * @pool: a #ClutterBindingPool + * @key_val: key symbol + * @modifiers: bitmask of modifiers + * @closure: a #GClosure + * + * A #GClosure variant of clutter_binding_pool_override_action(). + * + * Allows overriding the action for @key_val and @modifiers inside a + * #ClutterBindingPool. See clutter_binding_pool_install_closure(). + * + * When an action has been activated using clutter_binding_pool_activate() + * the passed @callback will be invoked (with @data). + * + * Actions can be blocked with clutter_binding_pool_block_action() + * and then unblocked using clutter_binding_pool_unblock_action(). + * + * Since: 1.0 + */ +void +clutter_binding_pool_override_closure (ClutterBindingPool *pool, + guint key_val, + ClutterModifierType modifiers, + GClosure *closure) +{ + ClutterBindingEntry *entry; + + g_return_if_fail (pool != NULL); + g_return_if_fail (key_val != 0); + g_return_if_fail (closure != NULL); + + entry = binding_pool_lookup_entry (pool, key_val, modifiers); + if (G_UNLIKELY (entry == NULL)) + { + g_warning ("There is no action for the given key symbol " + "of %d (modifiers: %d) installed inside the " + "binding pool.", + key_val, modifiers); + return; + } + + if (entry->closure) + { + g_closure_unref (entry->closure); + entry->closure = NULL; + } + + entry->closure = g_closure_ref (closure); + g_closure_sink (closure); + + if (G_CLOSURE_NEEDS_MARSHAL (closure)) + { + GClosureMarshal marshal; + + marshal = clutter_marshal_BOOLEAN__STRING_UINT_ENUM; + g_closure_set_marshal (closure, marshal); + } } /** @@ -531,6 +650,7 @@ clutter_binding_pool_remove_action (ClutterBindingPool *pool, ClutterModifierType modifiers) { ClutterBindingEntry remove_entry = { 0, }; + GSList *l; g_return_if_fail (pool != NULL); g_return_if_fail (key_val != 0); @@ -540,6 +660,18 @@ clutter_binding_pool_remove_action (ClutterBindingPool *pool, remove_entry.key_val = key_val; remove_entry.modifiers = modifiers; + for (l = pool->entries; l != NULL; l = l->data) + { + ClutterBindingEntry *e = l->data; + + if (e->key_val == remove_entry.key_val && + e->modifiers == remove_entry.modifiers) + { + pool->entries = g_slist_remove_link (pool->entries, l); + break; + } + } + g_hash_table_remove (pool->entries_hash, &remove_entry); } diff --git a/clutter/clutter-binding-pool.h b/clutter/clutter-binding-pool.h index 47692403e..55b5273c8 100644 --- a/clutter/clutter-binding-pool.h +++ b/clutter/clutter-binding-pool.h @@ -56,40 +56,49 @@ typedef gboolean (* ClutterBindingActionFunc) (GObject *gobject, guint key_val, ClutterModifierType modifiers); -ClutterBindingPool * clutter_binding_pool_new (const gchar *name); -ClutterBindingPool * clutter_binding_pool_get_for_class (gpointer klass); -ClutterBindingPool * clutter_binding_pool_find (const gchar *name); +ClutterBindingPool * clutter_binding_pool_new (const gchar *name); +ClutterBindingPool * clutter_binding_pool_get_for_class (gpointer klass); +ClutterBindingPool * clutter_binding_pool_find (const gchar *name); -void clutter_binding_pool_install_action (ClutterBindingPool *pool, - const gchar *action_name, - guint key_val, - ClutterModifierType modifiers, - GCallback callback, - gpointer data, - GDestroyNotify notify); -void clutter_binding_pool_install_closure (ClutterBindingPool *pool, - const gchar *action_name, - guint key_val, - ClutterModifierType modifiers, - GClosure *closure); +void clutter_binding_pool_install_action (ClutterBindingPool *pool, + const gchar *action_name, + guint key_val, + ClutterModifierType modifiers, + GCallback callback, + gpointer data, + GDestroyNotify notify); +void clutter_binding_pool_install_closure (ClutterBindingPool *pool, + const gchar *action_name, + guint key_val, + ClutterModifierType modifiers, + GClosure *closure); +void clutter_binding_pool_override_action (ClutterBindingPool *pool, + guint key_val, + ClutterModifierType modifiers, + GCallback callback, + gpointer data, + GDestroyNotify notify); +void clutter_binding_pool_override_closure (ClutterBindingPool *pool, + guint key_val, + ClutterModifierType modifiers, + GClosure *closure); -gchar ** clutter_binding_pool_list_actions (ClutterBindingPool *pool); -G_CONST_RETURN gchar *clutter_binding_pool_find_action (ClutterBindingPool *pool, - guint key_val, - ClutterModifierType modifiers); -void clutter_binding_pool_remove_action (ClutterBindingPool *pool, - guint key_val, - ClutterModifierType modifiers); +G_CONST_RETURN gchar *clutter_binding_pool_find_action (ClutterBindingPool *pool, + guint key_val, + ClutterModifierType modifiers); +void clutter_binding_pool_remove_action (ClutterBindingPool *pool, + guint key_val, + ClutterModifierType modifiers); -gboolean clutter_binding_pool_activate (ClutterBindingPool *pool, - guint key_val, - ClutterModifierType modifiers, - GObject *gobject); +gboolean clutter_binding_pool_activate (ClutterBindingPool *pool, + guint key_val, + ClutterModifierType modifiers, + GObject *gobject); -void clutter_binding_pool_block_action (ClutterBindingPool *pool, - const gchar *action_name); -void clutter_binding_pool_unblock_action (ClutterBindingPool *pool, - const gchar *action_name); +void clutter_binding_pool_block_action (ClutterBindingPool *pool, + const gchar *action_name); +void clutter_binding_pool_unblock_action (ClutterBindingPool *pool, + const gchar *action_name); G_END_DECLS diff --git a/clutter/clutter-clone-texture.c b/clutter/clutter-clone-texture.c index 5ea21b7ee..a2350cbab 100644 --- a/clutter/clutter-clone-texture.c +++ b/clutter/clutter-clone-texture.c @@ -25,7 +25,7 @@ /** * SECTION:clutter-clone-texture - * @short_description: Actor for cloning existing textures in an + * @short_description: Actor for cloning existing textures in an * efficient way. * * #ClutterCloneTexture allows the cloning of existing #ClutterTexture based @@ -39,7 +39,7 @@ #include "clutter-main.h" #include "clutter-feature.h" #include "clutter-actor.h" -#include "clutter-util.h" +#include "clutter-util.h" #include "clutter-enum-types.h" #include "clutter-private.h" #include "clutter-debug.h" @@ -157,16 +157,16 @@ clutter_clone_texture_paint (ClutterActor *self) clutter_actor_get_name (self) ? clutter_actor_get_name (self) : "unknown"); - /* parent texture may have been hidden, there for need to make sure its - * realised with resources available. + /* parent texture may have been hidden, there for need to make sure its + * realised with resources available. */ parent_texture = CLUTTER_ACTOR (priv->parent_texture); if (!CLUTTER_ACTOR_IS_REALIZED (parent_texture)) clutter_actor_realize (parent_texture); - /* If 'parent' texture isn't visible we run its paint to be sure it - * updates. Needed for TFP and likely FBOs. - * Potentially could cause issues + /* If 'parent' texture isn't visible we run its paint to be sure it + * updates. Needed for TFP and likely FBOs. + * Potentially could cause issues */ if (!clutter_actor_get_paint_visibility(parent_texture)) { @@ -221,6 +221,9 @@ set_parent_texture (ClutterCloneTexture *ctexture, ClutterActor *actor = CLUTTER_ACTOR (ctexture); gboolean was_visible = CLUTTER_ACTOR_IS_VISIBLE (ctexture); + if (priv->parent_texture == texture) + return; + if (priv->parent_texture) { g_object_unref (priv->parent_texture); @@ -230,7 +233,7 @@ set_parent_texture (ClutterCloneTexture *ctexture, clutter_actor_hide (actor); } - if (texture) + if (texture) { priv->parent_texture = g_object_ref (texture); @@ -244,14 +247,14 @@ set_parent_texture (ClutterCloneTexture *ctexture, clutter_actor_queue_relayout (actor); } - + } -static void +static void clutter_clone_texture_dispose (GObject *object) { ClutterCloneTexture *self = CLUTTER_CLONE_TEXTURE(object); - ClutterCloneTexturePrivate *priv = self->priv; + ClutterCloneTexturePrivate *priv = self->priv; if (priv->parent_texture) g_object_unref (priv->parent_texture); @@ -261,7 +264,7 @@ clutter_clone_texture_dispose (GObject *object) G_OBJECT_CLASS (clutter_clone_texture_parent_class)->dispose (object); } -static void +static void clutter_clone_texture_finalize (GObject *object) { G_OBJECT_CLASS (clutter_clone_texture_parent_class)->finalize (object); @@ -391,7 +394,7 @@ clutter_clone_texture_init (ClutterCloneTexture *self) * clutter_clone_texture_new: * @texture: a #ClutterTexture, or %NULL * - * Creates an efficient 'clone' of a pre-existing texture with which it + * Creates an efficient 'clone' of a pre-existing texture with which it * shares the underlying pixbuf data. * * You can use clutter_clone_texture_set_parent_texture() to change the @@ -412,7 +415,7 @@ clutter_clone_texture_new (ClutterTexture *texture) /** * clutter_clone_texture_get_parent_texture: * @clone: a #ClutterCloneTexture - * + * * Retrieves the parent #ClutterTexture used by @clone. * * Return value: a #ClutterTexture actor, or %NULL diff --git a/clutter/clutter-entry.c b/clutter/clutter-entry.c deleted file mode 100644 index 1ae661eda..000000000 --- a/clutter/clutter-entry.c +++ /dev/null @@ -1,1795 +0,0 @@ -/* - * Clutter. - * - * An OpenGL based 'interactive canvas' library. - * - * Authored By Matthew Allum - * Neil Jagdish Patel - * - * Copyright (C) 2006 OpenedHand - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -/** - * SECTION:clutter-entry - * @short_description: A single line text entry actor - * - * #ClutterEntry is a #ClutterTexture that allows single line text entry. - * - * #ClutterEntry is available since Clutter 0.4. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include - -#include "clutter-entry.h" - -#include "clutter-debug.h" -#include "clutter-enum-types.h" -#include "clutter-keysyms.h" -#include "clutter-main.h" -#include "clutter-marshal.h" -#include "clutter-private.h" -#include "clutter-rectangle.h" -#include "clutter-units.h" - -#include "cogl-pango.h" - -#define DEFAULT_FONT_NAME "Sans 10" -#define ENTRY_CURSOR_WIDTH 1 -#define ENTRY_PADDING 5 - -G_DEFINE_TYPE (ClutterEntry, clutter_entry, CLUTTER_TYPE_ACTOR); - -/* Probably move into main */ -static PangoContext *_context = NULL; - -static const ClutterColor default_text_color = { 0, 0, 0, 255 }; - -enum -{ - PROP_0, - - PROP_FONT_NAME, - PROP_TEXT, - PROP_COLOR, - PROP_ALIGNMENT, /* FIXME */ - PROP_POSITION, - PROP_CURSOR, - PROP_TEXT_VISIBLE, - PROP_MAX_LENGTH, - PROP_ENTRY_PADDING, - PROP_X_ALIGN -}; - -enum -{ - TEXT_CHANGED, - CURSOR_EVENT, - ACTIVATE, - - LAST_SIGNAL -}; - -static guint entry_signals[LAST_SIGNAL] = { 0, }; - -#define CLUTTER_ENTRY_GET_PRIVATE(obj) \ -(G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_ENTRY, ClutterEntryPrivate)) - -struct _ClutterEntryPrivate -{ - PangoContext *context; - PangoFontDescription *desc; - - ClutterColor fgcol; - - gchar *text; - gchar *font_name; - gboolean text_visible; - gunichar priv_char; - - gint extents_width; - gint extents_height; - - gint width; - gint n_chars; /* number of chars */ - gint n_bytes; /* number of bytes */ - - guint alignment : 2; - guint wrap : 1; - guint use_underline : 1; - guint use_markup : 1; - guint ellipsize : 3; - guint single_line_mode : 1; - guint wrap_mode : 3; - gint position; - gint text_x; - gint max_length; - gint entry_padding; - gdouble x_align; - - PangoAttrList *attrs; - PangoAttrList *effective_attrs; - PangoLayout *layout; - gint width_chars; - - ClutterGeometry cursor_pos; - gboolean show_cursor; -}; - -static void -clutter_entry_set_entry_padding (ClutterEntry *entry, - guint padding) -{ - ClutterEntryPrivate *priv = entry->priv; - - if (priv->entry_padding != padding) - { - priv->entry_padding = padding; - - if (CLUTTER_ACTOR_IS_VISIBLE (entry)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); - - g_object_notify (G_OBJECT (entry), "entry-padding"); - } -} - -static void -clutter_entry_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - ClutterEntry *entry; - ClutterEntryPrivate *priv; - - entry = CLUTTER_ENTRY (object); - priv = entry->priv; - - switch (prop_id) - { - case PROP_FONT_NAME: - clutter_entry_set_font_name (entry, g_value_get_string (value)); - break; - case PROP_TEXT: - clutter_entry_set_text (entry, g_value_get_string (value)); - break; - case PROP_COLOR: - clutter_entry_set_color (entry, clutter_value_get_color (value)); - break; - case PROP_ALIGNMENT: - clutter_entry_set_alignment (entry, g_value_get_enum (value)); - break; - case PROP_POSITION: - clutter_entry_set_cursor_position (entry, g_value_get_int (value)); - break; - case PROP_CURSOR: - clutter_entry_set_visible_cursor (entry, g_value_get_boolean (value)); - break; - case PROP_TEXT_VISIBLE: - clutter_entry_set_visibility (entry, g_value_get_boolean (value)); - break; - case PROP_MAX_LENGTH: - clutter_entry_set_max_length (entry, g_value_get_int (value)); - break; - case PROP_ENTRY_PADDING: - clutter_entry_set_entry_padding (entry, g_value_get_uint (value)); - break; - case PROP_X_ALIGN: - entry->priv->x_align = g_value_get_double (value); - clutter_actor_queue_redraw (CLUTTER_ACTOR (object)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -clutter_entry_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - ClutterEntry *entry; - ClutterEntryPrivate *priv; - - entry = CLUTTER_ENTRY(object); - priv = entry->priv; - - switch (prop_id) - { - case PROP_FONT_NAME: - g_value_set_string (value, priv->font_name); - break; - case PROP_TEXT: - g_value_set_string (value, priv->text); - break; - case PROP_COLOR: - clutter_value_set_color (value, &priv->fgcol); - break; - case PROP_ALIGNMENT: - g_value_set_enum (value, priv->alignment); - break; - case PROP_POSITION: - g_value_set_int (value, priv->position); - break; - case PROP_CURSOR: - g_value_set_boolean (value, priv->show_cursor); - break; - case PROP_TEXT_VISIBLE: - g_value_set_boolean (value, priv->text_visible); - break; - case PROP_MAX_LENGTH: - g_value_set_int (value, priv->max_length); - break; - case PROP_ENTRY_PADDING: - g_value_set_uint (value, priv->entry_padding); - break; - case PROP_X_ALIGN: - g_value_set_double (value, priv->x_align); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -clutter_entry_ensure_layout (ClutterEntry *entry, gint width) -{ - ClutterEntryPrivate *priv; - - priv = entry->priv; - - if (!priv->layout) - { - priv->layout = pango_layout_new (_context); - - if (priv->effective_attrs) - pango_layout_set_attributes (priv->layout, priv->effective_attrs); - - pango_layout_set_alignment (priv->layout, priv->alignment); - pango_layout_set_ellipsize (priv->layout, priv->ellipsize); - pango_layout_set_single_paragraph_mode (priv->layout, - priv->single_line_mode); - - pango_layout_set_font_description (priv->layout, priv->desc); - - if (priv->text_visible) - pango_layout_set_text (priv->layout, priv->text, priv->n_bytes); - else - { - GString *str = g_string_sized_new (priv->n_bytes); - gunichar invisible_char; - gchar buf[7]; - gint char_len, i; - - if (priv->priv_char != 0) - invisible_char = priv->priv_char; - else - invisible_char = '*'; - - /* we need to convert the string built of invisible characters - * into UTF-8 for it to be fed to the Pango layout - */ - memset (buf, 0, sizeof (buf)); - char_len = g_unichar_to_utf8 (invisible_char, buf); - - for (i = 0; i < priv->n_chars; i++) - g_string_append_len (str, buf, char_len); - - pango_layout_set_text (priv->layout, str->str, str->len); - - g_string_free (str, TRUE); - } - - if (priv->wrap) - pango_layout_set_wrap (priv->layout, priv->wrap_mode); - - if (priv->wrap && width > 0) - pango_layout_set_width (priv->layout, width * PANGO_SCALE); - else - pango_layout_set_width (priv->layout, -1); - - /* Prime the cache for the layout */ - cogl_pango_ensure_glyph_cache_for_layout (priv->layout); - } -} - -static void -clutter_entry_clear_layout (ClutterEntry *entry) -{ - if (entry->priv->layout) - { - g_object_unref (entry->priv->layout); - entry->priv->layout = NULL; - } -} - -static gint -offset_to_bytes (const gchar *text, gint pos) -{ - gchar *c = NULL; - gint i, j, len; - - if (pos < 1) - return pos; - - c = g_utf8_next_char (text); - j = 1; - len = strlen (text); - - for (i = 0; i < len; i++) - { - if (&text[i] == c) - { - if (j == pos) - break; - else - { - c = g_utf8_next_char (c); - j++; - } - } - } - return i; -} - - -static void -clutter_entry_ensure_cursor_position (ClutterEntry *entry) -{ - ClutterEntryPrivate *priv; - gint index_; - PangoRectangle rect; - gint priv_char_bytes; - - priv = entry->priv; - - /* If characters are invisible, get the byte-length of the invisible - * character. If priv_char is 0, we use '*', which is ASCII (1 byte). - */ - if (!priv->text_visible && priv->priv_char) - priv_char_bytes = g_unichar_to_utf8 (priv->priv_char, NULL); - else - priv_char_bytes = 1; - - if (priv->position == -1) - { - if (priv->text_visible) - index_ = strlen (priv->text); - else - index_ = priv->n_chars * priv_char_bytes; - } - else - { - if (priv->text_visible) - index_ = offset_to_bytes (priv->text, priv->position); - else - index_ = priv->position * priv_char_bytes; - } - - pango_layout_get_cursor_pos (priv->layout, index_, &rect, NULL); - priv->cursor_pos.x = rect.x / PANGO_SCALE; - priv->cursor_pos.y = rect.y / PANGO_SCALE; - priv->cursor_pos.width = ENTRY_CURSOR_WIDTH; - priv->cursor_pos.height = rect.height / PANGO_SCALE; - - g_signal_emit (entry, entry_signals[CURSOR_EVENT], 0, &priv->cursor_pos); -} - -static void -clutter_entry_clear_cursor_position (ClutterEntry *entry) -{ - entry->priv->cursor_pos.width = 0; -} - -void -clutter_entry_paint_cursor (ClutterEntry *entry) -{ - ClutterEntryPrivate *priv; - - priv = entry->priv; - - if (priv->show_cursor) - { - cogl_set_source_color4ub (priv->fgcol.red, - priv->fgcol.green, - priv->fgcol.blue, - priv->fgcol.alpha); - - cogl_rectangle (priv->cursor_pos.x, - priv->cursor_pos.y, - priv->cursor_pos.width, - priv->cursor_pos.height); - } -} - -static void -clutter_entry_paint (ClutterActor *self) -{ - ClutterEntry *entry; - ClutterEntryPrivate *priv; - PangoRectangle logical; - gint width, actor_width; - gint text_width; - gint cursor_x; - CoglColor color = { 0, }; - - entry = CLUTTER_ENTRY(self); - priv = entry->priv; - - if (priv->desc == NULL || priv->text == NULL) - { - CLUTTER_NOTE (ACTOR, "layout: %p , desc: %p, text %p", - priv->layout, - priv->desc, - priv->text); - return; - } - - if (priv->width < 0) - width = clutter_actor_get_width (self); - else - width = priv->width; - - cogl_clip_set (0, 0, - COGL_FIXED_FROM_INT (width), - COGL_FIXED_FROM_INT (clutter_actor_get_height (self))); - - actor_width = width - (2 * priv->entry_padding); - clutter_entry_ensure_layout (entry, actor_width); - clutter_entry_ensure_cursor_position (entry); - - pango_layout_get_extents (priv->layout, NULL, &logical); - text_width = logical.width / PANGO_SCALE; - - if (actor_width < text_width) - { - /* We need to do some scrolling */ - cursor_x = priv->cursor_pos.x; - - /* If the cursor is at the begining or the end of the text, the placement - * is easy, however, if the cursor is in the middle somewhere, we need to - * make sure the text doesn't move until the cursor is either in the - * far left or far right - */ - - if (priv->position == 0) - priv->text_x = 0; - else if (priv->position == -1) - { - priv->text_x = actor_width - text_width; - priv->cursor_pos.x += priv->text_x + priv->entry_padding; - } - else - { - if (priv->text_x <= 0) - { - gint diff = -1 * priv->text_x; - - if (cursor_x < diff) - priv->text_x += diff - cursor_x; - else if (cursor_x > (diff + actor_width)) - priv->text_x -= cursor_x - (diff+actor_width); - } - - priv->cursor_pos.x += priv->text_x + priv->entry_padding; - } - - } - else - { - priv->text_x = (actor_width - text_width) * priv->x_align; - priv->cursor_pos.x += priv->text_x + priv->entry_padding; - } - - cogl_color_set_from_4ub (&color, - priv->fgcol.red, - priv->fgcol.green, - priv->fgcol.blue, - clutter_actor_get_paint_opacity (self)); - - cogl_pango_render_layout (priv->layout, - priv->text_x + priv->entry_padding, 0, - &color, 0); - - if (CLUTTER_ENTRY_GET_CLASS (entry)->paint_cursor) - CLUTTER_ENTRY_GET_CLASS (entry)->paint_cursor (entry); - - cogl_clip_unset (); -} - -static void -clutter_entry_allocate (ClutterActor *self, - const ClutterActorBox *box, - gboolean absolute_origin_changed) -{ - ClutterEntry *entry = CLUTTER_ENTRY (self); - ClutterEntryPrivate *priv = entry->priv; - gint width; - - width = CLUTTER_UNITS_TO_DEVICE (box->x2 - box->x1); - - if (priv->width != width) - { - clutter_entry_clear_layout (entry); - clutter_entry_ensure_layout (entry, width); - - priv->width = width; - } - - CLUTTER_ACTOR_CLASS (clutter_entry_parent_class)->allocate (self, box, absolute_origin_changed); -} - -static inline void -clutter_entry_handle_key_event_internal (ClutterEntry *entry, - ClutterKeyEvent *event) -{ - gunichar key_unichar; - ClutterEntryPrivate *priv = entry->priv; - gint pos = priv->position; - gint len = 0; - gint keyval = clutter_key_event_symbol (event); - - if (priv->text) - len = g_utf8_strlen (priv->text, -1); - - switch (keyval) - { - case CLUTTER_Return: - case CLUTTER_KP_Enter: - case CLUTTER_ISO_Enter: - g_signal_emit (entry, entry_signals[ACTIVATE], 0); - break; - - case CLUTTER_Escape: - case CLUTTER_Up: - case CLUTTER_KP_Up: - case CLUTTER_Down: - case CLUTTER_KP_Down: - case CLUTTER_Shift_L: - case CLUTTER_Shift_R: - break; - - case CLUTTER_BackSpace: - if (pos != 0 && len != 0) - clutter_entry_delete_chars (entry, 1); - break; - - case CLUTTER_Delete: - case CLUTTER_KP_Delete: - if (len && pos != -1) - clutter_entry_delete_text (entry, pos, pos+1);; - break; - - case CLUTTER_Left: - case CLUTTER_KP_Left: - if (pos != 0 && len != 0) - { - if (pos == -1) - clutter_entry_set_cursor_position (entry, len - 1); - else - clutter_entry_set_cursor_position (entry, pos - 1); - } - break; - - case CLUTTER_Right: - case CLUTTER_KP_Right: - if (pos != -1 && len != 0) - { - if (pos != len) - clutter_entry_set_cursor_position (entry, pos + 1); - } - break; - - case CLUTTER_End: - case CLUTTER_KP_End: - clutter_entry_set_cursor_position (entry, -1); - break; - - case CLUTTER_Begin: - case CLUTTER_Home: - case CLUTTER_KP_Home: - clutter_entry_set_cursor_position (entry, 0); - break; - - default: - key_unichar = clutter_key_event_unicode (event); - if (g_unichar_validate (key_unichar)) - clutter_entry_insert_unichar (entry, key_unichar); - break; - } -} - -static gboolean -clutter_entry_key_press (ClutterActor *actor, - ClutterKeyEvent *event) -{ - clutter_entry_handle_key_event_internal (CLUTTER_ENTRY (actor), event); - - return TRUE; -} - -static void -clutter_entry_dispose (GObject *object) -{ - ClutterEntry *self = CLUTTER_ENTRY(object); - ClutterEntryPrivate *priv; - - priv = self->priv; - - if (priv->layout) - { - g_object_unref (priv->layout); - priv->layout = NULL; - } - - if (priv->context) - { - g_object_unref (priv->context); - priv->context = NULL; - } - - G_OBJECT_CLASS (clutter_entry_parent_class)->dispose (object); -} - -static void -clutter_entry_finalize (GObject *object) -{ - ClutterEntryPrivate *priv = CLUTTER_ENTRY (object)->priv; - - if (priv->desc) - pango_font_description_free (priv->desc); - - g_free (priv->text); - g_free (priv->font_name); - - G_OBJECT_CLASS (clutter_entry_parent_class)->finalize (object); -} - -static void -clutter_entry_class_init (ClutterEntryClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); - GParamSpec *pspec; - - klass->paint_cursor = clutter_entry_paint_cursor; - - actor_class->paint = clutter_entry_paint; - actor_class->allocate = clutter_entry_allocate; - actor_class->key_press_event = clutter_entry_key_press; - - gobject_class->finalize = clutter_entry_finalize; - gobject_class->dispose = clutter_entry_dispose; - gobject_class->set_property = clutter_entry_set_property; - gobject_class->get_property = clutter_entry_get_property; - - /** - * ClutterEntry:font-name: - * - * The font to be used by the entry, expressed in a string that - * can be parsed by pango_font_description_from_string(). - * - * Since: 0.4 - */ - g_object_class_install_property - (gobject_class, PROP_FONT_NAME, - g_param_spec_string ("font-name", - "Font Name", - "Pango font description", - NULL, - CLUTTER_PARAM_READWRITE)); - /** - * ClutterEntry:text: - * - * The text inside the entry. - * - * Since: 0.4 - */ - g_object_class_install_property - (gobject_class, PROP_TEXT, - g_param_spec_string ("text", - "Text", - "Text to render", - NULL, - CLUTTER_PARAM_READWRITE)); - /** - * ClutterEntry:color: - * - * The color of the text inside the entry. - * - * Since: 0.4 - */ - pspec = clutter_param_spec_color ("color", - "Color", - "The color of the text", - &default_text_color, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_COLOR, pspec); - /** - * ClutterEntry:alignment: - * - * The preferred alignment for the string. - * - * Since: 0.4 - */ - g_object_class_install_property - (gobject_class, PROP_ALIGNMENT, - g_param_spec_enum ("alignment", - "Alignment", - "The preferred alignment for the string,", - PANGO_TYPE_ALIGNMENT, - PANGO_ALIGN_LEFT, - CLUTTER_PARAM_READWRITE)); - /** - * ClutterEntry:position: - * - * The current input cursor position. -1 is taken to be the end of the text - * - * Since: 0.4 - */ - g_object_class_install_property - (gobject_class, PROP_POSITION, - g_param_spec_int ("position", - "Position", - "The cursor position", - -1, G_MAXINT, - -1, - CLUTTER_PARAM_READWRITE)); - - /** - * ClutterEntry:cursor-visible: - * - * Whether the input cursor is visible or not. - * - * Since: 0.4 - */ - g_object_class_install_property - (gobject_class, PROP_CURSOR, - g_param_spec_boolean ( "cursor-visible", - "Cursor Visible", - "Whether the input cursor is visible", - TRUE, - CLUTTER_PARAM_READWRITE)); - - /** - * ClutterEntry:text-visible: - * - * Whether the text is visible in plain form, or replaced by the - * character set by clutter_entry_set_invisible_char(). - * - * Since: 0.4 - */ - g_object_class_install_property - (gobject_class, PROP_TEXT_VISIBLE, - g_param_spec_boolean ("text-visible", - "Text Visible", - "Whether the text is visible in plain form", - TRUE, - CLUTTER_PARAM_READWRITE)); - - /** - * ClutterEntry:max-length: - * - * The maximum length of the entry text. - * - * Since: 0.4 - */ - g_object_class_install_property - (gobject_class, PROP_MAX_LENGTH, - g_param_spec_int ("max-length", - "Max Length", - "The maximum length of the entry text", - 0, G_MAXINT, - 0, - CLUTTER_PARAM_READWRITE)); - /** - * ClutterEntry:entry-padding: - * - * The padding space between the text and the entry right and left borders. - * - * Since: 0.4 - */ - g_object_class_install_property - (gobject_class, PROP_ENTRY_PADDING, - g_param_spec_uint ("entry-padding", - "Entry Padding", - "The padding space between the text and the left and " - "right borders", - 0, G_MAXUINT, - ENTRY_PADDING, - CLUTTER_PARAM_READWRITE)); - /** - * ClutterEntry:x-align: - * - * Horizontal alignment to be used for the text (0.0 for left alignment, - * 1.0 for right alignment). - * - * Since: 0.6 - */ - g_object_class_install_property (gobject_class, - PROP_X_ALIGN, - g_param_spec_double ("x-align", - "Horizontal Alignment", - "The horizontal alignment to be used for the text", - 0.0, 1.0, 0.0, - CLUTTER_PARAM_READWRITE)); - /** - * ClutterEntry::text-changed: - * @entry: the actor which received the event - * - * The ::text-changed signal is emitted after @entry's text changes - */ - entry_signals[TEXT_CHANGED] = - g_signal_new ("text-changed", - G_TYPE_FROM_CLASS (gobject_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterEntryClass, text_changed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - /** - * ClutterEntry::cursor-event: - * @entry: the actor which received the event - * @geometry: a #ClutterGeometry - * - * The ::cursor-event signal is emitted each time the input cursor's geometry - * changes, this could be a positional or size change. If you would like to - * implement your own input cursor, set the cursor-visible property to %FALSE, - * and connect to this signal to position and size your own cursor. - * - * Since: 0.4 - */ - entry_signals[CURSOR_EVENT] = - g_signal_new ("cursor-event", - G_TYPE_FROM_CLASS (gobject_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterEntryClass, cursor_event), - NULL, NULL, - clutter_marshal_VOID__BOXED, - G_TYPE_NONE, 1, - CLUTTER_TYPE_GEOMETRY | G_SIGNAL_TYPE_STATIC_SCOPE); - - /** - * ClutterEntry::activate: - * @entry: the actor which received the event - * - * The ::activate signal is emitted each time the entry is 'activated' - * by the user, normally by pressing the 'Enter' key. - * - * Since: 0.4 - */ - entry_signals[ACTIVATE] = - g_signal_new ("activate", - G_TYPE_FROM_CLASS (gobject_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterEntryClass, activate), - NULL, NULL, - clutter_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - g_type_class_add_private (gobject_class, sizeof (ClutterEntryPrivate)); -} - -static void -clutter_entry_init (ClutterEntry *self) -{ - ClutterEntryPrivate *priv; - gdouble resolution; - gint font_size; - - self->priv = priv = CLUTTER_ENTRY_GET_PRIVATE (self); - - if (G_UNLIKELY (_context == NULL)) - _context = _clutter_context_create_pango_context (CLUTTER_CONTEXT ()); - - resolution = pango_cairo_context_get_resolution (_context); - - priv->alignment = PANGO_ALIGN_LEFT; - priv->wrap = FALSE; - priv->wrap_mode = PANGO_WRAP_WORD; - priv->ellipsize = PANGO_ELLIPSIZE_NONE; - priv->use_underline = FALSE; - priv->use_markup = FALSE; - priv->layout = NULL; - priv->text = NULL; - priv->attrs = NULL; - priv->position = -1; - priv->priv_char = '*'; - priv->text_visible = TRUE; - priv->text_x = 0; - priv->max_length = 0; - priv->entry_padding = ENTRY_PADDING; - priv->x_align = 0.0; - - priv->fgcol = default_text_color; - - priv->font_name = g_strdup (DEFAULT_FONT_NAME); - priv->desc = pango_font_description_from_string (priv->font_name); - - /* we use the font size to set the default width and height, in case - * the user doesn't call clutter_actor_set_size(). - */ - font_size = PANGO_PIXELS (pango_font_description_get_size (priv->desc)) - * resolution - / 72.0; - clutter_actor_set_size (CLUTTER_ACTOR (self), font_size * 20, 50); - - priv->show_cursor = TRUE; -} - -/** - * clutter_entry_new_with_text: - * @font_name: the name (and size) of the font to be used - * @text: the text to be displayed - * - * Creates a new #ClutterEntry displaying @text using @font_name. - * - * Return value: the newly created #ClutterEntry - * - * Since: 0.4 - */ -ClutterActor * -clutter_entry_new_with_text (const gchar *font_name, - const gchar *text) -{ - ClutterActor *entry = clutter_entry_new (); - - g_object_set (entry, - "font-name", font_name, - "text", text, - NULL); - return entry; -} - -/** - * clutter_entry_new_full: - * @font_name: the name (and size) of the font to be used - * @text: the text to be displayed - * @color: #ClutterColor for text - * - * Creates a new #ClutterEntry displaying @text with @color - * using @font_name. - * - * Return value: the newly created #ClutterEntry - * - * Since: 0.4 - */ -ClutterActor * -clutter_entry_new_full (const gchar *font_name, - const gchar *text, - const ClutterColor *color) -{ - ClutterActor *entry; - - entry = clutter_entry_new_with_text (font_name, text); - clutter_entry_set_color (CLUTTER_ENTRY(entry), color); - - return entry; -} - -/** - * clutter_entry_new: - * - * Creates a new, empty #ClutterEntry. - * - * Returns: the newly created #ClutterEntry - */ -ClutterActor * -clutter_entry_new (void) -{ - ClutterActor *entry = g_object_new (CLUTTER_TYPE_ENTRY, - NULL); - clutter_actor_set_size (entry, 50, 50); - - return entry; -} - -/** - * clutter_entry_get_text: - * @entry: a #ClutterEntry - * - * Retrieves the text displayed by @entry. - * - * Return value: the text of the entry. The returned string is - * owned by #ClutterEntry and should not be modified or freed. - * - * Since: 0.4 - */ -G_CONST_RETURN gchar * -clutter_entry_get_text (ClutterEntry *entry) -{ - g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), NULL); - - return entry->priv->text; -} - -/** - * clutter_entry_set_text: - * @entry: a #ClutterEntry - * @text: the text to be displayed - * - * Sets @text as the text to be displayed by @entry. The - * ClutterEntry::text-changed signal is emitted. - * - * Since: 0.4 - */ -void -clutter_entry_set_text (ClutterEntry *entry, - const gchar *text) -{ - ClutterEntryPrivate *priv; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - g_return_if_fail (text != NULL); - - priv = entry->priv; - - g_object_ref (entry); - - if (priv->max_length > 0) - { - gint len = g_utf8_strlen (text, -1); - - if (len < priv->max_length) - { - g_free (priv->text); - - priv->text = g_strdup (text); - priv->n_bytes = strlen (text); - priv->n_chars = len; - } - else - { - gchar * n = g_malloc0 (priv->max_length + 1); - - g_utf8_strncpy (n, text, priv->max_length); - g_free (priv->text); - - priv->text = n; - priv->n_bytes = strlen (n); - priv->n_chars = priv->max_length; - } - } - else - { - g_free (priv->text); - - priv->text = g_strdup (text); - priv->n_bytes = strlen (text); - priv->n_chars = g_utf8_strlen (priv->text, -1); - } - - clutter_entry_clear_layout (entry); - clutter_entry_clear_cursor_position (entry); - /* Recreate the layout so the glyph cache will be primed */ - clutter_entry_ensure_layout (entry, -1); - - if (CLUTTER_ACTOR_IS_VISIBLE (entry)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); - - g_signal_emit (G_OBJECT (entry), entry_signals[TEXT_CHANGED], 0); - - g_object_notify (G_OBJECT (entry), "text"); - g_object_unref (entry); -} - -/** - * clutter_entry_get_font_name: - * @entry: a #ClutterEntry - * - * Retrieves the font used by @entry. - * - * Return value: a string containing the font name, in a format - * understandable by pango_font_description_from_string(). The - * string is owned by #ClutterEntry and should not be modified - * or freed. - * - * Since: 0.4 - */ -G_CONST_RETURN gchar * -clutter_entry_get_font_name (ClutterEntry *entry) -{ - g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), NULL); - - return entry->priv->font_name; -} - -/** - * clutter_entry_set_font_name: - * @entry: a #ClutterEntry - * @font_name: a font name and size, or %NULL for the default font - * - * Sets @font_name as the font used by @entry. - * - * @font_name must be a string containing the font name and its - * size, similarly to what you would feed to the - * pango_font_description_from_string() function. - * - * Since: 0.4 - */ -void -clutter_entry_set_font_name (ClutterEntry *entry, - const gchar *font_name) -{ - ClutterEntryPrivate *priv; - PangoFontDescription *desc; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - if (!font_name || font_name[0] == '\0') - font_name = DEFAULT_FONT_NAME; - - priv = entry->priv; - - if (strcmp (priv->font_name, font_name) == 0) - return; - - desc = pango_font_description_from_string (font_name); - if (!desc) - { - g_warning ("Attempting to create a PangoFontDescription for " - "font name `%s', but failed.", - font_name); - return; - } - - g_object_ref (entry); - - g_free (priv->font_name); - priv->font_name = g_strdup (font_name); - - if (priv->desc) - pango_font_description_free (priv->desc); - - priv->desc = desc; - - if (entry->priv->text && entry->priv->text[0] != '\0') - { - clutter_entry_clear_layout (entry); - /* Recreate the layout so the glyph cache will be primed */ - clutter_entry_ensure_layout (entry, -1); - - if (CLUTTER_ACTOR_IS_VISIBLE (entry)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); - } - - g_object_notify (G_OBJECT (entry), "font-name"); - g_object_unref (entry); -} - - -/** - * clutter_entry_set_color: - * @entry: a #ClutterEntry - * @color: a #ClutterColor - * - * Sets the color of @entry. - * - * Since: 0.4 - */ -void -clutter_entry_set_color (ClutterEntry *entry, - const ClutterColor *color) -{ - ClutterActor *actor; - ClutterEntryPrivate *priv; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - g_return_if_fail (color != NULL); - - priv = entry->priv; - - g_object_ref (entry); - - priv->fgcol.red = color->red; - priv->fgcol.green = color->green; - priv->fgcol.blue = color->blue; - priv->fgcol.alpha = color->alpha; - - actor = CLUTTER_ACTOR (entry); - - clutter_actor_set_opacity (actor, priv->fgcol.alpha); - - if (CLUTTER_ACTOR_IS_VISIBLE (actor)) - clutter_actor_queue_redraw (actor); - - g_object_notify (G_OBJECT (entry), "color"); - g_object_unref (entry); -} - -/** - * clutter_entry_get_color: - * @entry: a #ClutterEntry - * @color: return location for a #ClutterColor - * - * Retrieves the color of @entry. - * - * Since: 0.4 - */ -void -clutter_entry_get_color (ClutterEntry *entry, - ClutterColor *color) -{ - ClutterEntryPrivate *priv; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - g_return_if_fail (color != NULL); - - priv = entry->priv; - - color->red = priv->fgcol.red; - color->green = priv->fgcol.green; - color->blue = priv->fgcol.blue; - color->alpha = priv->fgcol.alpha; -} - -/** - * clutter_entry_get_layout: - * @entry: a #ClutterEntry - * - * Gets the #PangoLayout used to display the entry. - * The layout is useful to e.g. convert text positions to - * pixel positions. - * The returned layout is owned by the entry so need not be - * freed by the caller. - * - * Return value: the #PangoLayout for this entry - * - * Since: 0.4 - **/ -PangoLayout * -clutter_entry_get_layout (ClutterEntry *entry) -{ - g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), NULL); - - clutter_entry_ensure_layout (entry, -1); - - return entry->priv->layout; -} - -/** - * clutter_entry_set_alignment: - * @entry: a #ClutterEntry - * @alignment: A #PangoAlignment - * - * Sets text alignment of the entry. - * - * Since: 0.4 - */ -void -clutter_entry_set_alignment (ClutterEntry *entry, - PangoAlignment alignment) -{ - ClutterEntryPrivate *priv; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - priv = entry->priv; - - if (priv->alignment != alignment) - { - g_object_ref (entry); - - priv->alignment = alignment; - - clutter_entry_clear_layout (entry); - - if (CLUTTER_ACTOR_IS_VISIBLE (entry)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); - - g_object_notify (G_OBJECT (entry), "alignment"); - g_object_unref (entry); - } -} - -/** - * clutter_entry_get_alignment: - * @entry: a #ClutterEntry - * - * Returns the entry's text alignment - * - * Return value: The entry's #PangoAlignment - * - * Since: 0.4 - */ -PangoAlignment -clutter_entry_get_alignment (ClutterEntry *entry) -{ - g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), FALSE); - - return entry->priv->alignment; -} - -/** - * clutter_entry_set_cursor_position: - * @entry: a #ClutterEntry - * @position: the position of the cursor. - * - * Sets the position of the cursor. The @position must be less than or - * equal to the number of characters in the entry. A value of -1 indicates - * that the position should be set after the last character in the entry. - * Note that this position is in characters, not in bytes. - * - * Since: 0.6 - */ -void -clutter_entry_set_cursor_position (ClutterEntry *entry, - gint position) -{ - ClutterEntryPrivate *priv; - gint len; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - priv = entry->priv; - - if (priv->text == NULL) - return; - - len = g_utf8_strlen (priv->text, -1); - - if (position < 0 || position >= len) - priv->position = -1; - else - priv->position = position; - - clutter_entry_clear_cursor_position (entry); - - if (CLUTTER_ACTOR_IS_VISIBLE (entry)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); -} - -/** - * clutter_entry_get_cursor_position: - * @entry: a #ClutterEntry - * - * Gets the position, in characters, of the cursor in @entry. - * - * Return value: the position of the cursor. - * - * Since: 0.6 - */ -gint -clutter_entry_get_cursor_position (ClutterEntry *entry) -{ - ClutterEntryPrivate *priv; - - g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), 0); - - priv = entry->priv; - - return priv->position; -} - -/** - * clutter_entry_handle_key_event: - * @entry: a #ClutterEntry - * @kev: a #ClutterKeyEvent - * - * This function will handle a #ClutterKeyEvent, like those returned in a - * key-press/release-event, and will translate it for the @entry. This includes - * non-alphanumeric keys, such as the arrows keys, which will move the - * input cursor. You should use this function inside a handler for the - * ClutterStage::key-press-event or ClutterStage::key-release-event. - * - * Since: 0.4 - * - * Deprecated: 0.8: The key events will automatically be handled when - * giving the key focus to an entry using clutter_stage_set_key_focus(). - */ -void -clutter_entry_handle_key_event (ClutterEntry *entry, - ClutterKeyEvent *kev) -{ - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - clutter_entry_handle_key_event_internal (entry, kev); -} - -/** - * clutter_entry_insert_unichar: - * @entry: a #ClutterEntry - * @wc: a Unicode character - * - * Insert a character to the right of the current position of the cursor, - * and updates the position of the cursor. - * - * Since: 0.4 - */ -void -clutter_entry_insert_unichar (ClutterEntry *entry, - gunichar wc) -{ - ClutterEntryPrivate *priv; - GString *new = NULL; - glong pos; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - g_return_if_fail (g_unichar_validate (wc)); - - if (wc == 0) - return; - - priv = entry->priv; - - g_object_ref (entry); - - new = g_string_new (priv->text); - pos = offset_to_bytes (priv->text, priv->position); - new = g_string_insert_unichar (new, pos, wc); - - clutter_entry_set_text (entry, new->str); - - if (priv->position >= 0) - clutter_entry_set_cursor_position (entry, priv->position + 1); - - g_string_free (new, TRUE); - - g_object_notify (G_OBJECT (entry), "text"); - g_object_unref (entry); -} - -/** - * clutter_entry_delete_chars: - * @entry: a #ClutterEntry - * @len: the number of characters to remove. - * - * Characters are removed from before the current postion of the cursor. - * - * Since: 0.4 - */ -void -clutter_entry_delete_chars (ClutterEntry *entry, - guint num) -{ - ClutterEntryPrivate *priv; - GString *new = NULL; - gint len; - gint pos; - gint num_pos; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - priv = entry->priv; - - if (!priv->text) - return; - - g_object_ref (entry); - - len = g_utf8_strlen (priv->text, -1); - new = g_string_new (priv->text); - - if (priv->position == -1) - { - num_pos = offset_to_bytes (priv->text, len - num); - new = g_string_erase (new, num_pos, -1); - } - else - { - pos = offset_to_bytes (priv->text, priv->position - num); - num_pos = offset_to_bytes (priv->text, priv->position); - new = g_string_erase (new, pos, num_pos-pos); - } - clutter_entry_set_text (entry, new->str); - - if (priv->position > 0) - clutter_entry_set_cursor_position (entry, priv->position - num); - - g_string_free (new, TRUE); - - g_object_notify (G_OBJECT (entry), "text"); - g_object_unref (entry); -} - -/** - * clutter_entry_insert_text: - * @entry: a #ClutterEntry - * @text: the text to insert - * @position: the position at which to insert the text. - * - * Insert text at a specifc position. - * - * A value of 0 indicates that the text will be inserted before the first - * character in the entry's text, and a value of -1 indicates that the text - * will be inserted after the last character in the entry's text. - * - * Since: 0.4 - */ -void -clutter_entry_insert_text (ClutterEntry *entry, - const gchar *text, - gssize position) -{ - ClutterEntryPrivate *priv; - GString *new = NULL; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - priv = entry->priv; - - new = g_string_new (priv->text); - new = g_string_insert (new, position, text); - - clutter_entry_set_text (entry, new->str); - - g_string_free (new, TRUE); -} - -/** - * clutter_entry_delete_text: - * @entry: a #ClutterEntry - * @start_pos: the starting position. - * @end_pos: the end position. - * - * Deletes a sequence of characters. The characters that are deleted are - * those characters at positions from @start_pos up to, but not including, - * @end_pos. If @end_pos is negative, then the characters deleted will be - * those characters from @start_pos to the end of the text. - * - * Since: 0.4 - */ -void -clutter_entry_delete_text (ClutterEntry *entry, - gssize start_pos, - gssize end_pos) -{ - ClutterEntryPrivate *priv; - GString *new = NULL; - gint start_bytes; - gint end_bytes; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - priv = entry->priv; - - if (!priv->text) - return; - - start_bytes = offset_to_bytes (priv->text, start_pos); - end_bytes = offset_to_bytes (priv->text, end_pos); - - new = g_string_new (priv->text); - new = g_string_erase (new, start_bytes, end_bytes - start_bytes); - - clutter_entry_set_text (entry, new->str); - - g_string_free (new, TRUE); -} - -/** - * clutter_entry_set_visible_cursor: - * @entry: a #ClutterEntry - * @visible: whether the input cursor should be visible - * - * Sets the visibility of the input cursor. - * - * Since: 0.4 - */ -void -clutter_entry_set_visible_cursor (ClutterEntry *entry, - gboolean visible) -{ - ClutterEntryPrivate *priv; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - priv = entry->priv; - - if (priv->show_cursor != visible) - { - priv->show_cursor = visible; - - g_object_notify (G_OBJECT (entry), "cursor-visible"); - - if (CLUTTER_ACTOR_IS_VISIBLE (entry)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); - } -} - -/** - * clutter_entry_get_visible_cursor: - * @entry: a #ClutterEntry - * - * Returns the input cursor's visibility - * - * Return value: whether the input cursor is visible - * - * Since: 0.4 - */ -gboolean -clutter_entry_get_visible_cursor (ClutterEntry *entry) -{ - g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), FALSE); - - return entry->priv->show_cursor; -} - -/** - * clutter_entry_set_visibility: - * @entry: a #ClutterEntry - * @visible: %TRUE if the contents of the entry are displayed as plaintext. - * - * Sets whether the contents of the entry are visible or not. When visibility - * is set to %FALSE, characters are displayed as the invisible char, and will - * also appear that way when the text in the entry widget is copied elsewhere. - * - * The default invisible char is the asterisk '*', but it can be changed with - * clutter_entry_set_invisible_char(). - * - * Since: 0.4 - */ -void -clutter_entry_set_visibility (ClutterEntry *entry, gboolean visible) -{ - ClutterEntryPrivate *priv; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - priv = entry->priv; - - priv->text_visible = visible; - - clutter_entry_clear_layout (entry); - clutter_entry_clear_cursor_position (entry); - - if (CLUTTER_ACTOR_IS_VISIBLE (entry)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); -} - -/** - * clutter_entry_get_visibility: - * @entry: a #ClutterEntry - * - * Returns the entry text visibility. - * - * Return value: %TRUE if the contents of the entry are displayed as plaintext. - * - * Since: 0.4 - */ -gboolean -clutter_entry_get_visibility (ClutterEntry *entry) -{ - g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), TRUE); - - return entry->priv->text_visible; -} - -/** - * clutter_entry_set_invisible_char: - * @entry: a #ClutterEntry - * @wc: a Unicode character - * - * Sets the character to use in place of the actual text when - * clutter_entry_set_visibility() has been called to set text visibility - * to %FALSE. i.e. this is the character used in "password mode" to show the - * user how many characters have been typed. The default invisible char is an - * asterisk ('*'). If you set the invisible char to 0, then the user will get - * no feedback at all; there will be no text on the screen as they type. - * - * Since: 0.4 - */ -void -clutter_entry_set_invisible_char (ClutterEntry *entry, gunichar wc) -{ - ClutterEntryPrivate *priv; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - priv = entry->priv; - - priv->priv_char = wc; - - if (!priv->text_visible) - return; - - clutter_entry_clear_layout (entry); - clutter_entry_clear_cursor_position (entry); - - if (CLUTTER_ACTOR_IS_VISIBLE (entry)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); -} - -/** - * clutter_entry_get_invisible_char: - * @entry: a #ClutterEntry - * - * Returns the character to use in place of the actual text when text-visibility - * is set to %FALSE - * - * Return value: a Unicode character - * - **/ -gunichar -clutter_entry_get_invisible_char (ClutterEntry *entry) -{ - ClutterEntryPrivate *priv; - - g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), TRUE); - - priv = entry->priv; - - return priv->priv_char; -} - -/** - * clutter_entry_set_max_length: - * @entry: a #ClutterEntry - * @max: the maximum number of characters allowed in the entry; 0 - * to disable or -1 to set the length of the current string - * - * Sets the maximum allowed length of the contents of the actor. If the - * current contents are longer than the given length, then they will be - * truncated to fit. - * - * Since: 0.4 - */ -void -clutter_entry_set_max_length (ClutterEntry *entry, - gint max) -{ - ClutterEntryPrivate *priv; - gchar *new = NULL; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - priv = entry->priv; - - if (priv->max_length != max) - { - g_object_ref (entry); - - if (max < 0) - max = g_utf8_strlen (priv->text, -1); - - priv->max_length = max; - - new = g_strdup (priv->text); - clutter_entry_set_text (entry, new); - g_free (new); - - g_object_notify (G_OBJECT (entry), "max-length"); - g_object_unref (entry); - } -} - -/** - * clutter_entry_get_max_length: - * @entry: a #ClutterEntry - * - * Gets the maximum length of text that can be set into @entry. - * See clutter_entry_set_max_length(). - * - * Return value: the maximum number of characters. - * - * Since: 0.4 - */ -gint -clutter_entry_get_max_length (ClutterEntry *entry) -{ - g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), -1); - - return entry->priv->max_length; -} diff --git a/clutter/clutter-entry.h b/clutter/clutter-entry.h deleted file mode 100644 index b43408522..000000000 --- a/clutter/clutter-entry.h +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Clutter. - * - * An OpenGL based 'interactive canvas' library. - * - * Authored By Matthew Allum - * Neil Jagdish Patel . - */ - -#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) -#error "Only can be included directly." -#endif - -#ifndef __CLUTTER_ENTRY_H__ -#define __CLUTTER_ENTRY_H__ - -#include -#include -#include -#include - - -G_BEGIN_DECLS - -#define CLUTTER_TYPE_ENTRY (clutter_entry_get_type ()) - -#define CLUTTER_ENTRY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ - CLUTTER_TYPE_ENTRY, ClutterEntry)) - -#define CLUTTER_ENTRY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), \ - CLUTTER_TYPE_ENTRY, ClutterEntryClass)) - -#define CLUTTER_IS_ENTRY(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ - CLUTTER_TYPE_ENTRY)) - -#define CLUTTER_IS_ENTRY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), \ - CLUTTER_TYPE_ENTRY)) - -#define CLUTTER_ENTRY_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), \ - CLUTTER_TYPE_ENTRY, ClutterEntryClass)) - -typedef struct _ClutterEntry ClutterEntry; -typedef struct _ClutterEntryClass ClutterEntryClass; -typedef struct _ClutterEntryPrivate ClutterEntryPrivate; - -struct _ClutterEntry -{ - /*< private >*/ - ClutterActor parent_instance; - - ClutterEntryPrivate *priv; -}; - -/** - * ClutterEntryClass: - * @paint_cursor: virtual function for subclasses to use to draw a custom - * cursor instead of the default one - * @text_changed: signal class handler for ClutterEntry::text-changed - * @cursor_event: signal class handler for ClutterEntry::cursor-event - * @activate: signal class handler for ClutterEntry::activate - * - * Class fo entry actors. - * - * Since: 0.4 - */ -struct _ClutterEntryClass -{ - /*< private >*/ - ClutterActorClass parent_class; - - /*< public >*/ - /* vfuncs, not signals */ - void (* paint_cursor) (ClutterEntry *entry); - - /* signals */ - void (* text_changed) (ClutterEntry *entry); - void (* cursor_event) (ClutterEntry *entry, - ClutterGeometry *geometry); - void (* activate) (ClutterEntry *entry); - - /*< private >*/ - /* padding for future */ - void (*_clutter_entry_1) (void); - void (*_clutter_entry_2) (void); - void (*_clutter_entry_3) (void); - void (*_clutter_entry_4) (void); -}; - -GType clutter_entry_get_type (void) G_GNUC_CONST; - -ClutterActor * clutter_entry_new (void); -ClutterActor * clutter_entry_new_full (const gchar *font_name, - const gchar *text, - const ClutterColor *color); -ClutterActor * clutter_entry_new_with_text (const gchar *font_name, - const gchar *text); -void clutter_entry_set_text (ClutterEntry *entry, - const gchar *text); -G_CONST_RETURN gchar *clutter_entry_get_text (ClutterEntry *entry); -void clutter_entry_set_font_name (ClutterEntry *entry, - const gchar *font_name); -G_CONST_RETURN gchar *clutter_entry_get_font_name (ClutterEntry *entry); -void clutter_entry_set_color (ClutterEntry *entry, - const ClutterColor *color); -void clutter_entry_get_color (ClutterEntry *entry, - ClutterColor *color); -PangoLayout * clutter_entry_get_layout (ClutterEntry *entry); -void clutter_entry_set_alignment (ClutterEntry *entry, - PangoAlignment alignment); -PangoAlignment clutter_entry_get_alignment (ClutterEntry *entry); -void clutter_entry_set_cursor_position (ClutterEntry *entry, - gint position); -gint clutter_entry_get_cursor_position (ClutterEntry *entry); -void clutter_entry_insert_unichar (ClutterEntry *entry, - gunichar wc); -void clutter_entry_delete_chars (ClutterEntry *entry, - guint len); -void clutter_entry_insert_text (ClutterEntry *entry, - const gchar *text, - gssize position); -void clutter_entry_delete_text (ClutterEntry *entry, - gssize start_pos, - gssize end_pos); -void clutter_entry_set_visible_cursor (ClutterEntry *entry, - gboolean visible); -gboolean clutter_entry_get_visible_cursor (ClutterEntry *entry); - -void clutter_entry_set_visibility (ClutterEntry *entry, - gboolean visible); -gboolean clutter_entry_get_visibility (ClutterEntry *entry); -void clutter_entry_set_invisible_char (ClutterEntry *entry, - gunichar wc); -gunichar clutter_entry_get_invisible_char (ClutterEntry *entry); -void clutter_entry_set_max_length (ClutterEntry *entry, - gint max); -gint clutter_entry_get_max_length (ClutterEntry *entry); - -#ifndef CLUTTER_DISABLE_DEPRECATED -void clutter_entry_handle_key_event (ClutterEntry *entry, - ClutterKeyEvent *kev); -#endif - -G_END_DECLS - -#endif /* __CLUTTER_ENTRY_H__ */ diff --git a/clutter/clutter-label.c b/clutter/clutter-label.c deleted file mode 100644 index 74434e246..000000000 --- a/clutter/clutter-label.c +++ /dev/null @@ -1,1347 +0,0 @@ -/* - * Clutter. - * - * An OpenGL based 'interactive canvas' library. - * - * Authored By Matthew Allum - * - * Copyright (C) 2006 OpenedHand - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -/** - * SECTION:clutter-label - * @short_description: Actor for displaying text - * - * #ClutterLabel is a #ClutterActor that displays text using Pango. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "clutter-label.h" -#include "clutter-main.h" -#include "clutter-enum-types.h" -#include "clutter-private.h" -#include "clutter-debug.h" -#include "clutter-units.h" - -#include "cogl-pango.h" - -#define DEFAULT_FONT_NAME "Sans 10" - -G_DEFINE_TYPE (ClutterLabel, clutter_label, CLUTTER_TYPE_ACTOR) - -/* Probably move into main */ -static PangoContext *_context = NULL; - -static const ClutterColor default_text_color = { 0, 0, 0, 255 }; - -enum -{ - PROP_0, - PROP_FONT_NAME, - PROP_TEXT, - PROP_COLOR, - PROP_ATTRIBUTES, - PROP_USE_MARKUP, - PROP_ALIGNMENT, - PROP_WRAP, - PROP_WRAP_MODE, - PROP_JUSTIFY, - PROP_ELLIPSIZE, -}; - -#define CLUTTER_LABEL_GET_PRIVATE(obj) \ -(G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_LABEL, ClutterLabelPrivate)) - -typedef struct _ClutterLabelCachedLayout ClutterLabelCachedLayout; - -struct _ClutterLabelCachedLayout -{ - /* Cached layout. Pango internally caches the computed extents when - they are requested so there is no need to cache that as well */ - PangoLayout *layout; - /* The width that used to generate this layout */ - ClutterUnit width; - /* A number representing the age of this cache (so that when a new - layout is needed the last used cache is replaced) */ - guint age; -}; - -/* We need at least three cached layouts to run the allocation without - regenerating a new layout. First the layout will be generated at - full width to get the preferred width, then it will be generated at - the preferred width to get the preferred height and then it might - be regenerated at a different width to get the height for the - actual allocated width */ -#define CLUTTER_LABEL_N_CACHED_LAYOUTS 3 - -struct _ClutterLabelPrivate -{ - PangoFontDescription *font_desc; - - ClutterColor fgcol; - - gchar *text; - gchar *font_name; - - PangoAttrList *attrs; - PangoAttrList *effective_attrs; - - ClutterLabelCachedLayout cached_layouts[CLUTTER_LABEL_N_CACHED_LAYOUTS]; - guint cache_age; - - guint alignment : 2; - guint wrap : 1; - guint use_underline : 1; - guint use_markup : 1; - guint ellipsize : 3; - guint single_line_mode : 1; - guint wrap_mode : 3; - guint justify : 1; -}; - -/* - * clutter_label_create_layout_no_cache: - * @label: a #ClutterLabel - * @allocation_width: the width of the layout, or -1 - * - * Creates a new #PangoLayout for the given @allocation_width, using - * the layout properties of the @label. - * - * This function will not touch the glyphs cache. - * - * This function should be used by clutter_label_get_preferred_width() - * and clutter_label_get_preferred_height(). - */ -static PangoLayout * -clutter_label_create_layout_no_cache (ClutterLabel *label, - ClutterUnit allocation_width) -{ - ClutterLabelPrivate *priv = label->priv; - PangoLayout *layout; - - layout = pango_layout_new (_context); - - if (priv->effective_attrs) - pango_layout_set_attributes (layout, priv->effective_attrs); - - pango_layout_set_alignment (layout, priv->alignment); - pango_layout_set_single_paragraph_mode (layout, priv->single_line_mode); - - pango_layout_set_font_description (layout, priv->font_desc); - pango_layout_set_justify (layout, priv->justify); - - if (priv->text) - { - if (!priv->use_markup) - pango_layout_set_text (layout, priv->text, -1); - else - pango_layout_set_markup (layout, priv->text, -1); - } - - if (allocation_width > 0 && - (priv->ellipsize != PANGO_ELLIPSIZE_NONE || priv->wrap)) - { - int layout_width, layout_height; - - pango_layout_get_size (layout, &layout_width, &layout_height); - - /* No need to set ellipsize or wrap if we already have enough - * space, since we don't want to make the layout wider than it - * would be otherwise. - */ - - if (CLUTTER_UNITS_FROM_PANGO_UNIT (layout_width) > allocation_width) - { - if (priv->ellipsize != PANGO_ELLIPSIZE_NONE) - { - gint width; - - width = allocation_width > 0 - ? CLUTTER_UNITS_TO_PANGO_UNIT (allocation_width) - : -1; - - pango_layout_set_ellipsize (layout, priv->ellipsize); - pango_layout_set_width (layout, width); - } - else if (priv->wrap) - { - gint width; - - width = allocation_width > 0 - ? CLUTTER_UNITS_TO_PANGO_UNIT (allocation_width) - : -1; - - pango_layout_set_wrap (layout, priv->wrap_mode); - pango_layout_set_width (layout, width); - } - } - } - - return layout; -} - -static void -clutter_label_dirty_cache (ClutterLabel *label) -{ - ClutterLabelPrivate *priv = label->priv; - int i; - - /* Delete the cached layouts so they will be recreated the next time - they are needed */ - for (i = 0; i < CLUTTER_LABEL_N_CACHED_LAYOUTS; i++) - if (priv->cached_layouts[i].layout) - { - g_object_unref (priv->cached_layouts[i].layout); - priv->cached_layouts[i].layout = NULL; - } -} - -/* - * clutter_label_create_layout: - * @label: a #ClutterLabel - * @allocation_width: the allocation width - * - * Like clutter_label_create_layout_no_cache(), but will also ensure - * the glyphs cache. If a previously cached layout generated using the - * same width is available then that will be used instead of - * generating a new one. - */ -static PangoLayout * -clutter_label_create_layout (ClutterLabel *label, - ClutterUnit allocation_width) -{ - ClutterLabelPrivate *priv = label->priv; - int i; - ClutterLabelCachedLayout *oldest_cache = priv->cached_layouts; - gboolean found_free_cache = FALSE; - - /* Search for a cached layout with the same width and keep track of - the oldest one */ - for (i = 0; i < CLUTTER_LABEL_N_CACHED_LAYOUTS; i++) - { - if (priv->cached_layouts[i].layout == NULL) - { - /* Always prefer free cache spaces */ - found_free_cache = TRUE; - oldest_cache = priv->cached_layouts + i; - } - /* If this cached layout is using the same width then we can - just return that directly */ - else if (priv->cached_layouts[i].width == allocation_width) - { - CLUTTER_NOTE (ACTOR, "ClutterLabel: %p: cache hit for width %i", - label, CLUTTER_UNITS_TO_DEVICE (allocation_width)); - return priv->cached_layouts[i].layout; - } - else if (!found_free_cache && (priv->cached_layouts[i].age - < oldest_cache->age)) - oldest_cache = priv->cached_layouts + i; - } - - CLUTTER_NOTE (ACTOR, "ClutterLabel: %p: cache miss for width %i", - label, CLUTTER_UNITS_TO_DEVICE (allocation_width)); - - /* If we make it here then we didn't have a cached version so we - need to recreate the layout */ - if (oldest_cache->layout) - g_object_unref (oldest_cache->layout); - - oldest_cache->layout - = clutter_label_create_layout_no_cache (label, allocation_width); - - cogl_pango_ensure_glyph_cache_for_layout (oldest_cache->layout); - - /* Mark the 'time' this cache was created and advance the time */ - oldest_cache->age = priv->cache_age++; - - oldest_cache->width = allocation_width; - - return oldest_cache->layout; -} - -static void -clutter_label_paint (ClutterActor *self) -{ - ClutterLabel *label = CLUTTER_LABEL (self); - ClutterLabelPrivate *priv = label->priv; - PangoLayout *layout; - ClutterActorBox alloc = { 0, }; - CoglColor color = { 0, }; - - if (priv->font_desc == NULL || priv->text == NULL) - { - CLUTTER_NOTE (ACTOR, "desc: %p, text %p", - priv->font_desc ? priv->font_desc : 0x0, - priv->text ? priv->text : 0x0); - return; - } - - CLUTTER_NOTE (PAINT, "painting label (text:`%s')", priv->text); - - clutter_actor_get_allocation_box (self, &alloc); - layout = clutter_label_create_layout (label, alloc.x2 - alloc.x1); - - cogl_color_set_from_4ub (&color, - priv->fgcol.red, - priv->fgcol.green, - priv->fgcol.blue, - clutter_actor_get_paint_opacity (self)); - - cogl_pango_render_layout (layout, 0, 0, &color, 0); -} - -static void -clutter_label_get_preferred_width (ClutterActor *self, - ClutterUnit for_height, - ClutterUnit *min_width_p, - ClutterUnit *natural_width_p) -{ - ClutterLabel *label = CLUTTER_LABEL (self); - ClutterLabelPrivate *priv = label->priv; - PangoRectangle logical_rect = { 0, }; - PangoLayout *layout; - ClutterUnit layout_width; - - layout = clutter_label_create_layout (label, -1); - - pango_layout_get_extents (layout, NULL, &logical_rect); - - layout_width = logical_rect.width > 0 - ? CLUTTER_UNITS_FROM_PANGO_UNIT (logical_rect.width) - : 1; - - if (min_width_p) - { - if (priv->wrap || priv->ellipsize) - *min_width_p = 1; - else - *min_width_p = layout_width; - } - - if (natural_width_p) - *natural_width_p = layout_width; -} - -static void -clutter_label_get_preferred_height (ClutterActor *self, - ClutterUnit for_width, - ClutterUnit *min_height_p, - ClutterUnit *natural_height_p) -{ - ClutterLabel *label = CLUTTER_LABEL (self); - - if (for_width == 0) - { - if (min_height_p) - *min_height_p = 0; - - if (natural_height_p) - *natural_height_p = 0; - } - else - { - PangoLayout *layout; - PangoRectangle logical_rect = { 0, }; - ClutterUnit height; - - layout = clutter_label_create_layout (label, for_width); - - pango_layout_get_extents (layout, NULL, &logical_rect); - height = CLUTTER_UNITS_FROM_PANGO_UNIT (logical_rect.height); - - if (min_height_p) - *min_height_p = height; - - if (natural_height_p) - *natural_height_p = height; - } -} - -static void -clutter_label_allocate (ClutterActor *self, - const ClutterActorBox *box, - gboolean origin_changed) -{ - ClutterLabel *label = CLUTTER_LABEL (self); - ClutterActorClass *parent_class; - - /* Ensure that there is a cached label with the right width so that - we don't need to create the label during the paint run */ - clutter_label_create_layout (label, box->x2 - box->x1); - - parent_class = CLUTTER_ACTOR_CLASS (clutter_label_parent_class); - parent_class->allocate (self, box, origin_changed); -} - -static void -clutter_label_dispose (GObject *object) -{ - ClutterLabel *self = CLUTTER_LABEL(object); - ClutterLabelPrivate *priv; - - priv = self->priv; - - /* Get rid of the cached layouts */ - clutter_label_dirty_cache (self); - - G_OBJECT_CLASS (clutter_label_parent_class)->dispose (object); -} - -static void -clutter_label_finalize (GObject *object) -{ - ClutterLabel *self = CLUTTER_LABEL(object); - ClutterLabelPrivate *priv; - - priv = self->priv; - - if (priv->font_desc) - pango_font_description_free (priv->font_desc); - - g_free (priv->text); - g_free (priv->font_name); - - G_OBJECT_CLASS (clutter_label_parent_class)->finalize (object); -} - -static void -clutter_label_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - ClutterLabel *label; - ClutterLabelPrivate *priv; - - label = CLUTTER_LABEL(object); - priv = label->priv; - - switch (prop_id) - { - case PROP_FONT_NAME: - clutter_label_set_font_name (label, g_value_get_string (value)); - break; - case PROP_TEXT: - clutter_label_set_text (label, g_value_get_string (value)); - break; - case PROP_COLOR: - clutter_label_set_color (label, clutter_value_get_color (value)); - break; - case PROP_ATTRIBUTES: - clutter_label_set_attributes (label, g_value_get_boxed (value)); - break; - case PROP_ALIGNMENT: - clutter_label_set_alignment (label, g_value_get_enum (value)); - break; - case PROP_USE_MARKUP: - clutter_label_set_use_markup (label, g_value_get_boolean (value)); - break; - case PROP_WRAP: - clutter_label_set_line_wrap (label, g_value_get_boolean (value)); - break; - case PROP_JUSTIFY: - clutter_label_set_justify (label, g_value_get_boolean (value)); - break; - case PROP_WRAP_MODE: - clutter_label_set_line_wrap_mode (label, g_value_get_enum (value)); - break; - case PROP_ELLIPSIZE: - clutter_label_set_ellipsize (label, g_value_get_enum (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -clutter_label_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - ClutterLabel *label; - ClutterLabelPrivate *priv; - - label = CLUTTER_LABEL (object); - priv = label->priv; - - switch (prop_id) - { - case PROP_FONT_NAME: - g_value_set_string (value, priv->font_name); - break; - case PROP_TEXT: - g_value_set_string (value, priv->text); - break; - case PROP_COLOR: - clutter_value_set_color (value, &priv->fgcol); - break; - case PROP_ATTRIBUTES: - g_value_set_boxed (value, priv->attrs); - break; - case PROP_ALIGNMENT: - g_value_set_enum (value, priv->alignment); - break; - case PROP_USE_MARKUP: - g_value_set_boolean (value, priv->use_markup); - break; - case PROP_JUSTIFY: - g_value_set_boolean (value, priv->justify); - break; - case PROP_WRAP: - g_value_set_boolean (value, priv->wrap); - break; - case PROP_WRAP_MODE: - g_value_set_enum (value, priv->wrap_mode); - break; - case PROP_ELLIPSIZE: - g_value_set_enum (value, priv->ellipsize); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -clutter_label_class_init (ClutterLabelClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); - GParamSpec *pspec; - - actor_class->paint = clutter_label_paint; - actor_class->get_preferred_width = clutter_label_get_preferred_width; - actor_class->get_preferred_height = clutter_label_get_preferred_height; - actor_class->allocate = clutter_label_allocate; - - gobject_class->finalize = clutter_label_finalize; - gobject_class->dispose = clutter_label_dispose; - gobject_class->set_property = clutter_label_set_property; - gobject_class->get_property = clutter_label_get_property; - - /** - * ClutterLabel:font-name: - * - * The font to be used by the #ClutterLabel, as a string - * that can be parsed by pango_font_description_from_string(). - * - * Since: 0.2 - */ - pspec = g_param_spec_string ("font-name", - "Font Name", - "The font to be used by the label", - NULL, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_FONT_NAME, pspec); - - pspec = g_param_spec_string ("text", - "Text", - "The text to render", - NULL, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_TEXT, pspec); - - pspec = clutter_param_spec_color ("color", - "Font Color", - "Color of the font used by the label", - &default_text_color, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_COLOR, pspec); - - pspec = g_param_spec_boxed ("attributes", - "Attributes", - "A list of style attributes to apply to " - "the text of the label", - PANGO_TYPE_ATTR_LIST, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_ATTRIBUTES, pspec); - - /** - * ClutterLabel:use-markup: - * - * Whether the text of the label includes Pango markup. See - * pango_layout_set_markup() in the Pango documentation. - * - * Since: 0.2 - */ - pspec = g_param_spec_boolean ("use-markup", - "Use markup", - "Whether or not the text of the label " - "includes Pango markup", - FALSE, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_USE_MARKUP, pspec); - - /** - * ClutterLabel:wrap: - * - * Whether to wrap the lines of #ClutterLabel:text if the contents - * exceed the available allocation. The wrapping strategy is - * controlled by the #ClutterLabel:wrap-mode property. - * - * Since: 0.2 - */ - pspec = g_param_spec_boolean ("wrap", - "Line wrap", - "If set, wrap the lines if the text " - "becomes too wide", - FALSE, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_WRAP, pspec); - - /** - * ClutterLabel:wrap-mode: - * - * If #ClutterLabel:wrap is set to %TRUE, this property will - * control how the text is wrapped. - * - * Since: 0.2 - */ - pspec = g_param_spec_enum ("wrap-mode", - "Line wrap mode", - "Control how line-wrapping is done", - PANGO_TYPE_WRAP_MODE, - PANGO_WRAP_WORD, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_WRAP_MODE, pspec); - - pspec = g_param_spec_enum ("ellipsize", - "Ellipsize", - "The preferred place to ellipsize the string, " - "if the label does not have enough room to " - "display the entire string", - PANGO_TYPE_ELLIPSIZE_MODE, - PANGO_ELLIPSIZE_NONE, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_ELLIPSIZE, pspec); - - /** - * ClutterLabel:alignment: - * - * The preferred alignment for the text. This property controls - * the alignment of multi-line paragraphs. - * - * Since: 0.2 - */ - pspec = g_param_spec_enum ("alignment", - "Alignment", - "The preferred alignment for the string, " - "for multi-line text", - PANGO_TYPE_ALIGNMENT, - PANGO_ALIGN_LEFT, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_ALIGNMENT, pspec); - - /** - * ClutterLabel:justify: - * - * Whether the contents of the label should be justified on both - * margins. - * - * Since: 0.6 - */ - pspec = g_param_spec_boolean ("justify", - "Justify", - "Whether the contents of the label " - "should be justified", - FALSE, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_JUSTIFY, pspec); - - g_type_class_add_private (gobject_class, sizeof (ClutterLabelPrivate)); -} - -static void -clutter_label_init (ClutterLabel *self) -{ - ClutterLabelPrivate *priv; - int i; - - self->priv = priv = CLUTTER_LABEL_GET_PRIVATE (self); - - if (G_UNLIKELY (_context == NULL)) - _context = _clutter_context_create_pango_context (CLUTTER_CONTEXT ()); - - priv->alignment = PANGO_ALIGN_LEFT; - priv->wrap = FALSE; - priv->wrap_mode = PANGO_WRAP_WORD; - priv->ellipsize = PANGO_ELLIPSIZE_NONE; - priv->use_underline = FALSE; - priv->use_markup = FALSE; - priv->justify = FALSE; - - for (i = 0; i < CLUTTER_LABEL_N_CACHED_LAYOUTS; i++) - priv->cached_layouts[i].layout = NULL; - - priv->text = NULL; - priv->attrs = NULL; - - priv->fgcol = default_text_color; - - priv->font_name = g_strdup (DEFAULT_FONT_NAME); - priv->font_desc = pango_font_description_from_string (priv->font_name); -} - -/** - * clutter_label_new_with_text: - * @font_name: the name (and size) of the font to be used - * @text: the text to be displayed - * - * Creates a new #ClutterLabel displaying @text using @font_name. - * - * Return value: a #ClutterLabel - */ -ClutterActor* -clutter_label_new_with_text (const gchar *font_name, - const gchar *text) -{ - return g_object_new (CLUTTER_TYPE_LABEL, - "font-name", font_name, - "text", text, - NULL); -} - -/** - * clutter_label_new_full: - * @font_name: the name (and size) of the font to be used - * @text: the text to be displayed - * @color: #ClutterColor for text - * - * Creates a new #ClutterLabel displaying @text with @color - * using @font_name. - * - * Return value: a #ClutterLabel - */ -ClutterActor* -clutter_label_new_full (const gchar *font_name, - const gchar *text, - const ClutterColor *color) -{ - return g_object_new (CLUTTER_TYPE_LABEL, - "font-name", font_name, - "text", text, - "color", color, - NULL); -} - -/** - * clutter_label_new: - * - * Creates a new, empty #ClutterLabel. - * - * Returns: the newly created #ClutterLabel - */ -ClutterActor * -clutter_label_new (void) -{ - return g_object_new (CLUTTER_TYPE_LABEL, NULL); -} - -/** - * clutter_label_get_text: - * @label: a #ClutterLabel - * - * Retrieves the text displayed by @label - * - * Return value: the text of the label. The returned string is - * owned by #ClutterLabel and should not be modified or freed. - */ -G_CONST_RETURN gchar * -clutter_label_get_text (ClutterLabel *label) -{ - g_return_val_if_fail (CLUTTER_IS_LABEL (label), NULL); - - return label->priv->text; -} - -/** - * clutter_label_set_text: - * @label: a #ClutterLabel - * @text: the text to be displayed - * - * Sets @text as the text to be displayed by @label. - */ -void -clutter_label_set_text (ClutterLabel *label, - const gchar *text) -{ - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - - priv = label->priv; - - g_free (priv->text); - - priv->text = g_strdup (text); - - clutter_label_dirty_cache (label); - - clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); - - g_object_notify (G_OBJECT (label), "text"); -} - -/** - * clutter_label_get_font_name: - * @label: a #ClutterLabel - * - * Retrieves the font used by @label. - * - * Return value: a string containing the font name, in a format - * understandable by pango_font_description_from_string(). The - * string is owned by @label and should not be modified - * or freed. - */ -G_CONST_RETURN gchar * -clutter_label_get_font_name (ClutterLabel *label) -{ - g_return_val_if_fail (CLUTTER_IS_LABEL (label), NULL); - - return label->priv->font_name; -} - -/** - * clutter_label_set_font_name: - * @label: a #ClutterLabel - * @font_name: a font name and size, or %NULL for the default font - * - * Sets @font_name as the font used by @label. - * - * @font_name must be a string containing the font name and its - * size, similarly to what you would feed to the - * pango_font_description_from_string() function. - */ -void -clutter_label_set_font_name (ClutterLabel *label, - const gchar *font_name) -{ - ClutterLabelPrivate *priv; - PangoFontDescription *desc; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - - if (!font_name || font_name[0] == '\0') - font_name = DEFAULT_FONT_NAME; - - priv = label->priv; - - if (strcmp (priv->font_name, font_name) == 0) - return; - - desc = pango_font_description_from_string (font_name); - if (!desc) - { - g_warning ("Attempting to create a PangoFontDescription for " - "font name `%s', but failed.", - font_name); - return; - } - - g_free (priv->font_name); - priv->font_name = g_strdup (font_name); - - if (priv->font_desc) - pango_font_description_free (priv->font_desc); - - priv->font_desc = desc; - - clutter_label_dirty_cache (label); - - if (label->priv->text && label->priv->text[0] != '\0') - clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); - - g_object_notify (G_OBJECT (label), "font-name"); -} - - -/** - * clutter_label_set_color: - * @label: a #ClutterLabel - * @color: a #ClutterColor - * - * Sets the color of @label. - */ -void -clutter_label_set_color (ClutterLabel *label, - const ClutterColor *color) -{ - ClutterActor *actor; - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - g_return_if_fail (color != NULL); - - priv = label->priv; - - g_object_ref (label); - - priv->fgcol.red = color->red; - priv->fgcol.green = color->green; - priv->fgcol.blue = color->blue; - priv->fgcol.alpha = color->alpha; - - actor = CLUTTER_ACTOR (label); - - clutter_actor_set_opacity (actor, priv->fgcol.alpha); - - if (CLUTTER_ACTOR_IS_VISIBLE (actor)) - clutter_actor_queue_redraw (actor); - - g_object_notify (G_OBJECT (label), "color"); - g_object_unref (label); -} - -/** - * clutter_label_get_color: - * @label: a #ClutterLabel - * @color: return location for a #ClutterColor - * - * Retrieves the color of @label. - */ -void -clutter_label_get_color (ClutterLabel *label, - ClutterColor *color) -{ - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - g_return_if_fail (color != NULL); - - priv = label->priv; - - color->red = priv->fgcol.red; - color->green = priv->fgcol.green; - color->blue = priv->fgcol.blue; - color->alpha = priv->fgcol.alpha; -} - -/** - * clutter_label_set_ellipsize: - * @label: a #ClutterLabel - * @mode: a #PangoEllipsizeMode - * - * Sets the mode used to ellipsize (add an ellipsis: "...") to the text - * if there is not enough space to render the entire string. - * - * Since: 0.2 - **/ -void -clutter_label_set_ellipsize (ClutterLabel *label, - PangoEllipsizeMode mode) -{ - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - g_return_if_fail (mode >= PANGO_ELLIPSIZE_NONE && - mode <= PANGO_ELLIPSIZE_END); - - priv = label->priv; - - if ((PangoEllipsizeMode) priv->ellipsize != mode) - { - priv->ellipsize = mode; - - clutter_label_dirty_cache (label); - - clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); - - g_object_notify (G_OBJECT (label), "ellipsize"); - } -} - -/** - * clutter_label_get_ellipsize: - * @label: a #ClutterLabel - * - * Returns the ellipsizing position of the label. - * See clutter_label_set_ellipsize(). - * - * Return value: #PangoEllipsizeMode - * - * Since: 0.2 - **/ -PangoEllipsizeMode -clutter_label_get_ellipsize (ClutterLabel *label) -{ - g_return_val_if_fail (CLUTTER_IS_LABEL (label), PANGO_ELLIPSIZE_NONE); - - return label->priv->ellipsize; -} - -/** - * clutter_label_set_line_wrap: - * @label: a #ClutterLabel - * @wrap: the setting - * - * Toggles line wrapping within the #ClutterLabel widget. %TRUE makes - * it break lines if text exceeds the widget's size. %FALSE lets the - * text get cut off by the edge of the widget if it exceeds the widget - * size. - * - * Since: 0.2 - */ -void -clutter_label_set_line_wrap (ClutterLabel *label, - gboolean wrap) -{ - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - - priv = label->priv; - - wrap = wrap != FALSE; - - if (priv->wrap != wrap) - { - priv->wrap = wrap; - - clutter_label_dirty_cache (label); - - clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); - - g_object_notify (G_OBJECT (label), "wrap"); - } -} - -/** - * clutter_label_get_line_wrap: - * @label: a #ClutterLabel - * - * Returns whether lines in the label are automatically wrapped. - * See clutter_label_set_line_wrap (). - * - * Return value: %TRUE if the lines of the label are automatically wrapped. - * - * Since: 0.2 - */ -gboolean -clutter_label_get_line_wrap (ClutterLabel *label) -{ - g_return_val_if_fail (CLUTTER_IS_LABEL (label), FALSE); - - return label->priv->wrap; -} - -/** - * clutter_label_set_line_wrap_mode: - * @label: a #ClutterLabel - * @wrap_mode: the line wrapping mode - * - * If line wrapping is on (see clutter_label_set_line_wrap()) this controls how - * the line wrapping is done. The default is %PANGO_WRAP_WORD which means - * wrap on word boundaries. - * - * Since: 0.2 - **/ -void -clutter_label_set_line_wrap_mode (ClutterLabel *label, - PangoWrapMode wrap_mode) -{ - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - - priv = label->priv; - - if (priv->wrap_mode != wrap_mode) - { - priv->wrap_mode = wrap_mode; - - clutter_label_dirty_cache (label); - - clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); - - g_object_notify (G_OBJECT (label), "wrap-mode"); - } -} - -/** - * clutter_label_get_line_wrap_mode: - * @label: a #ClutterLabel - * - * Returns line wrap mode used by the label. - * See clutter_label_set_line_wrap_mode (). - * - * Return value: %TRUE if the lines of the label are automatically wrapped. - * - * Since: 0.2 - */ -PangoWrapMode -clutter_label_get_line_wrap_mode (ClutterLabel *label) -{ - g_return_val_if_fail (CLUTTER_IS_LABEL (label), FALSE); - - return label->priv->wrap_mode; -} - -/** - * clutter_label_get_layout: - * @label: a #ClutterLabel - * - * Gets the #PangoLayout used to display the label. - * The layout is useful to e.g. convert text positions to - * pixel positions. - * The returned layout is owned by the label so need not be - * freed by the caller. - * - * Return value: the #PangoLayout for this label - * - * Since: 0.2 - **/ -PangoLayout * -clutter_label_get_layout (ClutterLabel *label) -{ - ClutterUnit width; - - g_return_val_if_fail (CLUTTER_IS_LABEL (label), NULL); - - width = clutter_actor_get_widthu (CLUTTER_ACTOR (label)); - - return clutter_label_create_layout (label, width); -} - -static inline void -clutter_label_set_attributes_internal (ClutterLabel *label, - PangoAttrList *attrs) -{ -} - -/** - * clutter_label_set_attributes: - * @label: a #ClutterLabel - * @attrs: a #PangoAttrList - * - * Sets a #PangoAttrList; the attributes in the list are applied to the - * label text. The attributes set with this function will be ignored - * if the "use_markup" property - * is %TRUE. - * - * Since: 0.2 - **/ -void -clutter_label_set_attributes (ClutterLabel *label, - PangoAttrList *attrs) -{ - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - - priv = label->priv; - - if (attrs) - pango_attr_list_ref (attrs); - - if (priv->attrs) - pango_attr_list_unref (priv->attrs); - - if (!priv->use_markup) - { - if (attrs) - pango_attr_list_ref (attrs); - - if (priv->effective_attrs) - pango_attr_list_unref (priv->effective_attrs); - - priv->effective_attrs = attrs; - } - - priv->attrs = attrs; - - clutter_label_dirty_cache (label); - - g_object_notify (G_OBJECT (label), "attributes"); - - clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); -} - -/** - * clutter_label_get_attributes: - * @label: a #ClutterLabel - * - * Gets the attribute list that was set on the label using - * clutter_label_set_attributes(), if any. - * - * Return value: the attribute list, or %NULL if none was set. - * - * Since: 0.2 - **/ -PangoAttrList * -clutter_label_get_attributes (ClutterLabel *label) -{ - g_return_val_if_fail (CLUTTER_IS_LABEL (label), NULL); - - return label->priv->attrs; -} - -/** - * clutter_label_set_use_markup: - * @label: a #ClutterLabel - * @setting: %TRUE if the label's text should be parsed for markup. - * - * Sets whether the text of the label contains markup in Pango's text markup - * language. - **/ -void -clutter_label_set_use_markup (ClutterLabel *label, - gboolean setting) -{ - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - - priv = label->priv; - - if (priv->use_markup != setting) - { - priv->use_markup = setting; - - clutter_label_dirty_cache (label); - - clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); - - g_object_notify (G_OBJECT (label), "use-markup"); - } -} - -/** - * clutter_label_get_use_markup: - * @label: a #ClutterLabel - * - * Returns whether the label's text is interpreted as marked up with - * the Pango text markup - * language. See clutter_label_set_use_markup (). - * - * Return value: %TRUE if the label's text will be parsed for markup. - **/ -gboolean -clutter_label_get_use_markup (ClutterLabel *label) -{ - g_return_val_if_fail (CLUTTER_IS_LABEL (label), FALSE); - - return label->priv->use_markup; -} - -/** - * clutter_label_set_alignment: - * @label: a #ClutterLabel - * @alignment: A #PangoAlignment - * - * Sets text alignment of the label. - * - * The alignment will only be used when the contents of the - * label are enough to wrap, and the #ClutterLabel:wrap - * property is set to %TRUE. - **/ -void -clutter_label_set_alignment (ClutterLabel *label, - PangoAlignment alignment) -{ - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - - priv = label->priv; - - if (priv->alignment != alignment) - { - priv->alignment = alignment; - - clutter_label_dirty_cache (label); - - clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); - - g_object_notify (G_OBJECT (label), "alignment"); - } -} - -/** - * clutter_label_get_alignment: - * @label: a #ClutterLabel - * - * Returns the label's text alignment - * - * Return value: The label's #PangoAlignment - * - * Since: 0.2 - **/ -PangoAlignment -clutter_label_get_alignment (ClutterLabel *label) -{ - g_return_val_if_fail (CLUTTER_IS_LABEL (label), FALSE); - - return label->priv->alignment; -} - -/** - * clutter_label_set_justify: - * @label: a #ClutterLabel - * @justify: whether the text should be justified - * - * Sets whether the text of the @label actor should be justified - * on both margins. This setting is ignored if Clutter is compiled - * against Pango < 1.18. - * - * Since: 0.6 - */ -void -clutter_label_set_justify (ClutterLabel *label, - gboolean justify) -{ - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - - priv = label->priv; - - if (priv->justify != justify) - { - priv->justify = justify; - - clutter_label_dirty_cache (label); - - clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); - - g_object_notify (G_OBJECT (label), "justify"); - } -} - -/** - * clutter_label_get_justify: - * @label: a #ClutterLabel - * - * Retrieves whether the label should justify the text on both margins. - * - * Return value: %TRUE if the text should be justified - * - * Since: 0.6 - */ -gboolean -clutter_label_get_justify (ClutterLabel *label) -{ - g_return_val_if_fail (CLUTTER_IS_LABEL (label), FALSE); - - return label->priv->justify; -} diff --git a/clutter/clutter-label.h b/clutter/clutter-label.h deleted file mode 100644 index d6021c2f3..000000000 --- a/clutter/clutter-label.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Clutter. - * - * An OpenGL based 'interactive canvas' library. - * - * Authored By Matthew Allum - * - * Copyright (C) 2006 OpenedHand - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) -#error "Only can be included directly." -#endif - -#ifndef __CLUTTER_LABEL_H__ -#define __CLUTTER_LABEL_H__ - -#include -#include -#include - - -G_BEGIN_DECLS - -#define CLUTTER_TYPE_LABEL (clutter_label_get_type ()) - -#define CLUTTER_LABEL(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ - CLUTTER_TYPE_LABEL, ClutterLabel)) - -#define CLUTTER_LABEL_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), \ - CLUTTER_TYPE_LABEL, ClutterLabelClass)) - -#define CLUTTER_IS_LABEL(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ - CLUTTER_TYPE_LABEL)) - -#define CLUTTER_IS_LABEL_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), \ - CLUTTER_TYPE_LABEL)) - -#define CLUTTER_LABEL_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), \ - CLUTTER_TYPE_LABEL, ClutterLabelClass)) - -typedef struct _ClutterLabel ClutterLabel; -typedef struct _ClutterLabelClass ClutterLabelClass; -typedef struct _ClutterLabelPrivate ClutterLabelPrivate; - -struct _ClutterLabel -{ - ClutterActor parent; - - /*< private >*/ - ClutterLabelPrivate *priv; -}; - -struct _ClutterLabelClass -{ - /*< private >*/ - ClutterActorClass parent_class; - - void (*_clutter_label_1) (void); - void (*_clutter_label_2) (void); - void (*_clutter_label_3) (void); - void (*_clutter_label_4) (void); -}; - -GType clutter_label_get_type (void) G_GNUC_CONST; - -ClutterActor * clutter_label_new (void); - -ClutterActor* clutter_label_new_full (const gchar *font_name, - const gchar *text, - const ClutterColor *color); - -ClutterActor * clutter_label_new_with_text (const gchar *font_name, - const gchar *text); -void clutter_label_set_text (ClutterLabel *label, - const gchar *text); -G_CONST_RETURN gchar *clutter_label_get_text (ClutterLabel *label); -void clutter_label_set_font_name (ClutterLabel *label, - const gchar *font_name); -G_CONST_RETURN gchar *clutter_label_get_font_name (ClutterLabel *label); -void clutter_label_set_color (ClutterLabel *label, - const ClutterColor *color); -void clutter_label_get_color (ClutterLabel *label, - ClutterColor *color); -void clutter_label_set_ellipsize (ClutterLabel *label, - PangoEllipsizeMode mode); -PangoEllipsizeMode clutter_label_get_ellipsize (ClutterLabel *label); -void clutter_label_set_line_wrap (ClutterLabel *label, - gboolean wrap); -gboolean clutter_label_get_line_wrap (ClutterLabel *label); -void clutter_label_set_line_wrap_mode (ClutterLabel *label, - PangoWrapMode wrap_mode); -PangoWrapMode clutter_label_get_line_wrap_mode (ClutterLabel *label); -PangoLayout * clutter_label_get_layout (ClutterLabel *label); -void clutter_label_set_attributes (ClutterLabel *label, - PangoAttrList *attrs); -PangoAttrList * clutter_label_get_attributes (ClutterLabel *label); -void clutter_label_set_use_markup (ClutterLabel *label, - gboolean setting); -gboolean clutter_label_get_use_markup (ClutterLabel *label); -void clutter_label_set_alignment (ClutterLabel *label, - PangoAlignment alignment); -PangoAlignment clutter_label_get_alignment (ClutterLabel *label); -void clutter_label_set_justify (ClutterLabel *label, - gboolean justify); -gboolean clutter_label_get_justify (ClutterLabel *label); - -G_END_DECLS - -#endif /* __CLUTTER_LABEL_H__ */ diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 9edbce8c6..745fcbbd7 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -53,21 +53,23 @@ #include "pango/cogl-pango.h" /* main context */ -static ClutterMainContext *ClutterCntx = NULL; +static ClutterMainContext *ClutterCntx = NULL; /* main lock and locking/unlocking functions */ -static GMutex *clutter_threads_mutex = NULL; -static GCallback clutter_threads_lock = NULL; -static GCallback clutter_threads_unlock = NULL; +static GMutex *clutter_threads_mutex = NULL; +static GCallback clutter_threads_lock = NULL; +static GCallback clutter_threads_unlock = NULL; -static gboolean clutter_is_initialized = FALSE; -static gboolean clutter_show_fps = FALSE; -static gboolean clutter_fatal_warnings = FALSE; +static gboolean clutter_is_initialized = FALSE; +static gboolean clutter_show_fps = FALSE; +static gboolean clutter_fatal_warnings = FALSE; -static guint clutter_default_fps = 60; +static guint clutter_default_fps = 60; -static guint clutter_main_loop_level = 0; -static GSList *main_loops = NULL; +static guint clutter_main_loop_level = 0; +static GSList *main_loops = NULL; + +static PangoDirection clutter_text_direction = PANGO_DIRECTION_LTR; guint clutter_debug_flags = 0; /* global clutter debug flag */ @@ -401,23 +403,100 @@ _clutter_do_pick (ClutterStage *stage, return clutter_get_actor_by_gid (id); } +static PangoDirection +clutter_get_text_direction (void) +{ + PangoDirection dir = PANGO_DIRECTION_LTR; + const gchar *direction; + + direction = g_getenv ("CLUTTER_TEXT_DIRECTION"); + if (direction && *direction != '\0') + { + if (strcmp (direction, "rtl") == 0) + dir = PANGO_DIRECTION_RTL; + else if (strcmp (direction, "ltr") == 0) + dir = PANGO_DIRECTION_LTR; + } + else + { + /* Translate to default:RTL if you want your widgets + * to be RTL, otherwise translate to default:LTR. + * + * Do *not* translate it to "predefinito:LTR": if it + * it isn't default:LTR or default:RTL it will not work + */ + char *e = _("default:LTR"); + + if (strcmp (e, "default:RTL") == 0) + dir = PANGO_DIRECTION_RTL; + else if (strcmp (e, "default:LTR") == 0) + dir = PANGO_DIRECTION_LTR; + else + g_warning ("Whoever translated default:LTR did so wrongly."); + } + + return dir; +} + +static void +update_pango_context (ClutterBackend *backend, + PangoContext *context) +{ + PangoFontDescription *font_desc; + cairo_font_options_t *font_options; + const gchar *font_name; + gdouble resolution; + + /* update the text direction */ + pango_context_set_base_dir (context, clutter_text_direction); + + /* get the configuration for the PangoContext from the backend */ + font_name = clutter_backend_get_font_name (backend); + font_options = clutter_backend_get_font_options (backend); + resolution = clutter_backend_get_resolution (backend); + + font_desc = pango_font_description_from_string (font_name); + + if (resolution < 0) + resolution = 96.0; /* fall back */ + + pango_context_set_font_description (context, font_desc); + pango_cairo_context_set_font_options (context, font_options); + pango_cairo_context_set_resolution (context, resolution); + + pango_font_description_free (font_desc); +} + +PangoContext * +_clutter_context_get_pango_context (ClutterMainContext *self) +{ + if (G_UNLIKELY (self->pango_context == NULL)) + { + PangoContext *context; + + context = cogl_pango_font_map_create_context (self->font_map); + self->pango_context = context; + + g_signal_connect (self->backend, "resolution-changed", + G_CALLBACK (update_pango_context), + self->pango_context); + g_signal_connect (self->backend, "font-changed", + G_CALLBACK (update_pango_context), + self->pango_context); + } + + update_pango_context (self->backend, self->pango_context); + + return self->pango_context; +} + PangoContext * _clutter_context_create_pango_context (ClutterMainContext *self) { PangoContext *context; - gdouble resolution; - cairo_font_options_t *font_options; - - resolution = clutter_backend_get_resolution (self->backend); - if (resolution < 0) - resolution = 96.0; /* fall back */ context = cogl_pango_font_map_create_context (self->font_map); - - pango_cairo_context_set_resolution (context, resolution); - - font_options = clutter_backend_get_font_options (self->backend); - pango_cairo_context_set_font_options (context, font_options); + update_pango_context (self->backend, context); return context; } @@ -1004,6 +1083,17 @@ clutter_get_timestamp (void) #endif } +static gboolean +clutter_arg_direction_cb (const char *key, + const char *value, + gpointer user_data) +{ + clutter_text_direction = + (strcmp (value, "rtl") == 0) ? PANGO_DIRECTION_RTL + : PANGO_DIRECTION_LTR; + + return TRUE; +} #ifdef CLUTTER_ENABLE_DEBUG static gboolean @@ -1077,6 +1167,8 @@ clutter_init_real (GError **error) cogl_pango_font_map_set_resolution (ctx->font_map, resolution); cogl_pango_font_map_set_use_mipmapping (ctx->font_map, TRUE); + clutter_text_direction = clutter_get_text_direction (); + /* Stage will give us a GL Context etc */ stage = clutter_stage_get_default (); if (!stage) @@ -1142,16 +1234,19 @@ clutter_init_real (GError **error) static GOptionEntry clutter_args[] = { { "clutter-show-fps", 0, 0, G_OPTION_ARG_NONE, &clutter_show_fps, - "Show frames per second", NULL }, + N_("Show frames per second"), NULL }, { "clutter-default-fps", 0, 0, G_OPTION_ARG_INT, &clutter_default_fps, - "Default frame rate", "FPS" }, + N_("Default frame rate"), "FPS" }, { "g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &clutter_fatal_warnings, - "Make all warnings fatal", NULL }, + N_("Make all warnings fatal"), NULL }, + { "clutter-text-direction", 0, 0, G_OPTION_ARG_CALLBACK, + clutter_arg_direction_cb, + N_("Direction for the text"), "DIRECTION" }, #ifdef CLUTTER_ENABLE_DEBUG { "clutter-debug", 0, 0, G_OPTION_ARG_CALLBACK, clutter_arg_debug_cb, - "Clutter debugging flags to set", "FLAGS" }, + N_("Clutter debugging flags to set"), "FLAGS" }, { "clutter-no-debug", 0, 0, G_OPTION_ARG_CALLBACK, clutter_arg_no_debug_cb, - "Clutter debugging flags to unset", "FLAGS" }, + N_("Clutter debugging flags to unset"), "FLAGS" }, #endif /* CLUTTER_ENABLE_DEBUG */ { NULL, }, }; @@ -1294,13 +1389,14 @@ clutter_get_option_group (void) context = clutter_context_get_default (); group = g_option_group_new ("clutter", - "Clutter Options", - "Show Clutter Options", + _("Clutter Options"), + _("Show Clutter Options"), NULL, NULL); g_option_group_set_parse_hooks (group, pre_parse_hook, post_parse_hook); g_option_group_add_entries (group, clutter_args); + g_option_group_set_translation_domain (group, GETTEXT_PACKAGE); /* add backend-specific options */ _clutter_backend_add_options (context->backend, group); @@ -2118,6 +2214,9 @@ clutter_base_init (void) initialised = TRUE; + bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + /* initialise GLib type system */ g_type_init (); diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index 81e030adf..c65bfb066 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -38,6 +38,8 @@ #include +#include "pango/cogl-pango.h" + #include "clutter-backend.h" #include "clutter-event.h" #include "clutter-feature.h" @@ -45,7 +47,6 @@ #include "clutter-stage-manager.h" #include "clutter-stage-window.h" #include "clutter-stage.h" -#include "pango/cogl-pango.h" G_BEGIN_DECLS @@ -125,15 +126,17 @@ struct _ClutterMainContext gint fb_r_mask, fb_g_mask, fb_b_mask; gint fb_r_mask_used, fb_g_mask_used, fb_b_mask_used; - CoglPangoFontMap *font_map; /* Global font map */ + PangoContext *pango_context; /* Global Pango context */ + CoglPangoFontMap *font_map; /* Global font map */ - GSList *input_devices; /* For extra input devices, i.e - MultiTouch */ + GSList *input_devices; /* For extra input devices, i.e + MultiTouch */ }; #define CLUTTER_CONTEXT() (clutter_context_get_default ()) ClutterMainContext *clutter_context_get_default (void); PangoContext *_clutter_context_create_pango_context (ClutterMainContext *self); +PangoContext *_clutter_context_get_pango_context (ClutterMainContext *self); #define CLUTTER_PRIVATE_FLAGS(a) (((ClutterActor *) (a))->private_flags) #define CLUTTER_SET_PRIVATE_FLAGS(a,f) (CLUTTER_PRIVATE_FLAGS (a) |= (f)) diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index 7e5abe76f..f6d42288f 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -1201,7 +1201,7 @@ clutter_stage_read_pixels (ClutterStage *stage, /* The y co-ordinate should be given in OpenGL's coordinate system so 0 is the bottom row */ - y = stage_height - 1 - y - height; + y = stage_height - y - height; glFinish (); diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c new file mode 100644 index 000000000..4b7ae3f13 --- /dev/null +++ b/clutter/clutter-text.c @@ -0,0 +1,3757 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2008 Intel Corporation. + * + * Authored By: Øyvind Kolås + * Emmanuele Bassi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +/** + * SECTION:clutter-text + * @short_description: An actor for displaying and editing text + * + * #ClutterText is an actor that displays custom text using Pango + * as the text rendering engine. + * + * #ClutterText also allows inline editing of the text if the + * actor is set editable using clutter_text_set_editable(). + * + * Selection using keyboard or pointers can be enabled using + * clutter_text_set_selectable(). + * + * #ClutterText is available since Clutter 1.0 + */ + +/* TODO: undo/redo hooks? */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "clutter-text.h" + +#include "clutter-binding-pool.h" +#include "clutter-debug.h" +#include "clutter-enum-types.h" +#include "clutter-keysyms.h" +#include "clutter-main.h" +#include "clutter-private.h" /* includes pango/cogl-pango.h */ +#include "clutter-units.h" + +/* cursor width in pixels */ +#define DEFAULT_CURSOR_SIZE 2 + +/* We need at least three cached layouts to run the allocation without + * regenerating a new layout. First the layout will be generated at + * full width to get the preferred width, then it will be generated at + * the preferred width to get the preferred height and then it might + * be regenerated at a different width to get the height for the + * actual allocated width + */ +#define N_CACHED_LAYOUTS 3 + +#define CLUTTER_TEXT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_TEXT, ClutterTextPrivate)) + +typedef struct _LayoutCache LayoutCache; + +static const ClutterColor default_cursor_color = { 0, 0, 0, 255 }; +static const ClutterColor default_text_color = { 0, 0, 0, 255 }; + +G_DEFINE_TYPE (ClutterText, clutter_text, CLUTTER_TYPE_ACTOR); + +struct _LayoutCache +{ + /* Cached layout. Pango internally caches the computed extents + * when they are requested so there is no need to cache that as + * well + */ + PangoLayout *layout; + + /* The width that used to generate this layout */ + ClutterUnit width; + + /* A number representing the age of this cache (so that when a + * new layout is needed the last used cache is replaced) + */ + guint age; +}; + +struct _ClutterTextPrivate +{ + PangoFontDescription *font_desc; + + gchar *text; + gchar *font_name; + + ClutterColor text_color; + + LayoutCache cached_layouts[N_CACHED_LAYOUTS]; + guint cache_age; + + PangoAttrList *attrs; + PangoAttrList *effective_attrs; + + guint alignment : 2; + guint wrap : 1; + guint use_underline : 1; + guint use_markup : 1; + guint ellipsize : 3; + guint single_line_mode : 1; + guint wrap_mode : 3; + guint justify : 1; + guint editable : 1; + guint cursor_visible : 1; + guint activatable : 1; + guint selectable : 1; + guint in_select_drag : 1; + guint cursor_color_set : 1; + + /* current cursor position */ + gint position; + + /* current 'other end of selection' position */ + gint selection_bound; + + /* the x position in the PangoLayout, used to + * avoid drifting when repeatedly moving up|down + */ + gint x_pos; + + /* the x position of the PangoLayout when in + * single line mode, to scroll the contents of the + * text actor + */ + gint text_x; + + /* the length of the text, in bytes */ + gint n_bytes; + + /* the length of the text, in characters */ + gint n_chars; + + /* Where to draw the cursor */ + ClutterGeometry cursor_pos; + ClutterColor cursor_color; + guint cursor_size; + + gint max_length; + + gunichar password_char; +}; + +enum +{ + PROP_0, + + PROP_FONT_NAME, + PROP_TEXT, + PROP_COLOR, + PROP_USE_MARKUP, + PROP_ATTRIBUTES, + PROP_ALIGNMENT, + PROP_LINE_WRAP, + PROP_LINE_WRAP_MODE, + PROP_JUSTIFY, + PROP_ELLIPSIZE, + PROP_POSITION, + PROP_SELECTION_BOUND, + PROP_CURSOR_VISIBLE, + PROP_CURSOR_COLOR, + PROP_CURSOR_COLOR_SET, + PROP_CURSOR_SIZE, + PROP_EDITABLE, + PROP_SELECTABLE, + PROP_ACTIVATABLE, + PROP_PASSWORD_CHAR, + PROP_MAX_LENGTH, + PROP_SINGLE_LINE_MODE +}; + +enum +{ + TEXT_CHANGED, + CURSOR_EVENT, + ACTIVATE, + + LAST_SIGNAL +}; + +static guint text_signals[LAST_SIGNAL] = { 0, }; + +#define offset_real(t,p) ((p) == -1 ? g_utf8_strlen ((t), -1) : (p)) + +static gint +offset_to_bytes (const gchar *text, + gint pos) +{ + gchar *c = NULL; + gint i, j, len; + + if (pos < 0) + return strlen (text); + + c = g_utf8_next_char (text); + j = 1; + len = strlen (text); + + for (i = 0; i < len; i++) + { + if (&text[i] == c) + { + if (j == pos) + break; + else + { + c = g_utf8_next_char (c); + j++; + } + } + } + + return i; +} + +#define bytes_to_offset(t,p) (g_utf8_pointer_to_offset ((t), (t) + (p))) + + +static inline void +clutter_text_clear_selection (ClutterText *self) +{ + ClutterTextPrivate *priv = self->priv; + + priv->selection_bound = priv->position; +} + +static PangoLayout * +clutter_text_create_layout_no_cache (ClutterText *text, + ClutterUnit allocation_width) +{ + ClutterTextPrivate *priv = text->priv; + PangoContext *context; + PangoLayout *layout; + + context = clutter_actor_get_pango_context (CLUTTER_ACTOR (text)); + layout = pango_layout_new (context); + + pango_layout_set_font_description (layout, priv->font_desc); + + if (priv->effective_attrs) + pango_layout_set_attributes (layout, priv->effective_attrs); + + pango_layout_set_alignment (layout, priv->alignment); + pango_layout_set_single_paragraph_mode (layout, priv->single_line_mode); + pango_layout_set_justify (layout, priv->justify); + + if (priv->text) + { + if (priv->use_markup && !priv->editable) + pango_layout_set_markup (layout, priv->text, -1); + else + { + if (G_LIKELY (priv->password_char == 0)) + pango_layout_set_text (layout, priv->text, priv->n_bytes); + else + { + GString *str = g_string_sized_new (priv->n_bytes); + gunichar invisible_char; + gchar buf[7]; + gint char_len, i; + + invisible_char = priv->password_char; + + /* we need to convert the string built of invisible + * characters into UTF-8 for it to be fed to the Pango + * layout + */ + memset (buf, 0, sizeof (buf)); + char_len = g_unichar_to_utf8 (invisible_char, buf); + + for (i = 0; i < priv->n_chars; i++) + g_string_append_len (str, buf, char_len); + + pango_layout_set_text (layout, str->str, str->len); + + g_string_free (str, TRUE); + } + } + } + + if (allocation_width > 0 && + (priv->ellipsize != PANGO_ELLIPSIZE_NONE || priv->wrap)) + { + int layout_width, layout_height; + + pango_layout_get_size (layout, &layout_width, &layout_height); + + /* No need to set ellipsize or wrap if we already have enough + * space, since we don't want to make the layout wider than it + * would be otherwise. + */ + + if (CLUTTER_UNITS_FROM_PANGO_UNIT (layout_width) > allocation_width) + { + if (priv->ellipsize != PANGO_ELLIPSIZE_NONE) + { + gint width; + + width = allocation_width > 0 + ? CLUTTER_UNITS_TO_PANGO_UNIT (allocation_width) + : -1; + + pango_layout_set_ellipsize (layout, priv->ellipsize); + pango_layout_set_width (layout, width); + } + else if (priv->wrap) + { + gint width; + + width = allocation_width > 0 + ? CLUTTER_UNITS_TO_PANGO_UNIT (allocation_width) + : -1; + + pango_layout_set_wrap (layout, priv->wrap_mode); + pango_layout_set_width (layout, width); + } + } + } + + return layout; +} + +static void +clutter_text_dirty_cache (ClutterText *text) +{ + ClutterTextPrivate *priv = text->priv; + int i; + + /* Delete the cached layouts so they will be recreated the next time + they are needed */ + for (i = 0; i < N_CACHED_LAYOUTS; i++) + if (priv->cached_layouts[i].layout) + { + g_object_unref (priv->cached_layouts[i].layout); + priv->cached_layouts[i].layout = NULL; + } +} + +/* + * clutter_text_create_layout: + * @text: a #ClutterText + * @allocation_width: the allocation width + * + * Like clutter_text_create_layout_no_cache(), but will also ensure + * the glyphs cache. If a previously cached layout generated using the + * same width is available then that will be used instead of + * generating a new one. + */ +static PangoLayout * +clutter_text_create_layout (ClutterText *text, + ClutterUnit allocation_width) +{ + ClutterTextPrivate *priv = text->priv; + LayoutCache *oldest_cache = priv->cached_layouts; + gboolean found_free_cache = FALSE; + int i; + + /* Search for a cached layout with the same width and keep track of + the oldest one */ + for (i = 0; i < N_CACHED_LAYOUTS; i++) + { + if (priv->cached_layouts[i].layout == NULL) + { + /* Always prefer free cache spaces */ + found_free_cache = TRUE; + oldest_cache = priv->cached_layouts + i; + } + /* If this cached layout is using the same width then we can + just return that directly */ + else if (priv->cached_layouts[i].width == allocation_width) + { + CLUTTER_NOTE (ACTOR, "ClutterText: %p: cache hit for width %i", + text, + CLUTTER_UNITS_TO_DEVICE (allocation_width)); + + return priv->cached_layouts[i].layout; + } + else if (!found_free_cache && + (priv->cached_layouts[i].age < oldest_cache->age)) + { + oldest_cache = priv->cached_layouts + i; + } + } + + CLUTTER_NOTE (ACTOR, "ClutterText: %p: cache miss for width %i", + text, + CLUTTER_UNITS_TO_DEVICE (allocation_width)); + + /* If we make it here then we didn't have a cached version so we + need to recreate the layout */ + if (oldest_cache->layout) + g_object_unref (oldest_cache->layout); + + oldest_cache->layout = + clutter_text_create_layout_no_cache (text, allocation_width); + + cogl_pango_ensure_glyph_cache_for_layout (oldest_cache->layout); + + /* Mark the 'time' this cache was created and advance the time */ + oldest_cache->age = priv->cache_age++; + oldest_cache->width = allocation_width; + + return oldest_cache->layout; +} + +static gint +clutter_text_coords_to_position (ClutterText *text, + gint x, + gint y) +{ + gint index_; + gint px, py; + gint trailing; + + px = x * PANGO_SCALE; + py = y * PANGO_SCALE; + + pango_layout_xy_to_index (clutter_text_get_layout (text), + px, py, + &index_, &trailing); + + return index_ + trailing; +} + +/* + * clutter_text_position_to_coords: + * @self: a #ClutterText + * @position: position in characters + * @x: return location for the X coordinate, or %NULL + * @y: return location for the Y coordinate, or %NULL + * @line_height: return location for the line height, or %NULL + * + * Retrieves the coordinates of the given @position. + * + * Return value: %TRUE if the conversion was successful + */ +static gboolean +clutter_text_position_to_coords (ClutterText *self, + gint position, + ClutterUnit *x, + ClutterUnit *y, + ClutterUnit *line_height) +{ + ClutterTextPrivate *priv = self->priv; + PangoRectangle rect; + gint password_char_bytes = 1; + gint index_; + + if (priv->password_char != 0) + password_char_bytes = g_unichar_to_utf8 (priv->password_char, NULL); + + if (position == -1) + { + if (priv->password_char == 0) + index_ = priv->n_bytes; + else + index_ = priv->n_chars * password_char_bytes; + } + else if (position == 0) + { + index_ = 0; + } + else + { + if (priv->password_char == 0) + index_ = offset_to_bytes (priv->text, position); + else + index_ = priv->position * password_char_bytes; + } + + pango_layout_get_cursor_pos (clutter_text_get_layout (self), index_, + &rect, NULL); + + if (x) + *x = CLUTTER_UNITS_FROM_PANGO_UNIT (rect.x); + + if (y) + *y = CLUTTER_UNITS_FROM_PANGO_UNIT (rect.y); + + if (line_height) + *line_height = CLUTTER_UNITS_FROM_PANGO_UNIT (rect.height); + + /* FIXME: should return false if coords were outside text */ + return TRUE; +} + +static inline void +clutter_text_ensure_cursor_position (ClutterText *self) +{ + ClutterTextPrivate *priv = self->priv; + ClutterUnit x, y, cursor_height; + + x = y = cursor_height = 0; + clutter_text_position_to_coords (self, priv->position, + &x, &y, + &cursor_height); + + priv->cursor_pos.x = CLUTTER_UNITS_TO_DEVICE (x); + priv->cursor_pos.y = CLUTTER_UNITS_TO_DEVICE (y); + priv->cursor_pos.width = priv->cursor_size; + priv->cursor_pos.height = CLUTTER_UNITS_TO_DEVICE (cursor_height) - 2; + + g_signal_emit (self, text_signals[CURSOR_EVENT], 0, &priv->cursor_pos); +} + +static gboolean +clutter_text_truncate_selection (ClutterText *self) +{ + ClutterTextPrivate *priv = self->priv; + gint start_index; + gint end_index; + + if (!priv->text) + return TRUE; + + start_index = offset_real (priv->text, priv->position); + end_index = offset_real (priv->text, priv->selection_bound); + + if (end_index == start_index) + return FALSE; + + if (end_index < start_index) + { + gint temp = start_index; + start_index = end_index; + end_index = temp; + } + + clutter_text_delete_text (self, start_index, end_index); + + priv->position = start_index; + priv->selection_bound = start_index; + + return TRUE; +} + +static void +clutter_text_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ClutterText *self = CLUTTER_TEXT (gobject); + + switch (prop_id) + { + case PROP_TEXT: + clutter_text_set_text (self, g_value_get_string (value)); + break; + + case PROP_COLOR: + clutter_text_set_color (self, clutter_value_get_color (value)); + break; + + case PROP_FONT_NAME: + clutter_text_set_font_name (self, g_value_get_string (value)); + break; + + case PROP_USE_MARKUP: + clutter_text_set_use_markup (self, g_value_get_boolean (value)); + break; + + case PROP_ATTRIBUTES: + clutter_text_set_attributes (self, g_value_get_boxed (value)); + break; + + case PROP_ALIGNMENT: + clutter_text_set_alignment (self, g_value_get_enum (value)); + break; + + case PROP_LINE_WRAP: + clutter_text_set_line_wrap (self, g_value_get_boolean (value)); + break; + + case PROP_LINE_WRAP_MODE: + clutter_text_set_line_wrap_mode (self, g_value_get_enum (value)); + break; + + case PROP_JUSTIFY: + clutter_text_set_justify (self, g_value_get_boolean (value)); + break; + + case PROP_ELLIPSIZE: + clutter_text_set_ellipsize (self, g_value_get_enum (value)); + break; + + case PROP_POSITION: + clutter_text_set_cursor_position (self, g_value_get_int (value)); + break; + + case PROP_SELECTION_BOUND: + clutter_text_set_selection_bound (self, g_value_get_int (value)); + break; + + case PROP_CURSOR_VISIBLE: + clutter_text_set_cursor_visible (self, g_value_get_boolean (value)); + break; + + case PROP_CURSOR_COLOR: + clutter_text_set_cursor_color (self, g_value_get_boxed (value)); + break; + + case PROP_CURSOR_SIZE: + clutter_text_set_cursor_size (self, g_value_get_int (value)); + break; + + case PROP_EDITABLE: + clutter_text_set_editable (self, g_value_get_boolean (value)); + break; + + case PROP_ACTIVATABLE: + clutter_text_set_activatable (self, g_value_get_boolean (value)); + break; + + case PROP_SELECTABLE: + clutter_text_set_selectable (self, g_value_get_boolean (value)); + break; + + case PROP_PASSWORD_CHAR: + clutter_text_set_password_char (self, g_value_get_uint (value)); + break; + + case PROP_MAX_LENGTH: + clutter_text_set_max_length (self, g_value_get_int (value)); + break; + + case PROP_SINGLE_LINE_MODE: + clutter_text_set_single_line_mode (self, g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + } +} + +static void +clutter_text_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + ClutterTextPrivate *priv = CLUTTER_TEXT (gobject)->priv; + + switch (prop_id) + { + case PROP_TEXT: + g_value_set_string (value, priv->text); + break; + + case PROP_FONT_NAME: + g_value_set_string (value, priv->font_name); + break; + + case PROP_COLOR: + clutter_value_set_color (value, &priv->text_color); + break; + + case PROP_CURSOR_VISIBLE: + g_value_set_boolean (value, priv->cursor_visible); + break; + + case PROP_CURSOR_COLOR: + clutter_value_set_color (value, &priv->cursor_color); + break; + + case PROP_CURSOR_COLOR_SET: + g_value_set_boolean (value, priv->cursor_color_set); + break; + + case PROP_CURSOR_SIZE: + g_value_set_int (value, priv->cursor_size); + break; + + case PROP_POSITION: + g_value_set_int (value, CLUTTER_FIXED_TO_FLOAT (priv->position)); + break; + + case PROP_SELECTION_BOUND: + g_value_set_int (value, CLUTTER_FIXED_TO_FLOAT (priv->selection_bound)); + break; + + case PROP_EDITABLE: + g_value_set_boolean (value, priv->editable); + break; + + case PROP_SELECTABLE: + g_value_set_boolean (value, priv->selectable); + break; + + case PROP_ACTIVATABLE: + g_value_set_boolean (value, priv->activatable); + break; + + case PROP_PASSWORD_CHAR: + g_value_set_uint (value, priv->password_char); + break; + + case PROP_MAX_LENGTH: + g_value_set_int (value, priv->max_length); + break; + + case PROP_SINGLE_LINE_MODE: + g_value_set_boolean (value, priv->single_line_mode); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + } +} + +static void +clutter_text_dispose (GObject *gobject) +{ + ClutterText *self = CLUTTER_TEXT (gobject); + + /* get rid of the entire cache */ + clutter_text_dirty_cache (self); + + G_OBJECT_CLASS (clutter_text_parent_class)->dispose (gobject); +} + +static void +clutter_text_finalize (GObject *gobject) +{ + ClutterText *self = CLUTTER_TEXT (gobject); + ClutterTextPrivate *priv = self->priv; + + if (priv->font_desc) + pango_font_description_free (priv->font_desc); + + g_free (priv->text); + g_free (priv->font_name); + + G_OBJECT_CLASS (clutter_text_parent_class)->finalize (gobject); +} + +static void +cursor_paint (ClutterText *self) +{ + ClutterTextPrivate *priv = self->priv; + ClutterActor *actor = CLUTTER_ACTOR (self); + guint8 real_opacity; + + if (priv->editable && priv->cursor_visible) + { + if (priv->cursor_color_set) + { + real_opacity = clutter_actor_get_paint_opacity (actor) + * priv->cursor_color.alpha + / 255; + + cogl_set_source_color4ub (priv->cursor_color.red, + priv->cursor_color.green, + priv->cursor_color.blue, + real_opacity); + } + else + { + real_opacity = clutter_actor_get_paint_opacity (actor) + * priv->text_color.alpha + / 255; + + cogl_set_source_color4ub (priv->text_color.red, + priv->text_color.green, + priv->text_color.blue, + real_opacity); + } + + if (priv->position == 0) + priv->cursor_pos.x -= priv->cursor_size; + + if (priv->position == priv->selection_bound) + { + cogl_rectangle (priv->cursor_pos.x, + priv->cursor_pos.y, + priv->cursor_pos.width, + priv->cursor_pos.height); + } + else + { + PangoLayout *layout = clutter_text_get_layout (self); + const gchar *utf8 = priv->text; + gint lines; + gint start_index; + gint end_index; + gint line_no; + + if (priv->position == 0) + start_index = 0; + else + start_index = offset_to_bytes (utf8, priv->position); + + if (priv->selection_bound == 0) + end_index = 0; + else + end_index = offset_to_bytes (utf8, priv->selection_bound); + + if (start_index > end_index) + { + gint temp = start_index; + start_index = end_index; + end_index = temp; + } + + lines = pango_layout_get_line_count (layout); + + for (line_no = 0; line_no < lines; line_no++) + { + PangoLayoutLine *line; + gint n_ranges; + gint *ranges; + gint i; + gint index_; + gint maxindex; + ClutterUnit y, height; + + line = pango_layout_get_line_readonly (layout, line_no); + pango_layout_line_x_to_index (line, G_MAXINT, &maxindex, NULL); + if (maxindex < start_index) + continue; + + pango_layout_line_get_x_ranges (line, start_index, end_index, + &ranges, + &n_ranges); + pango_layout_line_x_to_index (line, 0, &index_, NULL); + + clutter_text_position_to_coords (self, + bytes_to_offset (utf8, index_), + NULL, &y, &height); + + for (i = 0; i < n_ranges; i++) + { + gint range_x; + gint range_width; + + range_x = ranges[i * 2] + / PANGO_SCALE; + range_width = (ranges[i * 2 + 1] - ranges[i * 2]) + / PANGO_SCALE; + + cogl_rectangle (range_x, + CLUTTER_UNITS_TO_DEVICE (y), + range_width, + CLUTTER_UNITS_TO_DEVICE (height)); + } + + g_free (ranges); + } + } + } +} + +static gboolean +clutter_text_button_press (ClutterActor *actor, + ClutterButtonEvent *event) +{ + ClutterText *self = CLUTTER_TEXT (actor); + ClutterTextPrivate *priv = self->priv; + ClutterUnit x, y; + gint index_; + + x = CLUTTER_UNITS_FROM_INT (event->x); + y = CLUTTER_UNITS_FROM_INT (event->y); + + clutter_actor_transform_stage_point (actor, x, y, &x, &y); + + index_ = clutter_text_coords_to_position (self, + CLUTTER_UNITS_TO_INT (x), + CLUTTER_UNITS_TO_INT (y)); + + clutter_text_set_cursor_position (self, bytes_to_offset (priv->text, index_)); + clutter_text_set_selection_bound (self, bytes_to_offset (priv->text, index_)); + + /* grab the pointer */ + priv->in_select_drag = TRUE; + clutter_grab_pointer (actor); + + /* we'll steal keyfocus if we do not have it */ + clutter_actor_grab_key_focus (actor); + + return TRUE; +} + +static gboolean +clutter_text_motion (ClutterActor *actor, + ClutterMotionEvent *mev) +{ + ClutterText *ttext = CLUTTER_TEXT (actor); + ClutterTextPrivate *priv = ttext->priv; + ClutterUnit x, y; + gint index_; + const gchar *text; + + if (!priv->in_select_drag) + return FALSE; + + text = clutter_text_get_text (ttext); + + x = CLUTTER_UNITS_FROM_INT (mev->x); + y = CLUTTER_UNITS_FROM_INT (mev->y); + + clutter_actor_transform_stage_point (actor, x, y, &x, &y); + + index_ = clutter_text_coords_to_position (ttext, + CLUTTER_UNITS_TO_INT (x), + CLUTTER_UNITS_TO_INT (y)); + + if (priv->selectable) + clutter_text_set_cursor_position (ttext, bytes_to_offset (text, index_)); + else + { + clutter_text_set_cursor_position (ttext, bytes_to_offset (text, index_)); + clutter_text_set_selection_bound (ttext, bytes_to_offset (text, index_)); + } + + return TRUE; +} + +static gboolean +clutter_text_button_release (ClutterActor *actor, + ClutterButtonEvent *bev) +{ + ClutterText *ttext = CLUTTER_TEXT (actor); + ClutterTextPrivate *priv = ttext->priv; + + if (priv->in_select_drag) + { + clutter_ungrab_pointer (); + priv->in_select_drag = FALSE; + + return TRUE; + } + + return FALSE; +} + +static gboolean +clutter_text_key_press (ClutterActor *actor, + ClutterKeyEvent *event) +{ + ClutterText *self = CLUTTER_TEXT (actor); + ClutterTextPrivate *priv = self->priv; + ClutterBindingPool *pool; + gboolean res; + gint keyval; + + if (!priv->editable) + return FALSE; + + keyval = clutter_key_event_symbol (event); + + pool = clutter_binding_pool_find (G_OBJECT_TYPE_NAME (actor)); + g_assert (pool != NULL); + + /* we allow passing synthetic events that only contain + * the Unicode value and not the key symbol + */ + if (keyval == 0 && (event->flags & CLUTTER_EVENT_FLAG_SYNTHETIC)) + res = FALSE; + else + res = clutter_binding_pool_activate (pool, keyval, + event->modifier_state, + G_OBJECT (actor)); + + /* if the key binding has handled the event we bail out + * as fast as we can; otherwise, we try to insert the + * Unicode character inside the key event into the text + * actor + */ + if (res) + return TRUE; + else + { + gunichar key_unichar = clutter_key_event_unicode (event); + + /* return is reported as CR, but we want LF */ + if (key_unichar == '\r') + key_unichar = '\n'; + + if (g_unichar_validate (key_unichar)) + { + /* truncate the eventual selection so that the + * Unicode character can replace it + */ + clutter_text_truncate_selection (self); + clutter_text_insert_unichar (self, key_unichar); + + return TRUE; + } + } + + return FALSE; +} + +#define TEXT_PADDING 2 + +static void +clutter_text_paint (ClutterActor *self) +{ + ClutterText *text = CLUTTER_TEXT (self); + ClutterTextPrivate *priv = text->priv; + PangoLayout *layout; + ClutterActorBox alloc = { 0, }; + CoglColor color = { 0, }; + guint8 real_opacity; + gint text_x = priv->text_x; + gboolean clip_set = FALSE; + + if (G_UNLIKELY (priv->font_desc == NULL || priv->text == NULL)) + { + CLUTTER_NOTE (ACTOR, "desc: %p, text %p", + priv->font_desc ? priv->font_desc : 0x0, + priv->text ? priv->text : 0x0); + return; + } + + clutter_actor_get_allocation_box (self, &alloc); + layout = clutter_text_create_layout (text, alloc.x2 - alloc.x1); + + if (priv->editable && priv->cursor_visible) + clutter_text_ensure_cursor_position (text); + + if (priv->editable && priv->single_line_mode) + { + PangoRectangle logical_rect = { 0, }; + gint actor_width, text_width; + + pango_layout_get_extents (layout, NULL, &logical_rect); + + cogl_clip_set (0, 0, + CLUTTER_UNITS_TO_FIXED (alloc.x2 - alloc.x1), + CLUTTER_UNITS_TO_FIXED (alloc.y2 - alloc.y1)); + clip_set = TRUE; + + actor_width = (CLUTTER_UNITS_TO_DEVICE (alloc.x2 - alloc.x1)) + - 2 * TEXT_PADDING; + text_width = logical_rect.width / PANGO_SCALE; + + if (actor_width < text_width) + { + gint cursor_x = priv->cursor_pos.x; + + if (priv->position == -1) + { + text_x = actor_width - text_width; + priv->cursor_pos.x += text_x + TEXT_PADDING; + } + else if (priv->position == 0) + { + text_x = 0; + } + else + { + if (text_x <= 0) + { + gint diff = -1 * text_x; + + if (cursor_x < diff) + text_x += diff - cursor_x; + else if (cursor_x > (diff + actor_width)) + text_x -= cursor_x - (diff - actor_width); + } + } + } + else + { + text_x = 0; + priv->cursor_pos.x += text_x + TEXT_PADDING; + } + } + else + text_x = 0; + + cursor_paint (text); + + real_opacity = clutter_actor_get_paint_opacity (self) + * priv->text_color.alpha + / 255; + + CLUTTER_NOTE (PAINT, "painting text (text:`%s')", priv->text); + + cogl_color_set_from_4ub (&color, + priv->text_color.red, + priv->text_color.green, + priv->text_color.blue, + real_opacity); + cogl_pango_render_layout (layout, text_x, 0, &color, 0); + + if (clip_set) + cogl_clip_unset (); + + priv->text_x = text_x; +} + +static void +clutter_text_get_preferred_width (ClutterActor *self, + ClutterUnit for_height, + ClutterUnit *min_width_p, + ClutterUnit *natural_width_p) +{ + ClutterText *text = CLUTTER_TEXT (self); + ClutterTextPrivate *priv = text->priv; + PangoRectangle logical_rect = { 0, }; + PangoLayout *layout; + gint logical_width; + ClutterUnit layout_width; + + layout = clutter_text_create_layout (text, -1); + + pango_layout_get_extents (layout, NULL, &logical_rect); + + /* the X coordinate of the logical rectangle might be non-zero + * according to the Pango documentation; hence, we need to offset + * the width accordingly + */ + logical_width = logical_rect.x + logical_rect.width; + + layout_width = logical_width > 0 + ? CLUTTER_UNITS_FROM_PANGO_UNIT (logical_width) + : 1; + + if (min_width_p) + { + if (priv->wrap || priv->ellipsize) + *min_width_p = 1; + else + *min_width_p = layout_width; + } + + if (natural_width_p) + *natural_width_p = layout_width; +} + +static void +clutter_text_get_preferred_height (ClutterActor *self, + ClutterUnit for_width, + ClutterUnit *min_height_p, + ClutterUnit *natural_height_p) +{ + ClutterText *text = CLUTTER_TEXT (self); + + if (for_width == 0) + { + if (min_height_p) + *min_height_p = 0; + + if (natural_height_p) + *natural_height_p = 0; + } + else + { + PangoLayout *layout; + PangoRectangle logical_rect = { 0, }; + gint logical_height; + ClutterUnit layout_height; + + layout = clutter_text_create_layout (text, for_width); + + pango_layout_get_extents (layout, NULL, &logical_rect); + + /* the Y coordinate of the logical rectangle might be non-zero + * according to the Pango documentation; hence, we need to offset + * the height accordingly + */ + logical_height = logical_rect.y + logical_rect.height; + + layout_height = CLUTTER_UNITS_FROM_PANGO_UNIT (logical_height); + + if (min_height_p) + *min_height_p = layout_height; + + if (natural_height_p) + *natural_height_p = layout_height; + } +} + +static void +clutter_text_allocate (ClutterActor *self, + const ClutterActorBox *box, + gboolean origin_changed) +{ + ClutterText *text = CLUTTER_TEXT (self); + ClutterActorClass *parent_class; + + /* Ensure that there is a cached layout with the right width so + * that we don't need to create the text during the paint run + */ + clutter_text_create_layout (text, box->x2 - box->x1); + + parent_class = CLUTTER_ACTOR_CLASS (clutter_text_parent_class); + parent_class->allocate (self, box, origin_changed); +} + +static gboolean +clutter_text_real_move_left (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterTextPrivate *priv = self->priv; + gint pos = priv->position; + gint len; + + len = priv->n_chars; + + if (pos != 0 && len !=0) + { + if (pos == -1) + clutter_text_set_cursor_position (self, len - 1); + else + clutter_text_set_cursor_position (self, pos - 1); + } + + if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (self); + + return TRUE; +} + +static gboolean +clutter_text_real_move_right (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterTextPrivate *priv = self->priv; + gint pos = priv->position; + gint len; + + len = priv->n_chars; + + if (pos != -1 && len !=0) + { + if (pos != len) + clutter_text_set_cursor_position (self, pos + 1); + } + + if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (self); + + return TRUE; +} + +static gboolean +clutter_text_real_move_up (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterTextPrivate *priv = self->priv; + PangoLayoutLine *layout_line; + PangoLayout *layout; + gint line_no; + gint index_; + gint x; + + layout = clutter_text_get_layout (self); + + if (priv->position == 0) + index_ = 0; + else + index_ = offset_to_bytes (priv->text, priv->position); + + pango_layout_index_to_line_x (layout, index_, + 0, + &line_no, &x); + + line_no -= 1; + if (line_no < 0) + return FALSE; + + if (priv->x_pos != -1) + x = priv->x_pos; + else + priv->x_pos = x; + + layout_line = pango_layout_get_line_readonly (layout, line_no); + if (!layout_line) + return FALSE; + + pango_layout_line_x_to_index (layout_line, x, &index_, NULL); + + { + gint pos = bytes_to_offset (priv->text, index_); + + clutter_text_set_cursor_position (self, pos); + } + + if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (self); + + return TRUE; +} + +static gboolean +clutter_text_real_move_down (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterTextPrivate *priv = self->priv; + PangoLayoutLine *layout_line; + PangoLayout *layout; + gint line_no; + gint index_; + gint x; + + layout = clutter_text_get_layout (self); + + if (priv->position == 0) + index_ = 0; + else + index_ = offset_to_bytes (priv->text, priv->position); + + pango_layout_index_to_line_x (layout, index_, + 0, + &line_no, &x); + + if (priv->x_pos != -1) + x = priv->x_pos; + else + priv->x_pos = x; + + layout_line = pango_layout_get_line_readonly (layout, line_no + 1); + if (!layout_line) + return FALSE; + + pango_layout_line_x_to_index (layout_line, x, &index_, NULL); + + { + gint pos = bytes_to_offset (priv->text, index_); + + clutter_text_set_cursor_position (self, pos); + } + + if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (self); + + return TRUE; +} + +static gboolean +clutter_text_real_line_start (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterTextPrivate *priv = self->priv; + PangoLayoutLine *layout_line; + PangoLayout *layout; + gint line_no; + gint index_; + gint position; + + layout = clutter_text_get_layout (self); + + if (priv->position == 0) + index_ = 0; + else + index_ = offset_to_bytes (priv->text, priv->position); + + pango_layout_index_to_line_x (layout, index_, + 0, + &line_no, NULL); + + layout_line = pango_layout_get_line_readonly (layout, line_no); + if (!layout_line) + return FALSE; + + pango_layout_line_x_to_index (layout_line, 0, &index_, NULL); + + position = bytes_to_offset (priv->text, index_); + clutter_text_set_cursor_position (self, position); + + if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (self); + + return TRUE; +} + +static gboolean +clutter_text_real_line_end (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterTextPrivate *priv = self->priv; + PangoLayoutLine *layout_line; + PangoLayout *layout; + gint line_no; + gint index_; + gint trailing; + gint position; + + layout = clutter_text_get_layout (self); + + if (priv->position == 0) + index_ = 0; + else + index_ = offset_to_bytes (priv->text, priv->position); + + pango_layout_index_to_line_x (layout, index_, + 0, + &line_no, NULL); + + layout_line = pango_layout_get_line_readonly (layout, line_no); + if (!layout_line) + return FALSE; + + pango_layout_line_x_to_index (layout_line, G_MAXINT, &index_, &trailing); + index_ += trailing; + + position = bytes_to_offset (priv->text, index_); + + clutter_text_set_cursor_position (self, position); + + if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (self); + + return TRUE; +} + +static gboolean +clutter_text_real_select_all (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + clutter_text_set_cursor_position (self, 0); + clutter_text_set_selection_bound (self, self->priv->n_chars); + + return TRUE; +} + +static gboolean +clutter_text_real_del_next (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterTextPrivate *priv = self->priv; + gint pos; + gint len; + + if (clutter_text_truncate_selection (self)) + return TRUE; + + pos = priv->position; + len = priv->n_chars; + + if (len && pos != -1 && pos < len) + clutter_text_delete_text (self, pos, pos + 1); + + return TRUE; +} + +static gboolean +clutter_text_real_del_prev (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterTextPrivate *priv = self->priv; + gint pos; + gint len; + + if (clutter_text_truncate_selection (self)) + return TRUE; + + pos = priv->position; + len = priv->n_chars; + + if (pos != 0 && len != 0) + { + if (pos == -1) + { + clutter_text_set_cursor_position (self, len - 1); + clutter_text_set_selection_bound (self, len - 1); + + clutter_text_delete_text (self, len - 1, len); + } + else + { + clutter_text_set_cursor_position (self, pos - 1); + clutter_text_set_selection_bound (self, pos - 1); + + clutter_text_delete_text (self, pos - 1, pos); + } + } + + return TRUE; +} + +static gboolean +clutter_text_real_activate (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + return clutter_text_activate (self); +} + +static inline void +clutter_text_add_move_binding (ClutterBindingPool *pool, + const gchar *action, + guint key_val, + GCallback callback) +{ + clutter_binding_pool_install_action (pool, action, + key_val, 0, + callback, + NULL, NULL); + clutter_binding_pool_install_action (pool, action, + key_val, CLUTTER_SHIFT_MASK, + callback, + NULL, NULL); +} + +static void +clutter_text_class_init (ClutterTextClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); + ClutterBindingPool *binding_pool; + GParamSpec *pspec; + + g_type_class_add_private (klass, sizeof (ClutterTextPrivate)); + + gobject_class->set_property = clutter_text_set_property; + gobject_class->get_property = clutter_text_get_property; + gobject_class->dispose = clutter_text_dispose; + gobject_class->finalize = clutter_text_finalize; + + actor_class->paint = clutter_text_paint; + actor_class->get_preferred_width = clutter_text_get_preferred_width; + actor_class->get_preferred_height = clutter_text_get_preferred_height; + actor_class->allocate = clutter_text_allocate; + actor_class->key_press_event = clutter_text_key_press; + actor_class->button_press_event = clutter_text_button_press; + actor_class->button_release_event = clutter_text_button_release; + actor_class->motion_event = clutter_text_motion; + + /** + * ClutterText:font-name: + * + * The font to be used by the #ClutterText, as a string + * that can be parsed by pango_font_description_from_string(). + * + * Since: 1.0 + */ + pspec = g_param_spec_string ("font-name", + "Font Name", + "The font to be used by the text", + NULL, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_FONT_NAME, pspec); + + /** + * ClutterText:text: + * + * The text to render inside the actor. + * + * Since: 1.0 + */ + pspec = g_param_spec_string ("text", + "Text", + "The text to render", + "", + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_TEXT, pspec); + + /** + * ClutterText:color: + * + * The color used to render the text. + * + * Since: 1.0 + */ + pspec = clutter_param_spec_color ("color", + "Font Color", + "Color of the font used by the text", + &default_text_color, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_COLOR, pspec); + + /** + * ClutterText:editable: + * + * Whether key events delivered to the actor causes editing. + * + * Since: 1.0 + */ + pspec = g_param_spec_boolean ("editable", + "Editable", + "Whether the text is editable", + TRUE, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_EDITABLE, pspec); + + /** + * ClutterText:selectable: + * + * Whether it is possible to select text, either using the pointer + * or the keyboard. + * + * Since: 1.0 + */ + pspec = g_param_spec_boolean ("selectable", + "Selectable", + "Whether the text is selectable", + TRUE, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_SELECTABLE, pspec); + + /** + * ClutterText:activatable: + * + * Toggles whether return invokes the activate signal or not. + * + * Since: 1.0 + */ + pspec = g_param_spec_boolean ("activatable", + "Activatable", + "Whether pressing return causes the " + "activate signal to be emitted", + TRUE, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_ACTIVATABLE, pspec); + + /** + * ClutterText:cursor-visible: + * + * Whether the input cursor is visible or not, it will only be visible + * if both #ClutterText:cursor-visible and #ClutterText:editable are + * set to %TRUE. + * + * Since: 1.0 + */ + pspec = g_param_spec_boolean ("cursor-visible", + "Cursor Visible", + "Whether the input cursor is visible", + TRUE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_CURSOR_VISIBLE, pspec); + + /** + * ClutterText:cursor-color: + * + * The color of the cursor. + * + * Since: 1.0 + */ + pspec = clutter_param_spec_color ("cursor-color", + "Cursor Color", + "Cursor Color", + &default_cursor_color, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_CURSOR_COLOR, pspec); + + /** + * ClutterText:cursor-color-set: + * + * Will be set to %TRUE if #ClutterText:cursor-color has been set. + * + * Since: 1.0 + */ + pspec = g_param_spec_boolean ("cursor-color-set", + "Cursor Color Set", + "Whether the cursor color has been set", + FALSE, + CLUTTER_PARAM_READABLE); + g_object_class_install_property (gobject_class, PROP_CURSOR_COLOR_SET, pspec); + + /** + * ClutterText:cursor-size: + * + * The size of the cursor, in pixels. If set to -1 the size used will + * be the default cursor size of 2 pixels. + * + * Since: 1.0 + */ + pspec = g_param_spec_int ("cursor-size", + "Cursor Size", + "The width of the cursor, in pixels", + -1, G_MAXINT, DEFAULT_CURSOR_SIZE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_CURSOR_SIZE, pspec); + + /** + * ClutterText:position: + * + * The current input cursor position. -1 is taken to be the end of the text + * + * Since: 1.0 + */ + pspec = g_param_spec_int ("position", + "Position", + "The cursor position", + -1, G_MAXINT, + -1, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_POSITION, pspec); + + /** + * ClutterText:selection-bound: + * + * The current input cursor position. -1 is taken to be the end of the text + * + * Since: 1.0 + */ + pspec = g_param_spec_int ("selection-bound", + "Selection-bound", + "The cursor position of the other end " + "of the selection", + -1, G_MAXINT, + -1, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_SELECTION_BOUND, pspec); + + /** + * ClutterText:attributes: + * + * A list of #PangoStyleAttributes to be applied to the + * contents of the #ClutterText actor. + * + * Since: 1.0 + */ + pspec = g_param_spec_boxed ("attributes", + "Attributes", + "A list of style attributes to apply to " + "the contents of the actor", + PANGO_TYPE_ATTR_LIST, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_ATTRIBUTES, pspec); + + /** + * ClutterText:use-markup: + * + * Whether the text includes Pango markup. See pango_layout_set_markup() + * in the Pango documentation. + * + * Since: 1.0 + */ + pspec = g_param_spec_boolean ("use-markup", + "Use markup", + "Whether or not the text " + "includes Pango markup", + FALSE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_USE_MARKUP, pspec); + + /** + * ClutterText:line-wrap: + * + * Whether to wrap the lines of #ClutterText:text if the contents + * exceed the available allocation. The wrapping strategy is + * controlled by the #ClutterText:line-wrap-mode property. + * + * Since: 1.0 + */ + pspec = g_param_spec_boolean ("line-wrap", + "Line wrap", + "If set, wrap the lines if the text " + "becomes too wide", + FALSE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_LINE_WRAP, pspec); + + /** + * ClutterText:line-wrap-mode: + * + * If #ClutterText:line-wrap is set to %TRUE, this property will + * control how the text is wrapped. + * + * Since: 1.0 + */ + pspec = g_param_spec_enum ("line-wrap-mode", + "Line wrap mode", + "Control how line-wrapping is done", + PANGO_TYPE_WRAP_MODE, + PANGO_WRAP_WORD, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_LINE_WRAP_MODE, pspec); + + /** + * ClutterText:ellipsize: + * + * The preferred place to ellipsize the contents of the #ClutterText actor + * + * Since: 1.0 + */ + pspec = g_param_spec_enum ("ellipsize", + "Ellipsize", + "The preferred place to ellipsize the string", + PANGO_TYPE_ELLIPSIZE_MODE, + PANGO_ELLIPSIZE_NONE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_ELLIPSIZE, pspec); + + /** + * ClutterText:alignment: + * + * The preferred alignment for the text. This property controls + * the alignment of multi-line paragraphs. + * + * Since: 1.0 + */ + pspec = g_param_spec_enum ("alignment", + "Alignment", + "The preferred alignment for the string, " + "for multi-line text", + PANGO_TYPE_ALIGNMENT, + PANGO_ALIGN_LEFT, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_ALIGNMENT, pspec); + + /** + * ClutterText:justify: + * + * Whether the contents of the #ClutterText should be justified + * on both margins. + * + * Since: 1.0 + */ + pspec = g_param_spec_boolean ("justify", + "Justify", + "Whether the text should be justified", + FALSE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_JUSTIFY, pspec); + + /** + * ClutterText:password-char: + * + * If non-zero, the character that should be used in place of + * the actual text in a password text actor. + * + * Since: 1.0 + */ + pspec = g_param_spec_unichar ("password-char", + "Password Character", + "If non-zero, use this character to " + "display the actor's contents", + 0, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_PASSWORD_CHAR, pspec); + + /** + * ClutterText:max-length: + * + * The maximum length of the contents of the #ClutterText actor. + * + * Since: 1.0 + */ + pspec = g_param_spec_int ("max-length", + "Max Length", + "Maximum length of the text inside the actor", + -1, G_MAXINT, 0, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_MAX_LENGTH, pspec); + + /** + * ClutterText:single-line-mode: + * + * Whether the #ClutterText actor should be in single line mode + * or not. A single line #ClutterText actor will only contain a + * single line of text, scrolling it in case its length is bigger + * than the allocated size. + * + * Setting this property will also set the #ClutterText:activatable + * property as a side-effect. + * + * Since: 1.0 + */ + pspec = g_param_spec_boolean ("single-line-mode", + "Single Line Mode", + "Whether the text should be a single line", + FALSE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_SINGLE_LINE_MODE, pspec); + + /** + * ClutterText::text-changed: + * @self: the #ClutterText that emitted the signal + * + * The ::text-changed signal is emitted after @actor's text changes + * + * Since: 1.0 + */ + text_signals[TEXT_CHANGED] = + g_signal_new ("text-changed", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterTextClass, text_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + /** + * ClutterText::cursor-event: + * @self: the #ClutterText that emitted the signal + * @geometry: the coordinates of the cursor + * + * The ::cursor-event signal is emitted whenever the cursor position + * changes inside a #ClutterText actor. Inside @geometry it is stored + * the current position and size of the cursor, relative to the actor + * itself. + * + * Since: 1.0 + */ + text_signals[CURSOR_EVENT] = + g_signal_new ("cursor-event", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterTextClass, cursor_event), + NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, 1, + CLUTTER_TYPE_GEOMETRY | G_SIGNAL_TYPE_STATIC_SCOPE); + + /** + * ClutterText::activate + * @self: the #ClutterText that emitted the signal + * + * The ::activate signal is emitted each time the actor is 'activated' + * by the user, normally by pressing the 'Enter' key. The signal is + * emitted only if #ClutterText:activatable is set to %TRUE. + * + * Since: 1.0 + */ + text_signals[ACTIVATE] = + g_signal_new ("activate", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterTextClass, activate), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + binding_pool = clutter_binding_pool_get_for_class (klass); + + clutter_text_add_move_binding (binding_pool, "move-left", + CLUTTER_Left, + G_CALLBACK (clutter_text_real_move_left)); + clutter_text_add_move_binding (binding_pool, "move-left", + CLUTTER_KP_Left, + G_CALLBACK (clutter_text_real_move_left)); + clutter_text_add_move_binding (binding_pool, "move-right", + CLUTTER_Right, + G_CALLBACK (clutter_text_real_move_right)); + clutter_text_add_move_binding (binding_pool, "move-right", + CLUTTER_KP_Right, + G_CALLBACK (clutter_text_real_move_right)); + clutter_text_add_move_binding (binding_pool, "move-up", + CLUTTER_Up, + G_CALLBACK (clutter_text_real_move_up)); + clutter_text_add_move_binding (binding_pool, "move-up", + CLUTTER_KP_Up, + G_CALLBACK (clutter_text_real_move_up)); + clutter_text_add_move_binding (binding_pool, "move-down", + CLUTTER_Down, + G_CALLBACK (clutter_text_real_move_down)); + clutter_text_add_move_binding (binding_pool, "move-down", + CLUTTER_KP_Down, + G_CALLBACK (clutter_text_real_move_down)); + + clutter_text_add_move_binding (binding_pool, "line-start", + CLUTTER_Home, + G_CALLBACK (clutter_text_real_line_start)); + clutter_text_add_move_binding (binding_pool, "line-start", + CLUTTER_KP_Home, + G_CALLBACK (clutter_text_real_line_start)); + clutter_text_add_move_binding (binding_pool, "line-start", + CLUTTER_Begin, + G_CALLBACK (clutter_text_real_line_start)); + clutter_text_add_move_binding (binding_pool, "line-end", + CLUTTER_End, + G_CALLBACK (clutter_text_real_line_end)); + clutter_text_add_move_binding (binding_pool, "line-end", + CLUTTER_KP_End, + G_CALLBACK (clutter_text_real_line_end)); + + clutter_binding_pool_install_action (binding_pool, "select-all", + CLUTTER_a, CLUTTER_CONTROL_MASK, + G_CALLBACK (clutter_text_real_select_all), + NULL, NULL); + + clutter_binding_pool_install_action (binding_pool, "delete-next", + CLUTTER_Delete, 0, + G_CALLBACK (clutter_text_real_del_next), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "delete-next", + CLUTTER_KP_Delete, 0, + G_CALLBACK (clutter_text_real_del_next), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "delete-prev", + CLUTTER_BackSpace, 0, + G_CALLBACK (clutter_text_real_del_prev), + NULL, NULL); + + clutter_binding_pool_install_action (binding_pool, "activate", + CLUTTER_Return, 0, + G_CALLBACK (clutter_text_real_activate), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "activate", + CLUTTER_KP_Enter, 0, + G_CALLBACK (clutter_text_real_activate), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "activate", + CLUTTER_ISO_Enter, 0, + G_CALLBACK (clutter_text_real_activate), + NULL, NULL); +} + +static void +clutter_text_init (ClutterText *self) +{ + ClutterTextPrivate *priv; + const gchar *font_name; + int i; + + self->priv = priv = CLUTTER_TEXT_GET_PRIVATE (self); + + priv->alignment = PANGO_ALIGN_LEFT; + priv->wrap = FALSE; + priv->wrap_mode = PANGO_WRAP_WORD; + priv->ellipsize = PANGO_ELLIPSIZE_NONE; + priv->use_underline = FALSE; + priv->use_markup = FALSE; + priv->justify = FALSE; + + for (i = 0; i < N_CACHED_LAYOUTS; i++) + priv->cached_layouts[i].layout = NULL; + + /* default to "" so that clutter_text_get_text() will + * return a valid string and we can safely call strlen() + * or strcmp() on it + */ + priv->text = g_strdup (""); + + priv->text_color = default_text_color; + priv->cursor_color = default_cursor_color; + + /* get the default font name from the context */ + font_name = clutter_backend_get_font_name (clutter_get_default_backend ()); + priv->font_name = g_strdup (font_name); + priv->font_desc = pango_font_description_from_string (font_name); + + priv->position = -1; + priv->selection_bound = -1; + + priv->x_pos = -1; + priv->cursor_visible = TRUE; + priv->editable = FALSE; + priv->selectable = TRUE; + + priv->cursor_color_set = FALSE; + + priv->password_char = 0; + + priv->max_length = 0; + + priv->cursor_size = DEFAULT_CURSOR_SIZE; +} + +/** + * clutter_text_new: + * + * Creates a new #ClutterText actor. This actor can be used to + * display and edit text. + * + * Return value: the newly created #ClutterText actor + * + * Since: 1.0 + */ +ClutterActor * +clutter_text_new (void) +{ + return g_object_new (CLUTTER_TYPE_TEXT, NULL); +} + +/** + * clutter_text_new_full: + * @font_name: a string with a font description + * @text: the contents of the actor + * @color: the color to be used to render @text + * + * Creates a new #ClutterText actor, using @font_name as the font + * description; @text will be used to set the contents of the actor; + * and @color will be used as the color to render @text. + * + * This function is equivalent to calling clutter_text_new(), + * clutter_text_set_font_name(), clutter_text_set_text() and + * clutter_text_set_color(). + * + * Return value: the newly created #ClutterText actor + * + * Since: 1.0 + */ +ClutterActor * +clutter_text_new_full (const gchar *font_name, + const gchar *text, + const ClutterColor *color) +{ + return g_object_new (CLUTTER_TYPE_TEXT, + "font-name", font_name, + "text", text, + "color", color, + NULL); +} + +/** + * clutter_text_new_with_text: + * @font_name: a string with a font description + * @text: the contents of the actor + * + * Creates a new #ClutterText actor, using @font_name as the font + * description; @text will be used to set the contents of the actor. + * + * This function is equivalent to calling clutter_text_new(), + * clutter_text_set_font_name(), and clutter_text_set_text(). + * + * Return value: the newly created #ClutterText actor + * + * Since: 1.0 + */ +ClutterActor * +clutter_text_new_with_text (const gchar *font_name, + const gchar *text) +{ + return g_object_new (CLUTTER_TYPE_TEXT, + "font-name", font_name, + "text", text, + NULL); +} + +/** + * clutter_text_set_editable: + * @self: a #ClutterText + * @editable: whether the #ClutterText should be editable + * + * Sets whether the #ClutterText actor should be editable. + * + * An editable #ClutterText with key focus set using + * clutter_actor_grab_key_focus() or clutter_stage_take_key_focus() + * will receive key events and will update its contents accordingly. + * + * Since: 1.0 + */ +void +clutter_text_set_editable (ClutterText *self, + gboolean editable) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->editable != editable) + { + priv->editable = editable; + + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "editable"); + } +} + +/** + * clutter_text_get_editable: + * @self: a #ClutterText + * + * Retrieves whether a #ClutterText is editable or not. + * + * Return value: %TRUE if the actor is editable + * + * Since: 1.0 + */ +gboolean +clutter_text_get_editable (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); + + return self->priv->editable; +} + +/** + * clutter_text_set_selectable: + * @self: a #ClutterText + * @selectable: whether the #ClutterText actor should be selectable + * + * Sets whether a #ClutterText actor should be selectable. + * + * A selectable #ClutterText will allow selecting its contents using + * the pointer or the keyboard. + * + * Since: 1.0 + */ +void +clutter_text_set_selectable (ClutterText *self, + gboolean selectable) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->selectable != selectable) + { + priv->selectable = selectable; + + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "selectable"); + } +} + +/** + * clutter_text_get_selectable: + * @self: a #ClutterText + * + * Retrieves whether a #ClutterText is selectable or not. + * + * Return value: %TRUE if the actor is selectable + * + * Since: 1.0 + */ +gboolean +clutter_text_get_selectable (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE); + + return self->priv->selectable; +} + +/** + * clutter_text_set_activatable: + * @self: a #ClutterText + * @activatable: whether the #ClutterText actor should be activatable + * + * Sets whether a #ClutterText actor should be activatable. + * + * An activatable #ClutterText actor will emit the #ClutterText::activate + * signal whenever the 'Enter' (or 'Return') key is pressed; if it is not + * activatable, a new line will be appended to the current content. + * + * An activatable #ClutterText must also be set as editable using + * clutter_text_set_editable(). + * + * Since: 1.0 + */ +void +clutter_text_set_activatable (ClutterText *self, + gboolean activatable) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->activatable != activatable) + { + priv->activatable = activatable; + + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "activatable"); + } +} + +/** + * clutter_text_get_activatable: + * @self: a #ClutterText + * + * Retrieves whether a #ClutterText is activatable or not. + * + * Return value: %TRUE if the actor is activatable + * + * Since: 1.0 + */ +gboolean +clutter_text_get_activatable (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE); + + return self->priv->activatable; +} + +/** + * clutter_text_activate: + * @self: a #ClutterText + * + * Emits the #ClutterText::activate signal, if @self has been set + * as activatable using clutter_text_set_activatable(). + * + * This function can be used to emit the ::activate signal inside + * a #ClutterActor::captured-event or #ClutterActor::key-press-event + * signal handlers before the default signal handler for the + * #ClutterText is invoked. + * + * Return value: %TRUE if the ::activate signal has been emitted, + * and %FALSE otherwise + * + * Since: 1.0 + */ +gboolean +clutter_text_activate (ClutterText *self) +{ + ClutterTextPrivate *priv; + + g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); + + priv = self->priv; + + if (priv->activatable) + { + g_signal_emit (self, text_signals[ACTIVATE], 0); + return TRUE; + } + + return FALSE; +} + +/** + * clutter_text_set_cursor_visible: + * @self: a #ClutterText + * @cursor_visible: whether the cursor should be visible + * + * Sets whether the cursor of a #ClutterText actor should be + * visible or not. + * + * The color of the cursor will be the same as the text color + * unless clutter_text_set_cursor_color() has been called. + * + * The size of the cursor can be set using clutter_text_set_cursor_size(). + * + * The position of the cursor can be changed programmatically using + * clutter_text_set_cursor_position(). + * + * Since: 1.0 + */ +void +clutter_text_set_cursor_visible (ClutterText *self, + gboolean cursor_visible) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->cursor_visible != cursor_visible) + { + priv->cursor_visible = cursor_visible; + + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "cursor-visible"); + } +} + +/** + * clutter_text_get_cursor_visible: + * @self: a #ClutterText + * + * Retrieves whether the cursor of a #ClutterText actor is visible. + * + * Return value: %TRUE if the cursor is visible + * + * Since: 1.0 + */ +gboolean +clutter_text_get_cursor_visible (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE); + + return self->priv->cursor_visible; +} + +/** + * clutter_text_set_cursor_color: + * @self: a #ClutterText + * @color: the color of the cursor, or %NULL to unset it + * + * Sets the color of the cursor of a #ClutterText actor. + * + * If @color is %NULL, the cursor color will be the same as the + * text color. + * + * Since: 1.0 + */ +void +clutter_text_set_cursor_color (ClutterText *self, + const ClutterColor *color) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (color) + { + priv->cursor_color = *color; + priv->cursor_color_set = TRUE; + } + else + priv->cursor_color_set = FALSE; + + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "cursor-color"); + g_object_notify (G_OBJECT (self), "cursor-color-set"); +} + +/** + * clutter_text_get_cursor_color: + * @self: a #ClutterText + * @color: return location for a #ClutterColor + * + * Retrieves the color of the cursor of a #ClutterText actor. + * + * Since: 1.0 + */ +void +clutter_text_get_cursor_color (ClutterText *self, + ClutterColor *color) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + g_return_if_fail (color != NULL); + + priv = self->priv; + + *color = priv->cursor_color; +} + +/** + * clutter_text_set_selection: + * @self: a #ClutterText + * @start_pos: start of the selection, in characters + * @end_pos: end of the selection, in characters + * + * Selects the region of text between @start_pos and @end_pos. + * + * This function changes the position of the cursor to match + * @start_pos and the selection bound to match @end_pos. + * + * Since: 1.0 + */ +void +clutter_text_set_selection (ClutterText *self, + gssize start_pos, + gssize end_pos) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (end_pos < 0) + end_pos = priv->n_chars; + + start_pos = MIN (priv->n_chars, start_pos); + end_pos = MIN (priv->n_chars, end_pos); + + g_object_freeze_notify (G_OBJECT (self)); + + clutter_text_set_cursor_position (self, start_pos); + clutter_text_set_selection_bound (self, end_pos); + + g_object_thaw_notify (G_OBJECT (self)); +} + +/** + * clutter_text_get_selection: + * @self: a #ClutterText + * + * Retrieves the currently selected text. + * + * Return value: a newly allocated string containing the currently + * selected text, or %NULL. Use g_free() to free the returned + * string. + * + * Since: 1.0 + */ +gchar * +clutter_text_get_selection (ClutterText *self) +{ + ClutterTextPrivate *priv; + gchar *str; + gint len; + gint start_index, end_index; + gint start_offset, end_offset; + + g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); + + priv = self->priv; + + start_index = priv->position; + end_index = priv->selection_bound; + + if (end_index == start_index) + return g_strdup (""); + + if (end_index < start_index) + { + gint temp = start_index; + start_index = end_index; + end_index = temp; + } + + start_offset = offset_to_bytes (priv->text, start_index); + end_offset = offset_to_bytes (priv->text, end_index); + len = end_offset - start_offset; + + str = g_malloc (len + 1); + g_utf8_strncpy (str, priv->text + start_offset, end_index - start_index); + + return str; +} + +/** + * clutter_text_set_selection_bound: + * @self: a #ClutterText + * @selection_bound: the position of the end of the selection, in characters + * + * Sets the other end of the selection, starting from the current + * cursor position. + * + * If @selection_bound is -1, the selection unset. + * + * Since: 1.0 + */ +void +clutter_text_set_selection_bound (ClutterText *self, + gint selection_bound) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->selection_bound != selection_bound) + { + priv->selection_bound = selection_bound; + + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "selection-bound"); + } +} + +/** + * clutter_text_get_selection_bound: + * @self: a #ClutterText + * + * Retrieves the other end of the selection of a #ClutterText actor, + * in characters from the current cursor position. + * + * Return value: the position of the other end of the selection + * + * Since: 1.0 + */ +gint +clutter_text_get_selection_bound (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), -1); + + return self->priv->selection_bound; +} + +/** + * clutter_text_get_font_name: + * @self: a #ClutterText + * + * Retrieves the font name as set by clutter_text_set_font_name(). + * + * Return value: a string containing the font name. The returned + * string is owned by the #ClutterText actor and should not be + * modified or freed + * + * Since: 1.0 + */ +G_CONST_RETURN gchar * +clutter_text_get_font_name (ClutterText *text) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (text), NULL); + + return text->priv->font_name; +} + +/** + * clutter_text_set_font_name: + * @self: a #ClutterText + * @font_name: a font name, or %NULL to set the default font name + * + * Sets the font used by a #ClutterText. The @font_name string + * must either be %NULL, which means that the font name from the + * default #ClutterBackend will be used; or be something that can + * be parsed by the pango_font_description_from_string() function, + * like: + * + * |[ + * clutter_text_set_font_name (text, "Sans 10pt"); + * clutter_text_set_font_name (text, "Serif 16px"); + * clutter_text_set_font_name (text, "Helvetica 10"); + * ]| + * + * Since: 1.0 + */ +void +clutter_text_set_font_name (ClutterText *self, + const gchar *font_name) +{ + ClutterTextPrivate *priv; + PangoFontDescription *desc; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + /* get the default font name from the backend */ + if (!font_name || font_name[0] == '\0') + font_name = clutter_backend_get_font_name (clutter_get_default_backend ()); + + priv = self->priv; + + if (priv->font_name && strcmp (priv->font_name, font_name) == 0) + return; + + desc = pango_font_description_from_string (font_name); + if (!desc) + { + g_warning ("Attempting to create a PangoFontDescription for " + "font name `%s', but failed.", + font_name); + return; + } + + g_free (priv->font_name); + priv->font_name = g_strdup (font_name); + + if (priv->font_desc) + pango_font_description_free (priv->font_desc); + + priv->font_desc = desc; + + clutter_text_dirty_cache (self); + + if (priv->text && priv->text[0] != '\0') + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "font-name"); +} + +/** + * clutter_text_get_text: + * @self: a #ClutterText + * + * Retrieves a pointer to the current contents of a #ClutterText + * actor. + * + * If you need a copy of the contents for manipulating, either + * use g_strdup() on the returned string, or use: + * + * |[ + * copy = clutter_text_get_chars (text, 0, -1); + * ]| + * + * Which will return a newly allocated string. + * + * Return value: the contents of the actor. The returned string + * is owned by the #ClutterText actor and should never be + * modified or freed + * + * Since: 1.0 + */ +G_CONST_RETURN gchar * +clutter_text_get_text (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); + + return self->priv->text; +} + +/** + * clutter_text_set_text: + * @self: a #ClutterText + * @text: the text to set + * + * Sets the contents of a #ClutterText actor. The @text string + * must not be %NULL; to unset the current contents of the + * #ClutterText actor simply pass "" (an empty string). + * + * Since: 1.0 + */ +void +clutter_text_set_text (ClutterText *self, + const gchar *text) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + g_return_if_fail (text != NULL); + + priv = self->priv; + + if (priv->max_length > 0) + { + gint len = g_utf8_strlen (text, -1); + + if (len < priv->max_length) + { + g_free (priv->text); + + priv->text = g_strdup (text); + priv->n_bytes = strlen (text); + priv->n_chars = len; + } + else + { + gchar *n = g_malloc0 (priv->max_length + 1); + + g_free (priv->text); + + g_utf8_strncpy (n, text, priv->max_length); + + priv->text = n; + priv->n_bytes = strlen (n); + priv->n_chars = priv->max_length; + } + } + else + { + g_free (priv->text); + + priv->text = g_strdup (text); + priv->n_bytes = strlen (text); + priv->n_chars = g_utf8_strlen (text, -1); + } + + clutter_text_dirty_cache (self); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_signal_emit (self, text_signals[TEXT_CHANGED], 0); + + g_object_notify (G_OBJECT (self), "text"); +} + +/** + * clutter_text_get_layout: + * @self: a #ClutterText + * + * Retrieves the current #PangoLayout used by a #ClutterText actor. + * + * Return value: a #PangoLayout. The returned object is owned by + * the #ClutterText actor and should not be modified or freed + * + * Since: 1.0 + */ +PangoLayout * +clutter_text_get_layout (ClutterText *self) +{ + ClutterUnit width; + + g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); + + width = clutter_actor_get_widthu (CLUTTER_ACTOR (self)); + + return clutter_text_create_layout (self, width); +} + +/** + * clutter_text_set_color: + * @self: a #ClutterText + * @color: a #ClutterColor + * + * Sets the color of the contents of a #ClutterText actor. + * + * The overall opacity of the #ClutterText actor will be the + * result of the alpha value of @color and the composited + * opacity of the actor itself on the scenegraph, as returned + * by clutter_actor_get_paint_opacity(). + * + * Since: 1.0 + */ +void +clutter_text_set_color (ClutterText *self, + const ClutterColor *color) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + g_return_if_fail (color != NULL); + + priv = self->priv; + + priv->text_color = *color; + + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "color"); +} + +/** + * clutter_text_get_color: + * @self: a #ClutterText + * @color: return location for a #ClutterColor + * + * Retrieves the text color as set by clutter_text_get_color(). + * + * Since: 1.0 + */ +void +clutter_text_get_color (ClutterText *self, + ClutterColor *color) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + g_return_if_fail (color != NULL); + + priv = self->priv; + + *color = priv->text_color; +} + +/** + * clutter_text_set_ellipsize: + * @self: a #ClutterText + * @mode: a #PangoEllipsizeMode + * + * Sets the mode used to ellipsize (add an ellipsis: "...") to the + * text if there is not enough space to render the entire contents + * of a #ClutterText actor + * + * Since: 1.0 + */ +void +clutter_text_set_ellipsize (ClutterText *self, + PangoEllipsizeMode mode) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + g_return_if_fail (mode >= PANGO_ELLIPSIZE_NONE && + mode <= PANGO_ELLIPSIZE_END); + + priv = self->priv; + + if ((PangoEllipsizeMode) priv->ellipsize != mode) + { + priv->ellipsize = mode; + + clutter_text_dirty_cache (self); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "ellipsize"); + } +} + +/** + * clutter_text_get_ellipsize: + * @self: a #ClutterText + * + * Returns the ellipsizing position of a #ClutterText actor, as + * set by clutter_text_set_ellipsize(). + * + * Return value: #PangoEllipsizeMode + * + * Since: 1.0 + */ +PangoEllipsizeMode +clutter_text_get_ellipsize (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), PANGO_ELLIPSIZE_NONE); + + return self->priv->ellipsize; +} + +/** + * clutter_text_get_line_wrap: + * @self: a #ClutterText + * + * Retrieves the value set using clutter_text_set_line_wrap(). + * + * Return value: %TRUE if the #ClutterText actor should wrap + * its contents + * + * Since: 1.0 + */ +gboolean +clutter_text_get_line_wrap (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); + + return self->priv->wrap; +} + +/** + * clutter_text_set_line_wrap: + * @self: a #ClutterText + * @line_wrap: whether the contents should wrap + * + * Sets whether the contents of a #ClutterText actor should wrap, + * if they don't fit the size assigned to the actor. + * + * Since: 1.0 + */ +void +clutter_text_set_line_wrap (ClutterText *self, + gboolean line_wrap) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->wrap != line_wrap) + { + priv->wrap = line_wrap; + + clutter_text_dirty_cache (self); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "line-wrap"); + } +} + +/** + * clutter_text_set_line_wrap_mode: + * @self: a #ClutterText + * @wrap_mode: the line wrapping mode + * + * If line wrapping is enabled (see clutter_text_set_line_wrap()) this + * function controls how the line wrapping is performed. The default is + * %PANGO_WRAP_WORD which means wrap on word boundaries. + * + * Since: 1.0 + */ +void +clutter_text_set_line_wrap_mode (ClutterText *self, + PangoWrapMode wrap_mode) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->wrap_mode != wrap_mode) + { + priv->wrap_mode = wrap_mode; + + clutter_text_dirty_cache (self); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "line-wrap-mode"); + } +} + +/** + * clutter_text_get_line_wrap_mode: + * @self: a #ClutterText + * + * Retrieves the line wrap mode used by the #ClutterText actor. + * + * See clutter_text_set_line_wrap_mode (). + * + * Return value: the wrap mode used by the #ClutterText + * + * Since: 1.0 + */ +PangoWrapMode +clutter_text_get_line_wrap_mode (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), PANGO_WRAP_WORD); + + return self->priv->wrap_mode; +} + +/** + * clutter_text_set_attributes: + * @self: a #ClutterText + * @attrs: a #PangoAttrList or %NULL to unset the attributes + * + * Sets the attributes list that are going to be applied to the + * #ClutterText contents. The attributes set with this function + * will be ignored if the #ClutterText:use_markup property is + * set to %TRUE. + * + * The #ClutterText actor will take a reference on the #PangoAttrList + * passed to this function. + * + * Since: 1.0 + */ +void +clutter_text_set_attributes (ClutterText *self, + PangoAttrList *attrs) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (attrs) + pango_attr_list_ref (attrs); + + if (priv->attrs) + pango_attr_list_unref (priv->attrs); + + if (!priv->use_markup) + { + if (attrs) + pango_attr_list_ref (attrs); + + if (priv->effective_attrs) + pango_attr_list_unref (priv->effective_attrs); + + priv->effective_attrs = attrs; + } + + priv->attrs = attrs; + + clutter_text_dirty_cache (self); + + g_object_notify (G_OBJECT (self), "attributes"); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); +} + +/** + * clutter_text_get_attributes: + * @self: a #ClutterText + * + * Gets the attribute list that was set on the #ClutterText actor + * clutter_text_set_attributes(), if any. + * + * Return value: the attribute list, or %NULL if none was set. The + * returned value is owned by the #ClutterText and should not be + * unreferenced. + * + * Since: 1.0 + */ +PangoAttrList * +clutter_text_get_attributes (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); + + return self->priv->attrs; +} + +/** + * clutter_text_set_alignment: + * @self: a #ClutterText + * @alignment: A #PangoAlignment + * + * Sets text alignment of the #ClutterText actor. + * + * The alignment will only be used when the contents of the + * #ClutterText actor are enough to wrap, and the #ClutterText:line-wrap + * property is set to %TRUE. + * + * Since: 1.0 + */ +void +clutter_text_set_alignment (ClutterText *self, + PangoAlignment alignment) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->alignment != alignment) + { + priv->alignment = alignment; + + clutter_text_dirty_cache (self); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "alignment"); + } +} + +/** + * clutter_text_get_alignment: + * @self: a #ClutterText + * + * Retrieves the alignment of @self. + * + * Return value: a #PangoAlignment + * + * Since 1.0 + */ +PangoAlignment +clutter_text_get_alignment (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), PANGO_ALIGN_LEFT); + + return self->priv->alignment; +} + +/** + * clutter_text_set_use_markup: + * @self: a #ClutterText + * @setting: %TRUE if the text should be parsed for markup. + * + * Sets whether the contents of the #ClutterText actor contains markup + * in Pango's text markup language. + * + * Calling this function on an editable #ClutterText will not cause + * the actor to parse any markup. + * + * Since: 1.0 + */ +void +clutter_text_set_use_markup (ClutterText *self, + gboolean setting) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->use_markup != setting) + { + priv->use_markup = setting; + + clutter_text_dirty_cache (self); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "use-markup"); + } +} + +/** + * clutter_text_get_use_markup: + * @self: a #ClutterText + * + * Retrieves whether the contents of the #ClutterText actor should be + * parsed for the Pango text markup. + * + * Return value: %TRUE if the contents will be parsed for markup + * + * Since: 1.0 + */ +gboolean +clutter_text_get_use_markup (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); + + return self->priv->use_markup; +} + +/** + * clutter_text_set_justify: + * @self: a #ClutterText + * @justify: whether the text should be justified + * + * Sets whether the text of the #ClutterText actor should be justified + * on both margins. This setting is ignored if Clutter is compiled + * against Pango < 1.18. + * + * Since: 1.0 + */ +void +clutter_text_set_justify (ClutterText *self, + gboolean justify) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->justify != justify) + { + priv->justify = justify; + + clutter_text_dirty_cache (self); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "justify"); + } +} + +/** + * clutter_text_get_justify: + * @self: a #ClutterText + * + * Retrieves whether the #ClutterText actor should justify its contents + * on both margins. + * + * Return value: %TRUE if the text should be justified + * + * Since: 0.6 + */ +gboolean +clutter_text_get_justify (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); + + return self->priv->justify; +} + +/** + * clutter_text_get_cursor_position: + * @self: a #ClutterText + * + * Retrieves the cursor position. + * + * Return value: the cursor position, in characters + * + * Since: 1.0 + */ +gint +clutter_text_get_cursor_position (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), -1); + + return self->priv->position; +} + +/** + * clutter_text_set_cursor_position: + * @self: a #ClutterText + * @position: the new cursor position, in characters + * + * Sets the cursor of a #ClutterText actor at @position. + * + * The position is expressed in characters, not in bytes. + * + * Since: 1.0 + */ +void +clutter_text_set_cursor_position (ClutterText *self, + gint position) +{ + ClutterTextPrivate *priv; + gint len; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + len = priv->n_chars; + + if (position < 0 || position >= len) + priv->position = -1; + else + priv->position = position; + + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); +} + +/** + * clutter_text_set_cursor_size: + * @self: a #ClutterText + * @size: the size of the cursor, in pixels, or -1 to use the + * default value + * + * Sets the size of the cursor of a #ClutterText. The cursor + * will only be visible if the #ClutterText:cursor-visible property + * is set to %TRUE. + * + * Since: 1.0 + */ +void +clutter_text_set_cursor_size (ClutterText *self, + gint size) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->cursor_size != size) + { + if (size < 0) + size = DEFAULT_CURSOR_SIZE; + + priv->cursor_size = size; + + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "cursor-size"); + } +} + +/** + * clutter_text_get_cursor_size: + * @self: a #ClutterText + * + * Retrieves the size of the cursor of a #ClutterText actor. + * + * Return value: the size of the cursor, in pixels + * + * Since: 1.0 + */ +guint +clutter_text_get_cursor_size (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), DEFAULT_CURSOR_SIZE); + + return self->priv->cursor_size; +} + +/** + * clutter_text_set_password_char: + * @self: a #ClutterText + * @wc: a Unicode character, or 0 to unset the password character + * + * Sets the character to use in place of the actual text in a + * password text actor. + * + * If @wc is 0 the text will be displayed as it is entered in the + * #ClutterText actor. + * + * Since: 1.0 + */ +void +clutter_text_set_password_char (ClutterText *self, + gunichar wc) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->password_char != wc) + { + priv->password_char = wc; + + clutter_text_dirty_cache (self); + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "password-char"); + } +} + +/** + * clutter_text_get_password_char: + * @self: a #ClutterText + * + * Retrieves the character to use in place of the actual text + * as set by clutter_text_set_password_char(). + * + * Return value: a Unicode character or 0 if the password + * character is not set + * + * Since: 1.0 + */ +gunichar +clutter_text_get_password_char (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0); + + return self->priv->password_char; +} + +/** + * clutter_text_set_max_length: + * @self: a #ClutterText + * @max: the maximum number of characters allowed in the text actor; 0 + * to disable or -1 to set the length of the current string + * + * Sets the maximum allowed length of the contents of the actor. If the + * current contents are longer than the given length, then they will be + * truncated to fit. + * + * Since: 1.0 + */ +void +clutter_text_set_max_length (ClutterText *self, + gint max) +{ + ClutterTextPrivate *priv; + gchar *new = NULL; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->max_length != max) + { + if (max < 0) + max = priv->n_chars; + + priv->max_length = max; + + new = g_strdup (priv->text); + clutter_text_set_text (self, new); + g_free (new); + + g_object_notify (G_OBJECT (self), "max-length"); + } +} + +/** + * clutter_text_get_max_length: + * @self: a #ClutterText + * + * Gets the maximum length of text that can be set into a text actor. + * + * See clutter_text_set_max_length(). + * + * Return value: the maximum number of characters. + * + * Since: 1.0 + */ +gint +clutter_text_get_max_length (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0); + + return self->priv->max_length; +} + +/** + * clutter_text_insert_unichar: + * @self: a #ClutterText + * @wc: a Unicode character + * + * Inserts @wc at the current cursor position of a + * #ClutterText actor. + * + * Since: 1.0 + */ +void +clutter_text_insert_unichar (ClutterText *self, + gunichar wc) +{ + ClutterTextPrivate *priv; + GString *new = NULL; + glong pos; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + g_return_if_fail (g_unichar_validate (wc)); + + if (wc == 0) + return; + + priv = self->priv; + + new = g_string_new (priv->text); + + if (priv->text) + pos = offset_to_bytes (priv->text, priv->position); + else + pos = 0; + + new = g_string_insert_unichar (new, pos, wc); + + clutter_text_set_text (self, new->str); + + if (priv->position >= 0) + { + clutter_text_set_cursor_position (self, priv->position + 1); + clutter_text_set_selection_bound (self, priv->position); + } + + g_string_free (new, TRUE); +} + +/** + * clutter_text_insert_text: + * @self: a #ClutterText + * @text: the text to be inserted + * @position: the position of the insertion, or -1 + * + * Inserts @text into a #ClutterActor at the given position. + * + * If @position is a negative number, the text will be appended + * at the end of the current contents of the #ClutterText. + * + * The position is expressed in characters, not in bytes. + * + * Since: 1.0 + */ +void +clutter_text_insert_text (ClutterText *self, + const gchar *text, + gssize position) +{ + ClutterTextPrivate *priv; + GString *new = NULL; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + g_return_if_fail (text != NULL); + + priv = self->priv; + + new = g_string_new (priv->text); + new = g_string_insert (new, position, text); + + clutter_text_set_text (self, new->str); + + g_string_free (new, TRUE); +} + +/** + * clutter_text_delete_text: + * @self: a #ClutterText + * @start_pos: starting position + * @end_pos: ending position + * + * Deletes the text inside a #ClutterText actor between @start_pos + * and @end_pos. + * + * The starting and ending positions are expressed in characters, + * not in bytes. + * + * Since: 1.0 + */ +void +clutter_text_delete_text (ClutterText *self, + gssize start_pos, + gssize end_pos) +{ + ClutterTextPrivate *priv; + GString *new = NULL; + gint start_bytes; + gint end_bytes; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (!priv->text) + return; + + if (start_pos == 0) + start_bytes = 0; + else + start_bytes = offset_to_bytes (priv->text, start_pos); + + if (end_pos == -1) + end_bytes = offset_to_bytes (priv->text, priv->n_chars); + else + end_bytes = offset_to_bytes (priv->text, end_pos); + + new = g_string_new (priv->text); + new = g_string_erase (new, start_bytes, end_bytes - start_bytes); + + clutter_text_set_text (self, new->str); + + g_string_free (new, TRUE); +} + +/** + * clutter_text_delete_chars: + * @self: a #ClutterText + * @n_chars: the number of characters to delete + * + * Deletes @n_chars inside a #ClutterText actor, starting from the + * current cursor position. + * + * Since: 1.0 + */ +void +clutter_text_delete_chars (ClutterText *self, + guint n_chars) +{ + ClutterTextPrivate *priv; + GString *new = NULL; + gint len; + gint pos; + gint num_pos; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (!priv->text) + return; + + len = priv->n_chars; + new = g_string_new (priv->text); + + if (priv->position == -1) + { + num_pos = offset_to_bytes (priv->text, priv->n_chars - n_chars); + new = g_string_erase (new, num_pos, -1); + } + else + { + pos = offset_to_bytes (priv->text, priv->position - n_chars); + num_pos = offset_to_bytes (priv->text, priv->position); + new = g_string_erase (new, pos, num_pos - pos); + } + + clutter_text_set_text (self, new->str); + + if (priv->position > 0) + clutter_text_set_cursor_position (self, priv->position - n_chars); + + g_string_free (new, TRUE); + + g_object_notify (G_OBJECT (self), "text"); +} + +/** + * clutter_text_get_chars: + * @self: a #ClutterText + * @start_pos: start of text, in characters + * @end_pos: end of text, in characters + * + * Retrieves the contents of the #ClutterText actor between + * @start_pos and @end_pos. + * + * The positions are specified in characters, not in bytes. + * + * Return value: a newly allocated string with the contents of + * the text actor between the specified positions. Use g_free() + * to free the resources when done + * + * Since: 1.0 + */ +gchar * +clutter_text_get_chars (ClutterText *self, + gssize start_pos, + gssize end_pos) +{ + ClutterTextPrivate *priv; + gint start_index, end_index; + + g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); + + priv = self->priv; + + if (end_pos < 0) + end_pos = priv->n_chars; + + start_pos = MIN (priv->n_chars, start_pos); + end_pos = MIN (priv->n_chars, end_pos); + + start_index = g_utf8_offset_to_pointer (priv->text, start_pos) + - priv->text; + end_index = g_utf8_offset_to_pointer (priv->text, end_pos) + - priv->text; + + return g_strndup (priv->text + start_index, end_index - start_index); +} + +/** + * clutter_text_set_single_line_mode: + * @self: a #ClutterText + * @single_line: whether to enable single line mode + * + * Sets whether a #ClutterText actor should be in single line mode + * or not. + * + * A text actor in single line mode will not wrap text and will clip + * the the visible area to the predefined size. The contents of the + * text actor will scroll to display the end of the text if its length + * is bigger than the allocated width. + * + * When setting the single line mode the #ClutterText:activatable + * property is also set as a side effect. Instead of entering a new + * line character, the text actor will emit the #ClutterText::activate + * signal. + * + * Since: 1.0 + */ +void +clutter_text_set_single_line_mode (ClutterText *self, + gboolean single_line) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->single_line_mode != single_line) + { + g_object_freeze_notify (G_OBJECT (self)); + + priv->single_line_mode = single_line; + + if (priv->single_line_mode) + { + priv->activatable = TRUE; + + g_object_notify (G_OBJECT (self), "activatable"); + } + + clutter_text_dirty_cache (self); + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "single-line-mode"); + + g_object_thaw_notify (G_OBJECT (self)); + } +} + +/** + * clutter_text_get_single_line_mode: + * @self: a #ClutterText + * + * Retrieves whether the #ClutterText actor is in single line mode. + * + * Return value: %TRUE if the #ClutterText actor is in single line mode + * + * Since: 1.0 + */ +gboolean +clutter_text_get_single_line_mode (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); + + return self->priv->single_line_mode; +} diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h new file mode 100644 index 000000000..2dc4bfa04 --- /dev/null +++ b/clutter/clutter-text.h @@ -0,0 +1,198 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2008 Intel Corporation. + * + * Authored By: Øyvind Kolås + * Emmanuele Bassi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __CLUTTER_TEXT_H__ +#define __CLUTTER_TEXT_H__ + +#include +#include + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_TEXT (clutter_text_get_type ()) +#define CLUTTER_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_TEXT, ClutterText)) +#define CLUTTER_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_TEXT, ClutterTextClass)) +#define CLUTTER_IS_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_TEXT)) +#define CLUTTER_IS_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_TEXT)) +#define CLUTTER_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_TEXT, ClutterTextClass)) + +typedef struct _ClutterText ClutterText; +typedef struct _ClutterTextPrivate ClutterTextPrivate; +typedef struct _ClutterTextClass ClutterTextClass; + +/** + * ClutterText: + * + * The #ClutterText struct contains only private data. + * + * Since: 1.0 + */ +struct _ClutterText +{ + /*< private >*/ + ClutterActor parent_instance; + + ClutterTextPrivate *priv; +}; + +/** + * ClutterTextClass: + * @text_changed: class handler for the #ClutterText::text-changed signal + * @activate: class handler for the #ClutterText::activate signal + * @cursor_event: class handler for the #ClutterText::cursor_event signal + * + * The #ClutterTextClass struct contains only private data. + * + * Since: 1.0 + */ +struct _ClutterTextClass +{ + /*< private >*/ + ClutterActorClass parent_class; + + /*< public >*/ + /* signals, not vfuncs */ + void (* text_changed) (ClutterText *self); + void (* activate) (ClutterText *self); + void (* cursor_event) (ClutterText *self, + const ClutterGeometry *geometry); + + /*< private >*/ + /* padding for future expansion */ + void (* _clutter_reserved1) (void); + void (* _clutter_reserved2) (void); + void (* _clutter_reserved3) (void); + void (* _clutter_reserved4) (void); + void (* _clutter_reserved5) (void); + void (* _clutter_reserved6) (void); + void (* _clutter_reserved7) (void); + void (* _clutter_reserved8) (void); +}; + +GType clutter_text_get_type (void) G_GNUC_CONST; + +ClutterActor * clutter_text_new (void); +ClutterActor * clutter_text_new_full (const gchar *font_name, + const gchar *text, + const ClutterColor *color); +ClutterActor * clutter_text_new_with_text (const gchar *font_name, + const gchar *text); + +G_CONST_RETURN gchar *clutter_text_get_text (ClutterText *self); +void clutter_text_set_text (ClutterText *self, + const gchar *text); +PangoLayout * clutter_text_get_layout (ClutterText *self); +void clutter_text_set_color (ClutterText *self, + const ClutterColor *color); +void clutter_text_get_color (ClutterText *self, + ClutterColor *color); +void clutter_text_set_font_name (ClutterText *self, + const gchar *font_name); +G_CONST_RETURN gchar *clutter_text_get_font_name (ClutterText *self); + +void clutter_text_set_ellipsize (ClutterText *self, + PangoEllipsizeMode mode); +PangoEllipsizeMode clutter_text_get_ellipsize (ClutterText *self); +void clutter_text_set_line_wrap (ClutterText *self, + gboolean line_wrap); +gboolean clutter_text_get_line_wrap (ClutterText *self); +void clutter_text_set_line_wrap_mode (ClutterText *self, + PangoWrapMode wrap_mode); +PangoWrapMode clutter_text_get_line_wrap_mode (ClutterText *self); +PangoLayout * clutter_text_get_layout (ClutterText *self); +void clutter_text_set_attributes (ClutterText *self, + PangoAttrList *attrs); +PangoAttrList * clutter_text_get_attributes (ClutterText *self); +void clutter_text_set_use_markup (ClutterText *self, + gboolean setting); +gboolean clutter_text_get_use_markup (ClutterText *self); +void clutter_text_set_alignment (ClutterText *self, + PangoAlignment alignment); +PangoAlignment clutter_text_get_alignment (ClutterText *self); +void clutter_text_set_justify (ClutterText *self, + gboolean justify); +gboolean clutter_text_get_justify (ClutterText *self); + +void clutter_text_insert_unichar (ClutterText *self, + gunichar wc); +void clutter_text_delete_chars (ClutterText *self, + guint n_chars); +void clutter_text_insert_text (ClutterText *self, + const gchar *text, + gssize position); +void clutter_text_delete_text (ClutterText *self, + gssize start_pos, + gssize end_pos); +gchar * clutter_text_get_chars (ClutterText *self, + gssize start_pos, + gssize end_pos); +void clutter_text_set_editable (ClutterText *self, + gboolean editable); +gboolean clutter_text_get_editable (ClutterText *self); +void clutter_text_set_activatable (ClutterText *self, + gboolean activatable); +gboolean clutter_text_get_activatable (ClutterText *self); + +gint clutter_text_get_cursor_position (ClutterText *self); +void clutter_text_set_cursor_position (ClutterText *self, + gint position); +void clutter_text_set_cursor_visible (ClutterText *self, + gboolean cursor_visible); +gboolean clutter_text_get_cursor_visible (ClutterText *self); +void clutter_text_set_cursor_color (ClutterText *self, + const ClutterColor *color); +void clutter_text_get_cursor_color (ClutterText *self, + ClutterColor *color); +void clutter_text_set_cursor_size (ClutterText *self, + gint size); +guint clutter_text_get_cursor_size (ClutterText *self); +void clutter_text_set_selectable (ClutterText *self, + gboolean selectable); +gboolean clutter_text_get_selectable (ClutterText *self); +void clutter_text_set_selection_bound (ClutterText *self, + gint selection_bound); +gint clutter_text_get_selection_bound (ClutterText *self); +void clutter_text_set_selection (ClutterText *self, + gssize start_pos, + gssize end_pos); +gchar * clutter_text_get_selection (ClutterText *self); +void clutter_text_set_password_char (ClutterText *self, + gunichar wc); +gunichar clutter_text_get_password_char (ClutterText *self); +void clutter_text_set_max_length (ClutterText *self, + gint max); +gint clutter_text_get_max_length (ClutterText *self); +void clutter_text_set_single_line_mode (ClutterText *self, + gboolean single_line); +gboolean clutter_text_get_single_line_mode (ClutterText *self); + +gboolean clutter_text_activate (ClutterText *self); + +G_END_DECLS + +#endif /* __CLUTTER_TEXT_H__ */ diff --git a/clutter/clutter-texture.c b/clutter/clutter-texture.c index dd95fc0a1..086a5134c 100644 --- a/clutter/clutter-texture.c +++ b/clutter/clutter-texture.c @@ -252,8 +252,8 @@ clutter_texture_realize (ClutterActor *actor) if (priv->texture != COGL_INVALID_HANDLE) cogl_texture_unref (priv->texture); - priv->texture - = cogl_texture_new_with_size + priv->texture + = cogl_texture_new_with_size (priv->width, priv->height, priv->no_slice ? -1 : priv->max_tile_waste, @@ -376,12 +376,12 @@ clutter_texture_get_preferred_height (ClutterActor *self, { /* Set the natural height so as to preserve the aspect ratio */ ClutterFixed ratio, width; - + ratio = COGL_FIXED_DIV (COGL_FIXED_FROM_INT (priv->height), COGL_FIXED_FROM_INT (priv->width)); width = CLUTTER_UNITS_TO_FIXED (for_width); - + *natural_height_p = CLUTTER_UNITS_FROM_FIXED (COGL_FIXED_MUL (ratio, width)); } @@ -417,7 +417,8 @@ clutter_texture_set_fbo_projection (ClutterActor *self) ClutterTexturePrivate *priv = CLUTTER_TEXTURE (self)->priv; ClutterVertex verts[4]; ClutterFixed viewport[4]; - ClutterFixed x_min, x_max, y_min, y_max; + ClutterUnit x_min, x_max, y_min, y_max; + ClutterFixed tx_min, tx_max, ty_min, ty_max; ClutterPerspective perspective; ClutterStage *stage; ClutterFixed tan_angle, near_size; @@ -450,21 +451,26 @@ clutter_texture_set_fbo_projection (ClutterActor *self) /* Convert the coordinates back to [-1,1] range */ cogl_get_viewport (viewport); - x_min = COGL_FIXED_DIV (x_min, viewport[2]) * 2 - COGL_FIXED_1; - x_max = COGL_FIXED_DIV (x_max, viewport[2]) * 2 - COGL_FIXED_1; - y_min = COGL_FIXED_DIV (y_min, viewport[3]) * 2 - COGL_FIXED_1; - y_max = COGL_FIXED_DIV (y_max, viewport[3]) * 2 - COGL_FIXED_1; + + tx_min = COGL_FIXED_DIV (CLUTTER_UNITS_TO_FIXED (x_min), viewport[2]) + * 2 - COGL_FIXED_1; + tx_max = COGL_FIXED_DIV (CLUTTER_UNITS_TO_FIXED (x_max), viewport[2]) + * 2 - COGL_FIXED_1; + ty_min = COGL_FIXED_DIV (CLUTTER_UNITS_TO_FIXED (y_min), viewport[3]) + * 2 - COGL_FIXED_1; + ty_max = COGL_FIXED_DIV (CLUTTER_UNITS_TO_FIXED (y_max), viewport[3]) + * 2 - COGL_FIXED_1; /* Set up a projection matrix so that the actor will be projected as if it was drawn at its original location */ - tan_angle = clutter_tani (CLUTTER_ANGLE_FROM_DEGX (perspective.fovy / 2)); + tan_angle = cogl_angle_tan (COGL_ANGLE_FROM_DEGX (perspective.fovy / 2)); near_size = COGL_FIXED_MUL (perspective.z_near, tan_angle); - cogl_frustum (COGL_FIXED_MUL (x_min, near_size), - COGL_FIXED_MUL (x_max, near_size), - COGL_FIXED_MUL (-y_min, near_size), - COGL_FIXED_MUL (-y_max, near_size), - perspective.z_near, perspective.z_far); + cogl_frustum (COGL_FIXED_MUL (tx_min, near_size), + COGL_FIXED_MUL (tx_max, near_size), + COGL_FIXED_MUL (-ty_min, near_size), + COGL_FIXED_MUL (-ty_max, near_size), + perspective.z_near, perspective.z_far); } static void @@ -544,8 +550,10 @@ clutter_texture_paint (ClutterActor *self) /* Restore the perspective matrix using cogl_perspective so that the inverse matrix will be right */ - cogl_perspective (perspective.fovy, perspective.aspect, - perspective.z_near, perspective.z_far); + cogl_perspective (perspective.fovy, + perspective.aspect, + perspective.z_near, + perspective.z_far); /* If there is a shader on top of the shader stack, turn it back on. */ if (shader) @@ -588,9 +596,9 @@ clutter_texture_paint (ClutterActor *self) /* Paint will have translated us */ cogl_texture_rectangle (priv->texture, 0, 0, - COGL_FIXED_FROM_INT (x_2 - x_1), - COGL_FIXED_FROM_INT (y_2 - y_1), - 0, 0, t_w, t_h); + COGL_FIXED_FROM_INT (x_2 - x_1), + COGL_FIXED_FROM_INT (y_2 - y_1), + 0, 0, t_w, t_h); } static void @@ -938,7 +946,7 @@ clutter_scriptable_iface_init (ClutterScriptableIface *iface) parent_scriptable_iface = g_type_interface_peek_parent (iface); if (!parent_scriptable_iface) - parent_scriptable_iface = g_type_default_interface_peek + parent_scriptable_iface = g_type_default_interface_peek (CLUTTER_TYPE_SCRIPTABLE); iface->set_custom_property = clutter_texture_set_custom_property; @@ -1028,7 +1036,7 @@ clutter_texture_load_from_local_data (ClutterTexture *texture) priv->local_data_rowstride, priv->local_data_has_alpha ? 4: 3, 0, NULL); - + g_free (priv->local_data); priv->local_data = NULL; } @@ -1136,7 +1144,7 @@ clutter_texture_set_from_data (ClutterTexture *texture, priv = texture->priv; - if ((new_texture = cogl_texture_new_from_data + if ((new_texture = cogl_texture_new_from_data (width, height, priv->no_slice ? -1 : priv->max_tile_waste, priv->filter_quality == CLUTTER_TEXTURE_QUALITY_HIGH, @@ -1171,7 +1179,7 @@ clutter_texture_set_from_data (ClutterTexture *texture, * @width: Width in pixels of image data. * @height: Height in pixels of image data * @rowstride: Distance in bytes between row starts. - * @bpp: bytes per pixel (Currently only 3 and 4 supported, + * @bpp: bytes per pixel (Currently only 3 and 4 supported, * depending on @has_alpha) * @flags: #ClutterTextureFlags * @error: return location for a #GError, or %NULL. @@ -1318,7 +1326,7 @@ clutter_texture_set_from_file (ClutterTexture *texture, g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - if ((new_texture = cogl_texture_new_from_file + if ((new_texture = cogl_texture_new_from_file (filename, priv->no_slice ? -1 : priv->max_tile_waste, priv->filter_quality == CLUTTER_TEXTURE_QUALITY_HIGH, @@ -1577,7 +1585,7 @@ clutter_texture_get_base_size (ClutterTexture *texture, * @width: Width in pixels of region to update. * @height: Height in pixels of region to update. * @rowstride: Distance in bytes between row starts on source buffer. - * @bpp: bytes per pixel (Currently only 3 and 4 supported, + * @bpp: bytes per pixel (Currently only 3 and 4 supported, * depending on @has_alpha) * @flags: #ClutterTextureFlags * @error: return location for a #GError, or %NULL @@ -1736,7 +1744,7 @@ on_fbo_parent_change (ClutterActor *actor, * * Note this function is intented as a utility call for uniformly applying * shaders to groups and other potential visual effects. It requires that - * the %CLUTTER_FEATURE_OFFSCREEN feature is supported by the current backend + * the %CLUTTER_FEATURE_OFFSCREEN feature is supported by the current backend * and the target system. * * Some tips on usage: @@ -1829,7 +1837,7 @@ clutter_texture_new_from_actor (ClutterActor *actor) return NULL; /* Hopefully now were good.. */ - texture = g_object_new (CLUTTER_TYPE_TEXTURE, + texture = g_object_new (CLUTTER_TYPE_TEXTURE, "disable-slicing", TRUE, NULL); diff --git a/clutter/clutter-texture.h b/clutter/clutter-texture.h index 84f6889b8..e15f798b1 100644 --- a/clutter/clutter-texture.h +++ b/clutter/clutter-texture.h @@ -80,8 +80,8 @@ struct _ClutterTextureClass { ClutterActorClass parent_class; - void (*size_change) (ClutterTexture *texture, - gint width, + void (*size_change) (ClutterTexture *texture, + gint width, gint height); void (*pixbuf_change) (ClutterTexture *texture); diff --git a/clutter/clutter.h b/clutter/clutter.h index 15208ce17..817dbef6c 100644 --- a/clutter/clutter.h +++ b/clutter/clutter.h @@ -47,14 +47,12 @@ #include "clutter-container.h" #include "clutter-deprecated.h" #include "clutter-effect.h" -#include "clutter-entry.h" #include "clutter-event.h" #include "clutter-feature.h" #include "clutter-frame-source.h" #include "clutter-group.h" #include "clutter-interval.h" #include "clutter-keysyms.h" -#include "clutter-label.h" #include "clutter-list-model.h" #include "clutter-main.h" #include "clutter-media.h" @@ -69,6 +67,7 @@ #include "clutter-stage.h" #include "clutter-stage-manager.h" #include "clutter-texture.h" +#include "clutter-text.h" #include "clutter-timeline.h" #include "clutter-timeout-pool.h" #include "clutter-types.h" diff --git a/clutter/cogl/cogl-texture.h b/clutter/cogl/cogl-texture.h index 117a12b14..3ca0d8137 100644 --- a/clutter/cogl/cogl-texture.h +++ b/clutter/cogl/cogl-texture.h @@ -148,7 +148,7 @@ gboolean cogl_is_texture (CoglHandle handle); /** * cogl_texture_get_width: * @handle: a #CoglHandle for a texture. - * + * * Query the width of a cogl texture. * * Returns: the width of the GPU side texture in pixels: @@ -158,7 +158,7 @@ guint cogl_texture_get_width (CoglHandle handle); /** * cogl_texture_get_height: * @handle: a #CoglHandle for a texture. - * + * * Query the height of a cogl texture. * * Returns: the height of the GPU side texture in pixels: @@ -168,7 +168,7 @@ guint cogl_texture_get_height (CoglHandle handle); /** * cogl_texture_get_format: * @handle: a #CoglHandle for a texture. - * + * * Query the #CoglPixelFormat of a cogl texture. * * Returns: the #CoglPixelFormat of the GPU side texture. @@ -179,7 +179,7 @@ CoglPixelFormat cogl_texture_get_format (CoglHandle handle); /** * cogl_texture_get_rowstride: * @handle: a #CoglHandle for a texture. - * + * * Query the rowstride of a cogl texture. * * Returns: the offset in bytes between each consequetive row of pixels. @@ -189,7 +189,7 @@ guint cogl_texture_get_rowstride (CoglHandle handle); /** * cogl_texture_get_max_waste: * @handle: a #CoglHandle for a texture. - * + * * Query the maximum wasted (unused) pixels in one dimension of a GPU side * texture. * @@ -200,7 +200,7 @@ gint cogl_texture_get_max_waste (CoglHandle handle); /** * cogl_texture_get_min_filter: * @handle: a #CoglHandle for a texture. - * + * * Query the currently set downscaling filter for a cogl texture. * * Returns: the current downscaling filter for a cogl texture. @@ -210,7 +210,7 @@ COGLenum cogl_texture_get_min_filter (CoglHandle handle); /** * cogl_texture_get_mag_filter: * @handle: a #CoglHandle for a texture. - * + * * Query the currently set downscaling filter for a cogl texture. * * Returns: the current downscaling filter for a cogl texture. @@ -220,7 +220,7 @@ COGLenum cogl_texture_get_mag_filter (CoglHandle handle); /** * cogl_texture_is_sliced: * @handle: a #CoglHandle for a texture. - * + * * Query if a texture is sliced (stored as multiple GPU side tecture * objects). * @@ -235,7 +235,7 @@ gboolean cogl_texture_is_sliced (CoglHandle handle); * @out_gl_handle: pointer to return location for the textures GL handle, or * NULL. * @out_gl_target: pointer to return location for the GL target type, or NULL. - * + * * Query the GL handles for a GPU side texture through it's #CoglHandle, * if the texture is spliced the data for the first sub texture will be * queried. @@ -255,7 +255,7 @@ gboolean cogl_texture_get_gl_texture (CoglHandle handle, * specified. * @data: memory location to write contents of buffer, or %NULL if we're * only querying the data size through the return value. - * + * * Copy the pixel data from a cogl texture to system memory. * * Returns: the size of the texture data in bytes (or 0 if the texture @@ -271,7 +271,7 @@ gint cogl_texture_get_data (CoglHandle handle, * @handle: a #CoglHandle. * @min_filter: the filter used when scaling the texture down. * @mag_filter: the filter used when magnifying the texture. - * + * * Changes the decimation and interpolation filters used when the texture is * drawn at other scales than 100%. */ @@ -297,7 +297,7 @@ void cogl_texture_set_filters (CoglHandle handle, * @data: the actual pixel data. * * Sets the pixels in a rectangular subregion of @handle from an in-memory - * buffer containing pixel data. + * buffer containing pixel data. * * Returns: %TRUE if the subregion upload was successful, otherwise %FALSE. */ @@ -385,6 +385,29 @@ void cogl_texture_polygon (CoglHandle handle, CoglTextureVertex *vertices, gboolean use_color); +/** + * cogl_texture_multiple_rectangles: + * @handle: a @CoglHandle. + * @verts: an array of vertices + * @n_rects: number of rectangles to draw + * + * Draws a series of rectangles in the same way that + * cogl_texture_rectangle() does. In some situations it can give a + * significant performance boost to use this function rather than + * calling cogl_texture_rectangle() separately for each rectangle. + * + * @verts should point to an array of #CoglFixeds with + * @n_rects * 8 elements. Each group of 8 values corresponds to the + * parameters x1, y1, x2, y2, tx1, ty1, tx2 and ty2 and have the same + * meaning as in cogl_texture_rectangle(). + * + * Since: 1.0 + */ +void cogl_texture_multiple_rectangles + (CoglHandle handle, + const CoglFixed *verts, + guint n_rects); + G_END_DECLS #endif /* __COGL_TEXTURE_H__ */ diff --git a/clutter/cogl/gl/cogl-context.c b/clutter/cogl/gl/cogl-context.c index d75086fbc..e2421f4ff 100644 --- a/clutter/cogl/gl/cogl-context.c +++ b/clutter/cogl/gl/cogl-context.c @@ -41,27 +41,29 @@ cogl_create_context () { if (_context != NULL) return FALSE; - + /* Allocate context memory */ _context = (CoglContext*) g_malloc (sizeof (CoglContext)); - + /* Init default values */ _context->feature_flags = 0; _context->features_cached = FALSE; - + _context->enable_flags = 0; _context->color_alpha = 255; - + _context->path_nodes = g_array_new (FALSE, FALSE, sizeof (CoglPathNode)); _context->last_path = 0; - + _context->texture_handles = NULL; - _context->texture_vertices_size = 0; - _context->texture_vertices = NULL; - + _context->texture_vertices = g_array_new (FALSE, FALSE, + sizeof (CoglTextureGLVertex)); + _context->texture_indices = g_array_new (FALSE, FALSE, + sizeof (GLushort)); + _context->fbo_handles = NULL; _context->draw_buffer = COGL_WINDOW_BUFFER; - + _context->blend_src_factor = CGL_SRC_ALPHA; _context->blend_dst_factor = CGL_ONE_MINUS_SRC_ALPHA; @@ -70,7 +72,7 @@ cogl_create_context () _context->program_handles = NULL; _context->mesh_handles = NULL; - + _context->pf_glGenRenderbuffersEXT = NULL; _context->pf_glBindRenderbufferEXT = NULL; _context->pf_glRenderbufferStorageEXT = NULL; @@ -82,7 +84,7 @@ cogl_create_context () _context->pf_glDeleteFramebuffersEXT = NULL; _context->pf_glBlitFramebufferEXT = NULL; _context->pf_glRenderbufferStorageMultisampleEXT = NULL; - + _context->pf_glCreateProgramObjectARB = NULL; _context->pf_glCreateShaderObjectARB = NULL; _context->pf_glShaderSourceARB = NULL; @@ -115,7 +117,7 @@ cogl_create_context () _context->pf_glUniformMatrix4fvARB = NULL; _context->pf_glDrawRangeElements = NULL; - + /* Init OpenGL state */ GE( glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE) ); GE( glColorMask (TRUE, TRUE, TRUE, FALSE) ); @@ -124,7 +126,7 @@ cogl_create_context () /* Initialise the clip stack */ _cogl_clip_stack_state_init (); - + return TRUE; } @@ -147,7 +149,12 @@ cogl_destroy_context () g_array_free (_context->shader_handles, TRUE); if (_context->program_handles) g_array_free (_context->program_handles, TRUE); - + + if (_context->texture_vertices) + g_array_free (_context->texture_vertices, TRUE); + if (_context->texture_indices) + g_array_free (_context->texture_indices, TRUE); + g_free (_context); } @@ -157,6 +164,6 @@ _cogl_context_get_default () /* Create if doesn't exist yet */ if (_context == NULL) cogl_create_context (); - + return _context; } diff --git a/clutter/cogl/gl/cogl-context.h b/clutter/cogl/gl/cogl-context.h index 8a2fc877d..d49330763 100644 --- a/clutter/cogl/gl/cogl-context.h +++ b/clutter/cogl/gl/cogl-context.h @@ -41,7 +41,7 @@ typedef struct /* Features cache */ CoglFeatureFlags feature_flags; gboolean features_cached; - + /* Enable cache */ gulong enable_flags; guint8 color_alpha; @@ -49,7 +49,7 @@ typedef struct COGLenum blend_dst_factor; gboolean enable_backface_culling; - + /* Primitives */ CoglFixedVec2 path_start; CoglFixedVec2 path_pen; @@ -60,12 +60,18 @@ typedef struct /* Cache of inverse projection matrix */ GLfloat inverse_projection[16]; - + /* Textures */ GArray *texture_handles; - CoglTextureGLVertex *texture_vertices; - gulong texture_vertices_size; - + GArray *texture_vertices; + GArray *texture_indices; + /* The gl texture number that the above vertices apply to. This to + detect when a different slice is encountered so that the vertices + can be flushed */ + GLuint texture_current; + GLenum texture_target; + GLenum texture_wrap_mode; + /* Framebuffer objects */ GArray *fbo_handles; CoglBufferTarget draw_buffer; @@ -78,10 +84,10 @@ typedef struct /* Clip stack */ CoglClipStackState clip; - + /* Mesh */ GArray *mesh_handles; - + /* Relying on glext.h to define these */ COGL_PFNGLGENRENDERBUFFERSEXTPROC pf_glGenRenderbuffersEXT; COGL_PFNGLDELETERENDERBUFFERSEXTPROC pf_glDeleteRenderbuffersEXT; @@ -95,7 +101,7 @@ typedef struct COGL_PFNGLDELETEFRAMEBUFFERSEXTPROC pf_glDeleteFramebuffersEXT; COGL_PFNGLBLITFRAMEBUFFEREXTPROC pf_glBlitFramebufferEXT; COGL_PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC pf_glRenderbufferStorageMultisampleEXT; - + COGL_PFNGLCREATEPROGRAMOBJECTARBPROC pf_glCreateProgramObjectARB; COGL_PFNGLCREATESHADEROBJECTARBPROC pf_glCreateShaderObjectARB; COGL_PFNGLSHADERSOURCEARBPROC pf_glShaderSourceARB; @@ -151,6 +157,6 @@ _cogl_context_get_default (); CoglContext *ctxvar = _cogl_context_get_default (); \ if (ctxvar == NULL) return retval; -#define NO_RETVAL +#define NO_RETVAL #endif /* __COGL_CONTEXT_H */ diff --git a/clutter/cogl/gl/cogl-texture.c b/clutter/cogl/gl/cogl-texture.c index 5a202ae6b..a27a8b253 100644 --- a/clutter/cogl/gl/cogl-texture.c +++ b/clutter/cogl/gl/cogl-texture.c @@ -76,7 +76,7 @@ _cogl_texture_bitmap_free (CoglTexture *tex) { if (tex->bitmap.data != NULL && tex->bitmap_owner) g_free (tex->bitmap.data); - + tex->bitmap.data = NULL; tex->bitmap_owner = FALSE; } @@ -87,7 +87,7 @@ _cogl_texture_bitmap_swap (CoglTexture *tex, { if (tex->bitmap.data != NULL && tex->bitmap_owner) g_free (tex->bitmap.data); - + tex->bitmap = *new_bitmap; tex->bitmap_owner = TRUE; } @@ -99,11 +99,11 @@ _cogl_span_iter_update (CoglSpanIter *iter) iter->span = &g_array_index (iter->array, CoglTexSliceSpan, iter->index); - + /* Offset next position by span size */ iter->next_pos = iter->pos + COGL_FIXED_FROM_INT (iter->span->size - iter->span->waste); - + /* Check if span intersects the area to cover */ if (iter->next_pos <= iter->cover_start || iter->pos >= iter->cover_end) @@ -112,15 +112,15 @@ _cogl_span_iter_update (CoglSpanIter *iter) iter->intersects = FALSE; return; } - + iter->intersects = TRUE; - + /* Clip start position to coverage area */ if (iter->pos < iter->cover_start) iter->intersect_start = iter->cover_start; else iter->intersect_start = iter->pos; - + /* Clip end position to coverage area */ if (iter->next_pos > iter->cover_end) iter->intersect_end = iter->cover_end; @@ -143,7 +143,7 @@ _cogl_span_iter_begin (CoglSpanIter *iter, iter->cover_start = cover_start; iter->cover_end = cover_end; iter->pos = iter->origin; - + /* Update intersection */ _cogl_span_iter_update (iter); } @@ -153,10 +153,10 @@ _cogl_span_iter_next (CoglSpanIter *iter) { /* Move current position */ iter->pos = iter->next_pos; - + /* Pick next slice (wrap when last reached) */ iter->index = (iter->index + 1) % iter->array->len; - + /* Update intersection */ _cogl_span_iter_update (iter); } @@ -175,10 +175,10 @@ prep_for_gl_pixels_upload (gint pixels_rowstride, gint pixels_bpp) { GE( glPixelStorei (GL_UNPACK_ROW_LENGTH, pixels_rowstride / pixels_bpp) ); - + GE( glPixelStorei (GL_UNPACK_SKIP_PIXELS, pixels_src_x) ); GE( glPixelStorei (GL_UNPACK_SKIP_ROWS, pixels_src_y) ); - + if (!(pixels_rowstride & 0x7)) GE( glPixelStorei (GL_UNPACK_ALIGNMENT, 8) ); else if (!(pixels_rowstride & 0x3)) @@ -240,7 +240,7 @@ _cogl_texture_upload_to_gl (CoglTexture *tex) gint bpp; gint x,y; guchar *waste_buf; - + bpp = _cogl_get_format_bpp (tex->bitmap.format); waste_buf = _cogl_texture_allocate_waste_buffer (tex); @@ -249,22 +249,22 @@ _cogl_texture_upload_to_gl (CoglTexture *tex) for (y = 0; y < tex->slice_y_spans->len; ++y) { y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, y); - + /* Iterate horizontal slices */ for (x = 0; x < tex->slice_x_spans->len; ++x) { x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, x); - + /* Pick the gl texture object handle */ gl_handle = g_array_index (tex->slice_gl_handles, GLuint, y * tex->slice_x_spans->len + x); - + /* Setup gl alignment to match rowstride and top-left corner */ prep_for_gl_pixels_upload (tex->bitmap.rowstride, x_span->start, y_span->start, bpp); - + /* Upload new image data */ GE( glBindTexture (tex->gl_target, gl_handle) ); @@ -364,27 +364,27 @@ _cogl_texture_download_from_gl (CoglTexture *tex, gint bpp; gint x,y; CoglBitmap slice_bmp; - + bpp = _cogl_get_format_bpp (target_bmp->format); - + /* Iterate vertical slices */ for (y = 0; y < tex->slice_y_spans->len; ++y) { y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, y); - + /* Iterate horizontal slices */ for (x = 0; x < tex->slice_x_spans->len; ++x) { /*if (x != 0 || y != 1) continue;*/ x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, x); - + /* Pick the gl texture object handle */ gl_handle = g_array_index (tex->slice_gl_handles, GLuint, y * tex->slice_x_spans->len + x); - + /* If there's any waste we need to copy manually (no glGetTexSubImage) */ - + if (y_span->waste != 0 || x_span->waste != 0) { /* Setup temp bitmap for slice subregion */ @@ -394,19 +394,19 @@ _cogl_texture_download_from_gl (CoglTexture *tex, slice_bmp.rowstride = bpp * slice_bmp.width; slice_bmp.data = (guchar*) g_malloc (slice_bmp.rowstride * slice_bmp.height); - + /* Setup gl alignment to 0,0 top-left corner */ prep_for_gl_pixels_download (slice_bmp.rowstride); - + /* Download slice image data into temp bmp */ GE( glBindTexture (tex->gl_target, gl_handle) ); - + GE (glGetTexImage (tex->gl_target, 0, /* level */ target_gl_format, target_gl_type, slice_bmp.data) ); - + /* Copy portion of slice from temp to target bmp */ _cogl_bitmap_copy_subregion (&slice_bmp, target_bmp, @@ -425,10 +425,10 @@ _cogl_texture_download_from_gl (CoglTexture *tex, + y_span->start * target_bmp->rowstride; prep_for_gl_pixels_download (target_bmp->rowstride); - + /* Download slice image data */ GE( glBindTexture (tex->gl_target, gl_handle) ); - + GE( glGetTexImage (tex->gl_target, 0, /* level */ target_gl_format, @@ -437,7 +437,7 @@ _cogl_texture_download_from_gl (CoglTexture *tex, } } } - + return TRUE; } @@ -463,7 +463,7 @@ _cogl_texture_upload_subregion_to_gl (CoglTexture *tex, gint inter_w = 0, inter_h = 0; gint local_x = 0, local_y = 0; guchar *waste_buf; - + bpp = _cogl_get_format_bpp (source_bmp->format); waste_buf = _cogl_texture_allocate_waste_buffer (tex); @@ -473,9 +473,9 @@ _cogl_texture_upload_subregion_to_gl (CoglTexture *tex, _cogl_span_iter_begin (&y_iter, tex->slice_y_spans, 0, COGL_FIXED_FROM_INT (dst_y), COGL_FIXED_FROM_INT (dst_y + height)); - + !_cogl_span_iter_end (&y_iter); - + _cogl_span_iter_next (&y_iter), source_y += inter_h ) { @@ -494,9 +494,9 @@ _cogl_texture_upload_subregion_to_gl (CoglTexture *tex, _cogl_span_iter_begin (&x_iter, tex->slice_x_spans, 0, COGL_FIXED_FROM_INT (dst_x), COGL_FIXED_FROM_INT (dst_x + width)); - + !_cogl_span_iter_end (&x_iter); - + _cogl_span_iter_next (&x_iter), source_x += inter_w ) { @@ -515,27 +515,27 @@ _cogl_texture_upload_subregion_to_gl (CoglTexture *tex, x_iter.intersect_start); inter_h = COGL_FIXED_TO_INT (y_iter.intersect_end - y_iter.intersect_start); - + /* Localize intersection top-left corner to slice*/ local_x = COGL_FIXED_TO_INT (x_iter.intersect_start - x_iter.pos); local_y = COGL_FIXED_TO_INT (y_iter.intersect_start - y_iter.pos); - + /* Pick slice GL handle */ gl_handle = g_array_index (tex->slice_gl_handles, GLuint, y_iter.index * tex->slice_x_spans->len + x_iter.index); - + /* Setup gl alignment to match rowstride and top-left corner */ prep_for_gl_pixels_upload (source_bmp->rowstride, source_x, source_y, bpp); - + /* Upload new image data */ GE( glBindTexture (tex->gl_target, gl_handle) ); - + GE( glTexSubImage2D (tex->gl_target, 0, local_x, local_y, inter_w, inter_h, @@ -657,12 +657,12 @@ _cogl_rect_slices_for_size (gint size_to_fill, { gint n_spans = 0; CoglTexSliceSpan span; - + /* Init first slice span */ span.start = 0; span.size = max_span_size; span.waste = 0; - + /* Repeat until whole area covered */ while (size_to_fill >= span.size) { @@ -672,7 +672,7 @@ _cogl_rect_slices_for_size (gint size_to_fill, size_to_fill -= span.size; n_spans++; } - + /* Add one last smaller slice span */ if (size_to_fill > 0) { @@ -680,7 +680,7 @@ _cogl_rect_slices_for_size (gint size_to_fill, if (out_spans) g_array_append_val (out_spans, span); n_spans++; } - + return n_spans; } @@ -692,15 +692,15 @@ _cogl_pot_slices_for_size (gint size_to_fill, { gint n_spans = 0; CoglTexSliceSpan span; - + /* Init first slice span */ span.start = 0; span.size = max_span_size; span.waste = 0; - + /* Fix invalid max_waste */ if (max_waste < 0) max_waste = 0; - + while (TRUE) { /* Is the whole area covered? */ @@ -729,7 +729,7 @@ _cogl_pot_slices_for_size (gint size_to_fill, } } } - + /* Can't get here */ return 0; } @@ -744,7 +744,7 @@ _cogl_texture_size_supported (GLenum gl_target, if (gl_target == GL_TEXTURE_2D) { /* Proxy texture allows for a quick check for supported size */ - + GLint new_width = 0; GE( glTexImage2D (GL_PROXY_TEXTURE_2D, 0, GL_RGBA, @@ -788,7 +788,7 @@ _cogl_texture_set_wrap_mode_parameter (CoglTexture *tex, static gboolean _cogl_texture_slices_create (CoglTexture *tex) -{ +{ gint bpp; gint max_width; gint max_height; @@ -800,11 +800,11 @@ _cogl_texture_slices_create (CoglTexture *tex) CoglTexSliceSpan *x_span; CoglTexSliceSpan *y_span; const GLfloat transparent_color[4] = { 0x00, 0x00, 0x00, 0x00 }; - + gint (*slices_for_size) (gint, gint, gint, GArray*); - + bpp = _cogl_get_format_bpp (tex->bitmap.format); - + /* Initialize size of largest slice according to supported features */ if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT)) { @@ -820,12 +820,12 @@ _cogl_texture_slices_create (CoglTexture *tex) tex->gl_target = GL_TEXTURE_2D; slices_for_size = _cogl_pot_slices_for_size; } - + /* Negative number means no slicing forced by the user */ if (tex->max_waste <= -1) { CoglTexSliceSpan span; - + /* Check if size supported else bail out */ if (!_cogl_texture_size_supported (tex->gl_target, tex->gl_format, @@ -835,25 +835,25 @@ _cogl_texture_slices_create (CoglTexture *tex) { return FALSE; } - + n_x_slices = 1; n_y_slices = 1; - - /* Init span arrays */ + + /* Init span arrays */ tex->slice_x_spans = g_array_sized_new (FALSE, FALSE, sizeof (CoglTexSliceSpan), 1); - + tex->slice_y_spans = g_array_sized_new (FALSE, FALSE, sizeof (CoglTexSliceSpan), 1); - + /* Add a single span for width and height */ span.start = 0; span.size = max_width; span.waste = max_width - tex->bitmap.width; g_array_append_val (tex->slice_x_spans, span); - + span.size = max_height; span.waste = max_height - tex->bitmap.height; g_array_append_val (tex->slice_y_spans, span); @@ -872,46 +872,46 @@ _cogl_texture_slices_create (CoglTexture *tex) max_width /= 2; else max_height /= 2; - + if (max_width == 0 || max_height == 0) return FALSE; } - + /* Determine the slices required to cover the bitmap area */ n_x_slices = slices_for_size (tex->bitmap.width, max_width, tex->max_waste, NULL); - + n_y_slices = slices_for_size (tex->bitmap.height, max_height, tex->max_waste, NULL); - + /* Init span arrays with reserved size */ tex->slice_x_spans = g_array_sized_new (FALSE, FALSE, sizeof (CoglTexSliceSpan), n_x_slices); - + tex->slice_y_spans = g_array_sized_new (FALSE, FALSE, sizeof (CoglTexSliceSpan), n_y_slices); - + /* Fill span arrays with info */ slices_for_size (tex->bitmap.width, max_width, tex->max_waste, tex->slice_x_spans); - + slices_for_size (tex->bitmap.height, max_height, tex->max_waste, tex->slice_y_spans); } - + /* Init and resize GL handle array */ n_slices = n_x_slices * n_y_slices; - + tex->slice_gl_handles = g_array_sized_new (FALSE, FALSE, sizeof (GLuint), n_slices); - + g_array_set_size (tex->slice_gl_handles, n_slices); /* Wrap mode not yet set */ @@ -921,19 +921,19 @@ _cogl_texture_slices_create (CoglTexture *tex) * (some implementations might supported faster * re-binding between textures inside a set) */ gl_handles = (GLuint*) tex->slice_gl_handles->data; - + GE( glGenTextures (n_slices, gl_handles) ); - - + + /* Init each GL texture object */ for (y = 0; y < n_y_slices; ++y) { y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, y); - + for (x = 0; x < n_x_slices; ++x) { x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, x); - + #if COGL_DEBUG printf ("CREATE SLICE (%d,%d)\n", x,y); printf ("size: (%d x %d)\n", @@ -964,19 +964,19 @@ _cogl_texture_slices_create (CoglTexture *tex) tex->gl_format, tex->gl_type, 0) ); } } - + return TRUE; } static void _cogl_texture_slices_free (CoglTexture *tex) -{ +{ if (tex->slice_x_spans != NULL) g_array_free (tex->slice_x_spans, TRUE); - + if (tex->slice_y_spans != NULL) g_array_free (tex->slice_y_spans, TRUE); - + if (tex->slice_gl_handles != NULL) { if (tex->is_foreign == FALSE) @@ -984,7 +984,7 @@ _cogl_texture_slices_free (CoglTexture *tex) GE( glDeleteTextures (tex->slice_gl_handles->len, (GLuint*) tex->slice_gl_handles->data) ); } - + g_array_free (tex->slice_gl_handles, TRUE); } } @@ -998,34 +998,34 @@ _cogl_pixel_format_from_gl_internal (GLenum gl_int_format, is re-matched against cogl when getting or setting texture image data. */ - + switch (gl_int_format) { case GL_ALPHA: case GL_ALPHA4: case GL_ALPHA8: case GL_ALPHA12: case GL_ALPHA16: - + *out_format = COGL_PIXEL_FORMAT_A_8; return TRUE; - + case GL_LUMINANCE: case GL_LUMINANCE4: case GL_LUMINANCE8: case GL_LUMINANCE12: case GL_LUMINANCE16: - + *out_format = COGL_PIXEL_FORMAT_G_8; return TRUE; - + case GL_RGB: case GL_RGB4: case GL_RGB5: case GL_RGB8: case GL_RGB10: case GL_RGB12: case GL_RGB16: case GL_R3_G3_B2: - + *out_format = COGL_PIXEL_FORMAT_RGB_888; return TRUE; - + case GL_RGBA: case GL_RGBA2: case GL_RGBA4: case GL_RGB5_A1: case GL_RGBA8: case GL_RGB10_A2: case GL_RGBA12: case GL_RGBA16: - + *out_format = COGL_PIXEL_FORMAT_RGBA_8888; return TRUE; } - + return FALSE; } @@ -1039,17 +1039,17 @@ _cogl_pixel_format_to_gl (CoglPixelFormat format, GLenum glintformat = 0; GLenum glformat = 0; GLenum gltype = 0; - + /* No premultiplied formats accepted by GL * (FIXME: latest hardware?) */ - + if (format & COGL_PREMULT_BIT) format = (format & COGL_UNPREMULT_MASK); - + /* Everything else accepted * (FIXME: check YUV support) */ required_format = format; - + /* Find GL equivalents */ switch (format) { @@ -1063,7 +1063,7 @@ _cogl_pixel_format_to_gl (CoglPixelFormat format, glformat = GL_LUMINANCE; gltype = GL_UNSIGNED_BYTE; break; - + case COGL_PIXEL_FORMAT_RGB_888: glintformat = GL_RGB; glformat = GL_RGB; @@ -1084,7 +1084,7 @@ _cogl_pixel_format_to_gl (CoglPixelFormat format, glformat = GL_BGRA; gltype = GL_UNSIGNED_BYTE; break; - + /* The following two types of channel ordering * have no GL equivalent unless defined using * system word byte ordering */ @@ -1097,7 +1097,7 @@ _cogl_pixel_format_to_gl (CoglPixelFormat format, gltype = GL_UNSIGNED_INT_8_8_8_8_REV; #endif break; - + case COGL_PIXEL_FORMAT_ABGR_8888: glintformat = GL_RGBA; glformat = GL_RGBA; @@ -1107,7 +1107,7 @@ _cogl_pixel_format_to_gl (CoglPixelFormat format, gltype = GL_UNSIGNED_INT_8_8_8_8_REV; #endif break; - + /* The following three types of channel ordering * are always defined using system word byte * ordering (even according to GLES spec) */ @@ -1126,19 +1126,19 @@ _cogl_pixel_format_to_gl (CoglPixelFormat format, glformat = GL_RGBA; gltype = GL_UNSIGNED_SHORT_5_5_5_1; break; - + /* FIXME: check extensions for YUV support */ default: break; } - + if (out_glintformat != NULL) *out_glintformat = glintformat; if (out_glformat != NULL) *out_glformat = glformat; if (out_gltype != NULL) *out_gltype = gltype; - + return required_format; } @@ -1149,7 +1149,7 @@ _cogl_texture_bitmap_prepare (CoglTexture *tex, CoglBitmap new_bitmap; CoglPixelFormat new_data_format; gboolean success; - + /* Was there any internal conversion requested? */ if (internal_format == COGL_PIXEL_FORMAT_ANY) internal_format = tex->bitmap.format; @@ -1159,21 +1159,21 @@ _cogl_texture_bitmap_prepare (CoglTexture *tex, &tex->gl_intformat, &tex->gl_format, &tex->gl_type); - + /* Convert to internal format */ if (new_data_format != tex->bitmap.format) { success = _cogl_bitmap_convert_and_premult (&tex->bitmap, &new_bitmap, new_data_format); - + if (!success) return FALSE; - + /* Update texture with new data */ _cogl_texture_bitmap_swap (tex, &new_bitmap); } - + return TRUE; } @@ -1197,15 +1197,15 @@ cogl_texture_new_with_size (guint width, CoglTexture *tex; gint bpp; gint rowstride; - + /* Since no data, we need some internal format */ if (internal_format == COGL_PIXEL_FORMAT_ANY) return COGL_INVALID_HANDLE; - + /* Rowstride from width */ bpp = _cogl_get_format_bpp (internal_format); rowstride = width * bpp; - + /* Init texture with empty bitmap */ tex = (CoglTexture*) g_malloc (sizeof (CoglTexture)); @@ -1214,36 +1214,36 @@ cogl_texture_new_with_size (guint width, tex->is_foreign = FALSE; tex->auto_mipmap = auto_mipmap; - + tex->bitmap.width = width; tex->bitmap.height = height; tex->bitmap.format = internal_format; tex->bitmap.rowstride = rowstride; tex->bitmap.data = NULL; tex->bitmap_owner = FALSE; - + tex->slice_x_spans = NULL; tex->slice_y_spans = NULL; tex->slice_gl_handles = NULL; - + tex->max_waste = max_waste; tex->min_filter = CGL_NEAREST; tex->mag_filter = CGL_NEAREST; - + /* Find closest GL format match */ tex->bitmap.format = _cogl_pixel_format_to_gl (internal_format, &tex->gl_intformat, &tex->gl_format, &tex->gl_type); - + /* Create slices for the given format and size */ if (!_cogl_texture_slices_create (tex)) { _cogl_texture_free (tex); return COGL_INVALID_HANDLE; } - + return _cogl_texture_handle_new (tex); } @@ -1259,20 +1259,20 @@ cogl_texture_new_from_data (guint width, { CoglTexture *tex; gint bpp; - + if (format == COGL_PIXEL_FORMAT_ANY) return COGL_INVALID_HANDLE; - + if (data == NULL) return COGL_INVALID_HANDLE; - + /* Rowstride from width if not given */ bpp = _cogl_get_format_bpp (format); if (rowstride == 0) rowstride = width * bpp; - + /* Create new texture and fill with given data */ tex = (CoglTexture*) g_malloc (sizeof (CoglTexture)); - + tex->ref_count = 1; COGL_HANDLE_DEBUG_NEW (texture, tex); @@ -1285,40 +1285,40 @@ cogl_texture_new_from_data (guint width, tex->bitmap.format = format; tex->bitmap.rowstride = rowstride; tex->bitmap_owner = FALSE; - + tex->slice_x_spans = NULL; tex->slice_y_spans = NULL; tex->slice_gl_handles = NULL; - + tex->max_waste = max_waste; tex->min_filter = CGL_NEAREST; tex->mag_filter = CGL_NEAREST; - + /* FIXME: If upload fails we should set some kind of * error flag but still return texture handle (this * is to keep the behavior equal to _new_from_file; * see below) */ - + if (!_cogl_texture_bitmap_prepare (tex, internal_format)) { _cogl_texture_free (tex); return COGL_INVALID_HANDLE; } - + if (!_cogl_texture_slices_create (tex)) { _cogl_texture_free (tex); return COGL_INVALID_HANDLE; } - + if (!_cogl_texture_upload_to_gl (tex)) { _cogl_texture_free (tex); return COGL_INVALID_HANDLE; } - + _cogl_texture_bitmap_free (tex); - + return _cogl_texture_handle_new (tex); } @@ -1331,7 +1331,7 @@ cogl_texture_new_from_file (const gchar *filename, { CoglBitmap bmp; CoglTexture *tex; - + g_return_val_if_fail (error == NULL || *error == NULL, COGL_INVALID_HANDLE); /* Try loading with imaging backend */ @@ -1346,27 +1346,27 @@ cogl_texture_new_from_file (const gchar *filename, *error = NULL; } } - + /* Create new texture and fill with loaded data */ tex = (CoglTexture*) g_malloc ( sizeof (CoglTexture)); - + tex->ref_count = 1; COGL_HANDLE_DEBUG_NEW (texture, tex); - + tex->is_foreign = FALSE; tex->auto_mipmap = auto_mipmap; tex->bitmap = bmp; tex->bitmap_owner = TRUE; - + tex->slice_x_spans = NULL; tex->slice_y_spans = NULL; tex->slice_gl_handles = NULL; - + tex->max_waste = max_waste; tex->min_filter = CGL_NEAREST; tex->mag_filter = CGL_NEAREST; - + /* FIXME: If upload fails we should set some kind of * error flag but still return texture handle if the * user decides to destroy another texture and upload @@ -1374,27 +1374,27 @@ cogl_texture_new_from_file (const gchar *filename, * in that case). As a rule then, everytime a valid * CoglHandle is returned, it should also be destroyed * with cogl_texture_unref at some point! */ - + if (!_cogl_texture_bitmap_prepare (tex, internal_format)) { _cogl_texture_free (tex); return COGL_INVALID_HANDLE; } - + if (!_cogl_texture_slices_create (tex)) { _cogl_texture_free (tex); return COGL_INVALID_HANDLE; } - + if (!_cogl_texture_upload_to_gl (tex)) { _cogl_texture_free (tex); return COGL_INVALID_HANDLE; } - + _cogl_texture_bitmap_free (tex); - + return _cogl_texture_handle_new (tex); } @@ -1413,7 +1413,7 @@ cogl_texture_new_from_foreign (GLuint gl_handle, robustness and for completeness in case one day GLES gains support for them. */ - + GLenum gl_error = 0; GLboolean gl_istexture; GLint gl_compressed = GL_FALSE; @@ -1448,23 +1448,23 @@ cogl_texture_new_from_foreign (GLuint gl_handle, glBindTexture (gl_target, gl_handle); if (glGetError () != GL_NO_ERROR) return COGL_INVALID_HANDLE; - + /* Obtain texture parameters (only level 0 we are interested in) */ - + GE( glGetTexLevelParameteriv (gl_target, 0, GL_TEXTURE_COMPRESSED, &gl_compressed) ); - + GE( glGetTexLevelParameteriv (gl_target, 0, GL_TEXTURE_INTERNAL_FORMAT, &gl_int_format) ); - + GE( glGetTexLevelParameteriv (gl_target, 0, GL_TEXTURE_WIDTH, &gl_width) ); - + GE( glGetTexLevelParameteriv (gl_target, 0, GL_TEXTURE_HEIGHT, &gl_height) ); @@ -1472,57 +1472,57 @@ cogl_texture_new_from_foreign (GLuint gl_handle, GE( glGetTexParameteriv (gl_target, GL_TEXTURE_MIN_FILTER, &gl_min_filter) ); - + GE( glGetTexParameteriv (gl_target, GL_TEXTURE_MAG_FILTER, &gl_mag_filter) ); - + GE( glGetTexParameteriv (gl_target, GL_GENERATE_MIPMAP, &gl_gen_mipmap) ); - + /* Validate width and height */ if (gl_width <= 0 || gl_height <= 0) return COGL_INVALID_HANDLE; - + /* Validate pot waste */ if (x_pot_waste < 0 || x_pot_waste >= gl_width || y_pot_waste < 0 || y_pot_waste >= gl_height) return COGL_INVALID_HANDLE; - + /* Compressed texture images not supported */ if (gl_compressed == GL_TRUE) return COGL_INVALID_HANDLE; - + /* Try and match to a cogl format */ if (!_cogl_pixel_format_from_gl_internal (gl_int_format, &format)) { return COGL_INVALID_HANDLE; } - + /* Create new texture */ tex = (CoglTexture*) g_malloc ( sizeof (CoglTexture)); - + tex->ref_count = 1; COGL_HANDLE_DEBUG_NEW (texture, tex); - + /* Setup bitmap info */ tex->is_foreign = TRUE; tex->auto_mipmap = (gl_gen_mipmap == GL_TRUE) ? TRUE : FALSE; - + bpp = _cogl_get_format_bpp (format); tex->bitmap.format = format; tex->bitmap.width = gl_width - x_pot_waste; tex->bitmap.height = gl_height - y_pot_waste; tex->bitmap.rowstride = tex->bitmap.width * bpp; tex->bitmap_owner = FALSE; - + tex->gl_target = gl_target; tex->gl_intformat = gl_int_format; tex->gl_format = gl_int_format; tex->gl_type = GL_UNSIGNED_BYTE; - + tex->min_filter = gl_min_filter; tex->mag_filter = gl_mag_filter; tex->max_waste = 0; @@ -1534,26 +1534,26 @@ cogl_texture_new_from_foreign (GLuint gl_handle, tex->slice_x_spans = g_array_sized_new (FALSE, FALSE, sizeof (CoglTexSliceSpan), 1); - + tex->slice_y_spans = g_array_sized_new (FALSE, FALSE, sizeof (CoglTexSliceSpan), 1); - + tex->slice_gl_handles = g_array_sized_new (FALSE, FALSE, sizeof (GLuint), 1); - + /* Store info for a single slice */ x_span.start = 0; x_span.size = gl_width; x_span.waste = x_pot_waste; g_array_append_val (tex->slice_x_spans, x_span); - + y_span.start = 0; y_span.size = gl_height; y_span.waste = y_pot_waste; g_array_append_val (tex->slice_y_spans, y_span); - + g_array_append_val (tex->slice_gl_handles, gl_handle); return _cogl_texture_handle_new (tex); @@ -1563,12 +1563,12 @@ guint cogl_texture_get_width (CoglHandle handle) { CoglTexture *tex; - + if (!cogl_is_texture (handle)) return 0; - + tex = _cogl_texture_pointer_from_handle (handle); - + return tex->bitmap.width; } @@ -1576,12 +1576,12 @@ guint cogl_texture_get_height (CoglHandle handle) { CoglTexture *tex; - + if (!cogl_is_texture (handle)) return 0; - + tex = _cogl_texture_pointer_from_handle (handle); - + return tex->bitmap.height; } @@ -1589,12 +1589,12 @@ CoglPixelFormat cogl_texture_get_format (CoglHandle handle) { CoglTexture *tex; - + if (!cogl_is_texture (handle)) return COGL_PIXEL_FORMAT_ANY; - + tex = _cogl_texture_pointer_from_handle (handle); - + return tex->bitmap.format; } @@ -1602,12 +1602,12 @@ guint cogl_texture_get_rowstride (CoglHandle handle) { CoglTexture *tex; - + if (!cogl_is_texture (handle)) return 0; - + tex = _cogl_texture_pointer_from_handle (handle); - + return tex->bitmap.rowstride; } @@ -1615,12 +1615,12 @@ gint cogl_texture_get_max_waste (CoglHandle handle) { CoglTexture *tex; - + if (!cogl_is_texture (handle)) return 0; - + tex = _cogl_texture_pointer_from_handle (handle); - + return tex->max_waste; } @@ -1628,18 +1628,18 @@ gboolean cogl_texture_is_sliced (CoglHandle handle) { CoglTexture *tex; - + if (!cogl_is_texture (handle)) return FALSE; - + tex = _cogl_texture_pointer_from_handle (handle); - + if (tex->slice_gl_handles == NULL) return FALSE; - + if (tex->slice_gl_handles->len <= 1) return FALSE; - + return TRUE; } @@ -1649,24 +1649,24 @@ cogl_texture_get_gl_texture (CoglHandle handle, GLenum *out_gl_target) { CoglTexture *tex; - + if (!cogl_is_texture (handle)) return FALSE; - + tex = _cogl_texture_pointer_from_handle (handle); - + if (tex->slice_gl_handles == NULL) return FALSE; - + if (tex->slice_gl_handles->len < 1) return FALSE; - + if (out_gl_handle != NULL) *out_gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0); - + if (out_gl_target != NULL) *out_gl_target = tex->gl_target; - + return TRUE; } @@ -1674,12 +1674,12 @@ COGLenum cogl_texture_get_min_filter (CoglHandle handle) { CoglTexture *tex; - + if (!cogl_is_texture (handle)) return 0; - + tex = _cogl_texture_pointer_from_handle (handle); - + return tex->min_filter; } @@ -1687,12 +1687,12 @@ COGLenum cogl_texture_get_mag_filter (CoglHandle handle) { CoglTexture *tex; - + if (!cogl_is_texture (handle)) return 0; - + tex = _cogl_texture_pointer_from_handle (handle); - + return tex->mag_filter; } @@ -1704,20 +1704,20 @@ cogl_texture_set_filters (CoglHandle handle, CoglTexture *tex; GLuint gl_handle; int i; - + if (!cogl_is_texture (handle)) return; - + tex = _cogl_texture_pointer_from_handle (handle); - + /* Store new values */ tex->min_filter = min_filter; tex->mag_filter = mag_filter; - + /* Make sure slices were created */ if (tex->slice_gl_handles == NULL) return; - + /* Apply new filters to every slice */ for (i=0; islice_gl_handles->len; ++i) { @@ -1753,37 +1753,37 @@ cogl_texture_set_region (CoglHandle handle, GLenum closest_gl_format; GLenum closest_gl_type; gboolean success; - + /* Check if valid texture handle */ if (!cogl_is_texture (handle)) return FALSE; - + tex = _cogl_texture_pointer_from_handle (handle); - + /* Check for valid format */ if (format == COGL_PIXEL_FORMAT_ANY) return FALSE; - + /* Shortcut out early if the image is empty */ if (width == 0 || height == 0) return TRUE; - + /* Init source bitmap */ source_bmp.width = width; source_bmp.height = height; source_bmp.format = format; source_bmp.data = (guchar*)data; - + /* Rowstride from width if none specified */ bpp = _cogl_get_format_bpp (format); source_bmp.rowstride = (rowstride == 0) ? width * bpp : rowstride; - + /* Find closest format to internal that's supported by GL */ closest_format = _cogl_pixel_format_to_gl (tex->bitmap.format, NULL, /* don't need */ &closest_gl_format, &closest_gl_type); - + /* If no direct match, convert */ if (closest_format != format) { @@ -1791,13 +1791,13 @@ cogl_texture_set_region (CoglHandle handle, success = _cogl_bitmap_convert_and_premult (&source_bmp, &temp_bmp, closest_format); - + /* Swap bitmaps if succeeded */ if (!success) return FALSE; source_bmp = temp_bmp; source_bmp_owner = TRUE; } - + /* Send data to GL */ _cogl_texture_upload_subregion_to_gl (tex, src_x, src_y, @@ -1806,11 +1806,11 @@ cogl_texture_set_region (CoglHandle handle, &source_bmp, closest_gl_format, closest_gl_type); - + /* Free data if owner */ if (source_bmp_owner) g_free (source_bmp.data); - + return TRUE; } @@ -1833,33 +1833,33 @@ cogl_texture_get_data (CoglHandle handle, guchar *src; guchar *dst; gint y; - + /* Check if valid texture handle */ if (!cogl_is_texture (handle)) return 0; - + tex = _cogl_texture_pointer_from_handle (handle); - + /* Default to internal format if none specified */ if (format == COGL_PIXEL_FORMAT_ANY) format = tex->bitmap.format; - + /* Rowstride from texture width if none specified */ bpp = _cogl_get_format_bpp (format); if (rowstride == 0) rowstride = tex->bitmap.width * bpp; - + /* Return byte size if only that requested */ byte_size = tex->bitmap.height * rowstride; if (data == NULL) return byte_size; - + /* Find closest format that's supported by GL */ closest_format = _cogl_pixel_format_to_gl (format, NULL, /* don't need */ &closest_gl_format, &closest_gl_type); - + closest_bpp = _cogl_get_format_bpp (closest_format); - + /* Is the requested format supported? */ if (closest_format == format) { @@ -1878,12 +1878,12 @@ cogl_texture_get_data (CoglHandle handle, target_bmp.data = (guchar*) g_malloc (target_bmp.height * target_bmp.rowstride); } - + /* Retrieve data from slices */ _cogl_texture_download_from_gl (tex, &target_bmp, closest_gl_format, closest_gl_type); - + /* Was intermediate used? */ if (closest_format != format) { @@ -1891,11 +1891,11 @@ cogl_texture_get_data (CoglHandle handle, success = _cogl_bitmap_convert_and_premult (&target_bmp, &new_bmp, format); - + /* Free intermediate data and return if failed */ g_free (target_bmp.data); if (!success) return 0; - + /* Copy to user buffer */ for (y = 0; y < new_bmp.height; ++y) { @@ -1903,14 +1903,103 @@ cogl_texture_get_data (CoglHandle handle, dst = data + y * rowstride; memcpy (dst, src, new_bmp.width); } - + /* Free converted data */ g_free (new_bmp.data); } - + return byte_size; } +static void +_cogl_texture_flush_vertices (void) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->texture_vertices->len > 0) + { + int needed_indices; + CoglTextureGLVertex *p + = (CoglTextureGLVertex *) ctx->texture_vertices->data; + + /* The indices are always the same sequence regardless of the + vertices so we only need to change it if there are more + vertices than ever before */ + needed_indices = ctx->texture_vertices->len / 4 * 6; + if (needed_indices > ctx->texture_indices->len) + { + int old_len = ctx->texture_indices->len; + int vert_num = old_len / 6 * 4; + int i; + GLushort *q; + + /* Add two triangles for each quad to the list of + indices. That makes six new indices but two of the + vertices in the triangles are shared. */ + g_array_set_size (ctx->texture_indices, needed_indices); + q = &g_array_index (ctx->texture_indices, GLushort, old_len); + + for (i = old_len; + i < ctx->texture_indices->len; + i += 6, vert_num += 4) + { + *(q++) = vert_num + 0; + *(q++) = vert_num + 1; + *(q++) = vert_num + 3; + + *(q++) = vert_num + 1; + *(q++) = vert_num + 2; + *(q++) = vert_num + 3; + } + } + + GE( glVertexPointer (2, GL_FLOAT, + sizeof (CoglTextureGLVertex), p->v ) ); + GE( glTexCoordPointer (2, GL_FLOAT, + sizeof (CoglTextureGLVertex), p->t ) ); + + GE( glBindTexture (ctx->texture_target, ctx->texture_current) ); + GE( ctx->pf_glDrawRangeElements (GL_TRIANGLES, + 0, ctx->texture_vertices->len - 1, + needed_indices, + GL_UNSIGNED_SHORT, + ctx->texture_indices->data) ); + + g_array_set_size (ctx->texture_vertices, 0); + } +} + +static void +_cogl_texture_add_quad_vertices (GLfloat x1, GLfloat y1, + GLfloat x2, GLfloat y2, + GLfloat tx1, GLfloat ty1, + GLfloat tx2, GLfloat ty2) +{ + CoglTextureGLVertex *p; + GLushort first_vert; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* Add the four vertices of the quad to the list of queued + vertices */ + first_vert = ctx->texture_vertices->len; + g_array_set_size (ctx->texture_vertices, first_vert + 4); + p = &g_array_index (ctx->texture_vertices, CoglTextureGLVertex, first_vert); + + p->v[0] = x1; p->v[1] = y1; + p->t[0] = tx1; p->t[1] = ty1; + p++; + p->v[0] = x1; p->v[1] = y2; + p->t[0] = tx1; p->t[1] = ty2; + p++; + p->v[0] = x2; p->v[1] = y2; + p->t[0] = tx2; p->t[1] = ty2; + p++; + p->v[0] = x2; p->v[1] = y1; + p->t[0] = tx2; p->t[1] = ty1; + p++; +} + static void _cogl_texture_quad_sw (CoglTexture *tex, CoglFixed x1, @@ -1931,38 +2020,21 @@ _cogl_texture_quad_sw (CoglTexture *tex, CoglFixed slice_tx2 , slice_ty2; CoglFixed slice_qx1 , slice_qy1; CoglFixed slice_qx2 , slice_qy2; - GLfloat tex_coords[8]; - GLfloat quad_coords[8]; GLuint gl_handle; - gulong enable_flags = (COGL_ENABLE_VERTEX_ARRAY - | COGL_ENABLE_TEXCOORD_ARRAY); - + _COGL_GET_CONTEXT (ctx, NO_RETVAL); #if COGL_DEBUG printf("=== Drawing Tex Quad (Software Tiling Mode) ===\n"); #endif - /* Prepare GL state */ - if (tex->gl_target == CGL_TEXTURE_RECTANGLE_ARB) - enable_flags |= COGL_ENABLE_TEXTURE_RECT; - else - enable_flags |= COGL_ENABLE_TEXTURE_2D; - - if (ctx->color_alpha < 255 - || tex->bitmap.format & COGL_A_BIT) - { - enable_flags |= COGL_ENABLE_BLEND; - } - - if (ctx->enable_backface_culling) - enable_flags |= COGL_ENABLE_BACKFACE_CULLING; - - cogl_enable (enable_flags); - /* We can't use hardware repeat so we need to set clamp to edge otherwise it might pull in edge pixels from the other side */ + if (ctx->texture_vertices->len > 0 + && ctx->texture_wrap_mode != GL_CLAMP_TO_EDGE) + _cogl_texture_flush_vertices (); _cogl_texture_set_wrap_mode_parameter (tex, GL_CLAMP_TO_EDGE); + ctx->texture_wrap_mode = GL_CLAMP_TO_EDGE; /* If the texture coordinates are backwards then swap both the geometry and texture coordinates so that the texture will be @@ -1986,21 +2058,18 @@ _cogl_texture_quad_sw (CoglTexture *tex, ty1 = ty2; ty2 = temp; } - - GE( glTexCoordPointer (2, GL_FLOAT, 0, tex_coords) ); - GE( glVertexPointer (2, GL_FLOAT, 0, quad_coords) ); - + /* Scale ratio from texture to quad widths */ tw = COGL_FIXED_FROM_INT (tex->bitmap.width); th = COGL_FIXED_FROM_INT (tex->bitmap.height); - + tqx = COGL_FIXED_DIV (x2 - x1, COGL_FIXED_MUL (tw, (tx2 - tx1))); tqy = COGL_FIXED_DIV (y2 - y1, COGL_FIXED_MUL (th, (ty2 - ty1))); /* Integral texture coordinate for first tile */ first_tx = COGL_FIXED_FROM_INT (COGL_FIXED_FLOOR (tx1)); first_ty = COGL_FIXED_FROM_INT (COGL_FIXED_FLOOR (ty1)); - + /* Denormalize texture coordinates */ first_tx = COGL_FIXED_MUL (first_tx, tw); first_ty = COGL_FIXED_MUL (first_ty, th); @@ -2012,21 +2081,21 @@ _cogl_texture_quad_sw (CoglTexture *tex, /* Quad coordinate of the first tile */ first_qx = x1 - COGL_FIXED_MUL (tx1 - first_tx, tqx); first_qy = y1 - COGL_FIXED_MUL (ty1 - first_ty, tqy); - - + + /* Iterate until whole quad height covered */ for (_cogl_span_iter_begin (&iter_y, tex->slice_y_spans, first_ty, ty1, ty2) ; !_cogl_span_iter_end (&iter_y) ; _cogl_span_iter_next (&iter_y) ) - { + { /* Discard slices out of quad early */ if (!iter_y.intersects) continue; - + /* Span-quad intersection in quad coordinates */ slice_qy1 = first_qy + COGL_FIXED_MUL (iter_y.intersect_start - first_ty, tqy); - + slice_qy2 = first_qy + COGL_FIXED_MUL (iter_y.intersect_end - first_ty, tqy); @@ -2050,14 +2119,14 @@ _cogl_texture_quad_sw (CoglTexture *tex, { /* Discard slices out of quad early */ if (!iter_x.intersects) continue; - + /* Span-quad intersection in quad coordinates */ slice_qx1 = first_qx + COGL_FIXED_MUL (iter_x.intersect_start - first_tx, tqx); - + slice_qx2 = first_qx + COGL_FIXED_MUL (iter_x.intersect_end - first_tx, tqx); - + /* Localize slice texture coordinates */ slice_tx1 = iter_x.intersect_start - iter_x.pos; slice_tx2 = iter_x.intersect_end - iter_x.pos; @@ -2081,30 +2150,28 @@ _cogl_texture_quad_sw (CoglTexture *tex, printf("tx2: %f\n", COGL_FIXED_TO_FLOAT (slice_tx2)); printf("ty2: %f\n", COGL_FIXED_TO_FLOAT (slice_ty2)); #endif - + /* Pick and bind opengl texture object */ gl_handle = g_array_index (tex->slice_gl_handles, GLuint, iter_y.index * iter_x.array->len + iter_x.index); - - GE( glBindTexture (tex->gl_target, gl_handle) ); -#define CFX_F COGL_FIXED_TO_FLOAT - - /* Draw textured quad */ - tex_coords[0] = CFX_F(slice_tx1); tex_coords[1] = CFX_F(slice_ty2); - tex_coords[2] = CFX_F(slice_tx2); tex_coords[3] = CFX_F(slice_ty2); - tex_coords[4] = CFX_F(slice_tx1); tex_coords[5] = CFX_F(slice_ty1); - tex_coords[6] = CFX_F(slice_tx2); tex_coords[7] = CFX_F(slice_ty1); + /* If we're using a different texture from the one already queued + then flush the vertices */ + if (ctx->texture_vertices->len > 0 + && gl_handle != ctx->texture_current) + _cogl_texture_flush_vertices (); + ctx->texture_target = tex->gl_target; + ctx->texture_current = gl_handle; - quad_coords[0] = CFX_F(slice_qx1); quad_coords[1] = CFX_F(slice_qy2); - quad_coords[2] = CFX_F(slice_qx2); quad_coords[3] = CFX_F(slice_qy2); - quad_coords[4] = CFX_F(slice_qx1); quad_coords[5] = CFX_F(slice_qy1); - quad_coords[6] = CFX_F(slice_qx2); quad_coords[7] = CFX_F(slice_qy1); - - GE (glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) ); - -#undef CFX_F + _cogl_texture_add_quad_vertices (COGL_FIXED_TO_FLOAT (slice_qx1), + COGL_FIXED_TO_FLOAT (slice_qy1), + COGL_FIXED_TO_FLOAT (slice_qx2), + COGL_FIXED_TO_FLOAT (slice_qy2), + COGL_FIXED_TO_FLOAT (slice_tx1), + COGL_FIXED_TO_FLOAT (slice_ty1), + COGL_FIXED_TO_FLOAT (slice_tx2), + COGL_FIXED_TO_FLOAT (slice_ty2)); } } } @@ -2120,37 +2187,16 @@ _cogl_texture_quad_hw (CoglTexture *tex, CoglFixed tx2, CoglFixed ty2) { - GLfloat tex_coords[8]; - GLfloat quad_coords[8]; GLuint gl_handle; CoglTexSliceSpan *x_span; CoglTexSliceSpan *y_span; - gulong enable_flags = (COGL_ENABLE_VERTEX_ARRAY - | COGL_ENABLE_TEXCOORD_ARRAY); + GLenum wrap_mode; #if COGL_DEBUG printf("=== Drawing Tex Quad (Hardware Tiling Mode) ===\n"); #endif - + _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - /* Prepare GL state */ - if (tex->gl_target == CGL_TEXTURE_RECTANGLE_ARB) - enable_flags |= COGL_ENABLE_TEXTURE_RECT; - else - enable_flags |= COGL_ENABLE_TEXTURE_2D; - - if (ctx->color_alpha < 255 - || tex->bitmap.format & COGL_A_BIT) - { - enable_flags |= COGL_ENABLE_BLEND; - } - - if (ctx->enable_backface_culling) - enable_flags |= COGL_ENABLE_BACKFACE_CULLING; - - cogl_enable (enable_flags); - /* If the texture coords are all in the range [0,1] then we want to clamp the coords to the edge otherwise it can pull in edge pixels @@ -2159,17 +2205,25 @@ _cogl_texture_quad_hw (CoglTexture *tex, && tx2 >= 0 && tx2 <= COGL_FIXED_1 && ty1 >= 0 && ty1 <= COGL_FIXED_1 && ty2 >= 0 && ty2 <= COGL_FIXED_1) - _cogl_texture_set_wrap_mode_parameter (tex, GL_CLAMP_TO_EDGE); + wrap_mode = GL_CLAMP_TO_EDGE; else - _cogl_texture_set_wrap_mode_parameter (tex, GL_REPEAT); + wrap_mode = GL_REPEAT; - GE( glTexCoordPointer (2, GL_FLOAT, 0, tex_coords) ); - GE( glVertexPointer (2, GL_FLOAT, 0, quad_coords) ); - /* Pick and bind opengl texture object */ gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0); - GE( glBindTexture (tex->gl_target, gl_handle) ); - + + /* If we're using a different texture from the one already queued + then flush the vertices */ + if (ctx->texture_vertices->len > 0 + && (gl_handle != ctx->texture_current + || ctx->texture_wrap_mode != wrap_mode)) + _cogl_texture_flush_vertices (); + ctx->texture_target = tex->gl_target; + ctx->texture_current = gl_handle; + ctx->texture_wrap_mode = wrap_mode; + + _cogl_texture_set_wrap_mode_parameter (tex, wrap_mode); + /* Don't include the waste in the texture coordinates */ x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0); y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0); @@ -2189,22 +2243,84 @@ _cogl_texture_quad_hw (CoglTexture *tex, ty2 *= y_span->size; } -#define CFX_F(x) COGL_FIXED_TO_FLOAT(x) + _cogl_texture_add_quad_vertices (COGL_FIXED_TO_FLOAT (x1), + COGL_FIXED_TO_FLOAT (y1), + COGL_FIXED_TO_FLOAT (x2), + COGL_FIXED_TO_FLOAT (y2), + COGL_FIXED_TO_FLOAT (tx1), + COGL_FIXED_TO_FLOAT (ty1), + COGL_FIXED_TO_FLOAT (tx2), + COGL_FIXED_TO_FLOAT (ty2)); +} - /* Draw textured quad */ - tex_coords[0] = CFX_F(tx1); tex_coords[1] = CFX_F(ty2); - tex_coords[2] = CFX_F(tx2); tex_coords[3] = CFX_F(ty2); - tex_coords[4] = CFX_F(tx1); tex_coords[5] = CFX_F(ty1); - tex_coords[6] = CFX_F(tx2); tex_coords[7] = CFX_F(ty1); +void +cogl_texture_multiple_rectangles (CoglHandle handle, + const CoglFixed *verts, + guint n_rects) +{ + CoglTexture *tex; + gulong enable_flags = (COGL_ENABLE_VERTEX_ARRAY + | COGL_ENABLE_TEXCOORD_ARRAY); - quad_coords[0] = CFX_F(x1); quad_coords[1] = CFX_F(y2); - quad_coords[2] = CFX_F(x2); quad_coords[3] = CFX_F(y2); - quad_coords[4] = CFX_F(x1); quad_coords[5] = CFX_F(y1); - quad_coords[6] = CFX_F(x2); quad_coords[7] = CFX_F(y1); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); - GE (glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) ); + /* Check if valid texture */ + if (!cogl_is_texture (handle)) + return; -#undef CFX_F + cogl_clip_ensure (); + + tex = _cogl_texture_pointer_from_handle (handle); + + /* Make sure we got stuff to draw */ + if (tex->slice_gl_handles == NULL) + return; + + if (tex->slice_gl_handles->len == 0) + return; + + /* Prepare GL state */ + if (tex->gl_target == CGL_TEXTURE_RECTANGLE_ARB) + enable_flags |= COGL_ENABLE_TEXTURE_RECT; + else + enable_flags |= COGL_ENABLE_TEXTURE_2D; + + if (ctx->color_alpha < 255 + || tex->bitmap.format & COGL_A_BIT) + enable_flags |= COGL_ENABLE_BLEND; + + if (ctx->enable_backface_culling) + enable_flags |= COGL_ENABLE_BACKFACE_CULLING; + + cogl_enable (enable_flags); + + g_array_set_size (ctx->texture_vertices, 0); + + while (n_rects-- > 0) + { + if (verts[4] != verts[6] && verts[5] != verts[7]) + { + /* If there is only one GL texture and either the texture is + NPOT (no waste) or all of the coordinates are in the + range [0,1] then we can use hardware tiling */ + if (tex->slice_gl_handles->len == 1 + && ((cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) + && tex->gl_target == GL_TEXTURE_2D) + || (verts[4] >= 0 && verts[4] <= COGL_FIXED_1 + && verts[6] >= 0 && verts[6] <= COGL_FIXED_1 + && verts[5] >= 0 && verts[5] <= COGL_FIXED_1 + && verts[7] >= 0 && verts[7] <= COGL_FIXED_1))) + _cogl_texture_quad_hw (tex, verts[0],verts[1], verts[2],verts[3], + verts[4],verts[5], verts[6],verts[7]); + else + _cogl_texture_quad_sw (tex, verts[0],verts[1], verts[2],verts[3], + verts[4],verts[5], verts[6],verts[7]); + } + + verts += 8; + } + + _cogl_texture_flush_vertices (); } void @@ -2218,39 +2334,18 @@ cogl_texture_rectangle (CoglHandle handle, CoglFixed tx2, CoglFixed ty2) { - CoglTexture *tex; - - /* Check if valid texture */ - if (!cogl_is_texture (handle)) - return; - - cogl_clip_ensure (); + CoglFixed verts[8]; - tex = _cogl_texture_pointer_from_handle (handle); - - /* Make sure we got stuff to draw */ - if (tex->slice_gl_handles == NULL) - return; - - if (tex->slice_gl_handles->len == 0) - return; - - if (tx1 == tx2 || ty1 == ty2) - return; + verts[0] = x1; + verts[1] = y1; + verts[2] = x2; + verts[3] = y2; + verts[4] = tx1; + verts[5] = ty1; + verts[6] = tx2; + verts[7] = ty2; - /* If there is only one GL texture and either the texture is NPOT - (no waste) or all of the coordinates are in the range [0,1] then - we can use hardware tiling */ - if (tex->slice_gl_handles->len == 1 - && ((cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) - && tex->gl_target == GL_TEXTURE_2D) - || (tx1 >= 0 && tx1 <= COGL_FIXED_1 - && tx2 >= 0 && tx2 <= COGL_FIXED_1 - && ty1 >= 0 && ty1 <= COGL_FIXED_1 - && ty2 >= 0 && ty2 <= COGL_FIXED_1))) - _cogl_texture_quad_hw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2); - else - _cogl_texture_quad_sw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2); + cogl_texture_multiple_rectangles (handle, verts, 1); } void @@ -2299,23 +2394,9 @@ cogl_texture_polygon (CoglHandle handle, /* Make sure there is enough space in the global texture vertex array. This is used so we can render the polygon with a single call to OpenGL but still support any number of vertices */ - if (ctx->texture_vertices_size < n_vertices) - { - guint nsize = ctx->texture_vertices_size; - - if (nsize == 0) - nsize = 1; - do - nsize *= 2; - while (nsize < n_vertices); - - ctx->texture_vertices_size = nsize; + g_array_set_size (ctx->texture_vertices, n_vertices); + p = (CoglTextureGLVertex *) ctx->texture_vertices->data; - ctx->texture_vertices = g_realloc (ctx->texture_vertices, - nsize - * sizeof (CoglTextureGLVertex)); - } - /* Prepare GL state */ enable_flags = (COGL_ENABLE_VERTEX_ARRAY | COGL_ENABLE_TEXCOORD_ARRAY @@ -2332,14 +2413,12 @@ cogl_texture_polygon (CoglHandle handle, if (use_color) { enable_flags |= COGL_ENABLE_COLOR_ARRAY; - GE( glColorPointer (4, GL_UNSIGNED_BYTE, sizeof (CoglTextureGLVertex), - ctx->texture_vertices[0].c) ); + GE( glColorPointer (4, GL_UNSIGNED_BYTE, + sizeof (CoglTextureGLVertex), p->c) ); } - GE( glVertexPointer (3, GL_FLOAT, sizeof (CoglTextureGLVertex), - ctx->texture_vertices[0].v) ); - GE( glTexCoordPointer (2, GL_FLOAT, sizeof (CoglTextureGLVertex), - ctx->texture_vertices[0].t) ); + GE( glVertexPointer (3, GL_FLOAT, sizeof (CoglTextureGLVertex), p->v ) ); + GE( glTexCoordPointer (2, GL_FLOAT, sizeof (CoglTextureGLVertex), p->t ) ); cogl_enable (enable_flags); @@ -2362,9 +2441,11 @@ cogl_texture_polygon (CoglHandle handle, gl_handle = g_array_index (tex->slice_gl_handles, GLuint, tex_num++); + p = (CoglTextureGLVertex *) ctx->texture_vertices->data; + /* Convert the vertices into an array of GLfloats ready to pass to OpenGL */ - for (i = 0, p = ctx->texture_vertices; i < n_vertices; i++, p++) + for (i = 0; i < n_vertices; i++, p++) { CoglFixed tx, ty; diff --git a/clutter/cogl/gles/cogl-context.c b/clutter/cogl/gles/cogl-context.c index c7c768dec..b09568de4 100644 --- a/clutter/cogl/gles/cogl-context.c +++ b/clutter/cogl/gles/cogl-context.c @@ -59,9 +59,11 @@ cogl_create_context () _context->last_path = 0; _context->texture_handles = NULL; - _context->texture_vertices_size = 0; - _context->texture_vertices = NULL; - + _context->texture_vertices = g_array_new (FALSE, FALSE, + sizeof (CoglTextureGLVertex)); + _context->texture_indices = g_array_new (FALSE, FALSE, + sizeof (GLushort)); + _context->fbo_handles = NULL; _context->program_handles = NULL; _context->shader_handles = NULL; @@ -104,8 +106,10 @@ cogl_destroy_context () #endif if (_context->texture_vertices) - g_free (_context->texture_vertices); - + g_array_free (_context->texture_vertices, TRUE); + if (_context->texture_indices) + g_array_free (_context->texture_indices, TRUE); + if (_context->texture_handles) g_array_free (_context->texture_handles, TRUE); if (_context->fbo_handles) diff --git a/clutter/cogl/gles/cogl-context.h b/clutter/cogl/gles/cogl-context.h index b62903efc..5d835712a 100644 --- a/clutter/cogl/gles/cogl-context.h +++ b/clutter/cogl/gles/cogl-context.h @@ -65,9 +65,14 @@ typedef struct /* Textures */ GArray *texture_handles; - CoglTextureGLVertex *texture_vertices; - gulong texture_vertices_size; - + GArray *texture_vertices; + GArray *texture_indices; + /* The gl texture number that the above vertices apply to. This to + detect when a different slice is encountered so that the vertices + can be flushed */ + GLuint texture_current; + GLenum texture_target; + /* Framebuffer objects */ GArray *fbo_handles; CoglBufferTarget draw_buffer; diff --git a/clutter/cogl/gles/cogl-texture.c b/clutter/cogl/gles/cogl-texture.c index 5a75eb389..915e82b23 100644 --- a/clutter/cogl/gles/cogl-texture.c +++ b/clutter/cogl/gles/cogl-texture.c @@ -2047,6 +2047,94 @@ cogl_texture_get_data (CoglHandle handle, return byte_size; } +static void +_cogl_texture_flush_vertices (void) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->texture_vertices->len > 0) + { + int needed_indices; + CoglTextureGLVertex *p + = (CoglTextureGLVertex *) ctx->texture_vertices->data; + + /* The indices are always the same sequence regardless of the + vertices so we only need to change it if there are more + vertices than ever before */ + needed_indices = ctx->texture_vertices->len / 4 * 6; + if (needed_indices > ctx->texture_indices->len) + { + int old_len = ctx->texture_indices->len; + int vert_num = old_len / 6 * 4; + int i; + GLushort *q; + + /* Add two triangles for each quad to the list of + indices. That makes six new indices but two of the + vertices in the triangles are shared. */ + g_array_set_size (ctx->texture_indices, needed_indices); + q = &g_array_index (ctx->texture_indices, GLushort, old_len); + + for (i = old_len; + i < ctx->texture_indices->len; + i += 6, vert_num += 4) + { + *(q++) = vert_num + 0; + *(q++) = vert_num + 1; + *(q++) = vert_num + 3; + + *(q++) = vert_num + 1; + *(q++) = vert_num + 2; + *(q++) = vert_num + 3; + } + } + + GE( glVertexPointer (2, GL_FLOAT, + sizeof (CoglTextureGLVertex), p->v ) ); + GE( glTexCoordPointer (2, GL_FLOAT, + sizeof (CoglTextureGLVertex), p->t ) ); + + GE( glBindTexture (ctx->texture_target, ctx->texture_current) ); + GE( glDrawElements (GL_TRIANGLES, + needed_indices, + GL_UNSIGNED_SHORT, + ctx->texture_indices->data) ); + + g_array_set_size (ctx->texture_vertices, 0); + } +} + +static void +_cogl_texture_add_quad_vertices (GLfloat x1, GLfloat y1, + GLfloat x2, GLfloat y2, + GLfloat tx1, GLfloat ty1, + GLfloat tx2, GLfloat ty2) +{ + CoglTextureGLVertex *p; + GLushort first_vert; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* Add the four vertices of the quad to the list of queued + vertices */ + first_vert = ctx->texture_vertices->len; + g_array_set_size (ctx->texture_vertices, first_vert + 4); + p = &g_array_index (ctx->texture_vertices, CoglTextureGLVertex, first_vert); + + p->v[0] = x1; p->v[1] = y1; + p->t[0] = tx1; p->t[1] = ty1; + p++; + p->v[0] = x1; p->v[1] = y2; + p->t[0] = tx1; p->t[1] = ty2; + p++; + p->v[0] = x2; p->v[1] = y2; + p->t[0] = tx2; p->t[1] = ty2; + p++; + p->v[0] = x2; p->v[1] = y1; + p->t[0] = tx2; p->t[1] = ty1; + p++; +} + static void _cogl_texture_quad_sw (CoglTexture *tex, CoglFixed x1, @@ -2067,31 +2155,13 @@ _cogl_texture_quad_sw (CoglTexture *tex, CoglFixed slice_tx2 , slice_ty2; CoglFixed slice_qx1 , slice_qy1; CoglFixed slice_qx2 , slice_qy2; - GLfloat tex_coords[8]; - GLfloat quad_coords[8]; GLuint gl_handle; - gulong enable_flags = (COGL_ENABLE_TEXTURE_2D - | COGL_ENABLE_VERTEX_ARRAY - | COGL_ENABLE_TEXCOORD_ARRAY); - + _COGL_GET_CONTEXT (ctx, NO_RETVAL); #if COGL_DEBUG printf("=== Drawing Tex Quad (Software Tiling Mode) ===\n"); #endif - - - /* Prepare GL state */ - if (ctx->color_alpha < 255 - || tex->bitmap.format & COGL_A_BIT) - { - enable_flags |= COGL_ENABLE_BLEND; - } - - if (ctx->enable_backface_culling) - enable_flags |= COGL_ENABLE_BACKFACE_CULLING; - - cogl_enable (enable_flags); /* If the texture coordinates are backwards then swap both the geometry and texture coordinates so that the texture will be @@ -2115,10 +2185,7 @@ _cogl_texture_quad_sw (CoglTexture *tex, ty1 = ty2; ty2 = temp; } - - GE( glTexCoordPointer (2, GL_FLOAT, 0, tex_coords) ); - GE( glVertexPointer (2, GL_FLOAT, 0, quad_coords) ); - + /* Scale ratio from texture to quad widths */ tw = COGL_FIXED_FROM_INT (tex->bitmap.width); th = COGL_FIXED_FROM_INT (tex->bitmap.height); @@ -2158,17 +2225,16 @@ _cogl_texture_quad_sw (CoglTexture *tex, slice_qy2 = first_qy + COGL_FIXED_MUL (iter_y.intersect_end - first_ty, tqy); - + /* Localize slice texture coordinates */ slice_ty1 = iter_y.intersect_start - iter_y.pos; slice_ty2 = iter_y.intersect_end - iter_y.pos; - + /* Normalize texture coordinates to current slice (rectangle texture targets take denormalized) */ slice_ty1 /= iter_y.span->size; slice_ty2 /= iter_y.span->size; - - + /* Iterate until whole quad width covered */ for (_cogl_span_iter_begin (&iter_x, tex->slice_x_spans, first_tx, tx1, tx2) ; @@ -2188,12 +2254,12 @@ _cogl_texture_quad_sw (CoglTexture *tex, /* Localize slice texture coordinates */ slice_tx1 = iter_x.intersect_start - iter_x.pos; slice_tx2 = iter_x.intersect_end - iter_x.pos; - + /* Normalize texture coordinates to current slice (rectangle texture targets take denormalized) */ slice_tx1 /= iter_x.span->size; slice_tx2 /= iter_x.span->size; - + #if COGL_DEBUG printf("~~~~~ slice (%d,%d)\n", iter_x.index, iter_y.index); printf("qx1: %f\n", COGL_FIXED_TO_FLOAT (slice_qx1)); @@ -2210,26 +2276,23 @@ _cogl_texture_quad_sw (CoglTexture *tex, gl_handle = g_array_index (tex->slice_gl_handles, GLuint, iter_y.index * iter_x.array->len + iter_x.index); - - GE( cogl_gles2_wrapper_bind_texture (tex->gl_target, gl_handle, - tex->gl_intformat) ); -#define CFX_F COGL_FIXED_TO_FLOAT - - /* Draw textured quad */ - tex_coords[0] = CFX_F(slice_tx1); tex_coords[1] = CFX_F(slice_ty2); - tex_coords[2] = CFX_F(slice_tx2); tex_coords[3] = CFX_F(slice_ty2); - tex_coords[4] = CFX_F(slice_tx1); tex_coords[5] = CFX_F(slice_ty1); - tex_coords[6] = CFX_F(slice_tx2); tex_coords[7] = CFX_F(slice_ty1); + /* If we're using a different texture from the one already queued + then flush the vertices */ + if (ctx->texture_vertices->len > 0 + && gl_handle != ctx->texture_current) + _cogl_texture_flush_vertices (); + ctx->texture_target = tex->gl_target; + ctx->texture_current = gl_handle; - quad_coords[0] = CFX_F(slice_qx1); quad_coords[1] = CFX_F(slice_qy2); - quad_coords[2] = CFX_F(slice_qx2); quad_coords[3] = CFX_F(slice_qy2); - quad_coords[4] = CFX_F(slice_qx1); quad_coords[5] = CFX_F(slice_qy1); - quad_coords[6] = CFX_F(slice_qx2); quad_coords[7] = CFX_F(slice_qy1); - - GE (glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) ); - -#undef CFX_F + _cogl_texture_add_quad_vertices (COGL_FIXED_TO_FLOAT (slice_qx1), + COGL_FIXED_TO_FLOAT (slice_qy1), + COGL_FIXED_TO_FLOAT (slice_qx2), + COGL_FIXED_TO_FLOAT (slice_qy2), + COGL_FIXED_TO_FLOAT (slice_tx1), + COGL_FIXED_TO_FLOAT (slice_ty1), + COGL_FIXED_TO_FLOAT (slice_tx2), + COGL_FIXED_TO_FLOAT (slice_ty2)); } } } @@ -2245,41 +2308,27 @@ _cogl_texture_quad_hw (CoglTexture *tex, CoglFixed tx2, CoglFixed ty2) { - GLfloat tex_coords[8]; - GLfloat quad_coords[8]; GLuint gl_handle; CoglTexSliceSpan *x_span; CoglTexSliceSpan *y_span; - gulong enable_flags = (COGL_ENABLE_TEXTURE_2D - | COGL_ENABLE_VERTEX_ARRAY - | COGL_ENABLE_TEXCOORD_ARRAY); #if COGL_DEBUG printf("=== Drawing Tex Quad (Hardware Tiling Mode) ===\n"); #endif - - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - /* Prepare GL state */ - if (ctx->color_alpha < 255 - || tex->bitmap.format & COGL_A_BIT) - { - enable_flags |= COGL_ENABLE_BLEND; - } - - if (ctx->enable_backface_culling) - enable_flags |= COGL_ENABLE_BACKFACE_CULLING; - cogl_enable (enable_flags); - - GE( glTexCoordPointer (2, GL_FLOAT, 0, tex_coords) ); - GE( glVertexPointer (2, GL_FLOAT, 0, quad_coords) ); - + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + /* Pick and bind opengl texture object */ gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0); - GE( cogl_gles2_wrapper_bind_texture (tex->gl_target, gl_handle, - tex->gl_intformat) ); - + + /* If we're using a different texture from the one already queued + then flush the vertices */ + if (ctx->texture_vertices->len > 0 + && gl_handle != ctx->texture_current) + _cogl_texture_flush_vertices (); + ctx->texture_target = tex->gl_target; + ctx->texture_current = gl_handle; + /* Don't include the waste in the texture coordinates */ x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0); y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0); @@ -2290,22 +2339,80 @@ _cogl_texture_quad_hw (CoglTexture *tex, ty1 = ty1 * (y_span->size - y_span->waste) / y_span->size; ty2 = ty2 * (y_span->size - y_span->waste) / y_span->size; -#define CFX_F(x) COGL_FIXED_TO_FLOAT(x) + _cogl_texture_add_quad_vertices (COGL_FIXED_TO_FLOAT (x1), + COGL_FIXED_TO_FLOAT (y1), + COGL_FIXED_TO_FLOAT (x2), + COGL_FIXED_TO_FLOAT (y2), + COGL_FIXED_TO_FLOAT (tx1), + COGL_FIXED_TO_FLOAT (ty1), + COGL_FIXED_TO_FLOAT (tx2), + COGL_FIXED_TO_FLOAT (ty2)); +} + +void +cogl_texture_multiple_rectangles (CoglHandle handle, + const CoglFixed *verts, + guint n_rects) +{ + CoglTexture *tex; + gulong enable_flags = (COGL_ENABLE_VERTEX_ARRAY + | COGL_ENABLE_TEXCOORD_ARRAY + | COGL_ENABLE_TEXTURE_2D); + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* Check if valid texture */ + if (!cogl_is_texture (handle)) + return; - /* Draw textured quad */ - tex_coords[0] = CFX_F(tx1); tex_coords[1] = CFX_F(ty2); - tex_coords[2] = CFX_F(tx2); tex_coords[3] = CFX_F(ty2); - tex_coords[4] = CFX_F(tx1); tex_coords[5] = CFX_F(ty1); - tex_coords[6] = CFX_F(tx2); tex_coords[7] = CFX_F(ty1); + cogl_clip_ensure (); - quad_coords[0] = CFX_F(x1); quad_coords[1] = CFX_F(y2); - quad_coords[2] = CFX_F(x2); quad_coords[3] = CFX_F(y2); - quad_coords[4] = CFX_F(x1); quad_coords[5] = CFX_F(y1); - quad_coords[6] = CFX_F(x2); quad_coords[7] = CFX_F(y1); + tex = _cogl_texture_pointer_from_handle (handle); + + /* Make sure we got stuff to draw */ + if (tex->slice_gl_handles == NULL) + return; + + if (tex->slice_gl_handles->len == 0) + return; - GE (glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) ); + /* Prepare GL state */ + if (ctx->color_alpha < 255 + || tex->bitmap.format & COGL_A_BIT) + enable_flags |= COGL_ENABLE_BLEND; -#undef CFX_F + if (ctx->enable_backface_culling) + enable_flags |= COGL_ENABLE_BACKFACE_CULLING; + + cogl_enable (enable_flags); + + g_array_set_size (ctx->texture_vertices, 0); + + while (n_rects-- > 0) + { + if (verts[4] != verts[6] && verts[5] != verts[7]) + { + /* If there is only one GL texture and either the texture is + NPOT (no waste) or all of the coordinates are in the + range [0,1] then we can use hardware tiling */ + if (tex->slice_gl_handles->len == 1 + && ((cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) + && tex->gl_target == GL_TEXTURE_2D) + || (verts[4] >= 0 && verts[4] <= COGL_FIXED_1 + && verts[6] >= 0 && verts[6] <= COGL_FIXED_1 + && verts[5] >= 0 && verts[5] <= COGL_FIXED_1 + && verts[7] >= 0 && verts[7] <= COGL_FIXED_1))) + _cogl_texture_quad_hw (tex, verts[0],verts[1], verts[2],verts[3], + verts[4],verts[5], verts[6],verts[7]); + else + _cogl_texture_quad_sw (tex, verts[0],verts[1], verts[2],verts[3], + verts[4],verts[5], verts[6],verts[7]); + } + + verts += 8; + } + + _cogl_texture_flush_vertices (); } void @@ -2319,47 +2426,18 @@ cogl_texture_rectangle (CoglHandle handle, CoglFixed tx2, CoglFixed ty2) { - CoglTexture *tex; - - /* Check if valid texture */ - if (!cogl_is_texture (handle)) - return; + CoglFixed verts[8]; - cogl_clip_ensure (); - - tex = _cogl_texture_pointer_from_handle (handle); - - /* Make sure we got stuff to draw */ - if (tex->slice_gl_handles == NULL) - return; - - if (tex->slice_gl_handles->len == 0) - return; - - if (tx1 == tx2 || ty1 == ty2) - return; - - /* Pick tiling mode according to hw support */ - if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) - && tex->slice_gl_handles->len == 1) - { - _cogl_texture_quad_hw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2); - } - else - { - if (tex->slice_gl_handles->len == 1 - && tx1 >= -COGL_FIXED_1 - && tx2 <= COGL_FIXED_1 - && ty1 >= -COGL_FIXED_1 - && ty2 <= COGL_FIXED_1) - { - _cogl_texture_quad_hw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2); - } - else - { - _cogl_texture_quad_sw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2); - } - } + verts[0] = x1; + verts[1] = y1; + verts[2] = x2; + verts[3] = y2; + verts[4] = tx1; + verts[5] = ty1; + verts[6] = tx2; + verts[7] = ty2; + + cogl_texture_multiple_rectangles (handle, verts, 1); } void @@ -2405,23 +2483,9 @@ cogl_texture_polygon (CoglHandle handle, /* Make sure there is enough space in the global texture vertex array. This is used so we can render the polygon with a single call to OpenGL but still support any number of vertices */ - if (ctx->texture_vertices_size < n_vertices) - { - guint nsize = ctx->texture_vertices_size; - - if (nsize == 0) - nsize = 1; - do - nsize *= 2; - while (nsize < n_vertices); - - ctx->texture_vertices_size = nsize; + g_array_set_size (ctx->texture_vertices, n_vertices); + p = (CoglTextureGLVertex *) ctx->texture_vertices->data; - ctx->texture_vertices = g_realloc (ctx->texture_vertices, - nsize - * sizeof (CoglTextureGLVertex)); - } - /* Prepare GL state */ enable_flags = (COGL_ENABLE_TEXTURE_2D | COGL_ENABLE_VERTEX_ARRAY @@ -2447,14 +2511,12 @@ cogl_texture_polygon (CoglHandle handle, if (use_color) { enable_flags |= COGL_ENABLE_COLOR_ARRAY; - GE( glColorPointer (4, GL_UNSIGNED_BYTE, sizeof (CoglTextureGLVertex), - ctx->texture_vertices[0].c) ); - } + GE( glColorPointer (4, GL_UNSIGNED_BYTE, + sizeof (CoglTextureGLVertex), p->c) ); + } - GE( glVertexPointer (3, GL_FLOAT, sizeof (CoglTextureGLVertex), - ctx->texture_vertices[0].v) ); - GE( glTexCoordPointer (2, GL_FLOAT, sizeof (CoglTextureGLVertex), - ctx->texture_vertices[0].t) ); + GE( glVertexPointer (3, GL_FLOAT, sizeof (CoglTextureGLVertex), p->v ) ); + GE( glTexCoordPointer (2, GL_FLOAT, sizeof (CoglTextureGLVertex), p->t ) ); cogl_enable (enable_flags); @@ -2464,7 +2526,7 @@ cogl_texture_polygon (CoglHandle handle, /* Convert the vertices into an array of GLfloats ready to pass to OpenGL */ - for (i = 0, p = ctx->texture_vertices; i < n_vertices; i++, p++) + for (i = 0; i < n_vertices; i++, p++) { #define CFX_F COGL_FIXED_TO_FLOAT diff --git a/clutter/glx/clutter-backend-glx.c b/clutter/glx/clutter-backend-glx.c index 9f4f47b4a..71276d161 100644 --- a/clutter/glx/clutter-backend-glx.c +++ b/clutter/glx/clutter-backend-glx.c @@ -29,6 +29,8 @@ #include #endif +#include + #include #include #include @@ -150,7 +152,7 @@ static const GOptionEntry entries[] = { "vblank", 0, 0, G_OPTION_ARG_STRING, &clutter_vblank_name, - "VBlank method to be used (none, dri or glx)", "METHOD" + N_("VBlank method to be used (none, dri or glx)"), "METHOD" }, { NULL } }; diff --git a/clutter/pango/cogl-pango-render.c b/clutter/pango/cogl-pango-render.c index bcf3b11fc..e69c412ac 100644 --- a/clutter/pango/cogl-pango-render.c +++ b/clutter/pango/cogl-pango-render.c @@ -51,6 +51,10 @@ struct _CoglPangoRenderer CoglPangoGlyphCache *mipmapped_glyph_cache; gboolean use_mipmapping; + + /* Array of rectangles to draw from the current texture */ + GArray *glyph_rectangles; + CoglHandle glyph_texture; }; struct _CoglPangoRendererClass @@ -58,6 +62,46 @@ struct _CoglPangoRendererClass PangoRendererClass class_instance; }; +static void +cogl_pango_renderer_glyphs_end (CoglPangoRenderer *priv) +{ + if (priv->glyph_rectangles->len > 0) + { + CoglFixed *rectangles = (CoglFixed *) priv->glyph_rectangles->data; + cogl_texture_multiple_rectangles (priv->glyph_texture, rectangles, + priv->glyph_rectangles->len / 8); + g_array_set_size (priv->glyph_rectangles, 0); + } +} + +static void +cogl_pango_renderer_draw_glyph (CoglPangoRenderer *priv, + CoglPangoGlyphCacheValue *cache_value, + CoglFixed x1, + CoglFixed y1) +{ + CoglFixed x2, y2; + CoglFixed *p; + + if (priv->glyph_rectangles->len > 0 + && priv->glyph_texture != cache_value->texture) + cogl_pango_renderer_glyphs_end (priv); + + priv->glyph_texture = cache_value->texture; + + x2 = x1 + CLUTTER_INT_TO_FIXED (cache_value->draw_width); + y2 = y1 + CLUTTER_INT_TO_FIXED (cache_value->draw_height); + + g_array_set_size (priv->glyph_rectangles, priv->glyph_rectangles->len + 8); + p = &g_array_index (priv->glyph_rectangles, CoglFixed, + priv->glyph_rectangles->len - 8); + + *(p++) = x1; *(p++) = y1; + *(p++) = x2; *(p++) = y2; + *(p++) = cache_value->tx1; *(p++) = cache_value->ty1; + *(p++) = cache_value->tx2; *(p++) = cache_value->ty2; +} + #define COGL_PANGO_UNIT_TO_FIXED(x) ((x) << (COGL_FIXED_Q - 10)) static void cogl_pango_renderer_finalize (GObject *object); @@ -89,6 +133,7 @@ cogl_pango_renderer_init (CoglPangoRenderer *priv) priv->glyph_cache = cogl_pango_glyph_cache_new (FALSE); priv->mipmapped_glyph_cache = cogl_pango_glyph_cache_new (TRUE); priv->use_mipmapping = FALSE; + priv->glyph_rectangles = g_array_new (FALSE, FALSE, sizeof (CoglFixed)); } static void @@ -111,6 +156,7 @@ cogl_pango_renderer_finalize (GObject *object) cogl_pango_glyph_cache_free (priv->mipmapped_glyph_cache); cogl_pango_glyph_cache_free (priv->glyph_cache); + g_array_free (priv->glyph_rectangles, TRUE); G_OBJECT_CLASS (cogl_pango_renderer_parent_class)->finalize (object); } @@ -376,10 +422,10 @@ cogl_pango_renderer_draw_box (int x, int y, static void cogl_pango_renderer_get_device_units (PangoRenderer *renderer, - int xin, - int yin, - CoglFixed *xout, - CoglFixed *yout) + int xin, + int yin, + CoglFixed *xout, + CoglFixed *yout) { const PangoMatrix *matrix; @@ -454,6 +500,7 @@ cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer, int xi, int yi) { + CoglPangoRenderer *priv = (CoglPangoRenderer *) renderer; CoglPangoGlyphCacheValue *cache_value; int i; @@ -474,6 +521,8 @@ cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer, { PangoFontMetrics *metrics; + cogl_pango_renderer_glyphs_end (priv); + if (font == NULL || (metrics = pango_font_get_metrics (font, NULL)) == NULL) { @@ -503,10 +552,14 @@ cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer, gi->glyph); if (cache_value == NULL) - cogl_pango_renderer_draw_box (COGL_FIXED_TO_INT (x), - COGL_FIXED_TO_INT (y), - PANGO_UNKNOWN_GLYPH_WIDTH, - PANGO_UNKNOWN_GLYPH_HEIGHT); + { + cogl_pango_renderer_glyphs_end (priv); + + cogl_pango_renderer_draw_box (COGL_FIXED_TO_INT (x), + COGL_FIXED_TO_INT (y), + PANGO_UNKNOWN_GLYPH_WIDTH, + PANGO_UNKNOWN_GLYPH_HEIGHT); + } else { CoglFixed width, height; @@ -517,15 +570,12 @@ cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer, width = x + COGL_FIXED_FROM_INT (cache_value->draw_width); height = y + COGL_FIXED_FROM_INT (cache_value->draw_height); - /* Render the glyph from the texture */ - cogl_texture_rectangle (cache_value->texture, - x, y, - width, height, - cache_value->tx1, cache_value->ty1, - cache_value->tx2, cache_value->ty2); + cogl_pango_renderer_draw_glyph (priv, cache_value, x, y); } } xi += gi->geometry.width; } + + cogl_pango_renderer_glyphs_end (priv); } diff --git a/clutter/x11/clutter-backend-x11.c b/clutter/x11/clutter-backend-x11.c index f05589b9f..412bb77da 100644 --- a/clutter/x11/clutter-backend-x11.c +++ b/clutter/x11/clutter-backend-x11.c @@ -23,6 +23,8 @@ #include "config.h" #endif +#include + #include #include #ifdef HAVE_UNISTD_H @@ -235,18 +237,18 @@ static const GOptionEntry entries[] = "display", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_STRING, &clutter_display_name, - "X display to use", "DISPLAY" + N_("X display to use"), "DISPLAY" }, { "screen", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_INT, &clutter_screen, - "X screen to use", "SCREEN" + N_("X screen to use"), "SCREEN" }, { "synch", 0, 0, G_OPTION_ARG_NONE, &clutter_synchronise, - "Make X calls synchronous", NULL, + N_("Make X calls synchronous"), NULL, }, { NULL } }; diff --git a/configure.ac b/configure.ac index 0df692407..ccfc2e0f5 100644 --- a/configure.ac +++ b/configure.ac @@ -580,7 +580,7 @@ AC_ARG_ENABLE([maintainer-flags], enable_maintainer_flags=maintainer_flags_default) if test "x$enable_maintainer_flags" = "xyes"; then - CPPFLAGS="$CPPFLAGS -Werror -Wall -Wshadow -Wcast-align -Wno-uninitialized" + CPPFLAGS="$CPPFLAGS -Werror -Wall -Wshadow -Wcast-align -Wno-uninitialized -Wempty-body -Wformat-security -Winit-self" fi @@ -637,6 +637,7 @@ AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", ALL_LINGUAS="" AM_GLIB_GNU_GETTEXT +GLIB_DEFINE_LOCALEDIR(LOCALEDIR) AC_CONFIG_FILES([ Makefile diff --git a/doc/reference/clutter/Makefile.am b/doc/reference/clutter/Makefile.am index ca941565f..d03a283d0 100644 --- a/doc/reference/clutter/Makefile.am +++ b/doc/reference/clutter/Makefile.am @@ -55,6 +55,7 @@ CFILE_GLOB=$(top_srcdir)/clutter/*.c # e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h IGNORE_HFILES=\ clutter.h \ + clutter-bezier.h \ clutter-debug.h \ clutter-deprecated.h \ clutter-enum-types.h \ diff --git a/doc/reference/clutter/clutter-docs.xml b/doc/reference/clutter/clutter-docs.xml index ffd06a561..e29317036 100644 --- a/doc/reference/clutter/clutter-docs.xml +++ b/doc/reference/clutter/clutter-docs.xml @@ -58,8 +58,7 @@ - - + diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 5948dd14a..7c9ed607b 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -66,48 +66,6 @@ clutter_unit_get_type clutter_param_unit_get_type -
-clutter-label -ClutterLabel -ClutterLabel -ClutterLabelClass -clutter_label_new -clutter_label_new_with_text -clutter_label_new_full -clutter_label_set_text -clutter_label_get_text -clutter_label_set_font_name -clutter_label_get_font_name -clutter_label_set_color -clutter_label_get_color -clutter_label_set_ellipsize -clutter_label_get_ellipsize -clutter_label_set_line_wrap -clutter_label_get_line_wrap -clutter_label_set_line_wrap_mode -clutter_label_get_line_wrap_mode -clutter_label_get_layout -clutter_label_set_attributes -clutter_label_get_attributes -clutter_label_set_use_markup -clutter_label_get_use_markup -clutter_label_set_alignment -clutter_label_get_alignment -clutter_label_get_justify -clutter_label_set_justify - - -CLUTTER_LABEL -CLUTTER_IS_LABEL -CLUTTER_TYPE_LABEL -CLUTTER_LABEL_CLASS -CLUTTER_IS_LABEL_CLASS -CLUTTER_LABEL_GET_CLASS - -ClutterLabelPrivate -clutter_label_get_type -
-
clutter-behaviour ClutterBehaviour @@ -476,6 +434,11 @@ clutter_actor_get_scalex clutter_actor_set_rotationx clutter_actor_get_rotationx + +clutter_actor_grab_key_focus +clutter_actor_get_pango_context +clutter_actor_create_pango_context + CLUTTER_TYPE_GEOMETRY CLUTTER_TYPE_ACTOR_BOX @@ -698,6 +661,8 @@ ClutterPathCallback ClutterPathNodeType clutter_path_new clutter_path_new_with_description + + clutter_path_add_move_to clutter_path_add_rel_move_to clutter_path_add_line_to @@ -708,6 +673,8 @@ clutter_path_add_close clutter_path_add_string clutter_path_add_node clutter_path_add_cairo_path + + clutter_path_get_n_nodes clutter_path_get_node clutter_path_get_nodes @@ -736,11 +703,9 @@ CLUTTER_PATH_CLASS CLUTTER_IS_PATH CLUTTER_IS_PATH_CLASS CLUTTER_PATH_GET_CLASS -CLUTTER_BEZIER_MAX_LENGTH CLUTTER_PATH_RELATIVE -ClutterBezier ClutterPathPrivate clutter_path_get_type clutter_path_node_get_type @@ -869,14 +834,16 @@ clutter_behaviour_ellipse_get_type clutter-backend ClutterBackend clutter_get_default_backend -clutter_backend_get_resolution clutter_backend_set_resolution -clutter_backend_get_double_click_time +clutter_backend_get_resolution clutter_backend_set_double_click_time -clutter_backend_get_double_click_distance +clutter_backend_get_double_click_time clutter_backend_set_double_click_distance +clutter_backend_get_double_click_distance clutter_backend_set_font_options clutter_backend_get_font_options +clutter_backend_set_font_name +clutter_backend_get_font_name CLUTTER_BACKEND CLUTTER_IS_BACKEND @@ -1219,50 +1186,6 @@ CLUTTER_COGL CLUTTER_NO_FPU
-
-clutter-entry -ClutterEntry -ClutterEntry -ClutterEntryClass -clutter_entry_new -clutter_entry_new_with_text -clutter_entry_new_full -clutter_entry_set_text -clutter_entry_get_text -clutter_entry_set_font_name -clutter_entry_get_font_name -clutter_entry_set_color -clutter_entry_get_color -clutter_entry_get_layout -clutter_entry_set_alignment -clutter_entry_get_alignment -clutter_entry_set_cursor_position -clutter_entry_get_cursor_position -clutter_entry_handle_key_event -clutter_entry_insert_unichar -clutter_entry_delete_chars -clutter_entry_insert_text -clutter_entry_delete_text -clutter_entry_set_visible_cursor -clutter_entry_get_visible_cursor -clutter_entry_set_visibility -clutter_entry_get_visibility -clutter_entry_set_invisible_char -clutter_entry_get_invisible_char -clutter_entry_set_max_length -clutter_entry_get_max_length - -CLUTTER_ENTRY -CLUTTER_IS_ENTRY -CLUTTER_TYPE_ENTRY -CLUTTER_ENTRY_CLASS -CLUTTER_IS_ENTRY_CLASS -CLUTTER_ENTRY_GET_CLASS - -ClutterEntryPrivate -clutter_entry_get_type -
-
clutter-effect Clutter Effects @@ -1533,23 +1456,25 @@ clutter_shader_release clutter_shader_is_compiled clutter_shader_set_is_enabled clutter_shader_get_is_enabled -clutter_shader_set_uniform_1f + + clutter_shader_set_uniform clutter_shader_get_cogl_program clutter_shader_get_cogl_fragment_shader clutter_shader_get_cogl_vertex_shader +clutter_shader_set_uniform_1f -CLUTTER_VALUE_HOLDS_SHADER_FLOAT ClutterShaderFloat +CLUTTER_VALUE_HOLDS_SHADER_FLOAT clutter_value_set_shader_float clutter_value_get_shader_float -CLUTTER_VALUE_HOLDS_SHADER_INT ClutterShaderInt +CLUTTER_VALUE_HOLDS_SHADER_INT clutter_value_set_shader_int clutter_value_get_shader_int -CLUTTER_VALUE_HOLDS_SHADER_MATRIX ClutterShaderMatrix +CLUTTER_VALUE_HOLDS_SHADER_MATRIX clutter_value_set_shader_matrix clutter_value_get_shader_matrix @@ -1572,6 +1497,9 @@ clutter_shader_float_get_type clutter_shader_int_get_type clutter_shader_matrix_get_type clutter_shader_error_quark +clutter_shader_float_get_type +clutter_shader_int_get_type +clutter_shader_matrix_get_type
@@ -1638,8 +1566,8 @@ clutter_interval_set_interval clutter_interval_get_interval -clutter_interval_validate clutter_interval_compute_value +clutter_interval_validate CLUTTER_TYPE_INTERVAL @@ -1668,7 +1596,8 @@ clutter_binding_pool_find clutter_binding_pool_install_action clutter_binding_pool_install_closure -clutter_binding_pool_list_actions +clutter_binding_pool_override_action +clutter_binding_pool_override_closure clutter_binding_pool_find_action clutter_binding_pool_remove_action clutter_binding_pool_block_action @@ -1679,8 +1608,8 @@ clutter_binding_pool_activate
-ClutterCairoTexture clutter-cairo-texture +ClutterCairoTexture ClutterCairoTexture ClutterCairoTextureClass clutter_cairo_texture_new @@ -1705,4 +1634,82 @@ CLUTTER_CAIRO_TEXTURE_GET_CLASS ClutterCairoTexturePrivate clutter_cairo_texture_get_type + +
+ClutterText +clutter-text +ClutterText +ClutterTextClass +clutter_text_new +clutter_text_new_full +clutter_text_new_with_text + + +clutter_text_set_text +clutter_text_get_text +clutter_text_set_activatable +clutter_text_get_activatable +clutter_text_set_alignment +clutter_text_get_alignment +clutter_text_set_attributes +clutter_text_get_attributes +clutter_text_set_color +clutter_text_get_color +clutter_text_set_ellipsize +clutter_text_get_ellipsize +clutter_text_set_font_name +clutter_text_get_font_name +clutter_text_set_password_char +clutter_text_get_password_char +clutter_text_set_justify +clutter_text_get_justify +clutter_text_get_layout +clutter_text_set_line_wrap +clutter_text_get_line_wrap +clutter_text_set_line_wrap_mode +clutter_text_get_line_wrap_mode +clutter_text_set_max_length +clutter_text_get_max_length +clutter_text_set_selectable +clutter_text_get_selectable +clutter_text_set_selection +clutter_text_get_selection +clutter_text_set_selection_bound +clutter_text_get_selection_bound +clutter_text_set_single_line_mode +clutter_text_get_single_line_mode +clutter_text_set_use_markup +clutter_text_get_use_markup + + +clutter_text_set_editable +clutter_text_get_editable +clutter_text_insert_text +clutter_text_insert_unichar +clutter_text_delete_chars +clutter_text_delete_text +clutter_text_get_chars +clutter_text_set_cursor_color +clutter_text_get_cursor_color +clutter_text_set_cursor_position +clutter_text_get_cursor_position +clutter_text_set_cursor_visible +clutter_text_get_cursor_visible +clutter_text_set_cursor_size +clutter_text_get_cursor_size + + +clutter_text_activate + + +CLUTTER_IS_TEXT +CLUTTER_IS_TEXT_CLASS +CLUTTER_TEXT +CLUTTER_TEXT_CLASS +CLUTTER_TEXT_GET_CLASS +CLUTTER_TYPE_TEXT + + +ClutterTextPrivate +clutter_text_get_type
diff --git a/doc/reference/clutter/clutter.types b/doc/reference/clutter/clutter.types index 2bac85c6e..f59698f02 100644 --- a/doc/reference/clutter/clutter.types +++ b/doc/reference/clutter/clutter.types @@ -6,7 +6,6 @@ clutter_stage_get_type clutter_rectangle_get_type clutter_texture_get_type clutter_clone_texture_get_type -clutter_label_get_type clutter_timeline_get_type clutter_media_get_type clutter_behaviour_get_type @@ -19,7 +18,6 @@ clutter_path_get_type clutter_behaviour_rotate_get_type clutter_behaviour_scale_get_type clutter_backend_get_type -clutter_entry_get_type clutter_script_get_type clutter_scriptable_get_type clutter_model_get_type @@ -29,3 +27,4 @@ clutter_score_get_type clutter_shader_get_type clutter_child_meta_get_type clutter_cairo_texture_get_type +clutter_text_get_type diff --git a/doc/reference/cogl/cogl-sections.txt b/doc/reference/cogl/cogl-sections.txt index 03247ff3c..dcd7c933f 100644 --- a/doc/reference/cogl/cogl-sections.txt +++ b/doc/reference/cogl/cogl-sections.txt @@ -120,6 +120,7 @@ cogl_texture_set_region cogl_texture_ref cogl_texture_unref cogl_texture_rectangle +cogl_texture_multiple_rectangles cogl_texture_polygon
diff --git a/po/POTFILES.in b/po/POTFILES.in index e69de29bb..76922d195 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -0,0 +1,14 @@ +clutter/clutter-actor.c +clutter/clutter-behaviour.c +clutter/clutter-color.c +clutter/clutter-container.c +clutter/clutter-event.c +clutter/clutter-fixed.c +clutter/clutter-fixed.h +clutter/clutter-main.c +clutter/clutter-stage-window.c +clutter/clutter-stage.c +clutter/clutter-texture.c +clutter/clutter-units.c +clutter/glx/clutter-backend-glx.c +clutter/x11/clutter-backend-x11.c diff --git a/po/clutter-0.8.pot b/po/clutter-0.8.pot deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am index ec6899c0c..e3c36a873 100644 --- a/tests/conform/Makefile.am +++ b/tests/conform/Makefile.am @@ -17,15 +17,15 @@ test_conformance_SOURCES = \ test-mesh-mutability.c \ test-path.c \ test-pick.c \ - test-label-cache.c \ - test-clutter-entry.c \ test-clutter-rectangle.c \ test-clutter-fixed.c \ test-actor-invariants.c \ test-paint-opacity.c \ test-backface-culling.c \ test-binding-pool.c \ - $(NULL) + test-clutter-text.c \ + test-text-cache.c \ + $(NULL) # For convenience, this provides a way to easily run individual unit tests: .PHONY: wrappers clean-wrappers diff --git a/tests/conform/test-binding-pool.c b/tests/conform/test-binding-pool.c index 4a6f4ebaa..9f7f24486 100644 --- a/tests/conform/test-binding-pool.c +++ b/tests/conform/test-binding-pool.c @@ -260,9 +260,9 @@ on_activate (KeyGroup *key_group, ClutterActor *child, gpointer data) { - gint index = GPOINTER_TO_INT (data); + gint _index = GPOINTER_TO_INT (data); - g_assert_cmpint (key_group->selected_index, ==, index); + g_assert_cmpint (key_group->selected_index, ==, _index); } void diff --git a/tests/conform/test-clutter-entry.c b/tests/conform/test-clutter-entry.c deleted file mode 100644 index 6562836b1..000000000 --- a/tests/conform/test-clutter-entry.c +++ /dev/null @@ -1,370 +0,0 @@ -#include -#include -#include - -#include "test-conform-common.h" - -typedef struct { - gunichar unichar; - const char bytes[6]; - gint nbytes; -} TestData; - -const TestData -test_data[] = { - { 0xe4, "\xc3\xa4", 2 }, /* LATIN SMALL LETTER A WITH DIAERESIS */ - { 0x2665, "\xe2\x99\xa5", 3 } /* BLACK HEART SUIT */ -}; - -void -test_entry_utf8_validation (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - int i; - - for (i = 0; i < G_N_ELEMENTS (test_data); i++) - { - const TestData *t = &test_data[i]; - gunichar unichar; - char bytes[6]; - int nbytes; - - g_assert (g_unichar_validate (t->unichar)); - - nbytes = g_unichar_to_utf8 (t->unichar, bytes); - bytes[nbytes] = '\0'; - g_assert (nbytes == t->nbytes); - g_assert (memcmp (t->bytes, bytes, nbytes) == 0); - - unichar = g_utf8_get_char_validated (bytes, nbytes); - g_assert (unichar == t->unichar); - } -} - -static int -get_nbytes (ClutterEntry *entry) -{ - const char *s = clutter_entry_get_text (entry); - return strlen (s); -} - -static int -get_nchars (ClutterEntry *entry) -{ - const char *s = clutter_entry_get_text (entry); - g_assert (g_utf8_validate (s, -1, NULL)); - return g_utf8_strlen (s, -1); -} - -#define DONT_MOVE_CURSOR (-2) - -static void -insert_unichar (ClutterEntry *entry, gunichar unichar, int position) -{ - if (position > DONT_MOVE_CURSOR) - { - clutter_entry_set_cursor_position (entry, position); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, position); - } - - clutter_entry_insert_unichar (entry, unichar); -} - -void -test_entry_empty (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - - g_assert (clutter_entry_get_text (entry) == NULL); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, -1); - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - -void -test_entry_set_empty (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - - /* annoyingly slightly different from initially empty */ - clutter_entry_set_text (entry, ""); - g_assert_cmpint (get_nchars (entry), ==, 0); - g_assert_cmpint (get_nbytes (entry), ==, 0); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, -1); - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - -void -test_entry_set_text (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - - clutter_entry_set_text (entry, "abcdef"); - g_assert_cmpint (get_nchars (entry), ==, 6); - g_assert_cmpint (get_nbytes (entry), ==, 6); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, -1); - - clutter_entry_set_cursor_position (entry, 5); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, 5); - - clutter_entry_set_text (entry, ""); - /* FIXME: cursor position should be -1? - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, -1); - */ - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - -void -test_entry_append_some (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - int i; - - for (i = 0; i < G_N_ELEMENTS (test_data); i++) - { - const TestData *t = &test_data[i]; - int j; - - for (j = 1; j <= 4; j++) - { - insert_unichar (entry, t->unichar, DONT_MOVE_CURSOR); - g_assert_cmpint (get_nchars (entry), ==, j); - g_assert_cmpint (get_nbytes (entry), ==, j * t->nbytes); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, -1); - } - - clutter_entry_set_text (entry, ""); - } - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - -void -test_entry_prepend_some (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - int i; - - for (i = 0; i < G_N_ELEMENTS (test_data); i++) - { - const TestData *t = &test_data[i]; - int j; - - clutter_entry_insert_unichar (entry, t->unichar); - g_assert_cmpint (get_nchars (entry), ==, 1); - g_assert_cmpint (get_nbytes (entry), ==, 1 * t->nbytes); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, -1); - - for (j = 2; j <= 4; j++) - { - insert_unichar (entry, t->unichar, 0); - g_assert_cmpint (get_nchars (entry), ==, j); - g_assert_cmpint (get_nbytes (entry), ==, j * t->nbytes); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, 1); - } - - clutter_entry_set_text (entry, ""); - } - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - -void -test_entry_insert (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - int i; - - for (i = 0; i < G_N_ELEMENTS (test_data); i++) - { - const TestData *t = &test_data[i]; - - clutter_entry_insert_unichar (entry, t->unichar); - clutter_entry_insert_unichar (entry, t->unichar); - - insert_unichar (entry, t->unichar, 1); - g_assert_cmpint (get_nchars (entry), ==, 3); - g_assert_cmpint (get_nbytes (entry), ==, 3 * t->nbytes); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, 2); - - clutter_entry_set_text (entry, ""); - } - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - -void -test_entry_delete_chars (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - int i; - - for (i = 0; i < G_N_ELEMENTS (test_data); i++) - { - const TestData *t = &test_data[i]; - int j; - - for (j = 0; j < 4; j++) - clutter_entry_insert_unichar (entry, t->unichar); - - clutter_entry_set_cursor_position (entry, 2); - clutter_entry_delete_chars (entry, 1); - g_assert_cmpint (get_nchars (entry), ==, 3); - g_assert_cmpint (get_nbytes (entry), ==, 3 * t->nbytes); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, 1); - - clutter_entry_set_cursor_position (entry, 2); - clutter_entry_delete_chars (entry, 1); - g_assert_cmpint (get_nchars (entry), ==, 2); - g_assert_cmpint (get_nbytes (entry), ==, 2 * t->nbytes); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, 1); - - clutter_entry_set_text (entry, ""); - } - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - -void -test_entry_delete_text (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - int i; - - for (i = 0; i < G_N_ELEMENTS (test_data); i++) - { - const TestData *t = &test_data[i]; - int j; - - for (j = 0; j < 4; j++) - clutter_entry_insert_unichar (entry, t->unichar); - - clutter_entry_set_cursor_position (entry, 3); - clutter_entry_delete_text (entry, 2, 4); - - g_assert_cmpint (get_nchars (entry), ==, 2); - g_assert_cmpint (get_nbytes (entry), ==, 2 * t->nbytes); - - /* FIXME: cursor position should be -1? - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, -1); - */ - - clutter_entry_set_text (entry, ""); - } - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - -static void -init_event (ClutterKeyEvent *event) -{ - event->type = CLUTTER_KEY_PRESS; - event->time = 0; /* not needed */ - event->flags = CLUTTER_EVENT_FLAG_SYNTHETIC; - event->stage = NULL; /* not needed */ - event->source = NULL; /* not needed */ - event->modifier_state = 0; - event->hardware_keycode = 0; /* not needed */ -} - -static void -send_keyval (ClutterEntry *entry, int keyval) -{ - ClutterKeyEvent event; - - init_event (&event); - event.keyval = keyval; - event.unicode_value = 0; /* should be ignored for cursor keys etc. */ - - clutter_entry_handle_key_event (entry, &event); -} - -static inline void -send_unichar (ClutterEntry *entry, gunichar unichar) -{ - ClutterKeyEvent event; - - init_event (&event); - event.keyval = 0; /* should be ignored for printable characters */ - event.unicode_value = unichar; - - clutter_entry_handle_key_event (entry, &event); -} - -void -test_entry_cursor (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - int i; - - for (i = 0; i < G_N_ELEMENTS (test_data); i++) - { - const TestData *t = &test_data[i]; - int j; - - for (j = 0; j < 4; ++j) - clutter_entry_insert_unichar (entry, t->unichar); - - clutter_entry_set_cursor_position (entry, 2); - - /* test cursor moves and is clamped */ - send_keyval (entry, CLUTTER_Left); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, 1); - - send_keyval (entry, CLUTTER_Left); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, 0); - - send_keyval (entry, CLUTTER_Left); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, 0); - - /* delete text containing the cursor */ - clutter_entry_set_cursor_position (entry, 3); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, 3); - - clutter_entry_delete_text (entry, 2, 4); - send_keyval (entry, CLUTTER_Left); - - /* FIXME: cursor position should be -1? - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, -1); - */ - - clutter_entry_set_text (entry, ""); - } - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - -void -test_entry_event (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - int i; - - for (i = 0; i < G_N_ELEMENTS (test_data); i++) - { - const TestData *t = &test_data[i]; - - send_unichar (entry, t->unichar); - - g_assert_cmpint (get_nchars (entry), ==, 1); - g_assert_cmpint (get_nbytes (entry), ==, 1 * t->nbytes); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, -1); - - clutter_entry_set_text (entry, ""); - } - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - diff --git a/tests/conform/test-clutter-text.c b/tests/conform/test-clutter-text.c new file mode 100644 index 000000000..6814013a8 --- /dev/null +++ b/tests/conform/test-clutter-text.c @@ -0,0 +1,431 @@ +#include +#include +#include + +#include "test-conform-common.h" + +typedef struct { + gunichar unichar; + const char bytes[6]; + gint nbytes; +} TestData; + +static const TestData +test_text_data[] = { + { 0xe4, "\xc3\xa4", 2 }, /* LATIN SMALL LETTER A WITH DIAERESIS */ + { 0x2665, "\xe2\x99\xa5", 3 } /* BLACK HEART SUIT */ +}; + +void +test_text_utf8_validation (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + int i; + + for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) + { + const TestData *t = &test_text_data[i]; + gunichar unichar; + char bytes[6]; + int nbytes; + + g_assert (g_unichar_validate (t->unichar)); + + nbytes = g_unichar_to_utf8 (t->unichar, bytes); + bytes[nbytes] = '\0'; + g_assert (nbytes == t->nbytes); + g_assert (memcmp (t->bytes, bytes, nbytes) == 0); + + unichar = g_utf8_get_char_validated (bytes, nbytes); + g_assert (unichar == t->unichar); + } +} + +static int +get_nbytes (ClutterText *text) +{ + const char *s = clutter_text_get_text (text); + return strlen (s); +} + +static int +get_nchars (ClutterText *text) +{ + const char *s = clutter_text_get_text (text); + g_assert (g_utf8_validate (s, -1, NULL)); + return g_utf8_strlen (s, -1); +} + +#define DONT_MOVE_CURSOR (-2) + +static void +insert_unichar (ClutterText *text, gunichar unichar, int position) +{ + if (position > DONT_MOVE_CURSOR) + { + clutter_text_set_cursor_position (text, position); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, position); + } + + clutter_text_insert_unichar (text, unichar); +} + +void +test_text_empty (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + + g_assert_cmpstr (clutter_text_get_text (text), ==, ""); + g_assert_cmpint (*clutter_text_get_text (text), ==, '\0'); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +void +test_text_set_empty (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + + /* annoyingly slightly different from initially empty */ + clutter_text_set_text (text, ""); + g_assert_cmpint (get_nchars (text), ==, 0); + g_assert_cmpint (get_nbytes (text), ==, 0); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +void +test_text_set_text (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + + clutter_text_set_text (text, "abcdef"); + g_assert_cmpint (get_nchars (text), ==, 6); + g_assert_cmpint (get_nbytes (text), ==, 6); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); + + clutter_text_set_cursor_position (text, 5); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 5); + + /* FIXME: cursor position should be -1? + clutter_text_set_text (text, ""); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); + */ + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +void +test_text_append_some (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + int i; + + for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) + { + const TestData *t = &test_text_data[i]; + int j; + + for (j = 1; j <= 4; j++) + { + insert_unichar (text, t->unichar, DONT_MOVE_CURSOR); + + g_assert_cmpint (get_nchars (text), ==, j); + g_assert_cmpint (get_nbytes (text), ==, j * t->nbytes); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); + } + + clutter_text_set_text (text, ""); + } + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +void +test_text_prepend_some (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + int i; + + for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) + { + const TestData *t = &test_text_data[i]; + int j; + + clutter_text_insert_unichar (text, t->unichar); + + g_assert_cmpint (get_nchars (text), ==, 1); + g_assert_cmpint (get_nbytes (text), ==, 1 * t->nbytes); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); + + for (j = 2; j <= 4; j++) + { + insert_unichar (text, t->unichar, 0); + + g_assert_cmpint (get_nchars (text), ==, j); + g_assert_cmpint (get_nbytes (text), ==, j * t->nbytes); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 1); + } + + clutter_text_set_text (text, ""); + } + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +void +test_text_insert (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + int i; + + for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) + { + const TestData *t = &test_text_data[i]; + + clutter_text_insert_unichar (text, t->unichar); + clutter_text_insert_unichar (text, t->unichar); + + insert_unichar (text, t->unichar, 1); + + g_assert_cmpint (get_nchars (text), ==, 3); + g_assert_cmpint (get_nbytes (text), ==, 3 * t->nbytes); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 2); + + clutter_text_set_text (text, ""); + } + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +void +test_text_delete_chars (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + int i; + + for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) + { + const TestData *t = &test_text_data[i]; + int j; + + for (j = 0; j < 4; j++) + clutter_text_insert_unichar (text, t->unichar); + + clutter_text_set_cursor_position (text, 2); + clutter_text_delete_chars (text, 1); + g_assert_cmpint (get_nchars (text), ==, 3); + g_assert_cmpint (get_nbytes (text), ==, 3 * t->nbytes); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 1); + + clutter_text_set_cursor_position (text, 2); + clutter_text_delete_chars (text, 1); + g_assert_cmpint (get_nchars (text), ==, 2); + g_assert_cmpint (get_nbytes (text), ==, 2 * t->nbytes); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 1); + + clutter_text_set_text (text, ""); + } + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +void +test_text_get_chars (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + gchar *chars; + + clutter_text_set_text (text, "00abcdef11"); + g_assert_cmpint (get_nchars (text), ==, 10); + g_assert_cmpint (get_nbytes (text), ==, 10); + g_assert_cmpstr (clutter_text_get_text (text), ==, "00abcdef11"); + + chars = clutter_text_get_chars (text, 2, -1); + g_assert_cmpstr (chars, ==, "abcdef11"); + g_free (chars); + + chars = clutter_text_get_chars (text, 0, 8); + g_assert_cmpstr (chars, ==, "00abcdef"); + g_free (chars); + + chars = clutter_text_get_chars (text, 2, 8); + g_assert_cmpstr (chars, ==, "abcdef"); + g_free (chars); + + chars = clutter_text_get_chars (text, 8, 12); + g_assert_cmpstr (chars, ==, "11"); + g_free (chars); + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +void +test_text_delete_text (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + int i; + + for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) + { + const TestData *t = &test_text_data[i]; + int j; + + for (j = 0; j < 4; j++) + clutter_text_insert_unichar (text, t->unichar); + + clutter_text_set_cursor_position (text, 3); + clutter_text_delete_text (text, 2, 4); + + g_assert_cmpint (get_nchars (text), ==, 2); + g_assert_cmpint (get_nbytes (text), ==, 2 * t->nbytes); + + /* FIXME: cursor position should be -1? + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); + */ + + clutter_text_set_text (text, ""); + } + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +void +test_text_password_char (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + + g_assert_cmpint (clutter_text_get_password_char (text), ==, 0); + + clutter_text_set_text (text, "hello"); + g_assert_cmpstr (clutter_text_get_text (text), ==, "hello"); + + clutter_text_set_password_char (text, '*'); + g_assert_cmpint (clutter_text_get_password_char (text), ==, '*'); + + g_assert_cmpstr (clutter_text_get_text (text), ==, "hello"); + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +static void +init_event (ClutterKeyEvent *event) +{ + event->type = CLUTTER_KEY_PRESS; + event->time = 0; /* not needed */ + event->flags = CLUTTER_EVENT_FLAG_SYNTHETIC; + event->stage = NULL; /* not needed */ + event->source = NULL; /* not needed */ + event->modifier_state = 0; + event->hardware_keycode = 0; /* not needed */ +} + +static void +send_keyval (ClutterText *text, int keyval) +{ + ClutterKeyEvent event; + + init_event (&event); + event.keyval = keyval; + event.unicode_value = 0; /* should be ignored for cursor keys etc. */ + + clutter_actor_event (CLUTTER_ACTOR (text), (ClutterEvent *) &event, FALSE); +} + +static void +send_unichar (ClutterText *text, gunichar unichar) +{ + ClutterKeyEvent event; + + init_event (&event); + event.keyval = 0; /* should be ignored for printable characters */ + event.unicode_value = unichar; + + clutter_actor_event (CLUTTER_ACTOR (text), (ClutterEvent *) &event, FALSE); +} + +void +test_text_cursor (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + int i; + + /* only editable entries listen to events */ + clutter_text_set_editable (text, TRUE); + + for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) + { + const TestData *t = &test_text_data[i]; + int j; + + for (j = 0; j < 4; ++j) + clutter_text_insert_unichar (text, t->unichar); + + clutter_text_set_cursor_position (text, 2); + + /* test cursor moves and is clamped */ + send_keyval (text, CLUTTER_Left); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 1); + + send_keyval (text, CLUTTER_Left); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 0); + + send_keyval (text, CLUTTER_Left); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 0); + + /* delete text containing the cursor */ + clutter_text_set_cursor_position (text, 3); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 3); + + clutter_text_delete_text (text, 2, 4); + send_keyval (text, CLUTTER_Left); + + /* FIXME: cursor position should be -1? + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); + */ + + clutter_text_set_text (text, ""); + } + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +void +test_text_event (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + int i; + + /* only editable entries listen to events */ + clutter_text_set_editable (text, TRUE); + + for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) + { + const TestData *t = &test_text_data[i]; + + send_unichar (text, t->unichar); + + g_assert_cmpint (get_nchars (text), ==, 1); + g_assert_cmpint (get_nbytes (text), ==, 1 * t->nbytes); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); + + clutter_text_set_text (text, ""); + } + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index ff9535baf..1b8d54c0f 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -68,22 +68,21 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/picking", test_pick); - TEST_CONFORM_SIMPLE ("/label", test_label_cache); - - TEST_CONFORM_SIMPLE ("/entry", test_entry_utf8_validation); - TEST_CONFORM_SIMPLE ("/entry", test_entry_empty); - TEST_CONFORM_SIMPLE ("/entry", test_entry_set_empty); - TEST_CONFORM_SIMPLE ("/entry", test_entry_set_text); - - TEST_CONFORM_SIMPLE ("/entry", test_entry_append_some); - TEST_CONFORM_SIMPLE ("/entry", test_entry_prepend_some); - TEST_CONFORM_SIMPLE ("/entry", test_entry_insert); - - TEST_CONFORM_SIMPLE ("/entry", test_entry_delete_chars); - TEST_CONFORM_SIMPLE ("/entry", test_entry_delete_text); - - TEST_CONFORM_SIMPLE ("/entry", test_entry_cursor); - TEST_CONFORM_SIMPLE ("/entry", test_entry_event); + /* ClutterText */ + TEST_CONFORM_SIMPLE ("/text", test_text_utf8_validation); + TEST_CONFORM_SIMPLE ("/text", test_text_empty); + TEST_CONFORM_SIMPLE ("/text", test_text_set_empty); + TEST_CONFORM_SIMPLE ("/text", test_text_set_text); + TEST_CONFORM_SIMPLE ("/text", test_text_append_some); + TEST_CONFORM_SIMPLE ("/text", test_text_prepend_some); + TEST_CONFORM_SIMPLE ("/text", test_text_insert); + TEST_CONFORM_SIMPLE ("/text", test_text_delete_chars); + TEST_CONFORM_SIMPLE ("/text", test_text_delete_text); + TEST_CONFORM_SIMPLE ("/text", test_text_cursor); + TEST_CONFORM_SIMPLE ("/text", test_text_event); + TEST_CONFORM_SIMPLE ("/text", test_text_get_chars); + TEST_CONFORM_SIMPLE ("/text", test_text_cache); + TEST_CONFORM_SIMPLE ("/text", test_text_password_char); TEST_CONFORM_SIMPLE ("/rectangle", test_rect_set_size); TEST_CONFORM_SIMPLE ("/rectangle", test_rect_set_color); diff --git a/tests/conform/test-paint-opacity.c b/tests/conform/test-paint-opacity.c index 77618a98e..3dd48e912 100644 --- a/tests/conform/test-paint-opacity.c +++ b/tests/conform/test-paint-opacity.c @@ -14,12 +14,12 @@ test_label_opacity (TestConformSimpleFixture *fixture, stage = clutter_stage_get_default (); - label = clutter_label_new_with_text ("Sans 18px", "Label, 50% opacity"); - clutter_label_set_color (CLUTTER_LABEL (label), &label_color); + label = clutter_text_new_with_text ("Sans 18px", "Label, 50% opacity"); + clutter_text_set_color (CLUTTER_TEXT (label), &label_color); if (g_test_verbose ()) g_print ("label 50%%.get_color()/1\n"); - clutter_label_get_color (CLUTTER_LABEL (label), &color_check); + clutter_text_get_color (CLUTTER_TEXT (label), &color_check); g_assert (color_check.alpha == label_color.alpha); clutter_container_add (CLUTTER_CONTAINER (stage), label, NULL); @@ -27,12 +27,16 @@ test_label_opacity (TestConformSimpleFixture *fixture, if (g_test_verbose ()) g_print ("label 50%%.get_color()/2\n"); - clutter_label_get_color (CLUTTER_LABEL (label), &color_check); + clutter_text_get_color (CLUTTER_TEXT (label), &color_check); g_assert (color_check.alpha == label_color.alpha); if (g_test_verbose ()) - g_print ("label 50%%.get_paint_opacity() = %d\n", - clutter_actor_get_paint_opacity (label)); + g_print ("label 50%%.get_paint_opacity()/1\n"); + g_assert (clutter_actor_get_paint_opacity (label) == 255); + + if (g_test_verbose ()) + g_print ("label 50%%.get_paint_opacity()/2\n"); + clutter_actor_set_opacity (label, 128); g_assert (clutter_actor_get_paint_opacity (label) == 128); clutter_actor_destroy (label); @@ -66,8 +70,7 @@ test_rectangle_opacity (TestConformSimpleFixture *fixture, g_assert (color_check.alpha == rect_color.alpha); if (g_test_verbose ()) - g_print ("rect 100%%.get_paint_opacity() = %d\n", - clutter_actor_get_paint_opacity (rect)); + g_print ("rect 100%%.get_paint_opacity()\n"); g_assert (clutter_actor_get_paint_opacity (rect) == 255); clutter_actor_destroy (rect); @@ -91,25 +94,24 @@ test_paint_opacity (TestConformSimpleFixture *fixture, clutter_actor_set_position (group1, 10, 30); clutter_actor_show (group1); - label = clutter_label_new_with_text ("Sans 18px", "Label+Group, 25% opacity"); - clutter_label_set_color (CLUTTER_LABEL (label), &label_color); + label = clutter_text_new_with_text ("Sans 18px", "Label+Group, 25% opacity"); + clutter_text_set_color (CLUTTER_TEXT (label), &label_color); if (g_test_verbose ()) g_print ("label 50%% + group 50%%.get_color()/1\n"); - clutter_label_get_color (CLUTTER_LABEL (label), &color_check); + clutter_text_get_color (CLUTTER_TEXT (label), &color_check); g_assert (color_check.alpha == label_color.alpha); clutter_container_add (CLUTTER_CONTAINER (group1), label, NULL); if (g_test_verbose ()) g_print ("label 50%% + group 50%%.get_color()/2\n"); - clutter_label_get_color (CLUTTER_LABEL (label), &color_check); + clutter_text_get_color (CLUTTER_TEXT (label), &color_check); g_assert (color_check.alpha == label_color.alpha); if (g_test_verbose ()) - g_print ("label 50%% + group 50%%.get_paint_opacity() = %d\n", - clutter_actor_get_paint_opacity (label)); - g_assert (clutter_actor_get_paint_opacity (label) == 64); + g_print ("label 50%% + group 50%%.get_paint_opacity() = 128\n"); + g_assert (clutter_actor_get_paint_opacity (label) == 128); clutter_actor_destroy (label); @@ -133,9 +135,7 @@ test_paint_opacity (TestConformSimpleFixture *fixture, g_assert (color_check.alpha == rect_color.alpha); if (g_test_verbose ()) - g_print ("rect 100%%.get_paint_opacity() = %d\n", - clutter_actor_get_paint_opacity (rect)); - + g_print ("rect 100%%.get_paint_opacity()\n"); g_assert (clutter_actor_get_paint_opacity (rect) == 128); clutter_actor_destroy (rect); diff --git a/tests/conform/test-label-cache.c b/tests/conform/test-text-cache.c similarity index 70% rename from tests/conform/test-label-cache.c rename to tests/conform/test-text-cache.c index e9298f8fd..d3a2cf5ec 100644 --- a/tests/conform/test-label-cache.c +++ b/tests/conform/test-text-cache.c @@ -34,7 +34,7 @@ on_paint (ClutterActor *label, CallbackData *data) /* Check whether the layout used for this paint is different from the layout used for the last paint */ - new_layout = clutter_label_get_layout (CLUTTER_LABEL (data->label)); + new_layout = clutter_text_get_layout (CLUTTER_TEXT (data->label)); data->layout_changed = data->old_layout != new_layout; if (data->old_layout) @@ -71,7 +71,17 @@ check_result (CallbackData *data, const char *note, if (memcmp (&test_extents, &data->label_extents, sizeof (PangoRectangle))) { if (g_test_verbose ()) - g_print ("extents are different, "); + g_print ("extents are different: expected: %d, %d, %d, %d " + "-> text: %d, %d, %d, %d\n", + test_extents.x / 1024, + test_extents.y / 1024, + test_extents.width / 1024, + test_extents.height / 1024, + data->label_extents.x / 1024, + data->label_extents.y / 1024, + data->label_extents.width / 1024, + data->label_extents.height / 1024); + fail = TRUE; } else @@ -119,29 +129,29 @@ do_tests (CallbackData *data) PangoAttribute *attr; /* TEST 1: change the text */ - clutter_label_set_text (CLUTTER_LABEL (data->label), "Counter 0"); + clutter_text_set_text (CLUTTER_TEXT (data->label), "Counter 0"); pango_layout_set_text (data->test_layout, "Counter 0", -1); - check_result (data, "Change text", TRUE); + g_assert (check_result (data, "Change text", TRUE) == FALSE); /* TEST 2: change a single character */ - clutter_label_set_text (CLUTTER_LABEL (data->label), "Counter 1"); + clutter_text_set_text (CLUTTER_TEXT (data->label), "Counter 1"); pango_layout_set_text (data->test_layout, "Counter 1", -1); - check_result (data, "Change a single character", TRUE); + g_assert (check_result (data, "Change a single character", TRUE) == FALSE); /* TEST 3: move the label */ clutter_actor_set_position (data->label, 10, 0); - check_result (data, "Move the label", FALSE); + g_assert (check_result (data, "Move the label", FALSE) == FALSE); /* TEST 4: change the font */ - clutter_label_set_font_name (CLUTTER_LABEL (data->label), "Serif 15"); + clutter_text_set_font_name (CLUTTER_TEXT (data->label), "Serif 15"); fd = pango_font_description_from_string ("Serif 15"); pango_layout_set_font_description (data->test_layout, fd); pango_font_description_free (fd); - check_result (data, "Change the font", TRUE); + g_assert (check_result (data, "Change the font", TRUE) == FALSE); /* TEST 5: change the color */ - clutter_label_set_color (CLUTTER_LABEL (data->label), &red); - check_result (data, "Change the color", FALSE); + clutter_text_set_color (CLUTTER_TEXT (data->label), &red); + g_assert (check_result (data, "Change the color", FALSE) == FALSE); /* TEST 6: change the attributes */ attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD); @@ -150,23 +160,23 @@ do_tests (CallbackData *data) attr_list = pango_attr_list_new (); pango_attr_list_insert (attr_list, attr); attr_list_copy = pango_attr_list_copy (attr_list); - clutter_label_set_attributes (CLUTTER_LABEL (data->label), attr_list); + clutter_text_set_attributes (CLUTTER_TEXT (data->label), attr_list); pango_layout_set_attributes (data->test_layout, attr_list_copy); pango_attr_list_unref (attr_list_copy); pango_attr_list_unref (attr_list); - check_result (data, "Change the attributes", TRUE); + g_assert (check_result (data, "Change the attributes", TRUE) == FALSE); /* TEST 7: change the text again */ - clutter_label_set_attributes (CLUTTER_LABEL (data->label), NULL); - clutter_label_set_text (CLUTTER_LABEL (data->label), long_text); + clutter_text_set_attributes (CLUTTER_TEXT (data->label), NULL); + clutter_text_set_text (CLUTTER_TEXT (data->label), long_text); pango_layout_set_attributes (data->test_layout, NULL); pango_layout_set_text (data->test_layout, long_text, -1); - check_result (data, "Change the text again", TRUE); + g_assert (check_result (data, "Change the text again", TRUE) == FALSE); /* TEST 8: enable markup */ - clutter_label_set_use_markup (CLUTTER_LABEL (data->label), TRUE); + clutter_text_set_use_markup (CLUTTER_TEXT (data->label), TRUE); pango_layout_set_markup (data->test_layout, long_text, -1); - check_result (data, "Enable markup", TRUE); + g_assert (check_result (data, "Enable markup", TRUE) == FALSE); /* This part can't be a test because Clutter won't restrict the width if wrapping and ellipsizing is disabled so the extents will @@ -178,39 +188,39 @@ do_tests (CallbackData *data) force_redraw (data); /* TEST 9: enable ellipsize */ - clutter_label_set_ellipsize (CLUTTER_LABEL (data->label), + clutter_text_set_ellipsize (CLUTTER_TEXT (data->label), PANGO_ELLIPSIZE_END); pango_layout_set_ellipsize (data->test_layout, PANGO_ELLIPSIZE_END); - check_result (data, "Enable ellipsize", TRUE); - clutter_label_set_ellipsize (CLUTTER_LABEL (data->label), + g_assert (check_result (data, "Enable ellipsize", TRUE) == FALSE); + clutter_text_set_ellipsize (CLUTTER_TEXT (data->label), PANGO_ELLIPSIZE_NONE); pango_layout_set_ellipsize (data->test_layout, PANGO_ELLIPSIZE_NONE); force_redraw (data); /* TEST 10: enable line wrap */ - clutter_label_set_line_wrap (CLUTTER_LABEL (data->label), TRUE); + clutter_text_set_line_wrap (CLUTTER_TEXT (data->label), TRUE); pango_layout_set_wrap (data->test_layout, PANGO_WRAP_WORD); - check_result (data, "Enable line wrap", TRUE); + g_assert (check_result (data, "Enable line wrap", TRUE) == FALSE); /* TEST 11: change wrap mode */ - clutter_label_set_line_wrap_mode (CLUTTER_LABEL (data->label), + clutter_text_set_line_wrap_mode (CLUTTER_TEXT (data->label), PANGO_WRAP_CHAR); pango_layout_set_wrap (data->test_layout, PANGO_WRAP_CHAR); - check_result (data, "Change wrap mode", TRUE); + g_assert (check_result (data, "Change wrap mode", TRUE) == FALSE); /* TEST 12: enable justify */ - clutter_label_set_justify (CLUTTER_LABEL (data->label), TRUE); + clutter_text_set_justify (CLUTTER_TEXT (data->label), TRUE); pango_layout_set_justify (data->test_layout, TRUE); /* Pango appears to have a bug which means that you can't change the justification after setting the text but this fixes it. See http://bugzilla.gnome.org/show_bug.cgi?id=551865 */ pango_layout_context_changed (data->test_layout); - check_result (data, "Enable justify", TRUE); + g_assert (check_result (data, "Enable justify", TRUE) == FALSE); /* TEST 13: change alignment */ - clutter_label_set_alignment (CLUTTER_LABEL (data->label), PANGO_ALIGN_RIGHT); + clutter_text_set_alignment (CLUTTER_TEXT (data->label), PANGO_ALIGN_RIGHT); pango_layout_set_alignment (data->test_layout, PANGO_ALIGN_RIGHT); - check_result (data, "Change alignment", TRUE); + g_assert (check_result (data, "Change alignment", TRUE) == FALSE); clutter_main_quit (); @@ -218,7 +228,7 @@ do_tests (CallbackData *data) } static PangoLayout * -make_layout_like_label (ClutterLabel *label) +make_layout_like_label (ClutterText *label) { PangoLayout *label_layout, *new_layout; PangoContext *context; @@ -226,7 +236,7 @@ make_layout_like_label (ClutterLabel *label) /* Make another layout using the same context as the layout from the label */ - label_layout = clutter_label_get_layout (label); + label_layout = clutter_text_get_layout (label); context = pango_layout_get_context (label_layout); new_layout = pango_layout_new (context); fd = pango_font_description_from_string (TEST_FONT); @@ -237,8 +247,8 @@ make_layout_like_label (ClutterLabel *label) } void -test_label_cache (TestConformSimpleFixture *fixture, - gconstpointer _data) +test_text_cache (TestConformSimpleFixture *fixture, + gconstpointer _data) { CallbackData data; @@ -246,9 +256,9 @@ test_label_cache (TestConformSimpleFixture *fixture, data.stage = clutter_stage_get_default (); - data.label = clutter_label_new_with_text (TEST_FONT, ""); + data.label = clutter_text_new_with_text (TEST_FONT, ""); - data.test_layout = make_layout_like_label (CLUTTER_LABEL (data.label)); + data.test_layout = make_layout_like_label (CLUTTER_TEXT (data.label)); g_signal_connect (data.label, "paint", G_CALLBACK (on_paint), &data); diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am index 6f98be0ea..33b371437 100644 --- a/tests/interactive/Makefile.am +++ b/tests/interactive/Makefile.am @@ -6,7 +6,6 @@ UNIT_TESTS = \ test-scale.c \ test-actors.c \ test-behave.c \ - test-entry.c \ test-project.c \ test-perspective.c \ test-rotate.c \ @@ -22,7 +21,6 @@ UNIT_TESTS = \ test-unproject.c \ test-viewport.c \ test-fbo.c \ - test-opacity.c \ test-multistage.c \ test-cogl-primitives.c \ test-cogl-tex-tile.c \ @@ -39,7 +37,9 @@ UNIT_TESTS = \ test-layout.c \ test-animation.c \ test-easing.c \ - test-binding-pool.c + test-binding-pool.c \ + test-text.c \ + test-text-field.c if X11_TESTS UNIT_TESTS += test-pixmap.c diff --git a/tests/interactive/test-clip.c b/tests/interactive/test-clip.c index 3b52fdb81..5ccddfbcc 100644 --- a/tests/interactive/test-clip.c +++ b/tests/interactive/test-clip.c @@ -15,7 +15,7 @@ typedef enum struct _Clip { ClipType type; - gint x1, y1, x2, y2; + gint x1, y1, x2, y2; }; struct _CallbackData @@ -24,7 +24,7 @@ struct _CallbackData CoglHandle hand; Clip current_clip; - + GSList *clips; }; @@ -122,7 +122,7 @@ on_paint (ClutterActor *actor, CallbackData *data) { int i; ClutterGeometry stage_size; - guint hand_width, hand_height; + gint hand_width, hand_height; GSList *node; clutter_actor_get_allocation_geometry (data->stage, &stage_size); @@ -242,7 +242,7 @@ on_motion (ClutterActor *stage, ClutterMotionEvent *event, { data->current_clip.x2 = event->x; data->current_clip.y2 = event->y; - + clutter_actor_queue_redraw (stage); } @@ -305,8 +305,8 @@ test_clip_main (int argc, char **argv) data.hand = cogl_texture_new_from_file ("redhand.png", 64, FALSE, COGL_PIXEL_FORMAT_ANY, NULL); - label = clutter_label_new_with_text ("Sans 12px", instructions); - clutter_label_set_line_wrap (CLUTTER_LABEL (label), TRUE); + label = clutter_text_new_with_text ("Sans 12px", instructions); + clutter_text_set_line_wrap (CLUTTER_TEXT (label), TRUE); clutter_actor_set_width (label, clutter_actor_get_width (data.stage) - 310); clutter_actor_set_y (label, clutter_actor_get_height (data.stage) diff --git a/tests/interactive/test-cogl-tex-polygon.c b/tests/interactive/test-cogl-tex-polygon.c index 5e237eda7..1e9d38b18 100644 --- a/tests/interactive/test-cogl-tex-polygon.c +++ b/tests/interactive/test-cogl-tex-polygon.c @@ -297,16 +297,16 @@ frame_cb (ClutterTimeline *timeline, } static void -update_toggle_text (ClutterLabel *button, gboolean val) +update_toggle_text (ClutterText *button, gboolean val) { - clutter_label_set_text (button, val ? "Enabled" : "Disabled"); + clutter_text_set_text (button, val ? "Enabled" : "Disabled"); } static gboolean on_toggle_click (ClutterActor *button, ClutterEvent *event, gboolean *toggle_val) { - update_toggle_text (CLUTTER_LABEL (button), *toggle_val = !*toggle_val); + update_toggle_text (CLUTTER_TEXT (button), *toggle_val = !*toggle_val); return TRUE; } @@ -315,12 +315,12 @@ static ClutterActor * make_toggle (const char *label_text, gboolean *toggle_val) { ClutterActor *group = clutter_group_new (); - ClutterActor *label = clutter_label_new_with_text ("Sans 14", label_text); - ClutterActor *button = clutter_label_new_with_text ("Sans 14", ""); + ClutterActor *label = clutter_text_new_with_text ("Sans 14", label_text); + ClutterActor *button = clutter_text_new_with_text ("Sans 14", ""); clutter_actor_set_reactive (button, TRUE); - update_toggle_text (CLUTTER_LABEL (button), *toggle_val); + update_toggle_text (CLUTTER_TEXT (button), *toggle_val); clutter_actor_set_position (button, clutter_actor_get_width (label) + 10, 0); clutter_container_add (CLUTTER_CONTAINER (group), label, button, NULL); @@ -373,7 +373,7 @@ test_cogl_tex_polygon_main (int argc, char *argv[]) clutter_actor_set_position (filtering_toggle, 0, clutter_actor_get_y (slicing_toggle) - clutter_actor_get_height (filtering_toggle)); - note = clutter_label_new_with_text ("Sans 10", "<- Click to change"); + note = clutter_text_new_with_text ("Sans 10", "<- Click to change"); clutter_actor_set_position (note, clutter_actor_get_width (filtering_toggle) + 10, (clutter_actor_get_height (stage) diff --git a/tests/interactive/test-depth.c b/tests/interactive/test-depth.c index a07eff234..34c024ede 100644 --- a/tests/interactive/test-depth.c +++ b/tests/interactive/test-depth.c @@ -26,7 +26,7 @@ raise_top (gpointer ignored) static ClutterActor * clone_box (ClutterTexture *original) { - guint width, height; + gint width, height; ClutterActor *group; ClutterActor *clone; @@ -83,10 +83,10 @@ janus_group (const gchar *front_text, group = clutter_group_new (); rectangle = clutter_rectangle_new_with_color (&slide_color); - front = clutter_label_new_with_text ("Sans 50px", front_text); - back = clutter_label_new_with_text ("Sans 50px", back_text); - clutter_label_set_color (CLUTTER_LABEL (front), &red); - clutter_label_set_color (CLUTTER_LABEL (back), &green); + front = clutter_text_new_with_text ("Sans 50px", front_text); + back = clutter_text_new_with_text ("Sans 50px", back_text); + clutter_text_set_color (CLUTTER_TEXT (front), &red); + clutter_text_set_color (CLUTTER_TEXT (back), &green); clutter_actor_get_size (front, &width, &height); clutter_actor_get_size (back, &width2, &height2); @@ -134,7 +134,7 @@ test_depth_main (int argc, char *argv[]) clutter_stage_add (stage, group); clutter_actor_show (group); - label = clutter_label_new_with_text ("Mono 26", "Clutter"); + label = clutter_text_new_with_text ("Mono 26", "Clutter"); clutter_actor_set_position (label, 120, 200); clutter_actor_show (label); diff --git a/tests/interactive/test-easing.c b/tests/interactive/test-easing.c index 555aa3c3a..22048ad4e 100644 --- a/tests/interactive/test-easing.c +++ b/tests/interactive/test-easing.c @@ -41,7 +41,7 @@ on_button_press (ClutterActor *actor, current_mode + 1, n_easing_modes); - clutter_label_set_text (CLUTTER_LABEL (easing_mode_label), text); + clutter_text_set_text (CLUTTER_TEXT (easing_mode_label), text); g_free (text); clutter_actor_get_size (main_stage, &stage_width, &stage_height); @@ -97,10 +97,10 @@ test_easing_main (int argc, char *argv[]) current_mode + 1, n_easing_modes); - label = clutter_label_new (); + label = clutter_text_new (); clutter_container_add_actor (CLUTTER_CONTAINER (stage), label); - clutter_label_set_font_name (CLUTTER_LABEL (label), "Sans 18px"); - clutter_label_set_text (CLUTTER_LABEL (label), text); + clutter_text_set_font_name (CLUTTER_TEXT (label), "Sans 18px"); + clutter_text_set_text (CLUTTER_TEXT (label), text); clutter_actor_get_size (label, &label_width, &label_height); clutter_actor_set_position (label, stage_width - label_width - 10, diff --git a/tests/interactive/test-fbo.c b/tests/interactive/test-fbo.c index bdf99f1cb..f30cbf049 100644 --- a/tests/interactive/test-fbo.c +++ b/tests/interactive/test-fbo.c @@ -43,9 +43,9 @@ make_source(void) clutter_group_add (source, actor); - actor = clutter_label_new_with_text ("Sans Bold 50px", "Clutter"); + actor = clutter_text_new_with_text ("Sans Bold 50px", "Clutter"); - clutter_label_set_color (CLUTTER_LABEL (actor), &yellow); + clutter_text_set_color (CLUTTER_TEXT (actor), &yellow); clutter_actor_set_y (actor, clutter_actor_get_height(source) + 5); clutter_group_add (source, actor); diff --git a/tests/interactive/test-layout.c b/tests/interactive/test-layout.c index 9fe17222e..c11900f10 100644 --- a/tests/interactive/test-layout.c +++ b/tests/interactive/test-layout.c @@ -787,7 +787,7 @@ test_layout_main (int argc, char *argv[]) clutter_container_add_actor (CLUTTER_CONTAINER (stage), box); - instructions = clutter_label_new_with_text ("Sans 14", + instructions = clutter_text_new_with_text ("Sans 14", "Instructions:\n" "a - add a new item\n" "d - remove last item\n" @@ -799,7 +799,7 @@ test_layout_main (int argc, char *argv[]) "s - use transformed box\n" "q - quit"); - clutter_label_set_use_markup (CLUTTER_LABEL (instructions), TRUE); + clutter_text_set_use_markup (CLUTTER_TEXT (instructions), TRUE); clutter_actor_set_position (instructions, 450, 10); clutter_container_add_actor (CLUTTER_CONTAINER (stage), instructions); diff --git a/tests/interactive/test-multistage.c b/tests/interactive/test-multistage.c index 19b9c750f..1021f8b90 100644 --- a/tests/interactive/test-multistage.c +++ b/tests/interactive/test-multistage.c @@ -50,10 +50,10 @@ on_button_press (ClutterActor *actor, clutter_container_add_actor (CLUTTER_CONTAINER (new_stage), tex); stage_label = g_strdup_printf ("Stage: %d", ++n_stages); - label = clutter_label_new_with_text ("Mono 12", stage_label); + label = clutter_text_new_with_text ("Mono 12", stage_label); - clutter_label_set_color (CLUTTER_LABEL (label), &white); - clutter_label_set_use_markup (CLUTTER_LABEL (label), TRUE); + clutter_text_set_color (CLUTTER_TEXT (label), &white); + clutter_text_set_use_markup (CLUTTER_TEXT (label), TRUE); width = (clutter_actor_get_width (new_stage) - clutter_actor_get_width (label)) / 2; height = (clutter_actor_get_height (new_stage) @@ -110,7 +110,7 @@ test_multistage_main (int argc, char *argv[]) G_CALLBACK (on_button_press), NULL); - label = clutter_label_new_with_text ("Mono 16", "Default stage"); + label = clutter_text_new_with_text ("Mono 16", "Default stage"); width = (clutter_actor_get_width (stage_default) - clutter_actor_get_width (label)) / 2; diff --git a/tests/interactive/test-opacity.c b/tests/interactive/test-opacity.c deleted file mode 100644 index cef053446..000000000 --- a/tests/interactive/test-opacity.c +++ /dev/null @@ -1,116 +0,0 @@ -#include -#include - -#include -#include - -#include - -G_MODULE_EXPORT int -test_opacity_main (int argc, char *argv[]) -{ - ClutterActor *stage, *group1, *group2, *label, *rect; - ClutterColor label_color = { 255, 0, 0, 128 }; - ClutterColor rect_color = { 0, 0, 255, 255 }; - ClutterColor color_check = { 0, }; - - clutter_init (&argc, &argv); - - stage = clutter_stage_get_default (); - - label = clutter_label_new_with_text ("Sans 18px", "Label, 50% opacity"); - clutter_label_set_color (CLUTTER_LABEL (label), &label_color); - - g_print ("label 50%%.get_color()/1\n"); - clutter_label_get_color (CLUTTER_LABEL (label), &color_check); - g_assert (color_check.alpha == label_color.alpha); - - clutter_container_add (CLUTTER_CONTAINER (stage), label, NULL); - clutter_actor_set_position (label, 10, 10); - - g_print ("label 50%%.get_color()/2\n"); - clutter_label_get_color (CLUTTER_LABEL (label), &color_check); - g_assert (color_check.alpha == label_color.alpha); - - g_print ("label 50%%.get_paint_opacity() = %d\n", - clutter_actor_get_paint_opacity (label)); - g_assert (clutter_actor_get_paint_opacity (label) == 128); - - clutter_actor_show (label); - - group1 = clutter_group_new (); - clutter_actor_set_opacity (group1, 128); - clutter_container_add (CLUTTER_CONTAINER (stage), group1, NULL); - clutter_actor_set_position (group1, 10, 30); - clutter_actor_show (group1); - - label = clutter_label_new_with_text ("Sans 18px", "Label+Group, 25% opacity"); - - clutter_label_set_color (CLUTTER_LABEL (label), &label_color); - - g_print ("label 50%% + group 50%%.get_color()/1\n"); - clutter_label_get_color (CLUTTER_LABEL (label), &color_check); - g_assert (color_check.alpha == label_color.alpha); - - clutter_container_add (CLUTTER_CONTAINER (group1), label, NULL); - - g_print ("label 50%% + group 50%%.get_color()/2\n"); - clutter_label_get_color (CLUTTER_LABEL (label), &color_check); - g_assert (color_check.alpha == label_color.alpha); - - g_print ("label 50%% + group 50%%.get_paint_opacity() = %d\n", - clutter_actor_get_paint_opacity (label)); - g_assert (clutter_actor_get_paint_opacity (label) == 64); - - clutter_actor_show (label); - - group2 = clutter_group_new (); - clutter_container_add (CLUTTER_CONTAINER (group1), group2, NULL); - clutter_actor_set_position (group2, 10, 60); - clutter_actor_show (group2); - - rect = clutter_rectangle_new_with_color (&rect_color); - clutter_actor_set_size (rect, 128, 128); - - g_print ("rect 100%% + group 100%% + group 50%%.get_color()/1\n"); - clutter_rectangle_get_color (CLUTTER_RECTANGLE (rect), &color_check); - g_assert (color_check.alpha == rect_color.alpha); - - clutter_container_add (CLUTTER_CONTAINER (group2), rect, NULL); - - g_print ("rect 100%% + group 100%% + group 50%%.get_color()/2\n"); - clutter_rectangle_get_color (CLUTTER_RECTANGLE (rect), &color_check); - g_assert (color_check.alpha == rect_color.alpha); - - g_print ("rect 100%%.get_paint_opacity() = %d\n", - clutter_actor_get_paint_opacity (rect)); - g_assert (clutter_actor_get_paint_opacity (rect) == 128); - - clutter_actor_show (rect); - - rect = clutter_rectangle_new_with_color (&rect_color); - clutter_actor_set_size (rect, 128, 128); - clutter_actor_set_position (rect, 150, 90); - - g_print ("rect 100%%.get_color()/1\n"); - clutter_rectangle_get_color (CLUTTER_RECTANGLE (rect), &color_check); - g_assert (color_check.alpha == rect_color.alpha); - - clutter_container_add (CLUTTER_CONTAINER (stage), rect, NULL); - - g_print ("rect 100%%.get_color()/2\n"); - clutter_rectangle_get_color (CLUTTER_RECTANGLE (rect), &color_check); - g_assert (color_check.alpha == rect_color.alpha); - - g_print ("rect 100%%.get_paint_opacity() = %d\n", - clutter_actor_get_paint_opacity (rect)); - g_assert (clutter_actor_get_paint_opacity (rect) == 255); - - clutter_actor_show (rect); - - clutter_actor_show_all (stage); - - clutter_main (); - - return EXIT_SUCCESS; -} diff --git a/tests/interactive/test-project.c b/tests/interactive/test-project.c index 1ac2176b7..33560df0b 100644 --- a/tests/interactive/test-project.c +++ b/tests/interactive/test-project.c @@ -224,8 +224,8 @@ test_project_main (int argc, char *argv[]) clutter_actor_set_rotation (rect, CLUTTER_Y_AXIS, 60, 0, 0, 0); clutter_group_add (CLUTTER_GROUP (main_stage), rect); - label = clutter_label_new_with_text ("Mono 8pt", "Drag the blue rectangles"); - clutter_label_set_color (CLUTTER_LABEL (label), &white); + label = clutter_text_new_with_text ("Mono 8pt", "Drag the blue rectangles"); + clutter_text_set_color (CLUTTER_TEXT (label), &white); clutter_actor_set_position (label, 10, 10); clutter_group_add (CLUTTER_GROUP (main_stage), label); diff --git a/tests/interactive/test-random-text.c b/tests/interactive/test-random-text.c index 18ebefff0..b73ec4b4f 100644 --- a/tests/interactive/test-random-text.c +++ b/tests/interactive/test-random-text.c @@ -45,7 +45,7 @@ on_idle (gpointer data) font_names[rand () % FONT_NAME_COUNT], rand () % (MAX_FONT_SIZE - MIN_FONT_SIZE) + MIN_FONT_SIZE); - label = clutter_label_new_with_text (font_name, text); + label = clutter_text_new_with_text (font_name, text); if (clutter_actor_get_height (label) > line_height) line_height = clutter_actor_get_height (label); diff --git a/tests/interactive/test-rotate.c b/tests/interactive/test-rotate.c index 377a309db..b147a7d24 100644 --- a/tests/interactive/test-rotate.c +++ b/tests/interactive/test-rotate.c @@ -33,8 +33,8 @@ test_rotate_main (int argc, char *argv[]) clutter_actor_show (hand); clutter_container_add_actor (CLUTTER_CONTAINER (stage), hand); - label = clutter_label_new_with_text ("Mono 16", "The Wonder of the Spinning Hand"); - clutter_label_set_alignment (CLUTTER_LABEL (label), PANGO_ALIGN_CENTER); + label = clutter_text_new_with_text ("Mono 16", "The Wonder of the Spinning Hand"); + clutter_text_set_alignment (CLUTTER_TEXT (label), PANGO_ALIGN_CENTER); clutter_actor_set_position (label, 150, 150); clutter_actor_set_size (label, 500, 100); clutter_actor_show (label); diff --git a/tests/interactive/test-scale.c b/tests/interactive/test-scale.c index 549fa298b..587cf2016 100644 --- a/tests/interactive/test-scale.c +++ b/tests/interactive/test-scale.c @@ -29,7 +29,7 @@ set_next_gravity (ClutterActor *actor) eclass = g_type_class_ref (CLUTTER_TYPE_GRAVITY); evalue = g_enum_get_value (eclass, gravity); - clutter_label_set_text (CLUTTER_LABEL (label), evalue->value_nick); + clutter_text_set_text (CLUTTER_TEXT (label), evalue->value_nick); g_type_class_unref (eclass); if (++gindex >= G_N_ELEMENTS (gravities)) @@ -59,8 +59,8 @@ test_scale_main (int argc, char *argv[]) clutter_group_add (CLUTTER_GROUP (stage), rect); - label = clutter_label_new_with_text ("Sans 20px", ""); - clutter_label_set_color (CLUTTER_LABEL (label), + label = clutter_text_new_with_text ("Sans 20px", ""); + clutter_text_set_color (CLUTTER_TEXT (label), &(ClutterColor) { 0xff, 0xff, 0xff, 0xff }); clutter_actor_set_position (label, clutter_actor_get_x (rect), diff --git a/tests/interactive/test-shader.c b/tests/interactive/test-shader.c index 4880a704a..1101d8246 100644 --- a/tests/interactive/test-shader.c +++ b/tests/interactive/test-shader.c @@ -354,7 +354,7 @@ test_shader_main (gint argc, gchar *argv[]) if (!child2) g_error("pixbuf load failed: %s", error ? error->message : "Unknown"); child3 = clutter_rectangle_new (); - child4 = clutter_label_new_with_text ("Sans 20px", "Shady stuff"); + child4 = clutter_text_new_with_text ("Sans 20px", "Shady stuff"); clutter_rectangle_set_color (child3, &color); clutter_actor_set_size (child3, 50, 50); diff --git a/tests/interactive/test-stage-read-pixels.c b/tests/interactive/test-stage-read-pixels.c index f13b7ebe7..b0d813970 100644 --- a/tests/interactive/test-stage-read-pixels.c +++ b/tests/interactive/test-stage-read-pixels.c @@ -23,14 +23,14 @@ make_label (void) gchar *text; gchar *argv[] = { "ls", "--help", NULL }; - label = clutter_label_new (); - clutter_label_set_font_name (CLUTTER_LABEL (label), "Sans 10"); + label = clutter_text_new (); + clutter_text_set_font_name (CLUTTER_TEXT (label), "Sans 10"); if (g_spawn_sync (NULL, argv, NULL, G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_SEARCH_PATH, NULL, NULL, &text, NULL, NULL, NULL)) { - clutter_label_set_text (CLUTTER_LABEL (label), text); + clutter_text_set_text (CLUTTER_TEXT (label), text); g_free (text); } diff --git a/tests/interactive/test-text-field.c b/tests/interactive/test-text-field.c new file mode 100644 index 000000000..ca582731d --- /dev/null +++ b/tests/interactive/test-text-field.c @@ -0,0 +1,117 @@ +#include +#include +#include + +#define FONT "Mono Bold 14px" + +static void +on_entry_paint (ClutterActor *actor, + gpointer data) +{ + ClutterActorBox allocation = { 0, }; + ClutterUnit width, height; + + clutter_actor_get_allocation_box (actor, &allocation); + width = allocation.x2 - allocation.x1; + height = allocation.y2 - allocation.y1; + + cogl_set_source_color4ub (255, 255, 255, 255); + cogl_path_round_rectangle (0, 0, + CLUTTER_UNITS_TO_FIXED (width), + CLUTTER_UNITS_TO_FIXED (height), + COGL_FIXED_FROM_INT (4), + COGL_ANGLE_FROM_DEG (1.0)); + cogl_path_stroke (); +} + +static void +on_entry_activate (ClutterText *text, + gpointer data) +{ + g_print ("Text activated: %s\n", clutter_text_get_text (text)); +} + +static ClutterActor * +create_label (const ClutterColor *color, + const gchar *text) +{ + ClutterActor *retval = clutter_text_new_full (FONT, text, color); + + clutter_text_set_editable (CLUTTER_TEXT (retval), FALSE); + clutter_text_set_selectable (CLUTTER_TEXT (retval), FALSE); + + return retval; +} + +static ClutterActor * +create_entry (const ClutterColor *color, + const gchar *text, + gunichar password_char, + gint max_length) +{ + ClutterActor *retval = clutter_text_new_full (FONT, text, color); + ClutterColor selection = { 0, }; + + clutter_actor_set_width (retval, 200); + clutter_actor_set_reactive (retval, TRUE); + + clutter_color_darken (color, &selection); + + clutter_text_set_editable (CLUTTER_TEXT (retval), TRUE); + clutter_text_set_selectable (CLUTTER_TEXT (retval), TRUE); + clutter_text_set_activatable (CLUTTER_TEXT (retval), TRUE); + clutter_text_set_single_line_mode (CLUTTER_TEXT (retval), TRUE); + clutter_text_set_password_char (CLUTTER_TEXT (retval), password_char); + clutter_text_set_cursor_color (CLUTTER_TEXT (retval), &selection); + clutter_text_set_max_length (CLUTTER_TEXT (retval), max_length); + + g_signal_connect (retval, "activate", + G_CALLBACK (on_entry_activate), + NULL); + g_signal_connect (retval, "paint", + G_CALLBACK (on_entry_paint), + NULL); + + return retval; +} + +G_MODULE_EXPORT gint +test_text_field_main (gint argc, + gchar **argv) +{ + ClutterActor *stage; + ClutterActor *text; + ClutterColor entry_color = {0x33, 0xff, 0x33, 0xff}; + ClutterColor label_color = {0xff, 0xff, 0xff, 0xff}; + ClutterColor background_color = {0x00, 0x00, 0x00, 0xff}; + gint height; + + clutter_init (&argc, &argv); + + stage = clutter_stage_get_default (); + clutter_stage_set_color (CLUTTER_STAGE (stage), &background_color); + + text = create_label (&label_color, "Input field: "); + clutter_actor_set_position (text, 10, 10); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), text); + + height = clutter_actor_get_height (text); + + text = create_entry (&entry_color, "some text", 0, 0); + clutter_actor_set_position (text, 200, 10); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), text); + + text = create_label (&label_color, "Password field: "); + clutter_actor_set_position (text, 10, height + 12); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), text); + + text = create_entry (&entry_color, "password", '*', 8); + clutter_actor_set_position (text, 200, height + 12); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), text); + + clutter_actor_show (stage); + + clutter_main (); + + return EXIT_SUCCESS; +} diff --git a/tests/interactive/test-text.c b/tests/interactive/test-text.c new file mode 100644 index 000000000..d9bf879a7 --- /dev/null +++ b/tests/interactive/test-text.c @@ -0,0 +1,89 @@ +#include + +#include +#include + +#define FONT "Mono Bold 22px" + +static gchar *runes = +"ᚠᛇᚻ᛫ᛒᛦᚦ᛫ᚠᚱᚩᚠᚢᚱ᛫ᚠᛁᚱᚪ᛫ᚷᛖᚻᚹᛦᛚᚳᚢᛗ\n" +"ᛋᚳᛖᚪᛚ᛫ᚦᛖᚪᚻ᛫ᛗᚪᚾᚾᚪ᛫ᚷᛖᚻᚹᛦᛚᚳ᛫ᛗᛁᚳᛚᚢᚾ᛫ᚻᛦᛏ᛫ᛞᚫᛚᚪᚾ\n" +"ᚷᛁᚠ᛫ᚻᛖ᛫ᚹᛁᛚᛖ᛫ᚠᚩᚱ᛫ᛞᚱᛁᚻᛏᚾᛖ᛫ᛞᚩᛗᛖᛋ᛫ᚻᛚᛇᛏᚪᚾ᛬\n"; + + +static void +cursor_event (ClutterText *text, + ClutterGeometry *geometry) +{ + gint y; + + y = clutter_actor_get_y (CLUTTER_ACTOR (text)); + + if (y + geometry->y < 50) + { + clutter_actor_set_y (CLUTTER_ACTOR (text), y + 100); + } + else if (y + geometry->y > 720) + { + clutter_actor_set_y (CLUTTER_ACTOR (text), y - 100); + } + +} + +G_MODULE_EXPORT gint +test_text_main (gint argc, + gchar **argv) +{ + ClutterActor *stage; + ClutterActor *text; + ClutterColor text_color = { 0x33, 0xff, 0x33, 0xff }; + ClutterColor cursor_color = { 0xff, 0x33, 0x33, 0xff }; + ClutterColor background_color = { 0x00, 0x00, 0x00, 0xff }; + + clutter_init (&argc, &argv); + + stage = clutter_stage_get_default (); + clutter_stage_set_color (CLUTTER_STAGE (stage), &background_color); + + text = clutter_text_new_full (FONT, "·", &text_color); + + clutter_container_add (CLUTTER_CONTAINER (stage), text, NULL); + clutter_actor_set_position (text, 40, 30); + clutter_actor_set_width (text, 1024); + clutter_text_set_line_wrap (CLUTTER_TEXT (text), TRUE); + + clutter_actor_set_reactive (text, TRUE); + clutter_stage_set_key_focus (CLUTTER_STAGE (stage), text); + + clutter_text_set_editable (CLUTTER_TEXT (text), TRUE); + clutter_text_set_selectable (CLUTTER_TEXT (text), TRUE); + clutter_text_set_cursor_color (CLUTTER_TEXT (text), &cursor_color); + + if (argv[1]) + { + GError *error = NULL; + gchar *utf8; + + g_file_get_contents (argv[1], &utf8, NULL, &error); + if (error) + { + utf8 = g_strconcat ("Unable to open '", argv[1], "':\n", + error->message, + NULL); + g_error_free (error); + } + + clutter_text_set_text (CLUTTER_TEXT (text), utf8); + } + else + clutter_text_set_text (CLUTTER_TEXT (text), runes); + + g_signal_connect (text, "cursor-event", G_CALLBACK (cursor_event), NULL); + + clutter_actor_set_size (stage, 1024, 768); + clutter_actor_show (stage); + + clutter_main (); + + return EXIT_SUCCESS; +} diff --git a/tests/interactive/test-threads.c b/tests/interactive/test-threads.c index 86eb6cfe3..5f946b653 100644 --- a/tests/interactive/test-threads.c +++ b/tests/interactive/test-threads.c @@ -42,7 +42,7 @@ test_thread_done_idle (gpointer user_data) g_print ("Thread completed\n"); - clutter_label_set_text (CLUTTER_LABEL (data->label), "Completed"); + clutter_text_set_text (CLUTTER_TEXT (data->label), "Completed"); clutter_timeline_stop (data->timeline); test_thread_data_free (data); @@ -67,7 +67,7 @@ update_label_idle (gpointer data) text = g_strdup_printf ("Count to %d", update->count); - clutter_label_set_text (CLUTTER_LABEL (update->thread_data->label), text); + clutter_text_set_text (CLUTTER_TEXT (update->thread_data->label), text); clutter_actor_set_width (update->thread_data->label, -1); if (update->count == 0) @@ -151,7 +151,7 @@ on_key_press_event (ClutterStage *stage, switch (clutter_key_event_symbol (event)) { case CLUTTER_s: - clutter_label_set_text (CLUTTER_LABEL (help_label), "Press 'q' to quit"); + clutter_text_set_text (CLUTTER_TEXT (help_label), "Press 'q' to quit"); clutter_timeline_start (timeline); @@ -191,10 +191,10 @@ test_threads_main (int argc, char *argv[]) clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); clutter_actor_set_size (stage, 600, 300); - count_label = clutter_label_new_with_text ("Mono 12", "Counter"); + count_label = clutter_text_new_with_text ("Mono 12", "Counter"); clutter_actor_set_position (count_label, 350, 50); - help_label = clutter_label_new_with_text ("Mono 12", "Press 's' to start"); + help_label = clutter_text_new_with_text ("Mono 12", "Press 's' to start"); clutter_actor_set_position (help_label, 50, 50); rect = clutter_rectangle_new_with_color (&rect_color); diff --git a/tests/interactive/test-unproject.c b/tests/interactive/test-unproject.c index 422647e84..95a83cfde 100644 --- a/tests/interactive/test-unproject.c +++ b/tests/interactive/test-unproject.c @@ -5,13 +5,15 @@ #include #include -ClutterActor *label; - #define RECT_L 200 #define RECT_T 150 #define RECT_W 320 #define RECT_H 240 +static ClutterActor *test_rectangle = NULL; +static ClutterActor *label = NULL; + + static gboolean on_event (ClutterStage *stage, ClutterEvent *event, @@ -29,15 +31,14 @@ on_event (ClutterStage *stage, actor = clutter_stage_get_actor_at_pos (stage, x, y); - if (clutter_actor_transform_stage_point (actor, - CLUTTER_UNITS_FROM_DEVICE (x), - CLUTTER_UNITS_FROM_DEVICE (y), - &xu2, &yu2)) + CLUTTER_UNITS_FROM_DEVICE (x), + CLUTTER_UNITS_FROM_DEVICE (y), + &xu2, &yu2)) { gchar *txt; - if (actor != CLUTTER_ACTOR (stage)) + if (actor == test_rectangle) txt = g_strdup_printf ("Click on rectangle\n" "Screen coords: [%d, %d]\n" "Local coords : [%d, %d]", @@ -52,12 +53,11 @@ on_event (ClutterStage *stage, CLUTTER_UNITS_TO_DEVICE (xu2), CLUTTER_UNITS_TO_DEVICE (yu2)); - clutter_label_set_text (CLUTTER_LABEL (label), txt); + clutter_text_set_text (CLUTTER_TEXT (label), txt); g_free (txt); } else - clutter_label_set_text (CLUTTER_LABEL (label), - "Unprojection failed."); + clutter_text_set_text (CLUTTER_TEXT (label), "Unprojection failed."); } break; @@ -75,9 +75,9 @@ test_unproject_main (int argc, char *argv[]) gchar *txt; ClutterActor *rect, *stage, *label0; int i, rotate_x = 0, rotate_y = 60, rotate_z = 0; - ClutterColor stage_clr = { 0x0, 0x0, 0x0, 0xff }, + ClutterColor stage_clr = { 0x0, 0x0, 0x0, 0xff }, white = { 0xff, 0xff, 0xff, 0xff }, - blue = { 0, 0xff, 0xff, 0xff }; + blue = { 0x0, 0xff, 0xff, 0xff }; for (i = 0; i < argc; ++i) { @@ -95,11 +95,12 @@ test_unproject_main (int argc, char *argv[]) } else if (!strncmp (argv[i], "--help", 6)) { - printf ("%s [--rotage-x=degrees] [--rotage-y=degrees] " - "[--rotage-z=degrees]\n", - argv[0]); + g_print ("%s [--rotage-x=degrees] " + "[--rotage-y=degrees] " + "[--rotage-z=degrees]\n", + argv[0]); - exit (0); + return EXIT_FAILURE; } } @@ -117,6 +118,7 @@ test_unproject_main (int argc, char *argv[]) clutter_actor_set_rotation (rect, CLUTTER_Y_AXIS, rotate_y, 0, 0, 0); clutter_actor_set_rotation (rect, CLUTTER_Z_AXIS, rotate_z, 0, 0, 0); clutter_group_add (CLUTTER_GROUP (stage), rect); + test_rectangle = rect; txt = g_strdup_printf ("Rectangle: L %d, R %d, T %d, B %d\n" "Rotation : x %d, y %d, z %d", @@ -124,8 +126,8 @@ test_unproject_main (int argc, char *argv[]) RECT_T, RECT_T + RECT_H, rotate_x, rotate_y, rotate_z); - label0 = clutter_label_new_with_text ("Mono 8pt", txt); - clutter_label_set_color (CLUTTER_LABEL (label0), &white); + label0 = clutter_text_new_with_text ("Mono 8pt", txt); + clutter_text_set_color (CLUTTER_TEXT (label0), &white); clutter_actor_set_position (label0, 10, 10); clutter_group_add (CLUTTER_GROUP (stage), label0); @@ -133,9 +135,9 @@ test_unproject_main (int argc, char *argv[]) g_free (txt); label = - clutter_label_new_with_text ("Mono 8pt", "Click around!"); + clutter_text_new_with_text ("Mono 8pt", "Click around!"); - clutter_label_set_color (CLUTTER_LABEL (label), &blue); + clutter_text_set_color (CLUTTER_TEXT (label), &blue); clutter_actor_set_position (label, 10, 50); clutter_group_add (CLUTTER_GROUP (stage), label); @@ -146,5 +148,8 @@ test_unproject_main (int argc, char *argv[]) clutter_main(); + test_rectangle = NULL; + label = NULL; + return EXIT_SUCCESS; } diff --git a/tests/micro-bench/test-text.c b/tests/micro-bench/test-text.c index a44f39d4e..afbb8b47b 100644 --- a/tests/micro-bench/test-text.c +++ b/tests/micro-bench/test-text.c @@ -80,14 +80,14 @@ main (int argc, char *argv[]) scale = 1.0; } - label = clutter_label_new_with_text (font_name, text); - clutter_label_set_color (CLUTTER_LABEL (label), &label_color); + label = clutter_text_new_with_text (font_name, text); + clutter_text_set_color (CLUTTER_TEXT (label), &label_color); clutter_actor_set_position (label, (1.0*STAGE_WIDTH/COLS)*col, (1.0*STAGE_HEIGHT/ROWS)*row); /*clutter_actor_set_clip (label, 0,0, (1.0*STAGE_WIDTH/COLS), (1.0*STAGE_HEIGHT/ROWS));*/ clutter_actor_set_scale (label, scale, scale); - clutter_label_set_line_wrap (CLUTTER_LABEL (label), FALSE); + clutter_text_set_line_wrap (CLUTTER_TEXT (label), FALSE); clutter_container_add_actor (CLUTTER_CONTAINER (stage), label); } } diff --git a/tests/tools/Makefile.am b/tests/tools/Makefile.am index af5efce4d..43fd46ca6 100644 --- a/tests/tools/Makefile.am +++ b/tests/tools/Makefile.am @@ -10,6 +10,12 @@ libdisable_npots_la_SOURCES = disable-npots.c libdisable_npots_la_LIBADD = -ldl +INCLUDES = \ + -I$(top_srcdir)/clutter \ + -I$(top_builddir)/clutter \ + $(CLUTTER_CFLAGS) \ + -D_GNU_SOURCE + all-local : disable-npots.sh clean-local : diff --git a/tests/tools/disable-npots.c b/tests/tools/disable-npots.c index baa1f5778..2a942d2af 100644 --- a/tests/tools/disable-npots.c +++ b/tests/tools/disable-npots.c @@ -4,13 +4,22 @@ * overrides glGetString and removes the extension strings. */ -#include +/* This is just included to get the right GL header */ +#include + #include #include #include #include #include +/* If RTLD_NEXT isn't available then try just using NULL */ +#ifdef RTLD_NEXT +#define LIB_HANDLE RTLD_NEXT +#else +#define LIB_HANDLE NULL +#endif + typedef const GLubyte * (* GetStringFunc) (GLenum name); static const char * const bad_strings[] @@ -23,16 +32,14 @@ const GLubyte * glGetString (GLenum name) { const GLubyte *ret = NULL; - static void *gl_lib = NULL; static GetStringFunc func = NULL; static GLubyte *extensions = NULL; - if (gl_lib == NULL - && (gl_lib = dlopen ("libGL.so", RTLD_LAZY)) == NULL) - fprintf (stderr, "dlopen: %s\n", dlerror ()); - else if (func == NULL - && (func = (GetStringFunc) dlsym (gl_lib, "glGetString")) == NULL) + if (func == NULL + && (func = (GetStringFunc) dlsym (LIB_HANDLE, "glGetString")) == NULL) fprintf (stderr, "dlsym: %s\n", dlerror ()); + else if (func == glGetString) + fprintf (stderr, "dlsym returned the wrapper of glGetString\n"); else { ret = (* func) (name);