diff options
Diffstat (limited to 'src/viewarea.cpp')
-rw-r--r-- | src/viewarea.cpp | 1691 |
1 files changed, 1691 insertions, 0 deletions
diff --git a/src/viewarea.cpp b/src/viewarea.cpp new file mode 100644 index 0000000..de384d7 --- /dev/null +++ b/src/viewarea.cpp @@ -0,0 +1,1691 @@ +/** + This file belong to the KMPlayer project, a movie player plugin for Konqueror + Copyright (C) 2007 Koos Vriezen <[email protected]> + + 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 <qapplication.h> +#include <qwidgetstack.h> +#include <qslider.h> +#include <qcursor.h> +#include <qimage.h> +#include <qmap.h> + +#include <kactioncollection.h> +#include <kstaticdeleter.h> +#include <kstatusbar.h> +#include <kstdaction.h> +#include <kshortcut.h> +#include <klocale.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 QMap <QString, ImageDataPtrW> ImageDataMap; + static KStaticDeleter <ImageDataMap> imageCacheDeleter; + static ImageDataMap * image_data_map; +} + +ImageData::ImageData( const QString & 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, QImage *img, cairo_surface_t *similar) { + int iw = img->width (); + int ih = img->height (); + + if (img->depth () < 24) { + QImage 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 QString & 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 = qt_xdisplay (); + return cairo_xlib_surface_create (display, id, + DefaultVisual (display, DefaultScreen (display)), w, h); + /*return cairo_xlib_surface_create_with_xrender_format ( + qt_xdisplay (), + id, + DefaultScreenOfDisplay (qt_xdisplay ()), + XRenderFindVisualFormat (qt_xdisplay (), + DefaultVisual (qt_xdisplay (), + DefaultScreen (qt_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, QColor c=QColor(), 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, QColor 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; + /* QTextEdit * edit = new QTextEdit; + edit->setReadOnly (true); + edit->setHScrollBarMode (QScrollView::AlwaysOff); + edit->setVScrollBarMode (QScrollView::AlwaysOff); + edit->setFrameShape (QFrame::NoFrame); + edit->setFrameShadow (QFrame::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); + QRect rect = p.clipRegion (QPainter::CoordPainter).boundingRect (); + rect = rect.intersect (QRect (xoff, yoff, w, h)); + QPixmap pix = QPixmap::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); + QString str = td->text; + struct Line { + Line (const QString & ln) : txt (ln), next(0) {} + QString 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 (QChar ('\n')); + bool skip_cr = false; + if (len > 1 && str[len-1] == QChar ('\r')) { + --len; + skip_cr = true; + } + QString 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 (QChar (' ')); + 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 = QColor (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 *); + QCursor 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 (QWidget * parent, View * view) + : QWidget (parent, "kde_kmplayer_viewarea", WResizeNoErase | WRepaintNoErase), + m_parent (parent), + m_view (view), + m_collection (new KActionCollection (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 (QColor (0, 0, 0)); + setAcceptDrops (true); + new KAction (i18n ("Fullscreen"), KShortcut (Qt::Key_F), this, 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 () { + killTimers (); + m_mouse_invisible_timer = m_repaint_timer = 0; + if (m_fullscreen) { + showNormal (); + reparent (m_parent, 0, QPoint (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 (QIconSet (QPixmap (playlist_xpm))); + } else { + m_topwindow_rect = topLevelWidget ()->geometry (); + reparent (0L, 0, qApp->desktop()->screenGeometry(this).topLeft(), true); + showFullScreen (); + for (unsigned i = 0; i < m_collection->count (); ++i) + m_collection->action (i)->setEnabled (true); + QPopupMenu * menu = m_view->controlPanel ()->popupMenu (); + QLabel * lbl = new QLabel (i18n ("Scale:"), menu); + scale_lbl_id = menu->insertItem (lbl, -1, 4); + QSlider * slider = new QSlider (50, 150, 10, m_fullscreen_scale, Qt::Horizontal, menu); + connect (slider, SIGNAL (valueChanged (int)), this, SLOT (scale (int))); + scale_slider_id = menu->insertItem (slider, -1, 5); + m_view->controlPanel ()->button (ControlPanel::button_playlist)->setIconSet (QIconSet (QPixmap (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; + 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 (QIconSet (QPixmap (normal_window_xpm))); + } else { + m_view->setControlPanelMode (KMPlayer::View::CP_Show); + m_view->setNoInfoMessages (false); + m_view->controlPanel ()->button (ControlPanel::button_playlist)->setIconSet (QIconSet (QPixmap (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 (QMouseEvent * 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 (QMouseEvent *) { + m_view->fullScreen (); // screensaver stuff +} + +KDE_NO_EXPORT void ViewArea::mouseMoveEvent (QMouseEvent * 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 (QRect(rect.x, rect.y, rect.w, rect.h), false); +#endif + if (m_repaint_timer) { + killTimer (m_repaint_timer); + m_repaint_timer = 0; + } + //XFlush (qt_xdisplay ()); +} + +KDE_NO_EXPORT void ViewArea::paintEvent (QPaintEvent * pe) { +#ifdef HAVE_CAIRO + if (surface->node) + scheduleRepaint (IRect (pe->rect ().x (), pe->rect ().y (), pe->rect ().width (), pe->rect ().height ())); + else +#endif + QWidget::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 (QResizeEvent *) { + 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 = QRect (x, y, w, h); + QRect 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 (QColor (QRgb (*bg_color)) != (m_view->viewer ()->paletteBackgroundColor ())) { + m_view->viewer()->setCurrentBackgroundColor (QColor (QRgb (*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 (QShowEvent *) { + resizeEvent (0L); +} + +KDE_NO_EXPORT void ViewArea::dropEvent (QDropEvent * de) { + m_view->dropEvent (de); +} + +KDE_NO_EXPORT void ViewArea::dragEnterEvent (QDragEnterEvent* dee) { + m_view->dragEnterEvent (dee); +} + +KDE_NO_EXPORT void ViewArea::contextMenuEvent (QContextMenuEvent * 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 (QTimerEvent * 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 (QCloseEvent * e) { + //kdDebug () << "closeEvent" << endl; + if (m_fullscreen) { + fullScreen (); + if (!m_parent->topLevelWidget ()->isVisible ()) + m_parent->topLevelWidget ()->show (); + e->ignore (); + } else + QWidget::closeEvent (e); +} + + +#include "viewarea.moc" |