You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kmplayer/src/viewarea.cpp

1692 lines
61 KiB

/**
This file belong to the KMPlayer project, a movie player plugin for Konqueror
Copyright (C) 2007 Koos Vriezen <koos.vriezen@gmail.com>
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**/
#include <config.h>
#include <stdlib.h>
#include <math.h>
#include <tqapplication.h>
#include <tqwidgetstack.h>
#include <tqslider.h>
#include <tqcursor.h>
#include <tqimage.h>
#include <tqmap.h>
#include <tdeactioncollection.h>
#include <kstaticdeleter.h>
#include <kstatusbar.h>
#include <kstdaction.h>
#include <tdeshortcut.h>
#include <tdelocale.h>
#include <kdebug.h>
#include "kmplayerview.h"
#include "kmplayercontrolpanel.h"
#include "playlistview.h"
#include "viewarea.h"
#ifdef HAVE_CAIRO
# include <cairo-xlib.h>
# include <cairo-xlib-xrender.h>
#endif
#include "kmplayer_smil.h"
#include "kmplayer_rp.h"
using namespace KMPlayer;
extern const char * normal_window_xpm[];
extern const char * playlist_xpm[];
//-------------------------------------------------------------------------
namespace KMPlayer {
typedef TQMap <TQString, ImageDataPtrW> ImageDataMap;
static KStaticDeleter <ImageDataMap> imageCacheDeleter;
static ImageDataMap * image_data_map;
}
ImageData::ImageData( const TQString & img) :
image (0L),
url (img) {
//if (img.isEmpty ())
// //kdDebug() << "New ImageData for " << this << endl;
//else
// //kdDebug() << "New ImageData for " << img << endl;
}
ImageData::~ImageData() {
if (!url.isEmpty ())
image_data_map->erase (url);
delete image;
}
#ifdef HAVE_CAIRO
static void copyImage (Surface *s, int w, int h, TQImage *img, cairo_surface_t *similar) {
int iw = img->width ();
int ih = img->height ();
if (img->depth () < 24) {
TQImage qi = img->convertDepth (32, 0);
*img = qi;
}
cairo_surface_t *sf = cairo_image_surface_create_for_data (
img->bits (),
img->hasAlphaBuffer () ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24,
iw, ih, img->bytesPerLine ());
cairo_pattern_t *img_pat = cairo_pattern_create_for_surface (sf);
cairo_pattern_set_extend (img_pat, CAIRO_EXTEND_NONE);
if (w != iw && h != ih) {
cairo_matrix_t mat;
cairo_matrix_init_scale (&mat, 1.0 * iw/w, 1.0 * ih/h);
cairo_pattern_set_matrix (img_pat, &mat);
}
if (!s->surface)
s->surface = cairo_surface_create_similar (similar,
img->hasAlphaBuffer () ?
CAIRO_CONTENT_COLOR_ALPHA : CAIRO_CONTENT_COLOR, w, h);
cairo_t *cr = cairo_create (s->surface);
cairo_set_source (cr, img_pat);
cairo_paint (cr);
cairo_destroy (cr);
cairo_pattern_destroy (img_pat);
cairo_surface_destroy (sf);
}
#endif
bool CachedImage::isEmpty () {
return !data || !data->image;
}
void CachedImage::setUrl (const TQString & url) {
if (url.isEmpty ()) {
data = ImageDataPtr (new ImageData (url));
} else {
ImageDataMap::iterator i = image_data_map->find (url);
if (i == image_data_map->end ()) {
data = ImageDataPtr (new ImageData (url));
image_data_map->insert (url, ImageDataPtrW (data));
} else {
ImageDataPtr safe = i.data ();
data = safe;
}
}
}
//-------------------------------------------------------------------------
namespace KMPlayer {
class KMPLAYER_NO_EXPORT ViewSurface : public Surface {
public:
ViewSurface (ViewArea * widget);
ViewSurface (ViewArea * widget, NodePtr owner, const SRect & rect);
~ViewSurface ();
void clear () { m_first_child = 0L; }
SurfacePtr createSurface (NodePtr owner, const SRect & rect);
IRect toScreen (Single x, Single y, Single w, Single h);
void resize (const SRect & rect);
void repaint ();
void repaint (const SRect &rect);
void video ();
NodePtrW current_video;
ViewArea * view_widget;
};
} // namespace
KDE_NO_CDTOR_EXPORT ViewSurface::ViewSurface (ViewArea * widget)
: Surface (NULL, SRect (0, 0, widget->width (), widget->height ())),
view_widget (widget)
{}
KDE_NO_CDTOR_EXPORT
ViewSurface::ViewSurface (ViewArea * widget, NodePtr owner, const SRect & rect)
: Surface (owner, rect), view_widget (widget) {}
KDE_NO_CDTOR_EXPORT ViewSurface::~ViewSurface() {
//kdDebug() << "~ViewSurface" << endl;
}
SurfacePtr ViewSurface::createSurface (NodePtr owner, const SRect & rect) {
SurfacePtr surface = new ViewSurface (view_widget, owner, rect);
appendChild (surface);
return surface;
}
KDE_NO_EXPORT void ViewSurface::resize (const SRect &r) {
bounds = r;
#ifdef HAVE_CAIRO
if (surface)
cairo_xlib_surface_set_size (surface, (int)r.width(), (int)r.height ());
#endif
/*if (rect == nrect)
;//return;
SRect pr = rect.unite (nrect); // for repaint
rect = nrect;*/
}
KDE_NO_EXPORT IRect ViewSurface::toScreen (Single x, Single y, Single w, Single h) {
Matrix matrix (0, 0, xscale, yscale);
matrix.translate (bounds.x (), bounds.y ());
for (SurfacePtr s = parentNode(); s; s = s->parentNode()) {
matrix.transform(Matrix (0, 0, s->xscale, s->yscale));
matrix.translate (s->bounds.x (), s->bounds.y ());
}
matrix.getXYWH (x, y, w, h);
return IRect (x, y, w, h);
}
KDE_NO_EXPORT
void ViewSurface::repaint (const SRect &r) {
markDirty ();
view_widget->scheduleRepaint (toScreen (r.x (), r.y (), r.width (), r.height ()));
//kdDebug() << "Surface::repaint x:" << (int)x << " y:" << (int)y << " w:" << (int)w << " h:" << (int)h << endl;
}
KDE_NO_EXPORT
void ViewSurface::repaint () {
markDirty ();
view_widget->scheduleRepaint (toScreen (0, 0, bounds.width (), bounds.height ()));
}
KDE_NO_EXPORT void ViewSurface::video () {
view_widget->setAudioVideoNode (node);
kdDebug() << "Surface::video:" << background_color << " " << (background_color & 0xff000000) << endl;
xscale = yscale = 1; // either scale width/heigt or use bounds
view_widget->setAudioVideoGeometry (toScreen (0, 0, bounds.width(), bounds.height ()),
(background_color & 0xff000000 ? &background_color : 0));
}
//-------------------------------------------------------------------------
#ifdef HAVE_CAIRO
static cairo_surface_t * cairoCreateSurface (Window id, int w, int h) {
Display * display = tqt_xdisplay ();
return cairo_xlib_surface_create (display, id,
DefaultVisual (display, DefaultScreen (display)), w, h);
/*return cairo_xlib_surface_create_with_xrender_format (
tqt_xdisplay (),
id,
DefaultScreenOfDisplay (tqt_xdisplay ()),
XRenderFindVisualFormat (tqt_xdisplay (),
DefaultVisual (tqt_xdisplay (),
DefaultScreen (tqt_xdisplay ()))),
w, h);*/
}
# define CAIRO_SET_SOURCE_RGB(cr,c) \
cairo_set_source_rgb ((cr), \
1.0 * (((c) >> 16) & 0xff) / 255, \
1.0 * (((c) >> 8) & 0xff) / 255, \
1.0 * (((c)) & 0xff) / 255)
class KMPLAYER_NO_EXPORT CairoPaintVisitor : public Visitor {
IRect clip;
cairo_surface_t * cairo_surface;
Matrix matrix;
// stack vars need for transitions
SMIL::MediaType *cur_media;
cairo_pattern_t * cur_pat;
cairo_matrix_t cur_mat;
float opacity;
bool toplevel;
void traverseRegion (SMIL::RegionBase * reg);
void updateExternal (SMIL::MediaType *av, SurfacePtr s);
void paint(SMIL::MediaType *, Surface *, int x, int y, const IRect &);
public:
cairo_t * cr;
CairoPaintVisitor (cairo_surface_t * cs, Matrix m,
const IRect & rect, TQColor c=TQColor(), bool toplevel=false);
~CairoPaintVisitor ();
using Visitor::visit;
void visit (Node * n);
void visit (SMIL::Layout *);
void visit (SMIL::Region *);
void visit (SMIL::Transition *);
void visit (SMIL::ImageMediaType *);
void visit (SMIL::TextMediaType *);
void visit (SMIL::Brush *);
void visit (SMIL::RefMediaType *);
void visit (SMIL::AVMediaType *);
void visit (RP::Imfl *);
void visit (RP::Fill *);
void visit (RP::Fadein *);
void visit (RP::Fadeout *);
void visit (RP::Crossfade *);
void visit (RP::Wipe *);
void visit (RP::ViewChange *);
};
KDE_NO_CDTOR_EXPORT
CairoPaintVisitor::CairoPaintVisitor (cairo_surface_t * cs, Matrix m,
const IRect & rect, TQColor c, bool top)
: clip (rect), cairo_surface (cs), matrix (m), toplevel (top) {
cr = cairo_create (cs);
if (toplevel) {
cairo_rectangle (cr, rect.x, rect.y, rect.w, rect.h);
cairo_clip (cr);
//cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
cairo_set_tolerance (cr, 0.5 );
cairo_push_group (cr);
cairo_set_source_rgb (cr,
1.0 * c.red () / 255, 1.0 * c.green () / 255, 1.0 * c.blue () / 255);
cairo_rectangle (cr, rect.x, rect.y, rect.w, rect.h);
cairo_fill (cr);
} else {
cairo_save (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
cairo_rectangle (cr, rect.x, rect.y, rect.w, rect.h);
cairo_fill (cr);
cairo_restore (cr);
}
}
KDE_NO_CDTOR_EXPORT CairoPaintVisitor::~CairoPaintVisitor () {
if (toplevel) {
cairo_pattern_t * pat = cairo_pop_group (cr);
//cairo_pattern_set_extend (pat, CAIRO_EXTEND_NONE);
cairo_set_source (cr, pat);
cairo_rectangle (cr, clip.x, clip.y, clip.w, clip.h);
cairo_fill (cr);
cairo_pattern_destroy (pat);
}
cairo_destroy (cr);
}
KDE_NO_EXPORT void CairoPaintVisitor::visit (Node * n) {
kdWarning() << "Paint called on " << n->nodeName() << endl;
}
KDE_NO_EXPORT void CairoPaintVisitor::traverseRegion (SMIL::RegionBase * reg) {
// next visit listeners
NodeRefListPtr nl = reg->listeners (mediatype_attached);
if (nl) {
for (NodeRefItemPtr c = nl->first(); c; c = c->nextSibling ())
if (c->data)
c->data->accept (this);
}
// finally visit children, accounting for z-order FIXME optimize
NodeRefList sorted;
for (NodePtr n = reg->firstChild (); n; n = n->nextSibling ()) {
if (n->id != SMIL::id_node_region)
continue;
SMIL::Region * r = static_cast <SMIL::Region *> (n.ptr ());
NodeRefItemPtr rn = sorted.first ();
for (; rn; rn = rn->nextSibling ())
if (r->z_order < convertNode <SMIL::Region> (rn->data)->z_order) {
sorted.insertBefore (new NodeRefItem (n), rn);
break;
}
if (!rn)
sorted.append (new NodeRefItem (n));
}
for (NodeRefItemPtr r = sorted.first (); r; r = r->nextSibling ())
r->data->accept (this);
}
KDE_NO_EXPORT void CairoPaintVisitor::visit (SMIL::Layout * reg) {
//kdDebug() << "Visit " << reg->nodeName() << endl;
SMIL::RegionBase *rb = convertNode <SMIL::RegionBase> (reg->rootLayout);
if (reg->surface () && rb) {
//cairo_save (cr);
Matrix m = matrix;
SRect rect = reg->region_surface->bounds;
Single x, y, w = rect.width(), h = rect.height();
matrix.getXYWH (x, y, w, h);
IRect clip_save = clip;
clip = clip.intersect (IRect (x, y, w, h));
rb->region_surface = reg->region_surface;
rb->region_surface->background_color = rb->background_color;
if (reg->region_surface->background_color & 0xff000000) {
CAIRO_SET_SOURCE_RGB (cr, reg->region_surface->background_color);
cairo_rectangle (cr, clip.x, clip.y, clip.w, clip.h);
cairo_fill (cr);
}
//cairo_rectangle (cr, xoff, yoff, w, h);
//cairo_clip (cr);
matrix = Matrix (0, 0, reg->region_surface->xscale, reg->region_surface->yscale);
matrix.transform (m);
traverseRegion (reg);
//cairo_restore (cr);
matrix = m;
clip = clip_save;
rb->region_surface = 0L;
}
}
KDE_NO_EXPORT void CairoPaintVisitor::visit (SMIL::Region * reg) {
Surface *s = reg->surface ();
if (s) {
SRect rect = s->bounds;
Matrix m = matrix;
Single x = rect.x(), y = rect.y(), w = rect.width(), h = rect.height();
matrix.getXYWH (x, y, w, h);
if (clip.intersect (IRect (x, y, w, h)).isEmpty ())
return;
matrix = Matrix (rect.x(), rect.y(), 1.0, 1.0);
matrix.transform (m);
IRect clip_save = clip;
clip = clip.intersect (IRect (x, y, w, h));
cairo_save (cr);
if ((SMIL::RegionBase::ShowAlways == reg->show_background ||
reg->m_AttachedMediaTypes->first ()) &&
(s->background_color & 0xff000000 ||
!reg->cached_img.isEmpty ())) {
cairo_save (cr);
if (s->background_color & 0xff000000) {
CAIRO_SET_SOURCE_RGB (cr, s->background_color);
cairo_rectangle (cr, clip.x, clip.y, clip.w, clip.h);
cairo_fill (cr);
}
if (!reg->cached_img.isEmpty ()) {
Single x1, y1;
Single w = reg->cached_img.data->image->width ();
Single h = reg->cached_img.data->image->height();
matrix.getXYWH (x1, y1, w, h);
if (!s->surface)
copyImage (s, w, h, reg->cached_img.data->image, cairo_surface);
cairo_pattern_t *pat = cairo_pattern_create_for_surface (s->surface);
cairo_pattern_set_extend (pat, CAIRO_EXTEND_REPEAT);
cairo_matrix_t mat;
cairo_matrix_init_translate (&mat, (int) -x, (int) -y);
cairo_pattern_set_matrix (pat, &mat);
cairo_set_source (cr, pat);
cairo_rectangle (cr, clip.x, clip.y, clip.w, clip.h);
cairo_fill (cr);
cairo_pattern_destroy (pat);
}
cairo_restore (cr);
}
traverseRegion (reg);
cairo_restore (cr);
matrix = m;
clip = clip_save;
}
}
#define CAIRO_SET_PATTERN_COND(cr,pat,mat) \
if (pat) { \
cairo_pattern_set_extend (cur_pat, CAIRO_EXTEND_NONE); \
cairo_pattern_set_matrix (pat, &mat); \
cairo_pattern_set_filter (pat, CAIRO_FILTER_FAST); \
cairo_set_source (cr, pat); \
}
KDE_NO_EXPORT void CairoPaintVisitor::visit (SMIL::Transition *trans) {
float perc = trans->start_progress + (trans->end_progress - trans->start_progress)*cur_media->trans_step / cur_media->trans_steps;
if (cur_media->trans_out_active)
perc = 1.0 - perc;
if (SMIL::Transition::Fade == trans->type) {
CAIRO_SET_PATTERN_COND(cr, cur_pat, cur_mat)
cairo_rectangle (cr, clip.x, clip.y, clip.w, clip.h);
opacity = perc;
} else if (SMIL::Transition::BarWipe == trans->type) {
IRect rect;
if (SMIL::Transition::SubTopToBottom == trans->sub_type) {
if (SMIL::Transition::dir_reverse == trans->direction) {
int dy = (int) ((1.0 - perc) * clip.h);
rect = IRect (clip.x, clip.y + dy, clip.w, clip.h - dy);
} else {
rect = IRect (clip.x, clip.y, clip.w, (int) (perc * clip.h));
}
} else {
if (SMIL::Transition::dir_reverse == trans->direction) {
int dx = (int) ((1.0 - perc) * clip.w);
rect = IRect (clip.x + dx, clip.y, clip.w - dx, clip.h);
} else {
rect = IRect (clip.x, clip.y, (int) (perc * clip.w), clip.h);
}
}
cairo_rectangle (cr, rect.x, rect.y, rect.w, rect.h);
CAIRO_SET_PATTERN_COND(cr, cur_pat, cur_mat)
} else if (SMIL::Transition::PushWipe == trans->type) {
int dx = 0, dy = 0;
if (SMIL::Transition::SubFromTop == trans->sub_type)
dy = -(int) ((1.0 - perc) * clip.h);
else if (SMIL::Transition::SubFromRight == trans->sub_type)
dx = (int) ((1.0 - perc) * clip.w);
else if (SMIL::Transition::SubFromBottom == trans->sub_type)
dy = (int) ((1.0 - perc) * clip.h);
else //if (SMIL::Transition::SubFromLeft == trans->sub_type)
dx = -(int) ((1.0 - perc) * clip.w);
cairo_matrix_translate (&cur_mat, -dx, -dy);
IRect rect = clip.intersect (IRect (clip.x + dx, clip.y + dy,
clip.w - dx, clip.h - dy));
cairo_rectangle (cr, rect.x, rect.y, rect.w, rect.h);
CAIRO_SET_PATTERN_COND(cr, cur_pat, cur_mat)
} else if (SMIL::Transition::IrisWipe == trans->type) {
CAIRO_SET_PATTERN_COND(cr, cur_pat, cur_mat)
if (SMIL::Transition::SubDiamond == trans->sub_type) {
cairo_rectangle (cr, clip.x, clip.y, clip.w, clip.h);
cairo_clip (cr);
int dx = (int) (perc * clip.w);
int dy = (int) (perc * clip.h);
int mx = clip.x + clip.w/2;
int my = clip.y + clip.h/2;
cairo_new_path (cr);
cairo_move_to (cr, mx, my - dy);
cairo_line_to (cr, mx + dx, my);
cairo_line_to (cr, mx, my + dy);
cairo_line_to (cr, mx - dx, my);
cairo_close_path (cr);
} else { // SubRectangle
int dx = (int) (0.5 * (1 - perc) * clip.w);
int dy = (int) (0.5 * (1 - perc) * clip.h);
cairo_rectangle (cr, clip.x + dx, clip.y + dy,
clip.w - 2 * dx, clip.h -2 * dy);
}
} else if (SMIL::Transition::ClockWipe == trans->type) {
cairo_rectangle (cr, clip.x, clip.y, clip.w, clip.h);
cairo_clip (cr);
int mx = clip.x + clip.w/2;
int my = clip.y + clip.h/2;
cairo_new_path (cr);
cairo_move_to (cr, mx, my);
float hw = 1.0 * clip.w/2;
float hh = 1.0 * clip.h/2;
float radius = sqrtf (hw * hw + hh * hh);
float phi;
switch (trans->sub_type) {
case SMIL::Transition::SubClockwiseThree:
phi = 0;
break;
case SMIL::Transition::SubClockwiseSix:
phi = M_PI / 2;
break;
case SMIL::Transition::SubClockwiseNine:
phi = M_PI;
break;
default: // Twelve
phi = -M_PI / 2;
break;
}
if (SMIL::Transition::dir_reverse == trans->direction)
cairo_arc_negative (cr, mx, my, radius, phi, phi - 2 * M_PI * perc);
else
cairo_arc (cr, mx, my, radius, phi, phi + 2 * M_PI * perc);
cairo_close_path (cr);
CAIRO_SET_PATTERN_COND(cr, cur_pat, cur_mat)
} else if (SMIL::Transition::BowTieWipe == trans->type) {
cairo_rectangle (cr, clip.x, clip.y, clip.w, clip.h);
cairo_clip (cr);
int mx = clip.x + clip.w/2;
int my = clip.y + clip.h/2;
cairo_new_path (cr);
cairo_move_to (cr, mx, my);
float hw = 1.0 * clip.w/2;
float hh = 1.0 * clip.h/2;
float radius = sqrtf (hw * hw + hh * hh);
float phi;
switch (trans->sub_type) {
case SMIL::Transition::SubHorizontal:
phi = 0;
break;
default: // Vertical
phi = -M_PI / 2;
break;
}
float dphi = 0.5 * M_PI * perc;
cairo_arc (cr, mx, my, radius, phi - dphi, phi + dphi);
cairo_close_path (cr);
cairo_new_sub_path (cr);
cairo_move_to (cr, mx, my);
if (SMIL::Transition::SubHorizontal == trans->sub_type)
cairo_arc (cr, mx, my, radius, M_PI + phi - dphi, M_PI + phi +dphi);
else
cairo_arc (cr, mx, my, radius, -phi - dphi, -phi + dphi);
cairo_close_path (cr);
CAIRO_SET_PATTERN_COND(cr, cur_pat, cur_mat)
} else if (SMIL::Transition::EllipseWipe == trans->type) {
cairo_rectangle (cr, clip.x, clip.y, clip.w, clip.h);
cairo_clip (cr);
int mx = clip.x + clip.w/2;
int my = clip.y + clip.h/2;
float hw = (double) clip.w/2;
float hh = (double) clip.h/2;
float radius = sqrtf (hw * hw + hh * hh);
cairo_save (cr);
cairo_new_path (cr);
cairo_translate (cr, (int) mx, (int) my);
cairo_move_to (cr, - Single (radius), 0);
if (SMIL::Transition::SubHorizontal == trans->sub_type)
cairo_scale (cr, 1.0, 0.6);
else if (SMIL::Transition::SubVertical == trans->sub_type)
cairo_scale (cr, 0.6, 1.0);
cairo_arc (cr, 0, 0, perc * radius, 0, 2 * M_PI);
cairo_close_path (cr);
cairo_restore (cr);
CAIRO_SET_PATTERN_COND(cr, cur_pat, cur_mat)
}
}
KDE_NO_EXPORT void CairoPaintVisitor::visit (SMIL::RefMediaType *ref) {
Surface *s = ref->surface ();
if (s) {
if (ref->external_tree)
updateExternal (ref, s);
else if (ref->needsVideoWidget ())
s->video ();
}
}
KDE_NO_EXPORT void CairoPaintVisitor::paint (SMIL::MediaType *mt, Surface *s,
int x, int y, const IRect &rect) {
cairo_save (cr);
opacity = 1.0;
cairo_matrix_init_translate (&cur_mat, -x, -y);
cur_pat = cairo_pattern_create_for_surface (s->surface);
if (mt->active_trans) {
IRect clip_save = clip;
clip = rect;
cur_media = mt;
mt->active_trans->accept (this);
clip = clip_save;
} else {
cairo_pattern_set_extend (cur_pat, CAIRO_EXTEND_NONE);
cairo_pattern_set_matrix (cur_pat, &cur_mat);
cairo_pattern_set_filter (cur_pat, CAIRO_FILTER_FAST);
cairo_set_source (cr, cur_pat);
cairo_rectangle (cr, rect.x, rect.y, rect.w, rect.h);
}
opacity *= mt->opacity / 100.0;
if (opacity < 0.99) {
cairo_clip (cr);
cairo_paint_with_alpha (cr, opacity);
} else {
cairo_fill (cr);
}
cairo_pattern_destroy (cur_pat);
cairo_restore (cr);
}
KDE_NO_EXPORT
void CairoPaintVisitor::updateExternal (SMIL::MediaType *av, SurfacePtr s) {
SRect rect = s->bounds;
Single x = rect.x ();
Single y = rect.y ();
Single w = rect.width();
Single h = rect.height();
matrix.getXYWH (x, y, w, h);
IRect clip_rect = clip.intersect (IRect (x, y, w, h));
if (!clip_rect.isValid ())
return;
if (!s->surface || s->dirty) {
Matrix m = matrix;
m.translate (-x, -y);
IRect r (clip_rect.x - (int) x - 1, clip_rect.y - (int) y - 1,
clip_rect.w + 3, clip_rect.h + 3);
if (!s->surface) {
s->surface = cairo_surface_create_similar (cairo_surface,
CAIRO_CONTENT_COLOR_ALPHA, (int) w, (int) h);
r = IRect (0, 0, w, h);
}
CairoPaintVisitor visitor (s->surface, m, r);
av->external_tree->accept (&visitor);
s->dirty = false;
}
paint (av, s.ptr (), x, y, clip_rect);
}
KDE_NO_EXPORT void CairoPaintVisitor::visit (SMIL::AVMediaType *av) {
Surface *s = av->surface ();
if (s) {
if (av->external_tree)
updateExternal (av, s);
else if (av->needsVideoWidget ())
s->video ();
}
}
KDE_NO_EXPORT void CairoPaintVisitor::visit (SMIL::ImageMediaType * img) {
//kdDebug() << "Visit " << img->nodeName() << " " << img->src << endl;
Surface *s = img->surface ();
if (!s)
return;
if (img->external_tree) {
updateExternal (img, s);
return;
}
ImageRuntime * ir = static_cast <ImageRuntime *> (img->runtime ());
ImageData * id = ir->cached_img.data.ptr ();
if (!id || !id->image || img->width <= 0 || img->height <= 0) {
s->remove();
return;
}
SRect rect = s->bounds;
Single x = rect.x ();
Single y = rect.y ();
Single w = rect.width();
Single h = rect.height();
matrix.getXYWH (x, y, w, h);
IRect clip_rect = clip.intersect (IRect (x, y, w, h));
if (clip_rect.isEmpty ())
return;
if (!s->surface || s->dirty)
copyImage (s, w, h, id->image, cairo_surface);
paint (img, s, x, y, clip_rect);
s->dirty = false;
}
KDE_NO_EXPORT void CairoPaintVisitor::visit (SMIL::TextMediaType * txt) {
TextRuntime * td = static_cast <TextRuntime *> (txt->runtime ());
Surface *s = txt->surface ();
//kdDebug() << "Visit " << txt->nodeName() << " " << td->text << endl;
if (!s)
return;
SRect rect = s->bounds;
Single x = rect.x (), y = rect.y(), w = rect.width(), h = rect.height();
matrix.getXYWH (x, y, w, h);
if (!s->surface) {
//kdDebug() << "new txt surface " << td->text << endl;
/* TQTextEdit * edit = new TQTextEdit;
edit->setReadOnly (true);
edit->setHScrollBarMode (TQScrollView::AlwaysOff);
edit->setVScrollBarMode (TQScrollView::AlwaysOff);
edit->setFrameShape (TQFrame::NoFrame);
edit->setFrameShadow (TQFrame::Plain);
edit->setGeometry (0, 0, w, h);
if (edit->length () == 0)
edit->setText (text);
if (w0 > 0)
font.setPointSize (int (1.0 * w * font_size / w0));
edit->setFont (font);
TQRect rect = p.clipRegion (TQPainter::CoordPainter).boundingRect ();
rect = rect.intersect (TQRect (xoff, yoff, w, h));
TQPixmap pix = TQPixmap::grabWidget (edit, rect.x () - (int) xoff,
rect.y () - (int) yoff, rect.width (), rect.height ());*/
float scale = 1.0 * w / rect.width (); // TODO: make an image
cairo_set_font_size (cr, scale * td->font_size);
cairo_font_extents_t txt_fnt;
cairo_font_extents (cr, &txt_fnt);
TQString str = td->text;
struct Line {
Line (const TQString & ln) : txt (ln), next(0) {}
TQString txt;
cairo_text_extents_t txt_ext;
Single xoff;
Line * next;
} *lines = 0, *last_line = 0;
Single y1 = y;
Single max_width;
int line_count = 0;
Single min_xoff = w;
while (!str.isEmpty ()) {
int len = str.find (TQChar ('\n'));
bool skip_cr = false;
if (len > 1 && str[len-1] == TQChar ('\r')) {
--len;
skip_cr = true;
}
TQString para = len > -1 ? str.left (len) : str;
Line * line = new Line (para);
++line_count;
if (!lines)
lines = line;
else
last_line->next = line;
last_line = line;
int ppos = 0;
while (true) {
cairo_text_extents (cr, line->txt.utf8 ().data (), &line->txt_ext);
float frag = line->txt_ext.width > 0.1
? w / line->txt_ext.width : 1.1;
if (frag < 1.0) {
int br_pos = int (line->txt.length () * frag); //educated guess
while (br_pos > 0) {
line->txt.truncate (br_pos);
br_pos = line->txt.findRev (TQChar (' '));
if (br_pos < 1)
break;
line->txt.truncate (br_pos);
cairo_text_extents (cr, line->txt.utf8 ().data (), &line->txt_ext);
if (line->txt_ext.width < (double)w)
break;
}
}
if (line->txt_ext.width > (double)max_width)
max_width = line->txt_ext.width;
if (td->halign == TextRuntime::align_center)
line->xoff = (w - Single (line->txt_ext.width)) / 2;
else if (td->halign == TextRuntime::align_right)
line->xoff = w - Single (line->txt_ext.width);
if (line->xoff < min_xoff)
min_xoff = line->xoff;
y1 += Single (txt_fnt.height);
ppos += line->txt.length () + 1;
if (ppos >= para.length ())
break;
line->next = new Line (para.mid (ppos));
++line_count;
line = line->next;
last_line = line;
}
if (len < 0)
break;
str = str.mid (len + (skip_cr ? 2 : 1));
}
// new coord in screen space
x += min_xoff;
w = (double)max_width + txt_fnt.max_x_advance / 2;
h = y1 - y /*txt_fnt.height + txt_fnt.descent*/;
s->surface = cairo_surface_create_similar (cairo_surface,
CAIRO_CONTENT_COLOR, (int) w, (int) h);
cairo_t * cr_txt = cairo_create (s->surface);
cairo_set_font_size (cr_txt, scale * td->font_size);
if (td->bg_opacity) { // TODO real alpha
CAIRO_SET_SOURCE_RGB (cr_txt, td->background_color);
cairo_paint (cr_txt);
}
CAIRO_SET_SOURCE_RGB (cr_txt, td->font_color);
y1 = 0;
while (lines) {
Line * line = lines;
line->xoff += Single (txt_fnt.max_x_advance / 4);
cairo_move_to (cr_txt, line->xoff - min_xoff, y1 + Single (txt_fnt.ascent));
cairo_show_text (cr_txt, line->txt.utf8 ().data ());
y1 += Single (txt_fnt.height);
lines = lines->next;
delete line;
}
//cairo_stroke (cr);
cairo_destroy (cr_txt);
// update bounds rect
Single sx = x, sy = y, sw = w, sh = h;
matrix.invXYWH (sx, sy, sw, sh);
txt->width = sw;
txt->height = sh;
s->bounds = txt->calculateBounds ();
// update coord. for painting below
x = s->bounds.x ();
y = s->bounds.y();
w = s->bounds.width();
h = s->bounds.height();
matrix.getXYWH (x, y, w, h);
}
IRect clip_rect = clip.intersect (IRect (x, y, w, h));
if (!clip_rect.isEmpty ())
paint (txt, s, x, y, clip_rect);
s->dirty = false;
}
KDE_NO_EXPORT void CairoPaintVisitor::visit (SMIL::Brush * brush) {
//kdDebug() << "Visit " << brush->nodeName() << endl;
Surface *s = brush->surface ();
if (s) {
cairo_save (cr);
opacity = 1.0;
SRect rect = s->bounds;
Single x, y, w = rect.width(), h = rect.height();
matrix.getXYWH (x, y, w, h);
unsigned int color = TQColor (brush->param ("color")).rgb ();
if (brush->active_trans) {
cur_media = brush;
cur_pat = NULL;
brush->active_trans->accept (this);
} else {
cairo_rectangle (cr, (int) x, (int) y, (int) w, (int) h);
}
opacity *= brush->opacity / 100.0;
if (opacity < 0.99)
cairo_set_source_rgba (cr,
1.0 * ((color >> 16) & 0xff) / 255,
1.0 * ((color >> 8) & 0xff) / 255,
1.0 * (color & 0xff) / 255,
opacity);
else
CAIRO_SET_SOURCE_RGB (cr, color);
cairo_fill (cr);
s->dirty = false;
cairo_restore (cr);
}
}
KDE_NO_EXPORT void CairoPaintVisitor::visit (RP::Imfl * imfl) {
if (imfl->surface ()) {
cairo_save (cr);
Matrix m = matrix;
Single x, y;
Single w = imfl->rp_surface->bounds.width();
Single h = imfl->rp_surface->bounds.height();
matrix.getXYWH (x, y, w, h);
cairo_rectangle (cr, x, y, w, h);
//cairo_clip (cr);
cairo_translate (cr, x, y);
cairo_scale (cr, w/imfl->width, h/imfl->height);
if (imfl->needs_scene_img)
cairo_push_group (cr);
for (NodePtr n = imfl->firstChild (); n; n = n->nextSibling ())
if (n->state >= Node::state_began &&
n->state < Node::state_deactivated) {
RP::TimingsBase * tb = convertNode<RP::TimingsBase>(n);
switch (n->id) {
case RP::id_node_viewchange:
if (!(int)tb->srcw)
tb->srcw = imfl->width;
if (!(int)tb->srch)
tb->srch = imfl->height;
// fall through
case RP::id_node_crossfade:
case RP::id_node_fadein:
case RP::id_node_fadeout:
case RP::id_node_fill:
case RP::id_node_wipe:
if (!(int)tb->w)
tb->w = imfl->width;
if (!(int)tb->h)
tb->h = imfl->height;
n->accept (this);
break;
}
}
if (imfl->needs_scene_img) {
cairo_pattern_t * pat = cairo_pop_group (cr);
cairo_pattern_set_extend (pat, CAIRO_EXTEND_NONE);
cairo_set_source (cr, pat);
cairo_paint (cr);
cairo_pattern_destroy (pat);
}
cairo_restore (cr);
matrix = m;
}
}
KDE_NO_EXPORT void CairoPaintVisitor::visit (RP::Fill * fi) {
//kdDebug() << "Visit " << fi->nodeName() << endl;
CAIRO_SET_SOURCE_RGB (cr, fi->color);
if ((int)fi->w && (int)fi->h) {
cairo_rectangle (cr, fi->x, fi->y, fi->w, fi->h);
cairo_fill (cr);
}
}
KDE_NO_EXPORT void CairoPaintVisitor::visit (RP::Fadein * fi) {
//kdDebug() << "Visit " << fi->nodeName() << endl;
if (fi->target && fi->target->id == RP::id_node_image) {
RP::Image *img = convertNode <RP::Image> (fi->target);
if (img->surface ()) {
Single sx = fi->srcx, sy = fi->srcy, sw = fi->srcw, sh = fi->srch;
if (!(int)sw)
sw = img->width;
if (!(int)sh)
sh = img->height;
if ((int)fi->w && (int)fi->h && (int)sw && (int)sh) {
if (!img->img_surface->surface)
copyImage (img->img_surface, img->width, img->height, img->cached_img.data->image, cairo_surface);
cairo_matrix_t matrix;
cairo_matrix_init_identity (&matrix);
float scalex = 1.0 * sw / fi->w;
float scaley = 1.0 * sh / fi->h;
cairo_matrix_scale (&matrix, scalex, scaley);
cairo_matrix_translate (&matrix,
1.0*sx/scalex - (double)fi->x,
1.0*sy/scaley - (double)fi->y);
cairo_save (cr);
cairo_rectangle (cr, fi->x, fi->y, fi->w, fi->h);
cairo_pattern_t *pat = cairo_pattern_create_for_surface (img->img_surface->surface);
cairo_pattern_set_extend (pat, CAIRO_EXTEND_NONE);
cairo_pattern_set_matrix (pat, &matrix);
cairo_set_source (cr, pat);
cairo_clip (cr);
cairo_paint_with_alpha (cr, 1.0 * fi->progress / 100);
cairo_restore (cr);
cairo_pattern_destroy (pat);
}
}
}
}
KDE_NO_EXPORT void CairoPaintVisitor::visit (RP::Fadeout * fo) {
//kdDebug() << "Visit " << fo->nodeName() << endl;
if (fo->progress > 0) {
CAIRO_SET_SOURCE_RGB (cr, fo->to_color);
if ((int)fo->w && (int)fo->h) {
cairo_save (cr);
cairo_rectangle (cr, fo->x, fo->y, fo->w, fo->h);
cairo_clip (cr);
cairo_paint_with_alpha (cr, 1.0 * fo->progress / 100);
cairo_restore (cr);
}
}
}
KDE_NO_EXPORT void CairoPaintVisitor::visit (RP::Crossfade * cf) {
//kdDebug() << "Visit " << cf->nodeName() << endl;
if (cf->target && cf->target->id == RP::id_node_image) {
RP::Image *img = convertNode <RP::Image> (cf->target);
if (img->surface ()) {
Single sx = cf->srcx, sy = cf->srcy, sw = cf->srcw, sh = cf->srch;
if (!(int)sw)
sw = img->width;
if (!(int)sh)
sh = img->height;
if ((int)cf->w && (int)cf->h && (int)sw && (int)sh) {
if (!img->img_surface->surface)
copyImage (img->img_surface, img->width, img->height, img->cached_img.data->image, cairo_surface);
cairo_save (cr);
cairo_matrix_t matrix;
cairo_matrix_init_identity (&matrix);
float scalex = 1.0 * sw / cf->w;
float scaley = 1.0 * sh / cf->h;
cairo_matrix_scale (&matrix, scalex, scaley);
cairo_matrix_translate (&matrix,
1.0*sx/scalex - (double)cf->x,
1.0*sy/scaley - (double)cf->y);
cairo_rectangle (cr, cf->x, cf->y, cf->w, cf->h);
cairo_pattern_t *pat = cairo_pattern_create_for_surface (img->img_surface->surface);
cairo_pattern_set_extend (pat, CAIRO_EXTEND_NONE);
cairo_pattern_set_matrix (pat, &matrix);
cairo_set_source (cr, pat);
cairo_clip (cr);
cairo_paint_with_alpha (cr, 1.0 * cf->progress / 100);
cairo_restore (cr);
cairo_pattern_destroy (pat);
}
}
}
}
KDE_NO_EXPORT void CairoPaintVisitor::visit (RP::Wipe * wipe) {
//kdDebug() << "Visit " << wipe->nodeName() << endl;
if (wipe->target && wipe->target->id == RP::id_node_image) {
RP::Image *img = convertNode <RP::Image> (wipe->target);
if (img->surface ()) {
Single x = wipe->x, y = wipe->y;
Single tx = x, ty = y;
Single w = wipe->w, h = wipe->h;
Single sx = wipe->srcx, sy = wipe->srcy, sw = wipe->srcw, sh = wipe->srch;
if (!(int)sw)
sw = img->width;
if (!(int)sh)
sh = img->height;
if (wipe->direction == RP::Wipe::dir_right) {
Single dx = w * 1.0 * wipe->progress / 100;
tx = x -w + dx;
w = dx;
} else if (wipe->direction == RP::Wipe::dir_left) {
Single dx = w * 1.0 * wipe->progress / 100;
tx = x + w - dx;
x = tx;
w = dx;
} else if (wipe->direction == RP::Wipe::dir_down) {
Single dy = h * 1.0 * wipe->progress / 100;
ty = y - h + dy;
h = dy;
} else if (wipe->direction == RP::Wipe::dir_up) {
Single dy = h * 1.0 * wipe->progress / 100;
ty = y + h - dy;
y = ty;
h = dy;
}
if ((int)w && (int)h) {
if (!img->img_surface->surface)
copyImage (img->img_surface, img->width, img->height, img->cached_img.data->image, cairo_surface);
cairo_matrix_t matrix;
cairo_matrix_init_identity (&matrix);
float scalex = 1.0 * sw / wipe->w;
float scaley = 1.0 * sh / wipe->h;
cairo_matrix_scale (&matrix, scalex, scaley);
cairo_matrix_translate (&matrix,
1.0*sx/scalex - (double)tx,
1.0*sy/scaley - (double)ty);
cairo_pattern_t *pat = cairo_pattern_create_for_surface (img->img_surface->surface);
cairo_pattern_set_extend (pat, CAIRO_EXTEND_NONE);
cairo_pattern_set_matrix (pat, &matrix);
cairo_set_source (cr, pat);
cairo_rectangle (cr, x, y, w, h);
cairo_fill (cr);
cairo_pattern_destroy (pat);
}
}
}
}
KDE_NO_EXPORT void CairoPaintVisitor::visit (RP::ViewChange * vc) {
//kdDebug() << "Visit " << vc->nodeName() << endl;
if (vc->unfinished () || vc->progress < 100) {
cairo_pattern_t * pat = cairo_pop_group (cr); // from imfl
cairo_pattern_set_extend (pat, CAIRO_EXTEND_NONE);
cairo_push_group (cr);
cairo_save (cr);
cairo_set_source (cr, pat);
cairo_paint (cr);
if ((int)vc->w && (int)vc->h && (int)vc->srcw && (int)vc->srch) {
cairo_matrix_t matrix;
cairo_matrix_init_identity (&matrix);
float scalex = 1.0 * vc->srcw / vc->w;
float scaley = 1.0 * vc->srch / vc->h;
cairo_matrix_scale (&matrix, scalex, scaley);
cairo_matrix_translate (&matrix,
1.0*vc->srcx/scalex - (double)vc->x,
1.0*vc->srcy/scaley - (double)vc->y);
cairo_pattern_set_matrix (pat, &matrix);
cairo_set_source (cr, pat);
cairo_rectangle (cr, vc->x, vc->y, vc->w, vc->h);
cairo_fill (cr);
}
cairo_pattern_destroy (pat);
cairo_restore (cr);
}
}
#endif
//-----------------------------------------------------------------------------
namespace KMPlayer {
class KMPLAYER_NO_EXPORT MouseVisitor : public Visitor {
Matrix matrix;
NodePtr node;
unsigned int event;
int x, y;
bool handled;
bool bubble_up;
public:
MouseVisitor (unsigned int evt, int x, int y);
KDE_NO_CDTOR_EXPORT ~MouseVisitor () {}
using Visitor::visit;
void visit (Node * n);
void visit (SMIL::Layout *);
void visit (SMIL::Region *);
void visit (SMIL::TimedMrl * n);
void visit (SMIL::MediaType * n);
void visit (SMIL::Anchor *);
void visit (SMIL::Area *);
TQCursor cursor;
};
} // namespace
KDE_NO_CDTOR_EXPORT
MouseVisitor::MouseVisitor (unsigned int evt, int a, int b)
: event (evt), x (a), y (b), handled (false), bubble_up (false) {
}
KDE_NO_EXPORT void MouseVisitor::visit (Node * n) {
kdDebug () << "Mouse event ignored for " << n->nodeName () << endl;
}
KDE_NO_EXPORT void MouseVisitor::visit (SMIL::Layout * layout) {
if (layout->surface ()) {
Matrix m = matrix;
SRect rect = layout->region_surface->bounds;
matrix = Matrix (rect.x(), rect.y(),
layout->region_surface->xscale, layout->region_surface->yscale);
matrix.transform (m);
NodePtr node_save = node;
node = layout;
for (NodePtr r = layout->firstChild (); r; r = r->nextSibling ()) {
if (r->id == SMIL::id_node_region)
r->accept (this);
if (!node->active ())
break;
}
node = node_save;
matrix = m;
}
}
KDE_NO_EXPORT void MouseVisitor::visit (SMIL::Region * region) {
if (region->surface ()) {
SRect rect = region->region_surface->bounds;
Single rx = rect.x(), ry = rect.y(), rw = rect.width(), rh = rect.height();
matrix.getXYWH (rx, ry, rw, rh);
handled = false;
bool inside = x > rx && x < rx+rw && y > ry && y< ry+rh;
if (!inside && (event == event_pointer_clicked || !region->has_mouse))
return;
Matrix m = matrix;
matrix = Matrix (rect.x(), rect.y(), 1.0, 1.0);
matrix.transform (m);
bubble_up = false;
bool child_handled = false;
if (inside)
for (NodePtr r = region->firstChild (); r; r = r->nextSibling ()) {
r->accept (this);
child_handled |= handled;
if (!node->active ())
break;
}
child_handled &= !bubble_up;
bubble_up = false;
int saved_event = event;
if (node->active ()) {
bool propagate_listeners = !child_handled;
if (event == event_pointer_moved) {
propagate_listeners = true; // always pass move events
if (region->has_mouse && (!inside || child_handled)) {
region->has_mouse = false;
event = event_outbounds;
} else if (inside && !child_handled && !region->has_mouse) {
region->has_mouse = true;
event = event_inbounds;
}
}// else // event_pointer_clicked
if (propagate_listeners) {
NodeRefListPtr nl = region->listeners (
event == event_pointer_moved ? mediatype_attached : event);
if (nl) {
for (NodeRefItemPtr c = nl->first(); c; c = c->nextSibling ()) {
if (c->data)
c->data->accept (this);
if (!node->active ())
break;
}
}
}
}
event = saved_event;
handled = inside;
matrix = m;
}
}
static void followLink (SMIL::LinkingBase * link) {
kdDebug() << "link to " << link->href << " clicked" << endl;
NodePtr n = link;
if (link->href.startsWith ("#")) {
SMIL::Smil * s = SMIL::Smil::findSmilNode (link);
if (s)
s->jump (link->href.mid (1));
else
kdError() << "In document jumps smil not found" << endl;
} else
for (NodePtr p = link->parentNode (); p; p = p->parentNode ()) {
if (n->mrl () && n->mrl ()->opener == p) {
p->setState (Node::state_deferred);
p->mrl ()->setParam (StringPool::attr_src, link->href, 0L);
p->activate ();
break;
}
n = p;
}
}
KDE_NO_EXPORT void MouseVisitor::visit (SMIL::Anchor * anchor) {
if (event == event_pointer_moved)
cursor.setShape (Qt::PointingHandCursor);
else if (event == event_pointer_clicked)
followLink (anchor);
}
KDE_NO_EXPORT void MouseVisitor::visit (SMIL::Area * area) {
NodePtr n = area->parentNode ();
if (n->id >= SMIL::id_node_first_mediatype &&
n->id < SMIL::id_node_last_mediatype) {
SMIL::MediaType * mt = convertNode <SMIL::MediaType> (n);
Surface *s = mt->surface ();
if (s) {
SRect rect = s->bounds;
Single x1 = rect.x (), x2 = rect.y ();
Single w = rect.width (), h = rect.height ();
matrix.getXYWH (x1, x2, w, h);
if (area->nr_coords > 1) {
Single left = area->coords[0].size (rect.width ());
Single top = area->coords[1].size (rect.height ());
matrix.getXY (left, top);
if (x < left || x > left + w || y < top || y > top + h)
return;
if (area->nr_coords > 3) {
Single right = area->coords[2].size (rect.width ());
Single bottom = area->coords[3].size (rect.height ());
matrix.getXY (right, bottom);
if (x > right || y > bottom)
return;
}
}
if (event == event_pointer_moved)
cursor.setShape (Qt::PointingHandCursor);
else {
NodeRefListPtr nl = area->listeners (event);
if (nl)
for (NodeRefItemPtr c = nl->first(); c; c = c->nextSibling ()) {
if (c->data)
c->data->accept (this);
if (!node->active ())
return;
}
if (event == event_pointer_clicked && !area->href.isEmpty ())
followLink (area);
}
}
}
}
KDE_NO_EXPORT void MouseVisitor::visit (SMIL::TimedMrl * timedmrl) {
timedmrl->runtime ()->processEvent (event);
}
KDE_NO_EXPORT void MouseVisitor::visit (SMIL::MediaType * mediatype) {
if (mediatype->sensitivity == SMIL::MediaType::sens_transparent) {
bubble_up = true;
return;
}
Surface *s = mediatype->surface ();
if (!s)
return;
if (s->node && s->node.ptr () != mediatype) {
s->node->accept (this);
return;
}
SRect rect = s->bounds;
Single rx = rect.x(), ry = rect.y(), rw = rect.width(), rh = rect.height();
matrix.getXYWH (rx, ry, rw, rh);
bool inside = x > rx && x < rx+rw && y > ry && y< ry+rh;
if (!inside && event == event_pointer_clicked)
return; // FIXME, also in/outbounds are bounds related
NodeRefListPtr nl = mediatype->listeners (
event == event_pointer_moved ? mediatype_attached : event);
if (nl)
for (NodeRefItemPtr c = nl->first(); c; c = c->nextSibling ()) {
if (c->data)
c->data->accept (this);
if (!node->active ())
return;
}
if (event != event_pointer_moved)
visit (static_cast <SMIL::TimedMrl *> (mediatype));
if (event != event_inbounds && event != event_outbounds) {
SMIL::RegionBase *r=convertNode<SMIL::RegionBase>(mediatype->region_node);
if (r && r->surface () &&
r->id != SMIL::id_node_smil &&
r->region_surface->node && r != r->region_surface->node.ptr ())
return r->region_surface->node->accept (this);
}
}
//-----------------------------------------------------------------------------
KDE_NO_CDTOR_EXPORT ViewArea::ViewArea (TQWidget * parent, View * view)
: TQWidget (parent, "kde_kmplayer_viewarea", WResizeNoErase | WRepaintNoErase),
m_parent (parent),
m_view (view),
m_collection (new TDEActionCollection (this)),
surface (new ViewSurface (this)),
m_mouse_invisible_timer (0),
m_repaint_timer (0),
m_fullscreen_scale (100),
scale_lbl_id (-1),
scale_slider_id (-1),
m_fullscreen (false),
m_minimal (false) {
setEraseColor (TQColor (0, 0, 0));
setAcceptDrops (true);
new TDEAction (i18n ("Fullscreen"), TDEShortcut (TQt::Key_F), TQT_TQOBJECT(this), TQT_SLOT (accelActivated ()), m_collection, "view_fullscreen_toggle");
setMouseTracking (true);
if (!image_data_map)
imageCacheDeleter.setObject (image_data_map, new ImageDataMap);
}
KDE_NO_CDTOR_EXPORT ViewArea::~ViewArea () {
}
KDE_NO_EXPORT void ViewArea::fullScreen () {
TQT_TQOBJECT(this)->killTimers ();
m_mouse_invisible_timer = m_repaint_timer = 0;
if (m_fullscreen) {
showNormal ();
reparent (m_parent, 0, TQPoint (0, 0), true);
static_cast <KDockWidget *> (m_parent)->setWidget (this);
for (unsigned i = 0; i < m_collection->count (); ++i)
m_collection->action (i)->setEnabled (false);
if (scale_lbl_id != -1) {
m_view->controlPanel ()->popupMenu ()->removeItem (scale_lbl_id);
m_view->controlPanel ()->popupMenu ()->removeItem (scale_slider_id);
scale_lbl_id = scale_slider_id = -1;
}
m_view->controlPanel ()->button (ControlPanel::button_playlist)->setIconSet (TQIconSet (TQPixmap (playlist_xpm)));
} else {
m_topwindow_rect = topLevelWidget ()->geometry ();
reparent (0L, 0, tqApp->desktop()->screenGeometry(this).topLeft(), true);
showFullScreen ();
for (unsigned i = 0; i < m_collection->count (); ++i)
m_collection->action (i)->setEnabled (true);
TQPopupMenu * menu = m_view->controlPanel ()->popupMenu ();
TQLabel * lbl = new TQLabel (i18n ("Scale:"), menu);
scale_lbl_id = menu->insertItem (lbl, -1, 4);
TQSlider * slider = new TQSlider (50, 150, 10, m_fullscreen_scale, Qt::Horizontal, menu);
connect (slider, TQT_SIGNAL (valueChanged (int)), this, TQT_SLOT (scale (int)));
scale_slider_id = menu->insertItem (slider, -1, 5);
m_view->controlPanel ()->button (ControlPanel::button_playlist)->setIconSet (TQIconSet (TQPixmap (normal_window_xpm)));
}
m_fullscreen = !m_fullscreen;
m_view->controlPanel()->popupMenu ()->setItemChecked (ControlPanel::menu_fullscreen, m_fullscreen);
#ifdef HAVE_CAIRO
if (surface->surface) {
cairo_surface_destroy (surface->surface);
surface->surface = 0L;
}
#endif
if (m_fullscreen) {
m_mouse_invisible_timer = startTimer(MOUSE_INVISIBLE_DELAY);
} else {
if (m_mouse_invisible_timer) {
killTimer (m_mouse_invisible_timer);
m_mouse_invisible_timer = 0;
}
unsetCursor();
}
}
void ViewArea::minimalMode () {
m_minimal = !m_minimal;
TQT_TQOBJECT(this)->killTimers ();
m_mouse_invisible_timer = m_repaint_timer = 0;
if (m_minimal) {
m_view->setViewOnly ();
m_view->setControlPanelMode (KMPlayer::View::CP_AutoHide);
m_view->setNoInfoMessages (true);
m_view->controlPanel ()->button (ControlPanel::button_playlist)->setIconSet (TQIconSet (TQPixmap (normal_window_xpm)));
} else {
m_view->setControlPanelMode (KMPlayer::View::CP_Show);
m_view->setNoInfoMessages (false);
m_view->controlPanel ()->button (ControlPanel::button_playlist)->setIconSet (TQIconSet (TQPixmap (playlist_xpm)));
}
m_topwindow_rect = topLevelWidget ()->geometry ();
}
KDE_NO_EXPORT void ViewArea::accelActivated () {
m_view->controlPanel()->popupMenu ()->activateItemAt (m_view->controlPanel()->popupMenu ()->indexOf (ControlPanel::menu_fullscreen));
}
KDE_NO_EXPORT void ViewArea::mousePressEvent (TQMouseEvent * e) {
if (surface->node) {
MouseVisitor visitor (event_pointer_clicked, e->x(), e->y());
surface->node->accept (&visitor);
}
e->accept ();
}
KDE_NO_EXPORT void ViewArea::mouseDoubleClickEvent (TQMouseEvent *) {
m_view->fullScreen (); // screensaver stuff
}
KDE_NO_EXPORT void ViewArea::mouseMoveEvent (TQMouseEvent * e) {
if (e->state () == Qt::NoButton) {
int vert_buttons_pos = height () - m_view->statusBarHeight ();
int cp_height = m_view->controlPanel ()->maximumSize ().height ();
m_view->delayedShowButtons (e->y() > vert_buttons_pos-cp_height &&
e->y() < vert_buttons_pos);
}
if (surface->node) {
MouseVisitor visitor (event_pointer_moved, e->x(), e->y());
surface->node->accept (&visitor);
setCursor (visitor.cursor);
}
e->accept ();
mouseMoved (); // for m_mouse_invisible_timer
}
KDE_NO_EXPORT void ViewArea::syncVisual (const IRect & rect) {
#ifdef HAVE_CAIRO
int ex = rect.x;
if (ex > 0)
ex--;
int ey = rect.y;
if (ey > 0)
ey--;
int ew = rect.w + 2;
int eh = rect.h + 2;
if (!surface->surface)
surface->surface = cairoCreateSurface (winId (), width (), height ());
if (surface->node && (!video_node ||
!convertNode <SMIL::MediaType> (video_node)->needsVideoWidget()))
setAudioVideoGeometry (IRect (), NULL);
CairoPaintVisitor visitor (surface->surface,
Matrix (surface->bounds.x(), surface->bounds.y(), 1.0, 1.0),
IRect (ex, ey, ew, eh), paletteBackgroundColor (), true);
if (surface->node)
surface->node->accept (&visitor);
#else
repaint (TQRect(rect.x, rect.y, rect.w, rect.h), false);
#endif
if (m_repaint_timer) {
killTimer (m_repaint_timer);
m_repaint_timer = 0;
}
//XFlush (tqt_xdisplay ());
}
KDE_NO_EXPORT void ViewArea::paintEvent (TQPaintEvent * pe) {
#ifdef HAVE_CAIRO
if (surface->node)
scheduleRepaint (IRect (pe->rect ().x (), pe->rect ().y (), pe->rect ().width (), pe->rect ().height ()));
else
#endif
TQWidget::paintEvent (pe);
}
KDE_NO_EXPORT void ViewArea::scale (int val) {
m_fullscreen_scale = val;
resizeEvent (0L);
}
KDE_NO_EXPORT void ViewArea::updateSurfaceBounds () {
Single x, y, w = width (), h = height ();
h -= m_view->statusBarHeight ();
h -= m_view->controlPanel ()->isVisible ()
? (m_view->controlPanelMode () == View::CP_Only
? h
: (Single) m_view->controlPanel()->maximumSize ().height ())
: Single (0);
surface->resize (SRect (x, y, w, h));
Mrl *mrl = surface->node ? surface->node->mrl () : NULL;
if (m_view->keepSizeRatio () &&
w > 0 && h > 0 &&
mrl && mrl->width > 0 && mrl->height > 0) {
double wasp = (double) w / h;
double masp = (double) mrl->width / mrl->height;
if (wasp > masp) {
Single tmp = w;
w = masp * h;
x += (tmp - w) / 2;
} else {
Single tmp = h;
h = Single (w / masp);
y += (tmp - h) / 2;
}
surface->xscale = 1.0 * w / mrl->width;
surface->yscale = 1.0 * h / mrl->height;
} else {
surface->xscale = 1.0;
surface->yscale = 1.0;
}
surface->bounds = SRect (x, y, w, h);
scheduleRepaint (IRect (0, 0, width (), height ()));
}
KDE_NO_EXPORT void ViewArea::resizeEvent (TQResizeEvent *) {
if (!m_view->controlPanel ()) return;
Single x, y, w = width (), h = height ();
Single hsb = m_view->statusBarHeight ();
Single hcp = m_view->controlPanel ()->isVisible ()
? (m_view->controlPanelMode () == View::CP_Only
? h-hsb
: (Single) m_view->controlPanel()->maximumSize ().height ())
: Single (0);
Single wws = w;
// move controlpanel over video when autohiding and playing
Single hws = h - (m_view->controlPanelMode () == View::CP_AutoHide &&
m_view->widgetStack ()->visibleWidget () == m_view->viewer ()
? Single (0)
: hcp) - hsb;
// now scale the regions and check if video region is already sized
if (surface->node) {
NodePtr n = surface->node;
surface = new ViewSurface (this);
surface->node = n;
}
updateSurfaceBounds ();
// finally resize controlpanel and video widget
if (m_view->controlPanel ()->isVisible ())
m_view->controlPanel ()->setGeometry (0, h-hcp-hsb, w, hcp);
if (m_view->statusBar ()->isVisible ())
m_view->statusBar ()->setGeometry (0, h-hsb, w, hsb);
if (m_fullscreen && wws == w && hws == h) {
wws = wws * m_fullscreen_scale / 100;
hws = hws * m_fullscreen_scale / 100;
x += (w - wws) / 2;
y += (h - hws) / 2;
}
if (!surface->node)
setAudioVideoGeometry (IRect (x, y, wws, hws), 0L);
}
KDE_NO_EXPORT
void ViewArea::setAudioVideoGeometry (const IRect &rect, unsigned int * bg_color) {
int x = rect.x, y = rect.y, w = rect.w, h = rect.h;
if (m_view->controlPanelMode() == View::CP_Only) {
w = h = 0;
} else if (!surface->node && m_view->keepSizeRatio ()) { // scale video widget inside region
int hfw = m_view->viewer ()->heightForWidth (w);
if (hfw > 0)
if (hfw > h) {
int old_w = w;
w = int ((1.0 * h * w)/(1.0 * hfw));
x += (old_w - w) / 2;
} else {
y += (h - hfw) / 2;
h = hfw;
}
}
m_av_geometry = TQRect (x, y, w, h);
TQRect wrect = m_view->widgetStack ()->geometry ();
if (m_av_geometry != wrect &&
!(m_av_geometry.width() <= 0 &&
wrect.width() <= 1 && wrect.height() <= 1)) {
m_view->widgetStack ()->setGeometry (x, y, w, h);
wrect.unite (m_av_geometry);
scheduleRepaint (IRect (wrect.x (), wrect.y (), wrect.width (), wrect.height ()));
}
if (bg_color)
if (TQColor (TQRgb (*bg_color)) != (m_view->viewer ()->paletteBackgroundColor ())) {
m_view->viewer()->setCurrentBackgroundColor (TQColor (TQRgb (*bg_color)));
scheduleRepaint (IRect (x, y, w, h));
}
}
KDE_NO_EXPORT void ViewArea::setAudioVideoNode (NodePtr n) {
video_node = n;
}
KDE_NO_EXPORT SurfacePtr ViewArea::getSurface (NodePtr node) {
static_cast <ViewSurface *> (surface.ptr ())->clear ();
surface->node = node;
m_view->viewer()->resetBackgroundColor ();
if (node) {
updateSurfaceBounds ();
return surface;
}
scheduleRepaint (IRect (0, 0, width (), height ()));
return 0L;
}
KDE_NO_EXPORT void ViewArea::showEvent (TQShowEvent *) {
resizeEvent (0L);
}
KDE_NO_EXPORT void ViewArea::dropEvent (TQDropEvent * de) {
m_view->dropEvent (de);
}
KDE_NO_EXPORT void ViewArea::dragEnterEvent (TQDragEnterEvent* dee) {
m_view->dragEnterEvent (dee);
}
KDE_NO_EXPORT void ViewArea::contextMenuEvent (TQContextMenuEvent * e) {
m_view->controlPanel ()->popupMenu ()->exec (e->globalPos ());
}
KDE_NO_EXPORT void ViewArea::mouseMoved () {
if (m_fullscreen) {
if (m_mouse_invisible_timer)
killTimer (m_mouse_invisible_timer);
unsetCursor ();
m_mouse_invisible_timer = startTimer (MOUSE_INVISIBLE_DELAY);
}
}
KDE_NO_EXPORT void ViewArea::scheduleRepaint (const IRect &rect) {
if (m_repaint_timer) {
m_repaint_rect = m_repaint_rect.unite (rect);
} else {
m_repaint_rect = rect;
m_repaint_timer = startTimer (10); // 100 per sec should do
}
}
KDE_NO_EXPORT void ViewArea::timerEvent (TQTimerEvent * e) {
if (e->timerId () == m_mouse_invisible_timer) {
killTimer (m_mouse_invisible_timer);
m_mouse_invisible_timer = 0;
if (m_fullscreen)
setCursor (BlankCursor);
} else if (e->timerId () == m_repaint_timer) {
killTimer (m_repaint_timer);
m_repaint_timer = 0;
//repaint (m_repaint_rect, false);
syncVisual (m_repaint_rect.intersect (IRect (0, 0, width (), height ())));
} else {
kdError () << "unknown timer " << e->timerId () << " " << m_repaint_timer << endl;
killTimer (e->timerId ());
}
}
KDE_NO_EXPORT void ViewArea::closeEvent (TQCloseEvent * e) {
//kdDebug () << "closeEvent" << endl;
if (m_fullscreen) {
fullScreen ();
if (!m_parent->topLevelWidget ()->isVisible ())
m_parent->topLevelWidget ()->show ();
e->ignore ();
} else
TQWidget::closeEvent (e);
}
#include "viewarea.moc"