File indexing completed on 2024-04-21 15:38:30

0001 /**
0002   This file belong to the KMPlayer project, a movie player plugin for Konqueror
0003   Copyright (C) 2007  Koos Vriezen <koos.vriezen@gmail.com>
0004 
0005   This library is free software; you can redistribute it and/or
0006   modify it under the terms of the GNU Lesser General Public
0007   License as published by the Free Software Foundation; either
0008   version 2 of the License, or (at your option) any later version.
0009 
0010   This library is distributed in the hope that it will be useful,
0011   but WITHOUT ANY WARRANTY; without even the implied warranty of
0012   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013   Lesser General Public License for more details.
0014 
0015   You should have received a copy of the GNU Lesser General Public
0016   License along with this library; if not, write to the Free Software
0017   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
0018 **/
0019 
0020 #include "config-kmplayer.h"
0021 
0022 #include <stdlib.h>
0023 #include <math.h>
0024 
0025 #include <qapplication.h>
0026 #include <qslider.h>
0027 #include <qcursor.h>
0028 #include <qmap.h>
0029 #include <QPalette>
0030 #include <QDesktopWidget>
0031 #include <QtX11Extras/QX11Info>
0032 #include <QPainter>
0033 #include <QMainWindow>
0034 #include <QWidgetAction>
0035 #include <QTextBlock>
0036 #include <QTextDocument>
0037 #include <QAbstractTextDocumentLayout>
0038 #include <QImage>
0039 #include <QAbstractNativeEventFilter>
0040 
0041 #include <kactioncollection.h>
0042 #include <kstatusbar.h>
0043 #include <kshortcut.h>
0044 #include <klocalizedstring.h>
0045 #include <kdebug.h>
0046 
0047 #include "kmplayerview.h"
0048 #include "kmplayercontrolpanel.h"
0049 #include "playlistview.h"
0050 #include "viewarea.h"
0051 #ifdef KMPLAYER_WITH_CAIRO
0052 # include <cairo-xcb.h>
0053 #endif
0054 #include "mediaobject.h"
0055 #include "kmplayer_smil.h"
0056 #include "kmplayer_rp.h"
0057 #include "mediaobject.h"
0058 
0059 #include <xcb/xcb.h>
0060 
0061 #include <kurl.h>
0062 
0063 using namespace KMPlayer;
0064 
0065 #if QT_VERSION >= 0x050600
0066 static qreal pixel_device_ratio;
0067 #endif
0068 //-------------------------------------------------------------------------
0069 
0070 #ifdef KMPLAYER_WITH_CAIRO
0071 static void clearSurface (cairo_t *cr, const IRect &rect) {
0072     cairo_save (cr);
0073     cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
0074     cairo_rectangle (cr, rect.x (), rect.y (), rect.width (), rect.height ());
0075     cairo_fill (cr);
0076     cairo_restore (cr);
0077 }
0078 
0079 void ImageData::copyImage (Surface *s, const SSize &sz, cairo_surface_t *similar, CalculatedSizer *zoom) {
0080     cairo_surface_t *src_sf;
0081     bool clear = false;
0082     int w = sz.width;
0083     int h = sz.height;
0084 
0085     if (surface) {
0086         src_sf = surface;
0087     } else {
0088         if (image->depth () < 24) {
0089             QImage qi = image->convertToFormat (QImage::Format_RGB32);
0090             *image = qi;
0091         }
0092         src_sf = cairo_image_surface_create_for_data (
0093                 image->bits (),
0094                 has_alpha ? CAIRO_FORMAT_ARGB32:CAIRO_FORMAT_RGB24,
0095                 width, height, image->bytesPerLine ());
0096         if (flags & ImagePixmap && !(flags & ImageAnimated)) {
0097             surface = cairo_surface_create_similar (similar,
0098                     has_alpha ? CAIRO_CONTENT_COLOR_ALPHA : CAIRO_CONTENT_COLOR,
0099                     width, height);
0100             cairo_pattern_t *pat = cairo_pattern_create_for_surface (src_sf);
0101             cairo_pattern_set_extend (pat, CAIRO_EXTEND_NONE);
0102             cairo_t *cr = cairo_create (surface);
0103             cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
0104             cairo_set_source (cr, pat);
0105             cairo_paint (cr);
0106             cairo_destroy (cr);
0107             cairo_pattern_destroy (pat);
0108             cairo_surface_destroy (src_sf);
0109             src_sf = surface;
0110             delete image;
0111             image = NULL;
0112         }
0113     }
0114 
0115     cairo_pattern_t *img_pat = cairo_pattern_create_for_surface (src_sf);
0116     cairo_pattern_set_extend (img_pat, CAIRO_EXTEND_NONE);
0117     if (zoom) {
0118         cairo_matrix_t mat;
0119         Single zx, zy, zw, zh;
0120         zoom->calcSizes (NULL, NULL, width, height, zx, zy, zw, zh);
0121         cairo_matrix_init_translate (&mat, zx, zy);
0122         cairo_matrix_scale (&mat, 1.0 * zw/w, 1.0 * zh/h);
0123         cairo_pattern_set_matrix (img_pat, &mat);
0124     } else if (w != width && h != height) {
0125         cairo_matrix_t mat;
0126         cairo_matrix_init_scale (&mat, 1.0 * width/w, 1.0 * height/h);
0127         cairo_pattern_set_matrix (img_pat, &mat);
0128     }
0129     if (!s->surface)
0130         s->surface = cairo_surface_create_similar (similar,
0131                 has_alpha ?
0132                 CAIRO_CONTENT_COLOR_ALPHA : CAIRO_CONTENT_COLOR, w, h);
0133     else
0134         clear = true;
0135     cairo_t *cr = cairo_create (s->surface);
0136     if (clear)
0137         clearSurface (cr, IRect (0, 0, w, h));
0138     cairo_set_source (cr, img_pat);
0139     cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
0140     cairo_paint (cr);
0141     cairo_destroy (cr);
0142 
0143     cairo_pattern_destroy (img_pat);
0144     if (!surface)
0145         cairo_surface_destroy (src_sf);
0146 }
0147 #endif
0148 
0149 //-------------------------------------------------------------------------
0150 
0151 #define REGION_SCROLLBAR_WIDTH 20
0152 
0153 #ifdef KMPLAYER_WITH_CAIRO
0154 
0155 # define CAIRO_SET_SOURCE_RGB(cr,c)           \
0156     cairo_set_source_rgb ((cr),               \
0157             1.0 * (((c) >> 16) & 0xff) / 255, \
0158             1.0 * (((c) >> 8) & 0xff) / 255,  \
0159             1.0 * (((c)) & 0xff) / 255)
0160 
0161 # define CAIRO_SET_SOURCE_ARGB(cr,c)          \
0162     cairo_set_source_rgba ((cr),              \
0163             1.0 * (((c) >> 16) & 0xff) / 255, \
0164             1.0 * (((c) >> 8) & 0xff) / 255,  \
0165             1.0 * (((c)) & 0xff) / 255,       \
0166             1.0 * (((c) >> 24) & 0xff) / 255)
0167 
0168 struct KMPLAYER_NO_EXPORT PaintContext
0169 {
0170     PaintContext (const Matrix& m, const IRect& c)
0171         : matrix (m)
0172         , clip (c)
0173         , fit (fit_default)
0174         , bg_repeat (SMIL::RegionBase::BgRepeat)
0175         , bg_image (NULL)
0176     {}
0177     Matrix matrix;
0178     IRect clip;
0179     Fit fit;
0180     SMIL::RegionBase::BackgroundRepeat bg_repeat;
0181     ImageData *bg_image;
0182 };
0183 
0184 class KMPLAYER_NO_EXPORT CairoPaintVisitor : public Visitor, public PaintContext {
0185     cairo_surface_t * cairo_surface;
0186     // stack vars need for transitions
0187     TransitionModule *cur_transition;
0188     cairo_pattern_t * cur_pat;
0189     cairo_matrix_t cur_mat;
0190     float opacity;
0191     bool toplevel;
0192 
0193     void traverseRegion (Node *reg, Surface *s);
0194     void updateExternal (SMIL::MediaType *av, SurfacePtr s);
0195     void paint (TransitionModule *trans, MediaOpacity mopacity, Surface *s,
0196                 const IPoint &p, const IRect &);
0197     void video (Mrl *mt, Surface *s);
0198 public:
0199     cairo_t * cr;
0200     CairoPaintVisitor (cairo_surface_t * cs, Matrix m,
0201             const IRect & rect, QColor c=QColor(), bool toplevel=false);
0202     ~CairoPaintVisitor ();
0203     using Visitor::visit;
0204     void visit (Node *);
0205     void visit (SMIL::Smil *);
0206     void visit (SMIL::Layout *);
0207     void visit (SMIL::RegionBase *);
0208     void visit (SMIL::Transition *);
0209     void visit (SMIL::TextMediaType *);
0210     void visit (SMIL::Brush *);
0211     void visit (SMIL::SmilText *);
0212     void visit (SMIL::RefMediaType *);
0213     void visit (RP::Imfl *);
0214     void visit (RP::Fill *);
0215     void visit (RP::Fadein *);
0216     void visit (RP::Fadeout *);
0217     void visit (RP::Crossfade *);
0218     void visit (RP::Wipe *);
0219     void visit (RP::ViewChange *);
0220 };
0221 
0222 KDE_NO_CDTOR_EXPORT
0223 CairoPaintVisitor::CairoPaintVisitor (cairo_surface_t * cs, Matrix m,
0224         const IRect & rect, QColor c, bool top)
0225  : PaintContext (m, rect), cairo_surface (cs), toplevel (top)
0226 {
0227     cr = cairo_create (cs);
0228     if (toplevel) {
0229         cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
0230         cairo_set_tolerance (cr, 0.5 );
0231         //cairo_push_group (cr);
0232         cairo_set_source_rgb (cr,
0233            1.0 * c.red () / 255, 1.0 * c.green () / 255, 1.0 * c.blue () / 255);
0234         cairo_rectangle (cr, rect.x(), rect.y(), rect.width(), rect.height());
0235         cairo_fill (cr);
0236     } else {
0237         clearSurface (cr, rect);
0238     }
0239 }
0240 
0241 KDE_NO_CDTOR_EXPORT CairoPaintVisitor::~CairoPaintVisitor () {
0242     /*if (toplevel) {
0243         cairo_pattern_t * pat = cairo_pop_group (cr);
0244         //cairo_pattern_set_extend (pat, CAIRO_EXTEND_NONE);
0245         cairo_set_source (cr, pat);
0246         cairo_rectangle (cr, clip.x, clip.y, clip.w, clip.h);
0247         cairo_fill (cr);
0248         cairo_pattern_destroy (pat);
0249     }*/
0250     cairo_destroy (cr);
0251 }
0252 
0253 KDE_NO_EXPORT void CairoPaintVisitor::visit (Node * n) {
0254     kWarning() << "Paint called on " << n->nodeName();
0255 }
0256 
0257 KDE_NO_EXPORT void CairoPaintVisitor::visit (SMIL::Smil *s) {
0258     if (s->active () && s->layout_node)
0259         s->layout_node->accept (this);
0260 }
0261 
0262 KDE_NO_EXPORT void CairoPaintVisitor::traverseRegion (Node *node, Surface *s) {
0263     ConnectionList *nl = nodeMessageReceivers (node, MsgSurfaceAttach);
0264     if (nl) {
0265         for (Connection *c = nl->first(); c; c = nl->next ())
0266             if (c->connecter)
0267                 c->connecter->accept (this);
0268     }
0269     /*for (SurfacePtr c = s->lastChild (); c; c = c->previousSibling ()) {
0270         if (c->node && c->node->id != SMIL::id_node_region &&
0271         c->node && c->node->id != SMIL::id_node_root_layout)
0272             c->node->accept (this);
0273         else
0274             break;
0275     }*/
0276     // finally visit region children
0277     for (SurfacePtr c = s->firstChild (); c; c = c->nextSibling ()) {
0278         if (c->node && c->node->id == SMIL::id_node_region)
0279             c->node->accept (this);
0280         else
0281             break;
0282     }
0283     s->dirty = false;
0284 }
0285 
0286 KDE_NO_EXPORT void CairoPaintVisitor::visit (SMIL::Layout *layout) {
0287     if (layout->root_layout)
0288         layout->root_layout->accept (this);
0289 }
0290 
0291 
0292 static void cairoDrawRect (cairo_t *cr, unsigned int color,
0293         int x, int y, int w, int h) {
0294     CAIRO_SET_SOURCE_ARGB (cr, color);
0295     cairo_rectangle (cr, x, y, w, h);
0296     cairo_fill (cr);
0297 }
0298 
0299 KDE_NO_EXPORT void CairoPaintVisitor::visit (SMIL::RegionBase *reg) {
0300     Surface *s = (Surface *) reg->role (RoleDisplay);
0301     if (s) {
0302         SRect rect = s->bounds;
0303 
0304         IRect scr = matrix.toScreen (rect);
0305         if (clip.intersect (scr).isEmpty ())
0306             return;
0307         PaintContext ctx_save = *(PaintContext *) this;
0308         matrix = Matrix (rect.x(), rect.y(), s->xscale, s->yscale);
0309         matrix.transform (ctx_save.matrix);
0310         clip = clip.intersect (scr);
0311         if (SMIL::RegionBase::BgInherit != reg->bg_repeat)
0312             bg_repeat = reg->bg_repeat;
0313         cairo_save (cr);
0314 
0315         Surface *cs = s->firstChild ();
0316         if (!s->virtual_size.isEmpty ())
0317             matrix.translate (-s->x_scroll, -s->y_scroll);
0318 
0319         if (fit_default != reg->fit)
0320             fit = reg->fit;
0321 
0322         ImageMedia *im = reg->media_info
0323             ? (ImageMedia *) reg->media_info->media
0324             : NULL;
0325 
0326         ImageData *bg_img = im && !im->isEmpty() ? im->cached_img.ptr () : NULL;
0327         if (reg->background_image == "inherit")
0328             bg_img = bg_image;
0329         else
0330             bg_image = bg_img;
0331         unsigned int bg_alpha = s->background_color & 0xff000000;
0332         if ((SMIL::RegionBase::ShowAlways == reg->show_background ||
0333                     reg->m_AttachedMediaTypes.first ()) &&
0334                 (bg_alpha || bg_img)) {
0335             cairo_save (cr);
0336             if (bg_alpha) {
0337                 cairo_rectangle (cr,
0338                         clip.x (), clip.y (), clip.width (), clip.height ());
0339                 if (bg_alpha < 0xff000000) {
0340                     CAIRO_SET_SOURCE_ARGB (cr, s->background_color);
0341                     cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
0342                     cairo_fill (cr);
0343                     cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
0344                 } else {
0345                     CAIRO_SET_SOURCE_RGB (cr, s->background_color);
0346                     cairo_fill (cr);
0347                 }
0348             }
0349             if (bg_img) {
0350                 Single w = bg_img->width;
0351                 Single h = bg_img->height;
0352                 matrix.getWH (w, h);
0353                 if (!s->surface)
0354                     bg_img->copyImage (s, SSize (w, h), cairo_surface);
0355                 if (bg_img->has_alpha)
0356                     cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
0357                 cairo_pattern_t *pat = cairo_pattern_create_for_surface (s->surface);
0358                 cairo_pattern_set_extend (pat, CAIRO_EXTEND_REPEAT);
0359                 cairo_matrix_t mat;
0360                 cairo_matrix_init_translate (&mat, -scr.x (), -scr.y ());
0361                 cairo_pattern_set_matrix (pat, &mat);
0362                 cairo_set_source (cr, pat);
0363                 int cw = clip.width ();
0364                 int ch = clip.height ();
0365                 switch (bg_repeat) {
0366                 case SMIL::RegionBase::BgRepeatX:
0367                     if (h < ch)
0368                         ch = h;
0369                     break;
0370                 case SMIL::RegionBase::BgRepeatY:
0371                     if (w < cw)
0372                         cw = w;
0373                     break;
0374                 case SMIL::RegionBase::BgNoRepeat:
0375                     if (w < cw)
0376                         cw = w;
0377                     if (h < ch)
0378                         ch = h;
0379                     break;
0380                 default:
0381                     break;
0382                 }
0383                 cairo_rectangle (cr, clip.x (), clip.y (), cw, ch);
0384                 cairo_fill (cr);
0385                 cairo_pattern_destroy (pat);
0386                 if (bg_img->has_alpha)
0387                     cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
0388             }
0389             cairo_restore (cr);
0390         }
0391         traverseRegion (reg, s);
0392         cs = s->firstChild ();
0393         if (cs && (s->scroll || cs->scroll) && cs == s->lastChild ()) {
0394             SRect r = cs->bounds;
0395             if (r.width () > rect.width () || r.height () > rect.height ()) {
0396                 if (s->virtual_size.isEmpty ())
0397                     s->x_scroll = s->y_scroll = 0;
0398                 s->virtual_size = r.size;
0399                 matrix.getWH (s->virtual_size.width, s->virtual_size.height);
0400                 s->virtual_size.width += REGION_SCROLLBAR_WIDTH;
0401                 s->virtual_size.height += REGION_SCROLLBAR_WIDTH;
0402                 const int vy = s->virtual_size.height;
0403                 const int vw = s->virtual_size.width;
0404                 int sbw = REGION_SCROLLBAR_WIDTH;
0405                 int sbx = scr.x () + scr.width () - sbw;
0406                 int sby = scr.y ();
0407                 int sbh = scr.height () - REGION_SCROLLBAR_WIDTH;
0408                 IRect sb_clip = clip.intersect (IRect (sbx, sby, sbw, sbh));
0409                 if (!sb_clip.isEmpty ()) {
0410                     int knob_h = sbh * scr.height () / vy;
0411                     int knob_y = scr.y () + s->y_scroll * sbh / vy;
0412                     IRect knob (sbx, knob_y, sbw, knob_h);
0413                     cairo_save (cr);
0414                     cairo_rectangle (cr, sb_clip.x (), sb_clip.y (),
0415                             sb_clip.width (), sb_clip.height ());
0416                     cairo_clip (cr);
0417                     cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
0418                     cairo_set_line_width (cr, 2);
0419                     CAIRO_SET_SOURCE_ARGB (cr, 0x80A0A0A0);
0420                     cairo_rectangle (cr, sbx + 1, sby + 1, sbw - 2, sbh - 2);
0421                     cairo_stroke (cr);
0422                     if (s->y_scroll)
0423                         cairoDrawRect (cr, 0x80000000,
0424                                 sbx + 2, sby + 2,
0425                                 sbw - 4, knob.y() - sby - 2);
0426                     cairoDrawRect (cr, 0x80808080,
0427                             knob.x() + 2, knob.y(),
0428                             knob.width() - 4, knob.height());
0429                     if (sby + sbh - knob.y() - knob.height() - 2 > 0)
0430                         cairoDrawRect (cr, 0x80000000,
0431                                 sbx + 2, knob.y() + knob.height(),
0432                                 sbw - 4, sby + sbh - knob.y() -knob.height()-2);
0433                     cairo_restore (cr);
0434                 }
0435                 sbh = REGION_SCROLLBAR_WIDTH;
0436                 sbx = scr.x ();
0437                 sby = scr.y () + scr.height () - sbh;
0438                 sbw = scr.width () - REGION_SCROLLBAR_WIDTH;
0439                 sb_clip = clip.intersect (IRect (sbx, sby, sbw, sbh));
0440                 if (!sb_clip.isEmpty ()) {
0441                     int knob_w = sbw * scr.width () / vw;
0442                     int knob_x = scr.x () + s->x_scroll * sbw / vw;
0443                     IRect knob (knob_x, sby, knob_w, sbh);
0444                     cairo_save (cr);
0445                     cairo_rectangle (cr, sb_clip.x (), sb_clip.y (),
0446                             sb_clip.width (), sb_clip.height ());
0447                     cairo_clip (cr);
0448                     cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
0449                     cairo_set_line_width (cr, 2);
0450                     CAIRO_SET_SOURCE_ARGB (cr, 0x80A0A0A0);
0451                     cairo_rectangle (cr, sbx + 1, sby + 1, sbw - 2, sbh - 2);
0452                     cairo_stroke (cr);
0453                     if (s->x_scroll)
0454                         cairoDrawRect (cr, 0x80000000,
0455                                 sbx + 2, sby + 2,
0456                                 knob.x() - sbx - 2, sbh - 4);
0457                     cairoDrawRect (cr, 0x80808080,
0458                             knob.x(), knob.y() + 2,
0459                             knob.width(), knob.height() - 4);
0460                     if (sbx + sbw - knob.x() - knob.width() - 2 > 0)
0461                         cairoDrawRect (cr, 0x80000000,
0462                                 knob.x() + knob.width(), sby + 2,
0463                                 sbx + sbw - knob.x() - knob.width()-2, sbh - 4);
0464                     cairo_restore (cr);
0465                 }
0466             }
0467         }
0468         cairo_restore (cr);
0469         *(PaintContext *) this = ctx_save;
0470         s->dirty = false;
0471     }
0472 }
0473 
0474 #define CAIRO_SET_PATTERN_COND(cr,pat,mat)                      \
0475     if (pat) {                                                  \
0476         cairo_pattern_set_extend (cur_pat, CAIRO_EXTEND_NONE);  \
0477         cairo_pattern_set_matrix (pat, &mat);                   \
0478         cairo_pattern_set_filter (pat, CAIRO_FILTER_FAST);      \
0479         cairo_set_source (cr, pat);                             \
0480     }
0481 
0482 KDE_NO_EXPORT void CairoPaintVisitor::visit (SMIL::Transition *trans) {
0483     float perc = trans->start_progress + (trans->end_progress - trans->start_progress)*cur_transition->trans_gain;
0484     if (cur_transition->trans_out_active)
0485         perc = 1.0 - perc;
0486     if (SMIL::Transition::Fade == trans->type) {
0487         CAIRO_SET_PATTERN_COND(cr, cur_pat, cur_mat)
0488         cairo_rectangle (cr, clip.x(), clip.y(), clip.width(), clip.height());
0489         opacity = perc;
0490     } else if (SMIL::Transition::BarWipe == trans->type) {
0491         IRect rect;
0492         if (SMIL::Transition::SubTopToBottom == trans->sub_type) {
0493             if (SMIL::Transition::dir_reverse == trans->direction) {
0494                 int dy = (int) ((1.0 - perc) * clip.height ());
0495                 rect = IRect (clip.x (), clip.y () + dy,
0496                         clip.width (), clip.height () - dy);
0497             } else {
0498                 rect = IRect (clip.x (), clip.y (),
0499                         clip.width (), (int) (perc * clip.height ()));
0500             }
0501         } else {
0502             if (SMIL::Transition::dir_reverse == trans->direction) {
0503                 int dx = (int) ((1.0 - perc) * clip.width ());
0504                 rect = IRect (clip.x () + dx, clip.y (),
0505                         clip.width () - dx, clip.height ());
0506             } else {
0507                 rect = IRect (clip.x (), clip.y (),
0508                         (int) (perc * clip.width ()), clip.height ());
0509             }
0510         }
0511         cairo_rectangle (cr, rect.x(), rect.y(), rect.width(), rect.height());
0512         CAIRO_SET_PATTERN_COND(cr, cur_pat, cur_mat)
0513     } else if (SMIL::Transition::PushWipe == trans->type) {
0514         int dx = 0, dy = 0;
0515         if (SMIL::Transition::SubFromTop == trans->sub_type)
0516             dy = -(int) ((1.0 - perc) * clip.height ());
0517         else if (SMIL::Transition::SubFromRight == trans->sub_type)
0518             dx = (int) ((1.0 - perc) * clip.width ());
0519         else if (SMIL::Transition::SubFromBottom == trans->sub_type)
0520             dy = (int) ((1.0 - perc) * clip.height ());
0521         else //if (SMIL::Transition::SubFromLeft == trans->sub_type)
0522             dx = -(int) ((1.0 - perc) * clip.width ());
0523         cairo_matrix_translate (&cur_mat, -dx, -dy);
0524         IRect rect = clip.intersect (IRect (clip.x () + dx, clip.y () + dy,
0525                     clip.width (), clip.height ()));
0526         cairo_rectangle (cr, rect.x(), rect.y(), rect.width(), rect.height());
0527         CAIRO_SET_PATTERN_COND(cr, cur_pat, cur_mat)
0528     } else if (SMIL::Transition::IrisWipe == trans->type) {
0529         CAIRO_SET_PATTERN_COND(cr, cur_pat, cur_mat)
0530         if (SMIL::Transition::SubDiamond == trans->sub_type) {
0531             cairo_rectangle (cr, clip.x(), clip.y(),clip.width(),clip.height());
0532             cairo_clip (cr);
0533             int dx = (int) (perc * clip.width ());
0534             int dy = (int) (perc * clip.height ());
0535             int mx = clip.x () + clip.width ()/2;
0536             int my = clip.y () + clip.height ()/2;
0537             cairo_new_path (cr);
0538             cairo_move_to (cr, mx, my - dy);
0539             cairo_line_to (cr, mx + dx, my);
0540             cairo_line_to (cr, mx, my + dy);
0541             cairo_line_to (cr, mx - dx, my);
0542             cairo_close_path (cr);
0543         } else { // SubRectangle
0544             int dx = (int) (0.5 * (1 - perc) * clip.width ());
0545             int dy = (int) (0.5 * (1 - perc) * clip.height ());
0546             cairo_rectangle (cr, clip.x () + dx, clip.y () + dy,
0547                     clip.width () - 2 * dx, clip.height () -2 * dy);
0548         }
0549     } else if (SMIL::Transition::ClockWipe == trans->type) {
0550         cairo_rectangle (cr, clip.x(), clip.y(), clip.width(), clip.height());
0551         cairo_clip (cr);
0552         int mx = clip.x () + clip.width ()/2;
0553         int my = clip.y () + clip.height ()/2;
0554         cairo_new_path (cr);
0555         cairo_move_to (cr, mx, my);
0556         float hw = 1.0 * clip.width ()/2;
0557         float hh = 1.0 * clip.height ()/2;
0558         float radius = sqrtf (hw * hw + hh * hh);
0559         float phi;
0560         switch (trans->sub_type) {
0561             case SMIL::Transition::SubClockwiseThree:
0562                 phi = 0;
0563                 break;
0564             case SMIL::Transition::SubClockwiseSix:
0565                 phi = M_PI / 2;
0566                 break;
0567             case SMIL::Transition::SubClockwiseNine:
0568                 phi = M_PI;
0569                 break;
0570             default: // Twelve
0571                 phi = -M_PI / 2;
0572                 break;
0573         }
0574         if (SMIL::Transition::dir_reverse == trans->direction)
0575             cairo_arc_negative (cr, mx, my, radius, phi, phi - 2 * M_PI * perc);
0576         else
0577             cairo_arc (cr, mx, my, radius, phi, phi + 2 * M_PI * perc);
0578         cairo_close_path (cr);
0579         CAIRO_SET_PATTERN_COND(cr, cur_pat, cur_mat)
0580     } else if (SMIL::Transition::BowTieWipe == trans->type) {
0581         cairo_rectangle (cr, clip.x(), clip.y(), clip.width(), clip.height());
0582         cairo_clip (cr);
0583         int mx = clip.x () + clip.width ()/2;
0584         int my = clip.y () + clip.height ()/2;
0585         cairo_new_path (cr);
0586         cairo_move_to (cr, mx, my);
0587         float hw = 1.0 * clip.width ()/2;
0588         float hh = 1.0 * clip.height ()/2;
0589         float radius = sqrtf (hw * hw + hh * hh);
0590         float phi;
0591         switch (trans->sub_type) {
0592             case SMIL::Transition::SubHorizontal:
0593                 phi = 0;
0594                 break;
0595             default: // Vertical
0596                 phi = -M_PI / 2;
0597                 break;
0598         }
0599         float dphi = 0.5 * M_PI * perc;
0600         cairo_arc (cr, mx, my, radius, phi - dphi, phi + dphi);
0601         cairo_close_path (cr);
0602         cairo_new_sub_path (cr);
0603         cairo_move_to (cr, mx, my);
0604         if (SMIL::Transition::SubHorizontal == trans->sub_type)
0605             cairo_arc (cr, mx, my, radius, M_PI + phi - dphi, M_PI + phi +dphi);
0606         else
0607             cairo_arc (cr, mx, my, radius, -phi - dphi, -phi + dphi);
0608         cairo_close_path (cr);
0609         CAIRO_SET_PATTERN_COND(cr, cur_pat, cur_mat)
0610     } else if (SMIL::Transition::EllipseWipe == trans->type) {
0611         cairo_rectangle (cr, clip.x(), clip.y(), clip.width(), clip.height());
0612         cairo_clip (cr);
0613         int mx = clip.x () + clip.width ()/2;
0614         int my = clip.y () + clip.height ()/2;
0615         float hw = (double) clip.width ()/2;
0616         float hh = (double) clip.height ()/2;
0617         float radius = sqrtf (hw * hw + hh * hh);
0618         cairo_save (cr);
0619         cairo_new_path (cr);
0620         cairo_translate (cr, (int) mx, (int) my);
0621         cairo_move_to (cr, - Single (radius), 0);
0622         if (SMIL::Transition::SubHorizontal == trans->sub_type)
0623             cairo_scale (cr, 1.0, 0.6);
0624         else if (SMIL::Transition::SubVertical == trans->sub_type)
0625             cairo_scale (cr, 0.6, 1.0);
0626         cairo_arc (cr, 0, 0, perc * radius, 0, 2 * M_PI);
0627         cairo_close_path (cr);
0628         cairo_restore (cr);
0629         CAIRO_SET_PATTERN_COND(cr, cur_pat, cur_mat)
0630     }
0631 }
0632 
0633 KDE_NO_EXPORT void CairoPaintVisitor::video (Mrl *m, Surface *s) {
0634     if (m->media_info &&
0635             m->media_info->media &&
0636             (MediaManager::Audio == m->media_info->type ||
0637              MediaManager::AudioVideo == m->media_info->type)) {
0638         AudioVideoMedia *avm = static_cast<AudioVideoMedia *> (m->media_info->media);
0639         if (avm->viewer ()) {
0640             if (s &&
0641                     avm->process &&
0642                     avm->process->state () > IProcess::Ready &&
0643                     strcmp (m->nodeName (), "audio")) {
0644                 s->xscale = s->yscale = 1; // either scale width/height or use bounds
0645                 avm->viewer ()->setGeometry (s->toScreen (s->bounds.size));
0646             } else {
0647                 avm->viewer ()->setGeometry (IRect (-60, -60, 50, 50));
0648             }
0649         }
0650     }
0651 }
0652 
0653 KDE_NO_EXPORT void CairoPaintVisitor::visit (SMIL::RefMediaType *ref) {
0654     Surface *s = ref->surface ();
0655     if (s && ref->external_tree) {
0656         updateExternal (ref, s);
0657         return;
0658     }
0659     if (!ref->media_info)
0660         return;
0661     if (fit_default != fit
0662             && fit_default == ref->fit
0663             && fit != ref->effective_fit) {
0664         ref->effective_fit = fit;
0665         s->resize (ref->calculateBounds(), false);
0666     }
0667     if (ref->media_info->media &&
0668             ref->media_info->media->type () == MediaManager::Image) {
0669         if (!s)
0670             return;
0671 
0672         IRect scr = matrix.toScreen (s->bounds);
0673         IRect clip_rect = clip.intersect (scr);
0674         if (clip_rect.isEmpty ())
0675             return;
0676 
0677         ImageMedia *im = static_cast <ImageMedia *> (ref->media_info->media);
0678         ImageData *id = im ? im->cached_img.ptr () : NULL;
0679         if (id && id->flags == ImageData::ImageScalable)
0680             im->render (scr.size);
0681         if (!id || im->isEmpty () || ref->size.isEmpty ()) {
0682             s->remove();
0683             return;
0684         }
0685         if (!s->surface || s->dirty)
0686             id->copyImage (s, SSize (scr.width (), scr.height ()), cairo_surface, ref->pan_zoom);
0687         paint (&ref->transition, ref->media_opacity, s, scr.point, clip_rect);
0688         s->dirty = false;
0689     } else {
0690         video (ref, s);
0691     }
0692 }
0693 
0694 KDE_NO_EXPORT void CairoPaintVisitor::paint (TransitionModule *trans,
0695         MediaOpacity mopacity, Surface *s,
0696         const IPoint &point, const IRect &rect) {
0697     cairo_save (cr);
0698     opacity = 1.0;
0699     cairo_matrix_init_translate (&cur_mat, -point.x, -point.y);
0700     cur_pat = cairo_pattern_create_for_surface (s->surface);
0701     if (trans->active_trans) {
0702         IRect clip_save = clip;
0703         clip = rect;
0704         cur_transition = trans;
0705         trans->active_trans->accept (this);
0706         clip = clip_save;
0707     } else {
0708         cairo_pattern_set_extend (cur_pat, CAIRO_EXTEND_NONE);
0709         cairo_pattern_set_matrix (cur_pat, &cur_mat);
0710         cairo_pattern_set_filter (cur_pat, CAIRO_FILTER_FAST);
0711         cairo_set_source (cr, cur_pat);
0712         cairo_rectangle (cr, rect.x(), rect.y(), rect.width(), rect.height());
0713     }
0714     opacity *= mopacity.opacity / 100.0;
0715     bool over = opacity < 0.99 ||
0716                 CAIRO_CONTENT_COLOR != cairo_surface_get_content (s->surface);
0717     cairo_operator_t op;
0718     if (over) {
0719         op = cairo_get_operator (cr);
0720         cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
0721     }
0722     if (opacity < 0.99) {
0723         cairo_clip (cr);
0724         cairo_paint_with_alpha (cr, opacity);
0725     } else {
0726         cairo_fill (cr);
0727     }
0728     if (over)
0729         cairo_set_operator (cr, op);
0730     cairo_pattern_destroy (cur_pat);
0731     cairo_restore (cr);
0732 }
0733 
0734 static Mrl *findActiveMrl (Node *n, bool *rp_or_smil) {
0735     Mrl *mrl = n->mrl ();
0736     if (mrl) {
0737         *rp_or_smil = (mrl->id >= SMIL::id_node_first &&
0738                 mrl->id < SMIL::id_node_last) ||
0739             (mrl->id >= RP::id_node_first &&
0740              mrl->id < RP::id_node_last);
0741         if (*rp_or_smil ||
0742                 (mrl->media_info &&
0743                  (MediaManager::Audio == mrl->media_info->type ||
0744                   MediaManager::AudioVideo == mrl->media_info->type)))
0745             return mrl;
0746     }
0747     for (Node *c = n->firstChild (); c; c = c->nextSibling ())
0748         if (c->active ()) {
0749             Mrl *m = findActiveMrl (c, rp_or_smil);
0750             if (m)
0751                 return m;
0752         }
0753     return NULL;
0754 }
0755 
0756 KDE_NO_EXPORT
0757 void CairoPaintVisitor::updateExternal (SMIL::MediaType *av, SurfacePtr s) {
0758     bool rp_or_smil = false;
0759     Mrl *ext_mrl = findActiveMrl (av->external_tree.ptr (), &rp_or_smil);
0760     if (!ext_mrl)
0761         return;
0762     if (!rp_or_smil) {
0763         video (ext_mrl, s.ptr ());
0764         return;
0765     }
0766     IRect scr = matrix.toScreen (s->bounds);
0767     IRect clip_rect = clip.intersect (scr);
0768     if (clip_rect.isEmpty ())
0769         return;
0770     if (!s->surface || s->dirty) {
0771         Matrix m = matrix;
0772         m.translate (-scr.x (), -scr.y ());
0773         m.scale (s->xscale, s->yscale);
0774         IRect r (clip_rect.x() - scr.x () - 1, clip_rect.y() - scr.y () - 1,
0775                 clip_rect.width() + 3, clip_rect.height() + 3);
0776         if (!s->surface) {
0777             s->surface = cairo_surface_create_similar (cairo_surface,
0778                     CAIRO_CONTENT_COLOR_ALPHA, scr.width (), scr.height ());
0779             r = IRect (0, 0, scr.size);
0780         }
0781         CairoPaintVisitor visitor (s->surface, m, r);
0782         ext_mrl->accept (&visitor);
0783         s->dirty = false;
0784     }
0785     paint (&av->transition, av->media_opacity, s.ptr (), scr.point, clip_rect);
0786 }
0787 
0788 static void setAlignment (QTextDocument &td, unsigned char align) {
0789     QTextOption opt = td.defaultTextOption();
0790     if (SmilTextProperties::AlignLeft == align)
0791         opt.setAlignment (Qt::AlignLeft);
0792     else if (SmilTextProperties::AlignCenter == align)
0793         opt.setAlignment (Qt::AlignCenter);
0794     else if (SmilTextProperties::AlignRight == align)
0795         opt.setAlignment (Qt::AlignRight);
0796     td.setDefaultTextOption (opt);
0797 }
0798 
0799 static void calculateTextDimensions (const QFont& font,
0800         const QString& text, Single w, Single h, Single maxh,
0801         int *pxw, int *pxh, bool markup_text,
0802         unsigned char align = SmilTextProperties::AlignLeft) {
0803     QTextDocument td;
0804     td.setDefaultFont( font );
0805     td.setDocumentMargin (0);
0806     QImage img (QSize ((int)w, (int)h), QImage::Format_RGB32);
0807     td.setPageSize (QSize ((int)w, (int)maxh));
0808     td.documentLayout()->setPaintDevice (&img);
0809     if (markup_text)
0810         td.setHtml( text );
0811     else
0812         td.setPlainText( text );
0813     setAlignment (td, align);
0814     QRectF r = td.documentLayout()->blockBoundingRect (td.lastBlock());
0815     *pxw = (int)td.idealWidth ();
0816     *pxh = (int)(r.y() + r.height());
0817 #if QT_VERSION >= 0x050600
0818     *pxw = qMin( (int)(*pxw + pixel_device_ratio), (int)w);
0819 #endif
0820 }
0821 
0822 static cairo_t *createContext (cairo_surface_t *similar, Surface *s, int w, int h) {
0823     unsigned int bg_alpha = s->background_color & 0xff000000;
0824     bool clear = s->surface;
0825     if (!s->surface)
0826         s->surface = cairo_surface_create_similar (similar,
0827                 bg_alpha < 0xff000000
0828                 ? CAIRO_CONTENT_COLOR_ALPHA
0829                 : CAIRO_CONTENT_COLOR,
0830                 w, h);
0831     cairo_t *cr = cairo_create (s->surface);
0832     if (clear)
0833         clearSurface (cr, IRect (0, 0, w, h));
0834     cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
0835 
0836     if (bg_alpha) {
0837         if (bg_alpha < 0xff000000)
0838             CAIRO_SET_SOURCE_ARGB (cr, s->background_color);
0839         else
0840             CAIRO_SET_SOURCE_RGB (cr, s->background_color);
0841         cairo_paint (cr);
0842     }
0843     return cr;
0844 }
0845 
0846 KDE_NO_EXPORT void CairoPaintVisitor::visit (SMIL::TextMediaType * txt) {
0847     if (!txt->media_info || !txt->media_info->media)
0848         return;
0849     TextMedia *tm = static_cast <TextMedia *> (txt->media_info->media);
0850     Surface *s = txt->surface ();
0851     if (!s)
0852         return;
0853     if (!s->surface) {
0854         txt->size = SSize ();
0855         s->bounds = txt->calculateBounds ();
0856     }
0857     IRect scr = matrix.toScreen (s->bounds);
0858     if (!s->surface || s->dirty) {
0859 
0860         int w = scr.width ();
0861         int pxw, pxh;
0862         Single ft_size = w * txt->font_size / (double)s->bounds.width ();
0863         bool clear = s->surface;
0864 
0865         QFont font (txt->font_name);
0866         font.setPixelSize(ft_size);
0867         if (clear) {
0868             pxw = scr.width ();
0869             pxh = scr.height ();
0870         } else {
0871             calculateTextDimensions (font, tm->text,
0872                     w, 2 * ft_size, scr.height (), &pxw, &pxh, false);
0873         }
0874         QTextDocument td;
0875         td.setDocumentMargin (0);
0876         td.setDefaultFont (font);
0877         bool have_alpha = (s->background_color & 0xff000000) < 0xff000000;
0878         QImage img (QSize (pxw, pxh), have_alpha ? QImage::Format_ARGB32 : QImage::Format_RGB32);
0879         img.fill (s->background_color);
0880         td.setPageSize (QSize (pxw, pxh + (int)ft_size));
0881         td.documentLayout()->setPaintDevice (&img);
0882         setAlignment (td, 1 + (int)txt->halign);
0883         td.setPlainText (tm->text);
0884         QPainter painter;
0885         painter.begin (&img);
0886         QAbstractTextDocumentLayout::PaintContext ctx;
0887         ctx.clip = QRect (0, 0, pxw, pxh);
0888         ctx.palette.setColor (QPalette::Text, QColor (QRgb (txt->font_color)));
0889         td.documentLayout()->draw (&painter, ctx);
0890         painter.end();
0891 
0892         cairo_t *cr_txt = createContext (cairo_surface, s, pxw, pxh);
0893         cairo_surface_t *src_sf = cairo_image_surface_create_for_data (
0894                 img.bits (),
0895                 have_alpha ? CAIRO_FORMAT_ARGB32:CAIRO_FORMAT_RGB24,
0896                 img.width(), img.height(), img.bytesPerLine ());
0897         cairo_pattern_t *pat = cairo_pattern_create_for_surface (src_sf);
0898         cairo_pattern_set_extend (pat, CAIRO_EXTEND_NONE);
0899         cairo_set_operator (cr_txt, CAIRO_OPERATOR_SOURCE);
0900         cairo_set_source (cr_txt, pat);
0901         cairo_paint (cr_txt);
0902         cairo_pattern_destroy (pat);
0903         cairo_surface_destroy (src_sf);
0904         cairo_destroy (cr_txt);
0905 
0906         // update bounds rect
0907         SRect rect = matrix.toUser (IRect (scr.point, ISize (pxw, pxh)));
0908         txt->size = rect.size;
0909         s->bounds = txt->calculateBounds ();
0910 
0911         // update coord. for painting below
0912         scr = matrix.toScreen (s->bounds);
0913     }
0914     IRect clip_rect = clip.intersect (scr);
0915     if (!clip_rect.isEmpty ())
0916         paint (&txt->transition, txt->media_opacity, s, scr.point, clip_rect);
0917     s->dirty = false;
0918 }
0919 
0920 KDE_NO_EXPORT void CairoPaintVisitor::visit (SMIL::Brush * brush) {
0921     Surface *s = brush->surface ();
0922     if (s) {
0923         opacity = 1.0;
0924         IRect clip_rect = clip.intersect (matrix.toScreen (s->bounds));
0925         if (clip_rect.isEmpty ())
0926             return;
0927         cairo_save (cr);
0928         if (brush->transition.active_trans) {
0929             cur_transition = &brush->transition;
0930             cur_pat = NULL;
0931             brush->transition.active_trans->accept (this);
0932         } else {
0933             cairo_rectangle (cr, clip_rect.x (), clip_rect.y (),
0934                     clip_rect.width (), clip_rect.height ());
0935         }
0936         unsigned int color = brush->color.color;
0937         if (!color) {
0938             color = brush->background_color.color;
0939             opacity *= brush->background_color.opacity / 100.0;
0940         } else {
0941             opacity *= brush->color.opacity / 100.0;
0942         }
0943         opacity *= brush->media_opacity.opacity / 100.0;
0944         if (opacity < 0.99) {
0945             cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
0946             cairo_set_source_rgba (cr,
0947                     1.0 * ((color >> 16) & 0xff) / 255,
0948                     1.0 * ((color >> 8) & 0xff) / 255,
0949                     1.0 * (color & 0xff) / 255,
0950                     opacity);
0951         } else {
0952             CAIRO_SET_SOURCE_RGB (cr, color);
0953         }
0954         cairo_fill (cr);
0955         if (opacity < 0.99)
0956             cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
0957         s->dirty = false;
0958         cairo_restore (cr);
0959     }
0960 }
0961 
0962 struct SmilTextBlock {
0963     SmilTextBlock (const QFont& f, const QString &t,
0964             IRect r, unsigned char a)
0965         : font (f), rich_text (t), rect (r), align (a), next (NULL) {}
0966 
0967     QFont font;
0968     QString rich_text;
0969     IRect rect;
0970     unsigned char align;
0971 
0972     SmilTextBlock *next;
0973 };
0974 
0975 struct KMPLAYER_NO_EXPORT SmilTextInfo {
0976     SmilTextInfo (const SmilTextProperties &p) : props (p) {}
0977 
0978     void span (float scale);
0979 
0980     SmilTextProperties props;
0981     QString span_text;
0982 };
0983 
0984 class KMPLAYER_NO_EXPORT SmilTextVisitor : public Visitor {
0985 public:
0986     SmilTextVisitor (int w, float s, const SmilTextProperties &p)
0987         : first (NULL), last (NULL), width (w), voffset (0),
0988           scale (s), max_font_size (0), info (p) {
0989          info.span (scale);
0990     }
0991     using Visitor::visit;
0992     void visit (TextNode *);
0993     void visit (SMIL::TextFlow *);
0994     void visit (SMIL::TemporalMoment *);
0995 
0996     void addRichText (const QString &txt);
0997     void push ();
0998 
0999     SmilTextBlock *first;
1000     SmilTextBlock *last;
1001 
1002     int width;
1003     int voffset;
1004     float scale;
1005     float max_font_size;
1006     SmilTextInfo info;
1007     QString rich_text;
1008 };
1009 
1010 void SmilTextInfo::span (float scale) {
1011     QString s = "<span style=\"";
1012     if (props.font_size.size () > -1)
1013         s += "font-size:" + QString::number ((int)(scale * props.font_size.size ())) + "px;";
1014     s += "font-family:" + props.font_family + ";";
1015     if (props.font_color > -1)
1016         s += QString().sprintf ("color:#%06x;", props.font_color);
1017     if (props.background_color > -1)
1018         s += QString().sprintf ("background-color:#%06x;", props.background_color);
1019     if (SmilTextProperties::StyleInherit != props.font_style) {
1020         s += "font-style:";
1021         switch (props.font_style) {
1022             case SmilTextProperties::StyleOblique:
1023                 s += "oblique;";
1024                 break;
1025             case SmilTextProperties::StyleItalic:
1026                 s += "italic;";
1027                 break;
1028             default:
1029                 s += "normal;";
1030                 break;
1031         }
1032     }
1033     if (SmilTextProperties::WeightInherit != props.font_weight) {
1034         s += "font-weight:";
1035         switch (props.font_weight) {
1036             case SmilTextProperties::WeightBold:
1037                 s += "bold;";
1038                 break;
1039             default:
1040                 s += "normal;";
1041                 break;
1042         }
1043     }
1044     s += "\">";
1045     span_text = s;
1046 }
1047 
1048 void SmilTextVisitor::addRichText (const QString &txt) {
1049     if (!info.span_text.isEmpty ())
1050         rich_text += info.span_text;
1051     rich_text += txt;
1052     if (!info.span_text.isEmpty ())
1053         rich_text += "</span>";
1054 }
1055 
1056 void SmilTextVisitor::push () {
1057     if (!rich_text.isEmpty ()) {
1058         int pxw, pxh;
1059         float fs = info.props.font_size.size ();
1060         if (fs < 0)
1061             fs = TextMedia::defaultFontSize ();
1062         float maxfs = max_font_size;
1063         if (maxfs < 1.0)
1064             maxfs = fs;
1065         fs *= scale;
1066         maxfs *= scale;
1067 
1068         QFont font ("Sans");
1069         font.setPixelSize((int)fs);
1070         calculateTextDimensions (font, rich_text.toUtf8 ().constData (),
1071                 width, 2 * maxfs, 1024, &pxw, &pxh, true, info.props.text_align);
1072         int x = 0;
1073         if (SmilTextProperties::AlignCenter == info.props.text_align)
1074             x = (width - pxw) / 2;
1075         else if (SmilTextProperties::AlignRight == info.props.text_align)
1076             x = width - pxw;
1077         SmilTextBlock *block = new SmilTextBlock (font, rich_text,
1078                 IRect (x, voffset, pxw, pxh), info.props.text_align);
1079         voffset += pxh;
1080         max_font_size = 0;
1081         rich_text.clear();
1082         if (!first) {
1083             first = last = block;
1084         } else {
1085             last->next = block;
1086             last = block;
1087         }
1088     }
1089 }
1090 
1091 void SmilTextVisitor::visit (TextNode *text) {
1092     QString buffer;
1093     QTextStream out (&buffer, QIODevice::WriteOnly);
1094     out << XMLStringlet (text->nodeValue ());
1095     addRichText (buffer);
1096     if (text->nextSibling ())
1097         text->nextSibling ()->accept (this);
1098 }
1099 
1100 void SmilTextVisitor::visit (SMIL::TextFlow *flow) {
1101     bool new_block = SMIL::id_node_p == flow->id ||
1102         SMIL::id_node_br == flow->id ||
1103         SMIL::id_node_div == flow->id;
1104     if ((new_block && !rich_text.isEmpty ()) || flow->firstChild ()) {
1105         float fs = info.props.font_size.size ();
1106         if (fs < 0)
1107             fs = TextMedia::defaultFontSize ();
1108         int par_extra = SMIL::id_node_p == flow->id
1109             ? (int)(scale * fs) : 0;
1110         voffset += par_extra;
1111 
1112         SmilTextInfo saved_info = info;
1113         if (new_block)
1114             push ();
1115 
1116         info.props.mask (flow->props);
1117         if ((float)info.props.font_size.size () > max_font_size)
1118             max_font_size = info.props.font_size.size ();
1119         info.span (scale);
1120 
1121         if (flow->firstChild ())
1122             flow->firstChild ()->accept (this);
1123 
1124         if (rich_text.isEmpty ())
1125             par_extra = 0;
1126         if (new_block && flow->firstChild ())
1127             push ();
1128         voffset += par_extra;
1129 
1130         info = saved_info;
1131     }
1132     if (flow->nextSibling ())
1133         flow->nextSibling ()->accept (this);
1134 }
1135 
1136 void SmilTextVisitor::visit (SMIL::TemporalMoment *tm) {
1137     if (tm->state >= Node::state_began
1138             && tm->nextSibling ())
1139         tm->nextSibling ()->accept (this);
1140 }
1141 
1142 KDE_NO_EXPORT void CairoPaintVisitor::visit (SMIL::SmilText *txt) {
1143     Surface *s = txt->surface ();
1144     if (!s)
1145         return;
1146 
1147     SRect rect = s->bounds;
1148     IRect scr = matrix.toScreen (rect);
1149 
1150     if (!s->surface) {
1151 
1152         int w = scr.width ();
1153         float scale = 1.0 * w / (double)s->bounds.width ();
1154         SmilTextVisitor info (w, scale, txt->props);
1155 
1156         Node *first = txt->firstChild ();
1157         for (Node *n = first; n; n = n->nextSibling ())
1158             if (SMIL::id_node_clear == n->id) {
1159                 if (n->state >= Node::state_began)
1160                     first = n->nextSibling ();
1161                 else
1162                     break;
1163             }
1164         if (first)
1165             first->accept (&info);
1166 
1167         info.push ();
1168         if (info.first) {
1169             cairo_t *cr_txt = createContext (cairo_surface, s, (int) w, info.voffset);
1170 
1171             CAIRO_SET_SOURCE_RGB (cr_txt, 0);
1172             SmilTextBlock *b = info.first;
1173             int hoff = 0;
1174             int voff = 0;
1175             while (b) {
1176                 cairo_translate (cr_txt, b->rect.x() - hoff, b->rect.y() - voff);
1177                 QTextDocument td;
1178                 td.setDocumentMargin (0);
1179                 td.setDefaultFont (b->font);
1180                 bool have_alpha = (s->background_color & 0xff000000) < 0xff000000;
1181                 QImage img (QSize (b->rect.width(), b->rect.height()), have_alpha ? QImage::Format_ARGB32 : QImage::Format_RGB32);
1182                 img.fill (s->background_color);
1183                 td.setPageSize (QSize (b->rect.width(), b->rect.height() + 10));
1184                 setAlignment (td, b->align);
1185                 td.documentLayout()->setPaintDevice (&img);
1186                 td.setHtml (b->rich_text);
1187                 QPainter painter;
1188                 painter.begin (&img);
1189                 QAbstractTextDocumentLayout::PaintContext ctx;
1190                 ctx.clip = QRect (QPoint (0, 0), img.size ());
1191                 td.documentLayout()->draw (&painter, ctx);
1192                 painter.end();
1193 
1194                 cairo_surface_t *src_sf = cairo_image_surface_create_for_data (
1195                         img.bits (),
1196                         have_alpha ? CAIRO_FORMAT_ARGB32:CAIRO_FORMAT_RGB24,
1197                         img.width(), img.height(), img.bytesPerLine ());
1198                 cairo_pattern_t *pat = cairo_pattern_create_for_surface (src_sf);
1199                 cairo_pattern_set_extend (pat, CAIRO_EXTEND_NONE);
1200                 cairo_set_operator (cr_txt, CAIRO_OPERATOR_SOURCE);
1201                 cairo_set_source (cr_txt, pat);
1202                 cairo_rectangle (cr_txt, 0, 0, b->rect.width(), b->rect.height());
1203                 cairo_fill (cr_txt);
1204                 cairo_pattern_destroy (pat);
1205                 cairo_surface_destroy (src_sf);
1206 
1207                 hoff = b->rect.x ();
1208                 voff = b->rect.y ();
1209                 SmilTextBlock *tmp = b;
1210                 b = b->next;
1211                 delete tmp;
1212             }
1213             cairo_destroy (cr_txt);
1214 
1215             // update bounds rect
1216             s->bounds = matrix.toUser (IRect (scr.point, ISize (w, info.voffset)));
1217             txt->size = s->bounds.size;
1218             txt->updateBounds (false);
1219 
1220             // update coord. for painting below
1221             scr = matrix.toScreen (s->bounds);
1222         }
1223     }
1224     IRect clip_rect = clip.intersect (scr);
1225     if (s->surface && !clip_rect.isEmpty ())
1226         paint (&txt->transition, txt->media_opacity, s, scr.point, clip_rect);
1227     s->dirty = false;
1228 }
1229 
1230 KDE_NO_EXPORT void CairoPaintVisitor::visit (RP::Imfl * imfl) {
1231     if (imfl->surface ()) {
1232         cairo_save (cr);
1233         Matrix m = matrix;
1234         IRect scr = matrix.toScreen (SRect (0, 0, imfl->rp_surface->bounds.size));
1235         int w = scr.width ();
1236         int h = scr.height ();
1237         cairo_rectangle (cr, scr.x (), scr.y (), w, h);
1238         //cairo_clip (cr);
1239         cairo_translate (cr, scr.x (), scr.y ());
1240         cairo_scale (cr, 1.0*w/(double)imfl->size.width, 1.0*h/(double)imfl->size.height);
1241         if (imfl->needs_scene_img)
1242             cairo_push_group (cr);
1243         for (NodePtr n = imfl->firstChild (); n; n = n->nextSibling ())
1244             if (n->state >= Node::state_began &&
1245                     n->state < Node::state_deactivated) {
1246                 RP::TimingsBase * tb = convertNode<RP::TimingsBase>(n);
1247                 switch (n->id) {
1248                     case RP::id_node_viewchange:
1249                         if (!(int)tb->srcw)
1250                             tb->srcw = imfl->size.width;
1251                         if (!(int)tb->srch)
1252                             tb->srch = imfl->size.height;
1253                         // fall through
1254                     case RP::id_node_crossfade:
1255                     case RP::id_node_fadein:
1256                     case RP::id_node_fadeout:
1257                     case RP::id_node_fill:
1258                     case RP::id_node_wipe:
1259                         if (!(int)tb->w)
1260                             tb->w = imfl->size.width;
1261                         if (!(int)tb->h)
1262                             tb->h = imfl->size.height;
1263                         n->accept (this);
1264                         break;
1265                 }
1266             }
1267         if (imfl->needs_scene_img) {
1268             cairo_pattern_t * pat = cairo_pop_group (cr);
1269             cairo_pattern_set_extend (pat, CAIRO_EXTEND_NONE);
1270             cairo_set_source (cr, pat);
1271             cairo_paint (cr);
1272             cairo_pattern_destroy (pat);
1273         }
1274         cairo_restore (cr);
1275         matrix = m;
1276     }
1277 }
1278 
1279 KDE_NO_EXPORT void CairoPaintVisitor::visit (RP::Fill * fi) {
1280     CAIRO_SET_SOURCE_RGB (cr, fi->color);
1281     if ((int)fi->w && (int)fi->h) {
1282         cairo_rectangle (cr, fi->x, fi->y, fi->w, fi->h);
1283         cairo_fill (cr);
1284     }
1285 }
1286 
1287 KDE_NO_EXPORT void CairoPaintVisitor::visit (RP::Fadein * fi) {
1288     if (fi->target && fi->target->id == RP::id_node_image) {
1289         RP::Image *img = convertNode <RP::Image> (fi->target);
1290         ImageMedia *im = img && img->media_info
1291             ? static_cast <ImageMedia*> (img->media_info->media) : NULL;
1292         if (im && img->surface ()) {
1293             Single sx = fi->srcx, sy = fi->srcy, sw = fi->srcw, sh = fi->srch;
1294             if (!(int)sw)
1295                 sw = img->size.width;
1296             if (!(int)sh)
1297                 sh = img->size.height;
1298             if ((int)fi->w && (int)fi->h && (int)sw && (int)sh) {
1299                 if (!img->img_surface->surface)
1300                     im->cached_img->copyImage (img->img_surface,
1301                             img->size, cairo_surface);
1302                 cairo_matrix_t matrix;
1303                 cairo_matrix_init_identity (&matrix);
1304                 float scalex = 1.0 * sw / fi->w;
1305                 float scaley = 1.0 * sh / fi->h;
1306                 cairo_matrix_scale (&matrix, scalex, scaley);
1307                 cairo_matrix_translate (&matrix,
1308                         1.0*sx/scalex - (double)fi->x,
1309                         1.0*sy/scaley - (double)fi->y);
1310                 cairo_save (cr);
1311                 cairo_rectangle (cr, fi->x, fi->y, fi->w, fi->h);
1312                 cairo_pattern_t *pat = cairo_pattern_create_for_surface (img->img_surface->surface);
1313                 cairo_pattern_set_extend (pat, CAIRO_EXTEND_NONE);
1314                 cairo_pattern_set_matrix (pat, &matrix);
1315                 cairo_set_source (cr, pat);
1316                 cairo_clip (cr);
1317                 cairo_paint_with_alpha (cr, 1.0 * fi->progress / 100);
1318                 cairo_restore (cr);
1319                 cairo_pattern_destroy (pat);
1320             }
1321         }
1322     }
1323 }
1324 
1325 KDE_NO_EXPORT void CairoPaintVisitor::visit (RP::Fadeout * fo) {
1326     if (fo->progress > 0) {
1327         CAIRO_SET_SOURCE_RGB (cr, fo->to_color);
1328         if ((int)fo->w && (int)fo->h) {
1329             cairo_save (cr);
1330             cairo_rectangle (cr, fo->x, fo->y, fo->w, fo->h);
1331             cairo_clip (cr);
1332             cairo_paint_with_alpha (cr, 1.0 * fo->progress / 100);
1333             cairo_restore (cr);
1334         }
1335     }
1336 }
1337 
1338 KDE_NO_EXPORT void CairoPaintVisitor::visit (RP::Crossfade * cf) {
1339     if (cf->target && cf->target->id == RP::id_node_image) {
1340         RP::Image *img = convertNode <RP::Image> (cf->target);
1341         ImageMedia *im = img && img->media_info
1342             ? static_cast <ImageMedia*> (img->media_info->media) : NULL;
1343         if (im && img->surface ()) {
1344             Single sx = cf->srcx, sy = cf->srcy, sw = cf->srcw, sh = cf->srch;
1345             if (!(int)sw)
1346                 sw = img->size.width;
1347             if (!(int)sh)
1348                 sh = img->size.height;
1349             if ((int)cf->w && (int)cf->h && (int)sw && (int)sh) {
1350                 if (!img->img_surface->surface)
1351                     im->cached_img->copyImage (img->img_surface,
1352                             img->size, cairo_surface);
1353                 cairo_save (cr);
1354                 cairo_matrix_t matrix;
1355                 cairo_matrix_init_identity (&matrix);
1356                 float scalex = 1.0 * sw / cf->w;
1357                 float scaley = 1.0 * sh / cf->h;
1358                 cairo_matrix_scale (&matrix, scalex, scaley);
1359                 cairo_matrix_translate (&matrix,
1360                         1.0*sx/scalex - (double)cf->x,
1361                         1.0*sy/scaley - (double)cf->y);
1362                 cairo_rectangle (cr, cf->x, cf->y, cf->w, cf->h);
1363                 cairo_pattern_t *pat = cairo_pattern_create_for_surface (img->img_surface->surface);
1364                 cairo_pattern_set_extend (pat, CAIRO_EXTEND_NONE);
1365                 cairo_pattern_set_matrix (pat, &matrix);
1366                 cairo_set_source (cr, pat);
1367                 cairo_clip (cr);
1368                 cairo_paint_with_alpha (cr, 1.0 * cf->progress / 100);
1369                 cairo_restore (cr);
1370                 cairo_pattern_destroy (pat);
1371             }
1372         }
1373     }
1374 }
1375 
1376 KDE_NO_EXPORT void CairoPaintVisitor::visit (RP::Wipe * wipe) {
1377     if (wipe->target && wipe->target->id == RP::id_node_image) {
1378         RP::Image *img = convertNode <RP::Image> (wipe->target);
1379         ImageMedia *im = img && img->media_info
1380             ? static_cast <ImageMedia*> (img->media_info->media) : NULL;
1381         if (im && img->surface ()) {
1382             Single x = wipe->x, y = wipe->y;
1383             Single tx = x, ty = y;
1384             Single w = wipe->w, h = wipe->h;
1385             Single sx = wipe->srcx, sy = wipe->srcy, sw = wipe->srcw, sh = wipe->srch;
1386             if (!(int)sw)
1387                 sw = img->size.width;
1388             if (!(int)sh)
1389                 sh = img->size.height;
1390             if (wipe->direction == RP::Wipe::dir_right) {
1391                 Single dx = w * 1.0 * wipe->progress / 100;
1392                 tx = x -w + dx;
1393                 w = dx;
1394             } else if (wipe->direction == RP::Wipe::dir_left) {
1395                 Single dx = w * 1.0 * wipe->progress / 100;
1396                 tx = x + w - dx;
1397                 x = tx;
1398                 w = dx;
1399             } else if (wipe->direction == RP::Wipe::dir_down) {
1400                 Single dy = h * 1.0 * wipe->progress / 100;
1401                 ty = y - h + dy;
1402                 h = dy;
1403             } else if (wipe->direction == RP::Wipe::dir_up) {
1404                 Single dy = h * 1.0 * wipe->progress / 100;
1405                 ty = y + h - dy;
1406                 y = ty;
1407                 h = dy;
1408             }
1409 
1410             if ((int)w && (int)h) {
1411                 if (!img->img_surface->surface)
1412                     im->cached_img->copyImage (img->img_surface,
1413                             img->size, cairo_surface);
1414                 cairo_matrix_t matrix;
1415                 cairo_matrix_init_identity (&matrix);
1416                 float scalex = 1.0 * sw / wipe->w;
1417                 float scaley = 1.0 * sh / wipe->h;
1418                 cairo_matrix_scale (&matrix, scalex, scaley);
1419                 cairo_matrix_translate (&matrix,
1420                         1.0*sx/scalex - (double)tx,
1421                         1.0*sy/scaley - (double)ty);
1422                 cairo_pattern_t *pat = cairo_pattern_create_for_surface (img->img_surface->surface);
1423                 cairo_pattern_set_extend (pat, CAIRO_EXTEND_NONE);
1424                 cairo_pattern_set_matrix (pat, &matrix);
1425                 cairo_set_source (cr, pat);
1426                 cairo_rectangle (cr, x, y, w, h);
1427                 cairo_fill (cr);
1428                 cairo_pattern_destroy (pat);
1429             }
1430         }
1431     }
1432 }
1433 
1434 KDE_NO_EXPORT void CairoPaintVisitor::visit (RP::ViewChange * vc) {
1435     if (vc->unfinished () || vc->progress < 100) {
1436         cairo_pattern_t * pat = cairo_pop_group (cr); // from imfl
1437         cairo_pattern_set_extend (pat, CAIRO_EXTEND_NONE);
1438         cairo_push_group (cr);
1439         cairo_save (cr);
1440         cairo_set_source (cr, pat);
1441         cairo_paint (cr);
1442         if ((int)vc->w && (int)vc->h && (int)vc->srcw && (int)vc->srch) {
1443             cairo_matrix_t matrix;
1444             cairo_matrix_init_identity (&matrix);
1445             float scalex = 1.0 * vc->srcw / vc->w;
1446             float scaley = 1.0 * vc->srch / vc->h;
1447             cairo_matrix_scale (&matrix, scalex, scaley);
1448             cairo_matrix_translate (&matrix,
1449                     1.0*vc->srcx/scalex - (double)vc->x,
1450                     1.0*vc->srcy/scaley - (double)vc->y);
1451             cairo_pattern_set_matrix (pat, &matrix);
1452             cairo_set_source (cr, pat);
1453             cairo_rectangle (cr, vc->x, vc->y, vc->w, vc->h);
1454             cairo_fill (cr);
1455         }
1456         cairo_pattern_destroy (pat);
1457         cairo_restore (cr);
1458     }
1459 }
1460 
1461 #endif
1462 
1463 //-----------------------------------------------------------------------------
1464 
1465 namespace KMPlayer {
1466 
1467 class KMPLAYER_NO_EXPORT MouseVisitor : public Visitor {
1468     ViewArea *view_area;
1469     Matrix matrix;
1470     NodePtrW source;
1471     const MessageType event;
1472     int x, y;
1473     bool handled;
1474     bool bubble_up;
1475 
1476     bool deliverAndForward (Node *n, Surface *s, bool inside, bool deliver);
1477     void surfaceEvent (Node *mt, Surface *s);
1478 public:
1479     MouseVisitor (ViewArea *v, MessageType evt, Matrix m, int x, int y);
1480     KDE_NO_CDTOR_EXPORT ~MouseVisitor () {}
1481     using Visitor::visit;
1482     void visit (Node * n);
1483     void visit (Element *);
1484     void visit (SMIL::Smil *);
1485     void visit (SMIL::Layout *);
1486     void visit (SMIL::RegionBase *);
1487     void visit (SMIL::MediaType * n);
1488     void visit (SMIL::SmilText * n);
1489     void visit (SMIL::Anchor *);
1490     void visit (SMIL::Area *);
1491     QCursor cursor;
1492 };
1493 
1494 } // namespace
1495 
1496 KDE_NO_CDTOR_EXPORT
1497 MouseVisitor::MouseVisitor (ViewArea *v, MessageType evt, Matrix m, int a, int b)
1498   : view_area (v), matrix (m), event (evt), x (a), y (b),
1499     handled (false), bubble_up (false) {
1500 }
1501 
1502 KDE_NO_EXPORT void MouseVisitor::visit (Node * n) {
1503     kDebug () << "Mouse event ignored for " << n->nodeName ();
1504 }
1505 
1506 KDE_NO_EXPORT void MouseVisitor::visit (SMIL::Smil *s) {
1507     if (s->active () && s->layout_node)
1508         s->layout_node->accept (this);
1509 }
1510 
1511 KDE_NO_EXPORT void MouseVisitor::visit (SMIL::Layout * layout) {
1512     if (layout->root_layout)
1513         layout->root_layout->accept (this);
1514 }
1515 
1516 KDE_NO_EXPORT void MouseVisitor::visit (SMIL::RegionBase *region) {
1517     Surface *s = (Surface *) region->role (RoleDisplay);
1518     if (s) {
1519         SRect rect = s->bounds;
1520         IRect scr = matrix.toScreen (rect);
1521         int rx = scr.x(), ry = scr.y(), rw = scr.width(), rh = scr.height();
1522         handled = false;
1523         bool inside = x > rx && x < rx+rw && y > ry && y< ry+rh;
1524         if (!inside && (event == MsgEventClicked || !s->has_mouse))
1525             return;
1526 
1527         if (event == MsgEventClicked && !s->virtual_size.isEmpty () &&
1528                 x > rx + rw - REGION_SCROLLBAR_WIDTH) {
1529             const int sbh = rh - REGION_SCROLLBAR_WIDTH;
1530             const int vy = s->virtual_size.height;
1531             const int knob_h = sbh * rh / vy;
1532             int knob_y = y - ry - 0.5 * knob_h;
1533             if (knob_y < 0)
1534                 knob_y = 0;
1535             else if (knob_y + knob_h > sbh)
1536                 knob_y = sbh - knob_h;
1537             s->y_scroll = vy * knob_y / sbh;
1538             view_area->scheduleRepaint (scr);
1539             return;
1540         }
1541         if (event == MsgEventClicked && !s->virtual_size.isEmpty () &&
1542                 y > ry + rh - REGION_SCROLLBAR_WIDTH) {
1543             const int sbw = rw - REGION_SCROLLBAR_WIDTH;
1544             const int vw = s->virtual_size.width;
1545             const int knob_w = sbw * rw / vw;
1546             int knob_x = x - rx - 0.5 * knob_w;
1547             if (knob_x < 0)
1548                 knob_x = 0;
1549             else if (knob_x + knob_w > sbw)
1550                 knob_x = sbw - knob_w;
1551             s->x_scroll = vw * knob_x / sbw;
1552             view_area->scheduleRepaint (scr);
1553             return;
1554         }
1555 
1556         NodePtrW src = source;
1557         source = region;
1558         Matrix m = matrix;
1559         matrix = Matrix (rect.x(), rect.y(), 1.0, 1.0);
1560         matrix.transform (m);
1561         if (!s->virtual_size.isEmpty ())
1562             matrix.translate (-s->x_scroll, -s->y_scroll);
1563         bubble_up = false;
1564 
1565         bool child_handled = false;
1566         if (inside || s->has_mouse)
1567             for (SurfacePtr c = s->firstChild (); c; c = c->nextSibling ()) {
1568                 if (c->node && c->node->id == SMIL::id_node_region) {
1569                     c->node->accept (this);
1570                     child_handled |= handled;
1571                     if (!source || !source->active ())
1572                         break;
1573                 } else {
1574                     break;
1575                 }
1576             }
1577         child_handled &= !bubble_up;
1578         bubble_up = false;
1579         if (source && source->active ())
1580             deliverAndForward (region, s, inside, !child_handled);
1581 
1582         handled = inside;
1583         matrix = m;
1584         source = src;
1585     }
1586 }
1587 
1588 static void followLink (SMIL::LinkingBase * link) {
1589     kDebug() << "link to " << link->href << " clicked";
1590     if (link->href.startsWith ("#")) {
1591         SMIL::Smil * s = SMIL::Smil::findSmilNode (link);
1592         if (s)
1593             s->jump (link->href.mid (1));
1594         else
1595             kError() << "In document jumps smil not found" << endl;
1596     } else {
1597         PlayListNotify *notify = link->document ()->notify_listener;
1598         if (notify && !link->target.isEmpty ()) {
1599             notify->openUrl(KUrl(link->href), link->target, QString());
1600         } else {
1601             NodePtr n = link;
1602             for (NodePtr p = link->parentNode (); p; p = p->parentNode ()) {
1603                 if (n->mrl () && n->mrl ()->opener == p) {
1604                     p->setState (Node::state_deferred);
1605                     p->mrl ()->setParam (Ids::attr_src, link->href, 0L);
1606                     p->activate ();
1607                     break;
1608                 }
1609                 n = p;
1610             }
1611         }
1612     }
1613 }
1614 
1615 KDE_NO_EXPORT void MouseVisitor::visit (SMIL::Anchor * anchor) {
1616     if (event == MsgEventPointerMoved)
1617         cursor.setShape (Qt::PointingHandCursor);
1618     else if (event == MsgEventClicked)
1619         followLink (anchor);
1620 }
1621 
1622 KDE_NO_EXPORT void MouseVisitor::visit (SMIL::Area * area) {
1623     NodePtr n = area->parentNode ();
1624     Surface *s = (Surface *) n->role (RoleDisplay);
1625     if (s) {
1626         SRect rect = s->bounds;
1627         IRect scr = matrix.toScreen (rect);
1628         int w = scr.width (), h = scr.height ();
1629         if (area->nr_coords > 1) {
1630             Single left = area->coords[0].size (rect.width ());
1631             Single top = area->coords[1].size (rect.height ());
1632             matrix.getXY (left, top);
1633             if (x < left || x > left + w || y < top || y > top + h)
1634                 return;
1635             if (area->nr_coords > 3) {
1636                 Single right = area->coords[2].size (rect.width ());
1637                 Single bottom = area->coords[3].size (rect.height ());
1638                 matrix.getXY (right, bottom);
1639                 if (x > right || y > bottom)
1640                     return;
1641             }
1642         }
1643         if (event == MsgEventPointerMoved)
1644             cursor.setShape (Qt::PointingHandCursor);
1645         else {
1646             ConnectionList *nl = nodeMessageReceivers (area, event);
1647             if (nl)
1648                 for (Connection *c = nl->first(); c; c = nl->next ()) {
1649                     if (c->connecter)
1650                         c->connecter->accept (this);
1651                     if (!source || !source->active ())
1652                         return;
1653                 }
1654             if (event == MsgEventClicked && !area->href.isEmpty ())
1655                 followLink (area);
1656         }
1657     }
1658 }
1659 
1660 KDE_NO_EXPORT void MouseVisitor::visit (Element *elm) {
1661     Runtime *rt = (Runtime *) elm->role (RoleTiming);
1662     if (rt) {
1663         Posting mouse_event (source, event);
1664         rt->message (event, &mouse_event);
1665     }
1666 }
1667 
1668 bool MouseVisitor::deliverAndForward (Node *node, Surface *s, bool inside, bool deliver) {
1669     bool forward = deliver;
1670     MessageType user_event = event;
1671     if (event == MsgEventPointerMoved) {
1672         forward = true; // always pass move events
1673         if (inside && !s->has_mouse) {
1674             deliver = true;
1675             user_event = MsgEventPointerInBounds;
1676         } else if (!inside && s->has_mouse) {
1677             deliver = true;
1678             user_event = MsgEventPointerOutBounds;
1679         } else if (!inside) {
1680             return false;
1681         } else {
1682             deliver = false;
1683         }
1684     }
1685     s->has_mouse = inside;
1686 
1687     if (event != MsgEventPointerMoved && !inside)
1688         return false;
1689 
1690     NodePtrW node_save = node;
1691 
1692     if (forward) {
1693         ConnectionList *nl = nodeMessageReceivers (node, MsgSurfaceAttach);
1694         if (nl) {
1695             NodePtr node_save = source;
1696             source = node;
1697 
1698             for (Connection *c = nl->first(); c; c = nl->next ()) {
1699                 if (c->connecter)
1700                     c->connecter->accept (this);
1701                 if (!source || !source->active ())
1702                     break;
1703             }
1704             source = node_save;
1705         }
1706     }
1707     if (!node_save || !node->active ())
1708         return false;
1709     if (deliver) {
1710         Posting mouse_event (node, user_event);
1711         node->deliver (user_event, &mouse_event);
1712     }
1713     if (!node_save || !node->active ())
1714         return false;
1715     return true;
1716 }
1717 
1718 void MouseVisitor::surfaceEvent (Node *node, Surface *s) {
1719     if (!s)
1720         return;
1721     if (s->node && s->node.ptr () != node) {
1722         s->node->accept (this);
1723         return;
1724     }
1725     SRect rect = s->bounds;
1726     IRect scr = matrix.toScreen (rect);
1727     int rx = scr.x(), ry = scr.y(), rw = scr.width(), rh = scr.height();
1728     const bool inside = x > rx && x < rx+rw && y > ry && y< ry+rh;
1729     const bool had_mouse = s->has_mouse;
1730     if (deliverAndForward (node, s, inside, true) &&
1731             (inside || had_mouse) &&
1732             s->firstChild () && s->firstChild ()->node) {
1733         Matrix m = matrix;
1734         matrix = Matrix (rect.x(), rect.y(), s->xscale, s->yscale);
1735         matrix.transform (m);
1736         s->firstChild ()->node->accept (this);
1737         matrix = m;
1738     }
1739 }
1740 
1741 KDE_NO_EXPORT void MouseVisitor::visit (SMIL::MediaType *mt) {
1742     if (mt->sensitivity == SMIL::MediaType::sens_transparent)
1743         bubble_up = true;
1744     else
1745         surfaceEvent (mt, mt->surface ());
1746 }
1747 
1748 KDE_NO_EXPORT void MouseVisitor::visit (SMIL::SmilText *st) {
1749     surfaceEvent (st, st->surface ());
1750 }
1751 
1752 //-----------------------------------------------------------------------------
1753 
1754 namespace KMPlayer {
1755 class KMPLAYER_NO_EXPORT ViewerAreaPrivate {
1756 public:
1757     ViewerAreaPrivate (ViewArea *v)
1758         : m_view_area (v), backing_store (0), gc(0),
1759           screen(NULL), visual(NULL), width(0), height(0)
1760     {}
1761     ~ViewerAreaPrivate() {
1762         destroyBackingStore ();
1763         if (gc) {
1764             xcb_connection_t* connection = QX11Info::connection();
1765             xcb_free_gc(connection, gc);
1766         }
1767     }
1768     void clearSurface (Surface *s) {
1769 #ifdef KMPLAYER_WITH_CAIRO
1770         if (s->surface) {
1771             cairo_surface_destroy (s->surface);
1772             s->surface = 0L;
1773         }
1774         destroyBackingStore ();
1775 #endif
1776     }
1777     void resizeSurface (Surface *s) {
1778 #ifdef KMPLAYER_WITH_CAIRO
1779 #if QT_VERSION >= 0x050600
1780         int w = (int)(m_view_area->width() * m_view_area->devicePixelRatioF());
1781         int h = (int)(m_view_area->height() * m_view_area->devicePixelRatioF());
1782 #else
1783         int w = m_view_area->width ();
1784         int h = m_view_area->height ();
1785 #endif
1786         if ((w != width || h != height) && s->surface) {
1787             clearSurface (s);
1788             width = w;
1789             height = h;
1790         }
1791 #endif
1792     }
1793 #ifdef KMPLAYER_WITH_CAIRO
1794     cairo_surface_t *createSurface (int w, int h) {
1795         xcb_connection_t* connection = QX11Info::connection();
1796         destroyBackingStore ();
1797         xcb_screen_t* scr = screen_of_display(connection, QX11Info::appScreen());
1798         backing_store = xcb_generate_id(connection);
1799         xcb_void_cookie_t cookie = xcb_create_pixmap_checked(connection, scr->root_depth, backing_store, m_view_area->winId(), w, h);
1800         xcb_generic_error_t* error = xcb_request_check(connection, cookie);
1801         if (error) {
1802             qDebug("failed to create pixmap");
1803             return NULL;
1804         }
1805         return cairo_xcb_surface_create(connection, backing_store, visual_of_screen(connection, scr), w, h);
1806     }
1807     void swapBuffer (const IRect &sr, int dx, int dy) {
1808         xcb_connection_t* connection = QX11Info::connection();
1809         if (!gc) {
1810             gc = xcb_generate_id(connection);
1811             uint32_t values[] = { XCB_GX_COPY, XCB_FILL_STYLE_SOLID,
1812                 XCB_SUBWINDOW_MODE_CLIP_BY_CHILDREN, 0 };
1813             xcb_create_gc(connection, gc, backing_store,
1814                     XCB_GC_FUNCTION | XCB_GC_FILL_STYLE |
1815                     XCB_GC_SUBWINDOW_MODE | XCB_GC_GRAPHICS_EXPOSURES, values);
1816         }
1817         xcb_copy_area(connection, backing_store, m_view_area->winId(),
1818                 gc, sr.x(), sr.y(), dx, dy, sr.width (), sr.height ());
1819         xcb_flush(connection);
1820     }
1821 #endif
1822     void destroyBackingStore () {
1823 #ifdef KMPLAYER_WITH_CAIRO
1824         if (backing_store) {
1825             xcb_connection_t* connection = QX11Info::connection();
1826             xcb_free_pixmap(connection, backing_store);
1827         }
1828 #endif
1829         backing_store = 0;
1830     }
1831     xcb_screen_t *screen_of_display(xcb_connection_t* c, int num)
1832     {
1833         if (!screen) {
1834             xcb_screen_iterator_t iter;
1835 
1836             iter = xcb_setup_roots_iterator (xcb_get_setup (c));
1837             for (; iter.rem; --num, xcb_screen_next (&iter))
1838                 if (num == 0) {
1839                     screen = iter.data;
1840                     break;
1841                 }
1842         }
1843         return screen;
1844     }
1845 
1846     xcb_visualtype_t* visual_of_screen(xcb_connection_t* c, xcb_screen_t* screen)
1847     {
1848         if (!visual) {
1849             xcb_depth_iterator_t depth_iter = xcb_screen_allowed_depths_iterator (screen);
1850             for (; depth_iter.rem; xcb_depth_next (&depth_iter)) {
1851                 xcb_visualtype_iterator_t visual_iter = xcb_depth_visuals_iterator (depth_iter.data);
1852                 for (; visual_iter.rem; xcb_visualtype_next (&visual_iter)) {
1853                     if (screen->root_visual == visual_iter.data->visual_id) {
1854                         visual = visual_iter.data;
1855                         break;
1856                     }
1857                 }
1858             }
1859         }
1860         return visual;
1861     }
1862     ViewArea *m_view_area;
1863     xcb_drawable_t backing_store;
1864     xcb_gcontext_t gc;
1865     xcb_screen_t* screen;
1866     xcb_visualtype_t* visual;
1867     int width;
1868     int height;
1869 };
1870 
1871 class KMPLAYER_NO_EXPORT RepaintUpdater {
1872 public:
1873     RepaintUpdater (Node *n, RepaintUpdater *nx) : node (n), next (nx) {}
1874 
1875     NodePtrW node;
1876     RepaintUpdater *next;
1877 };
1878 
1879 }
1880 
1881 KDE_NO_CDTOR_EXPORT ViewArea::ViewArea (QWidget *, View * view, bool paint_bg)
1882 // : QWidget (parent, "kde_kmplayer_viewarea", WResizeNoErase | WRepaintNoErase),
1883  : //QWidget (parent),
1884    d (new ViewerAreaPrivate (this)),
1885    m_view (view),
1886    m_collection (new KActionCollection (this)),
1887    surface (new Surface (this)),
1888    m_mouse_invisible_timer (0),
1889    m_repaint_timer (0),
1890    m_restore_fullscreen_timer (0),
1891    m_fullscreen (false),
1892    m_minimal (false),
1893    m_updaters_enabled (true),
1894    m_paint_background (paint_bg) {
1895     if (!paint_bg)
1896         setAttribute (Qt::WA_NoSystemBackground, true);
1897     QPalette palette;
1898     palette.setColor (backgroundRole(), QColor (0, 0, 0));
1899     setPalette (palette);
1900     setAcceptDrops (true);
1901     //new KAction (i18n ("Fullscreen"), KShortcut (Qt::Key_F), this, SLOT (accelActivated ()), m_collection, "view_fullscreen_toggle");
1902     setMouseTracking (true);
1903     setFocusPolicy (Qt::ClickFocus);
1904     QCoreApplication::instance()->installNativeEventFilter(this);
1905 }
1906 
1907 KDE_NO_CDTOR_EXPORT ViewArea::~ViewArea () {
1908     delete d;
1909 }
1910 
1911 KDE_NO_EXPORT void ViewArea::stopTimers () {
1912     if (m_mouse_invisible_timer) {
1913         killTimer (m_mouse_invisible_timer);
1914         m_mouse_invisible_timer = 0;
1915     }
1916     if (m_repaint_timer) {
1917         killTimer (m_repaint_timer);
1918         m_repaint_timer = 0;
1919     }
1920 }
1921 
1922 KDE_NO_EXPORT void ViewArea::fullScreen () {
1923     stopTimers ();
1924     if (m_fullscreen) {
1925         setVisible(false);
1926         setWindowState(windowState() & ~Qt::WindowFullScreen); // reset
1927         if (!m_restore_fullscreen_timer)
1928             m_restore_fullscreen_timer = startTimer(25);
1929         for (int i = 0; i < m_collection->count (); ++i)
1930             m_collection->action (i)->setEnabled (false);
1931         m_view->controlPanel()->enableFullscreenButton(false);
1932         unsetCursor();
1933     } else {
1934         m_topwindow_rect = topLevelWidget ()->geometry ();
1935 #if QT_VERSION >= 0x050200
1936         m_view->dockArea()->takeCentralWidget();
1937 #else
1938         setParent (0L);
1939 #endif
1940         move(qApp->desktop()->screenGeometry(this).topLeft());
1941         setVisible(true);
1942         setWindowState( windowState() ^ Qt::WindowFullScreen ); // set
1943         for (int i = 0; i < m_collection->count (); ++i)
1944             m_collection->action (i)->setEnabled (true);
1945         m_view->controlPanel()->enableFullscreenButton(true);
1946         m_mouse_invisible_timer = startTimer(MOUSE_INVISIBLE_DELAY);
1947     }
1948     m_fullscreen = !m_fullscreen;
1949     m_view->controlPanel()->fullscreenAction->setChecked (m_fullscreen);
1950 
1951     d->clearSurface (surface.ptr ());
1952     emit fullScreenChanged ();
1953 }
1954 
1955 void ViewArea::minimalMode () {
1956     m_minimal = !m_minimal;
1957     stopTimers ();
1958     m_mouse_invisible_timer = m_repaint_timer = 0;
1959     if (m_minimal) {
1960         m_view->setViewOnly ();
1961         m_view->setControlPanelMode (KMPlayer::View::CP_AutoHide);
1962         m_view->setNoInfoMessages (true);
1963         m_view->controlPanel()->enableFullscreenButton(true);
1964     } else {
1965         m_view->setControlPanelMode (KMPlayer::View::CP_Show);
1966         m_view->setNoInfoMessages (false);
1967         m_view->controlPanel()->enableFullscreenButton(false);
1968     }
1969     m_topwindow_rect = topLevelWidget ()->geometry ();
1970 }
1971 
1972 KDE_NO_EXPORT void ViewArea::accelActivated () {
1973     m_view->controlPanel()->fullscreenAction->trigger ();
1974 }
1975 
1976 KDE_NO_EXPORT void ViewArea::keyPressEvent (QKeyEvent *e) {
1977     if (surface->node) {
1978         QString txt = e->text ();
1979         if (!txt.isEmpty ())
1980             surface->node->document ()->message (MsgAccessKey,
1981                     (void *)(long) txt[0].unicode ());
1982     }
1983 }
1984 
1985 KDE_NO_EXPORT void ViewArea::mousePressEvent (QMouseEvent * e) {
1986 #if QT_VERSION >= 0x050600
1987     int devicex = (int)(e->x() * devicePixelRatioF());
1988     int devicey = (int)(e->y() * devicePixelRatioF());
1989 #else
1990     int devicex = e->x();
1991     int devicey = e->y();
1992 #endif
1993     if (surface->node) {
1994         MouseVisitor visitor (this, MsgEventClicked,
1995                 Matrix (surface->bounds.x (), surface->bounds.y (),
1996                     surface->xscale, surface->yscale),
1997                 devicex, devicey);
1998         surface->node->accept (&visitor);
1999     }
2000 }
2001 
2002 KDE_NO_EXPORT void ViewArea::mouseDoubleClickEvent (QMouseEvent *) {
2003     m_view->fullScreen (); // screensaver stuff
2004 }
2005 
2006 KDE_NO_EXPORT void ViewArea::mouseMoveEvent (QMouseEvent * e) {
2007     if (e->buttons () == Qt::NoButton)
2008         m_view->mouseMoved (e->x (), e->y ());
2009     if (surface->node) {
2010 #if QT_VERSION >= 0x050600
2011         int devicex = (int)(e->x() * devicePixelRatioF());
2012         int devicey = (int)(e->y() * devicePixelRatioF());
2013 #else
2014         int devicex = e->x();
2015         int devicey = e->y();
2016 #endif
2017         MouseVisitor visitor (this, MsgEventPointerMoved,
2018                 Matrix (surface->bounds.x (), surface->bounds.y (),
2019                     surface->xscale, surface->yscale),
2020                 devicex, devicey);
2021         surface->node->accept (&visitor);
2022         setCursor (visitor.cursor);
2023     }
2024     e->accept ();
2025     mouseMoved (); // for m_mouse_invisible_timer
2026 }
2027 
2028 KDE_NO_EXPORT void ViewArea::syncVisual () {
2029 #if QT_VERSION >= 0x050600
2030     pixel_device_ratio = devicePixelRatioF();
2031     int w = (int)(width() * devicePixelRatioF());
2032     int h = (int)(height() * devicePixelRatioF());
2033 #else
2034     int w = width();
2035     int h = height();
2036 #endif
2037     IRect rect = m_repaint_rect.intersect (IRect (0, 0, w, h));
2038 #ifdef KMPLAYER_WITH_CAIRO
2039     if (surface->node) {
2040         int ex = rect.x ();
2041         if (ex > 0)
2042             ex--;
2043         int ey = rect.y ();
2044         if (ey > 0)
2045             ey--;
2046         int ew = rect.width () + 2;
2047         int eh = rect.height () + 2;
2048         IRect swap_rect;
2049         cairo_surface_t *merge = NULL;
2050         cairo_pattern_t *pat = NULL;
2051         cairo_t *cr = NULL;
2052         if (!surface->surface) {
2053             surface->surface = d->createSurface(w, h);
2054             swap_rect = IRect (ex, ey, ew, eh);
2055             CairoPaintVisitor visitor (surface->surface,
2056                     Matrix (surface->bounds.x(), surface->bounds.y(),
2057                         surface->xscale, surface->yscale),
2058                     swap_rect,
2059                     palette ().color (backgroundRole ()), true);
2060             surface->node->accept (&visitor);
2061             m_update_rect = IRect ();
2062         } else if (!rect.isEmpty ()) {
2063             merge = cairo_surface_create_similar (surface->surface,
2064                     CAIRO_CONTENT_COLOR, ew, eh);
2065             {
2066                 CairoPaintVisitor visitor (merge,
2067                         Matrix (surface->bounds.x()-ex, surface->bounds.y()-ey,
2068                             surface->xscale, surface->yscale),
2069                         IRect (0, 0, ew, eh),
2070                         palette ().color (backgroundRole ()), true);
2071                 surface->node->accept (&visitor);
2072             }
2073             cr = cairo_create (surface->surface);
2074             pat = cairo_pattern_create_for_surface (merge);
2075             cairo_pattern_set_extend (pat, CAIRO_EXTEND_NONE);
2076             cairo_matrix_t mat;
2077             cairo_matrix_init_translate (&mat, (int) -ex, (int) -ey);
2078             cairo_pattern_set_matrix (pat, &mat);
2079             cairo_set_source (cr, pat);
2080             //cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
2081             cairo_rectangle (cr, ex, ey, ew, eh);
2082             //cairo_fill (cr);
2083             cairo_clip (cr);
2084             cairo_paint_with_alpha (cr, .8);
2085             swap_rect = IRect (ex, ey, ew, eh).unite (m_update_rect);
2086             m_update_rect = IRect (ex, ey, ew, eh);
2087         } else {
2088             swap_rect = m_update_rect;
2089             m_update_rect = IRect ();
2090         }
2091         d->swapBuffer (swap_rect, swap_rect.x (), swap_rect.y ());
2092         if (merge) {
2093             cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
2094             cairo_rectangle (cr, ex, ey, ew, eh);
2095             cairo_fill (cr);
2096             cairo_destroy (cr);
2097             cairo_pattern_destroy (pat);
2098             cairo_surface_destroy (merge);
2099         }
2100         cairo_surface_flush (surface->surface);
2101     } else
2102 #endif
2103     {
2104         m_update_rect = IRect ();
2105 #if QT_VERSION >= 0x050600
2106         repaint(QRect(rect.x() / devicePixelRatioF(),
2107                       rect.y() / devicePixelRatioF(),
2108                       rect.width() / devicePixelRatioF(),
2109                       rect.height() / devicePixelRatioF()));
2110 #else
2111         repaint (QRect(rect.x(), rect.y(), rect.width(), rect.height()));
2112 #endif
2113     }
2114 }
2115 
2116 KDE_NO_EXPORT void ViewArea::paintEvent (QPaintEvent * pe) {
2117 #ifdef KMPLAYER_WITH_CAIRO
2118     if (surface->node) {
2119 #if QT_VERSION >= 0x050600
2120         int x = (int)(pe->rect().x() * devicePixelRatioF());
2121         int y = (int)(pe->rect().y() * devicePixelRatioF());
2122         int w = (int)(pe->rect().width() * devicePixelRatioF());
2123         int h = (int)(pe->rect().height() * devicePixelRatioF());
2124 #else
2125         int x = pe->rect().x();
2126         int y = pe->rect().y();
2127         int w = pe->rect().width();
2128         int h = pe->rect().height();
2129 #endif
2130         scheduleRepaint(IRect(x, y, w, h));
2131     } else
2132 #endif
2133         if (m_fullscreen || m_paint_background)
2134     {
2135         QPainter p (this);
2136         p.fillRect (pe->rect (), QBrush (palette ().color (backgroundRole ())));
2137         p.end ();
2138     }
2139 }
2140 
2141 QPaintEngine *ViewArea::paintEngine () const {
2142 #ifdef KMPLAYER_WITH_CAIRO
2143     if (surface->node)
2144         return NULL;
2145     else
2146 #endif
2147         return QWidget::paintEngine ();
2148 }
2149 
2150 KDE_NO_EXPORT void ViewArea::scale (int) {
2151     resizeEvent (0L);
2152 }
2153 
2154 KDE_NO_EXPORT void ViewArea::updateSurfaceBounds () {
2155 #if QT_VERSION >= 0x050600
2156     int devicew = (int)(width() * devicePixelRatioF());
2157     int deviceh = (int)(height() * devicePixelRatioF());
2158 #else
2159     int devicew = width(), deviceh = height();
2160 #endif
2161     Single x, y, w = devicew, h = deviceh;
2162     h -= m_view->statusBarHeight ();
2163     h -= m_view->controlPanel ()->isVisible () && !m_fullscreen
2164         ? (m_view->controlPanelMode () == View::CP_Only
2165                 ? h
2166                 : (Single) m_view->controlPanel()->maximumSize ().height ())
2167         : Single (0);
2168 
2169     int scale = m_view->controlPanel ()->scale_slider->sliderPosition ();
2170     if (scale != 100) {
2171         int nw = w * 1.0 * scale / 100;
2172         int nh = h * 1.0 * scale / 100;
2173         x += (w - nw) / 2;
2174         y += (h - nh) / 2;
2175         w = nw;
2176         h = nh;
2177     }
2178     if (surface->node) {
2179         d->resizeSurface (surface.ptr ());
2180         surface->resize (SRect (x, y, w, h));
2181         surface->node->message (MsgSurfaceBoundsUpdate, (void *) true);
2182     }
2183     scheduleRepaint (IRect (0, 0, devicew, deviceh));
2184 }
2185 
2186 KDE_NO_EXPORT void ViewArea::resizeEvent (QResizeEvent *) {
2187     if (!m_view->controlPanel ()) return;
2188     Single x, y, w = width (), h = height ();
2189     Single hsb = m_view->statusBarHeight ();
2190     int hcp = m_view->controlPanel ()->isVisible ()
2191         ? (m_view->controlPanelMode () == View::CP_Only
2192                 ? h-hsb
2193                 : (Single) m_view->controlPanel()->maximumSize ().height ())
2194         : Single (0);
2195     // move controlpanel over video when autohiding and playing
2196     bool auto_hide = m_view->controlPanelMode () == View::CP_AutoHide;
2197     h -= Single (auto_hide ? 0 : hcp) - hsb;
2198     // now scale the regions and check if video region is already sized
2199     updateSurfaceBounds ();
2200 
2201     // finally resize controlpanel and video widget
2202     if (m_view->controlPanel ()->isVisible ())
2203         m_view->controlPanel ()->setGeometry (0, h-(auto_hide ? hcp:0), w, hcp);
2204     if (m_view->statusBar ()->isVisible ())
2205         m_view->statusBar ()->setGeometry (0, h-hsb, w, hsb);
2206     int scale = m_view->controlPanel ()->scale_slider->sliderPosition ();
2207     Single ws = w * scale / 100;
2208     Single hs = h * scale / 100;
2209     x += (w - ws) / 2;
2210     y += (h - hs) / 2;
2211     m_view->console ()->setGeometry (0, 0, w, h);
2212     m_view->picture ()->setGeometry (0, 0, w, h);
2213     if (!surface->node && video_widgets.size () == 1) {
2214 #if QT_VERSION >= 0x050600
2215         x *= devicePixelRatioF();
2216         y *= devicePixelRatioF();
2217         ws *= devicePixelRatioF();
2218         hs *= devicePixelRatioF();
2219 #endif
2220         video_widgets.first ()->setGeometry (IRect (x, y, ws, hs));
2221     }
2222 }
2223 
2224 KDE_NO_EXPORT Surface *ViewArea::getSurface (Mrl *mrl) {
2225     surface->clear ();
2226     surface->node = mrl;
2227     kDebug() << mrl;
2228     //m_view->viewer()->resetBackgroundColor ();
2229     if (mrl) {
2230         updateSurfaceBounds ();
2231 #ifdef KMPLAYER_WITH_CAIRO
2232         setAttribute (Qt::WA_OpaquePaintEvent, true);
2233         setAttribute (Qt::WA_PaintOnScreen, true);
2234 #endif
2235         return surface.ptr ();
2236     } else {
2237 #ifdef KMPLAYER_WITH_CAIRO
2238         setAttribute (Qt::WA_OpaquePaintEvent, false);
2239         setAttribute (Qt::WA_PaintOnScreen, false);
2240         d->clearSurface (surface.ptr ());
2241 #endif
2242     }
2243 #if QT_VERSION >= 0x050600
2244     int devicew = (int)(width() * devicePixelRatioF());
2245     int deviceh = (int)(height() * devicePixelRatioF());
2246     scheduleRepaint (IRect (0, 0, devicew, deviceh));
2247 #else
2248     scheduleRepaint (IRect (0, 0, width (), height ()));
2249 #endif
2250     return 0L;
2251 }
2252 
2253 KDE_NO_EXPORT void ViewArea::showEvent (QShowEvent *) {
2254     resizeEvent (0L);
2255 }
2256 
2257 KDE_NO_EXPORT void ViewArea::dropEvent (QDropEvent * de) {
2258     m_view->dropEvent (de);
2259 }
2260 
2261 KDE_NO_EXPORT void ViewArea::dragEnterEvent (QDragEnterEvent* dee) {
2262     m_view->dragEnterEvent (dee);
2263 }
2264 
2265 KDE_NO_EXPORT void ViewArea::contextMenuEvent (QContextMenuEvent * e) {
2266     m_view->controlPanel ()->popupMenu->exec (e->globalPos ());
2267 }
2268 
2269 KDE_NO_EXPORT void ViewArea::mouseMoved () {
2270     if (m_fullscreen) {
2271         if (m_mouse_invisible_timer)
2272             killTimer (m_mouse_invisible_timer);
2273         unsetCursor ();
2274         m_mouse_invisible_timer = startTimer (MOUSE_INVISIBLE_DELAY);
2275     }
2276 }
2277 
2278 KDE_NO_EXPORT void ViewArea::scheduleRepaint (const IRect &rect) {
2279     if (m_repaint_timer) {
2280         m_repaint_rect = m_repaint_rect.unite (rect);
2281     } else {
2282         m_repaint_rect = rect;
2283         m_repaint_timer = startTimer (25);
2284     }
2285 }
2286 
2287 KDE_NO_EXPORT ConnectionList *ViewArea::updaters () {
2288     if (!m_repaint_timer)
2289         m_repaint_timer = startTimer (25);
2290     return &m_updaters;
2291 }
2292 
2293 KDE_NO_EXPORT
2294 void ViewArea::enableUpdaters (bool enable, unsigned int skip) {
2295     m_updaters_enabled = enable;
2296     Connection *connect = m_updaters.first ();
2297     if (enable && connect) {
2298         UpdateEvent event (connect->connecter->document (), skip);
2299         for (; connect; connect = m_updaters.next ())
2300             if (connect->connecter)
2301                 connect->connecter->message (MsgSurfaceUpdate, &event);
2302         if (!m_repaint_timer)
2303             m_repaint_timer = startTimer (25);
2304     } else if (!enable && m_repaint_timer &&
2305             m_repaint_rect.isEmpty () && m_update_rect.isEmpty ()) {
2306         killTimer (m_repaint_timer);
2307         m_repaint_timer = 0;
2308     }
2309 }
2310 
2311 KDE_NO_EXPORT void ViewArea::timerEvent (QTimerEvent * e) {
2312     if (e->timerId () == m_mouse_invisible_timer) {
2313         killTimer (m_mouse_invisible_timer);
2314         m_mouse_invisible_timer = 0;
2315         if (m_fullscreen)
2316             setCursor (QCursor (Qt::BlankCursor));
2317     } else if (e->timerId () == m_repaint_timer) {
2318         Connection *connect = m_updaters.first ();
2319         int count = 0;
2320         if (m_updaters_enabled && connect) {
2321             UpdateEvent event (connect->connecter->document (), 0);
2322             for (; connect; count++, connect = m_updaters.next ())
2323                 if (connect->connecter)
2324                     connect->connecter->message (MsgSurfaceUpdate, &event);
2325         }
2326         //repaint (m_repaint_rect, false);
2327         if (!m_repaint_rect.isEmpty () || !m_update_rect.isEmpty ()) {
2328             syncVisual ();
2329             m_repaint_rect = IRect ();
2330         }
2331         if (m_update_rect.isEmpty () &&
2332                 (!m_updaters_enabled || !m_updaters.first ())) {
2333             killTimer (m_repaint_timer);
2334             m_repaint_timer = 0;
2335         }
2336     } else if (e->timerId () == m_restore_fullscreen_timer) {
2337         xcb_connection_t* connection = QX11Info::connection();
2338         xcb_get_window_attributes_cookie_t cookie = xcb_get_window_attributes(connection, winId());
2339         xcb_get_window_attributes_reply_t* attrs = xcb_get_window_attributes_reply(connection, cookie, NULL);
2340         if (attrs->map_state == XCB_MAP_STATE_UNMAPPED) {
2341             m_view->dockArea ()->setCentralWidget (this);
2342             killTimer(m_restore_fullscreen_timer);
2343             m_restore_fullscreen_timer = 0;
2344         }
2345         free(attrs);
2346     } else {
2347         kError () << "unknown timer " << e->timerId () << " " << m_repaint_timer << endl;
2348         killTimer (e->timerId ());
2349     }
2350 }
2351 
2352 KDE_NO_EXPORT void ViewArea::closeEvent (QCloseEvent * e) {
2353     //kDebug () << "closeEvent";
2354     if (m_fullscreen) {
2355         m_view->fullScreen();
2356         if (!m_view->topLevelWidget ()->isVisible ())
2357             m_view->topLevelWidget ()->setVisible (true);
2358         e->ignore ();
2359     } else
2360         QWidget::closeEvent (e);
2361 }
2362 
2363 IViewer *ViewArea::createVideoWidget () {
2364     VideoOutput *viewer = new VideoOutput (this, m_view);
2365     video_widgets.push_back (viewer);
2366     viewer->setGeometry (IRect (-60, -60, 50, 50));
2367     viewer->setVisible (true);
2368     m_view->controlPanel ()->raise ();
2369     return viewer;
2370 }
2371 
2372 void ViewArea::destroyVideoWidget (IViewer *widget) {
2373     int i = video_widgets.indexOf(widget);
2374     if (i >= 0) {
2375         IViewer *viewer = widget;
2376         delete viewer;
2377         video_widgets.removeAt(i);
2378     } else {
2379         kWarning () << "destroyVideoWidget widget not found" << endl;
2380     }
2381 }
2382 
2383 void ViewArea::setVideoWidgetVisible (bool show) {
2384     const VideoWidgetList::iterator e = video_widgets.end ();
2385     for (VideoWidgetList::iterator it = video_widgets.begin (); it != e; ++it)
2386         static_cast <VideoOutput *> (*it)->setVisible (show);
2387 }
2388 
2389 static void setXSelectInput(WId wid, uint32_t mask) {
2390     xcb_connection_t* connection = QX11Info::connection();
2391     const uint32_t values[] = { mask };
2392     xcb_change_window_attributes(connection, wid, XCB_CW_EVENT_MASK, values);
2393     xcb_query_tree_cookie_t biscuit = xcb_query_tree(connection, wid);
2394     xcb_query_tree_reply_t *reply = xcb_query_tree_reply(connection, biscuit, NULL);
2395     if (reply) {
2396         xcb_window_t *chlds = xcb_query_tree_children(reply);
2397         for (int i = 0; i < xcb_query_tree_children_length(reply); i++)
2398             setXSelectInput(chlds[i], mask);
2399         free(reply);
2400     } else {
2401         qDebug("failed to get x children");
2402     }
2403 }
2404 
2405 bool ViewArea::nativeEventFilter(const QByteArray& eventType, void * message, long *result) {
2406     if (eventType != "xcb_generic_event_t")
2407         return false;
2408 
2409     xcb_generic_event_t* event = (xcb_generic_event_t*)message;
2410     switch (event->response_type & ~0x80) {
2411     case XCB_UNMAP_NOTIFY: {
2412         xcb_unmap_notify_event_t* ev = (xcb_unmap_notify_event_t*)event;
2413         if (ev->event != ev->window) {
2414             const VideoWidgetList::iterator e = video_widgets.end ();
2415             for (VideoWidgetList::iterator i=video_widgets.begin(); i != e; ++i) {
2416                 if (ev->event == (*i)->ownHandle()) {
2417                     (*i)->embedded(0);
2418                     break;
2419                 }
2420             }
2421         }
2422         break;
2423     }
2424     case XCB_MAP_NOTIFY: {
2425         xcb_map_notify_event_t* ev = (xcb_map_notify_event_t*)event;
2426         if (!ev->override_redirect && ev->event != ev->window) {
2427             xcb_connection_t* connection = QX11Info::connection();
2428             const VideoWidgetList::iterator e = video_widgets.end ();
2429             for (VideoWidgetList::iterator i=video_widgets.begin(); i != e; ++i) {
2430                 if (ev->event == (*i)->ownHandle()) {
2431                     (*i)->embedded(ev->window);
2432                     return false;
2433                 }
2434                 xcb_window_t p = ev->event;
2435                 xcb_window_t w = ev->window;
2436                 xcb_window_t v = (*i)->clientHandle ();
2437                 xcb_window_t va = winId ();
2438                 xcb_window_t root = 0;
2439                 while (p != v) {
2440                     xcb_query_tree_cookie_t cookie = xcb_query_tree(connection, w);
2441                     xcb_query_tree_reply_t *reply = xcb_query_tree_reply(connection, cookie, NULL);
2442                     if (reply) {
2443                         p = reply->parent;
2444                         root = reply->root;
2445                         free(reply);
2446                     } else {
2447                         qDebug("failed to get x parent");
2448                         break;
2449                     }
2450                     if (p == va || p == v || p == root)
2451                         break;
2452                     w = p;
2453                 }
2454                 if (p == v) {
2455                     setXSelectInput (ev->window,
2456                             static_cast <VideoOutput *>(*i)->inputMask ());
2457                     break;
2458                 }
2459             }
2460         }
2461         break;
2462     }
2463     case XCB_MOTION_NOTIFY: {
2464         xcb_motion_notify_event_t* ev = (xcb_motion_notify_event_t*)event;
2465         if (m_view->controlPanelMode () == View::CP_AutoHide) {
2466             const VideoWidgetList::iterator e = video_widgets.end ();
2467             for (VideoWidgetList::iterator i=video_widgets.begin(); i != e; ++i) {
2468                 QPoint p = mapToGlobal (QPoint (0, 0));
2469                 int x = ev->root_x - p.x ();
2470                 int y = ev->root_y - p.y ();
2471 #if QT_VERSION >= 0x050600
2472                 m_view->mouseMoved(x / devicePixelRatioF(), y / devicePixelRatioF());
2473                 int devicew = (int)(width() * devicePixelRatioF());
2474                 int deviceh = (int)(height() * devicePixelRatioF());
2475 #else
2476                 m_view->mouseMoved (x, y);
2477                 int devicew = width();
2478                 int deviceh = height();
2479 #endif
2480                 if (x > 0 && x < devicew && y > 0 && y < deviceh)
2481                     mouseMoved ();
2482             }
2483         }
2484         break;
2485     }
2486     case XCB_KEY_PRESS: {
2487         xcb_key_press_event_t* ev = (xcb_key_press_event_t*)event;
2488         const VideoWidgetList::iterator e = video_widgets.end ();
2489         for (VideoWidgetList::iterator i=video_widgets.begin(); i != e; ++i)
2490             if ((*i)->clientHandle () == ev->event &&
2491                     static_cast <VideoOutput *>(*i)->inputMask() & XCB_EVENT_MASK_KEY_PRESS) {
2492                 if (ev->detail == 41 /*FIXME 'f'*/)
2493                     m_view->fullScreen ();
2494                 break;
2495             }
2496         break;
2497     }
2498     default:
2499         break;
2500     }
2501     return false;
2502 }
2503 
2504 //----------------------------------------------------------------------
2505 
2506 KDE_NO_CDTOR_EXPORT VideoOutput::VideoOutput (QWidget *parent, View * view)
2507   : QX11EmbedContainer (parent),
2508     m_plain_window(0), m_client_window(0), resized_timer(0),
2509     m_bgcolor (0), m_aspect (0.0),
2510     m_view (view)
2511 {
2512     setAcceptDrops (true);
2513     connect (view->viewArea (), SIGNAL (fullScreenChanged ()),
2514              this, SLOT (fullScreenChanged ()));
2515     kDebug() << "VideoOutput::VideoOutput" << endl;
2516     setMonitoring (MonitorAll);
2517     setAttribute (Qt::WA_NoSystemBackground, true);
2518 
2519     xcb_connection_t* connection = QX11Info::connection();
2520     xcb_get_window_attributes_cookie_t cookie = xcb_get_window_attributes(connection, winId());
2521     xcb_get_window_attributes_reply_t* attrs = xcb_get_window_attributes_reply(connection, cookie, NULL);
2522     if (!(attrs->your_event_mask & XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY))
2523         setXSelectInput(winId(), attrs->your_event_mask | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY);
2524     free(attrs);
2525     //setProtocol (QXEmbed::XPLAIN);
2526 }
2527 
2528 KDE_NO_CDTOR_EXPORT VideoOutput::~VideoOutput () {
2529     kDebug() << "VideoOutput::~VideoOutput" << endl;
2530     if (m_plain_window) {
2531         xcb_connection_t* connection = QX11Info::connection();
2532         xcb_destroy_window(connection, m_plain_window);
2533         xcb_flush(connection);
2534         m_plain_window = 0;
2535     }
2536 }
2537 
2538 void VideoOutput::useIndirectWidget (bool inderect) {
2539     kDebug () << "setIntermediateWindow " << !!m_plain_window << "->" << inderect;
2540     if (!clientWinId () || !!m_plain_window != inderect) {
2541         xcb_connection_t* connection = QX11Info::connection();
2542         if (inderect) {
2543             if (!m_plain_window) {
2544                 xcb_screen_t* scr = m_view->viewArea()->d->screen_of_display(connection, QX11Info::appScreen());
2545                 m_plain_window = xcb_generate_id(connection);
2546                 uint32_t values[] = { scr->black_pixel, m_input_mask };
2547 #if QT_VERSION >= 0x050600
2548                 int devicew = (int)(width() * devicePixelRatioF());
2549                 int deviceh = (int)(height() * devicePixelRatioF());
2550 #else
2551                 int devicew = width();
2552                 int deviceh = height();
2553 #endif
2554                 xcb_create_window(connection,
2555                         XCB_COPY_FROM_PARENT, m_plain_window, winId(),
2556                         0, 0, devicew, deviceh,
2557                         1, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT,
2558                         XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK, values);
2559                 xcb_map_window(connection, m_plain_window);
2560                 xcb_flush(connection);
2561                 //XSync (QX11Info::display (), false);
2562                 //embedClient (m_plain_window);
2563             }
2564             //XClearWindow (QX11Info::display(), m_plain_window);
2565         } else {
2566             if (m_plain_window) {
2567                 xcb_unmap_window(connection, m_plain_window);
2568                 discardClient ();
2569                 xcb_destroy_window(connection, m_plain_window);
2570                 xcb_flush(connection);
2571                 m_plain_window = 0;
2572                 //XSync (QX11Info::display (), false);
2573             }
2574         }
2575     }
2576 }
2577 
2578 KDE_NO_EXPORT void VideoOutput::embedded(WindowId handle) {
2579     kDebug () << "windowChanged " << (int)clientWinId ();
2580     m_client_window = handle;
2581     if (clientWinId () && !resized_timer)
2582          resized_timer = startTimer (50);
2583     if (clientWinId())
2584         setXSelectInput (clientWinId (), m_input_mask);
2585 }
2586 
2587 KDE_NO_EXPORT void VideoOutput::resizeEvent (QResizeEvent *) {
2588     if (clientWinId () && !resized_timer)
2589          resized_timer = startTimer (50);
2590 }
2591 
2592 KDE_NO_EXPORT void VideoOutput::timerEvent (QTimerEvent *e) {
2593     if (e->timerId () == resized_timer) {
2594         killTimer (resized_timer);
2595         resized_timer = 0;
2596         if (clientWinId ()) {
2597             xcb_connection_t* connection = QX11Info::connection();
2598 #if QT_VERSION >= 0x050600
2599             uint32_t devicew = (uint32_t)(width() * devicePixelRatioF());
2600             uint32_t deviceh = (uint32_t)(height() * devicePixelRatioF());
2601 #else
2602             uint32_t devicew = width();
2603             uint32_t deviceh = height();
2604 #endif
2605             uint32_t values[] = { 0, 0, devicew, deviceh };
2606             xcb_configure_window(connection, clientWinId(),
2607                     XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
2608                     XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
2609                     values);
2610             xcb_flush(connection);
2611         }
2612     }
2613 }
2614 
2615 WindowId VideoOutput::windowHandle () {
2616     //return m_plain_window ? clientWinId () : winId ();
2617     return m_plain_window ? m_plain_window : winId ();
2618 }
2619 
2620 WindowId VideoOutput::ownHandle () {
2621     return winId ();
2622 }
2623 
2624 WindowId VideoOutput::clientHandle () {
2625     return clientWinId ();
2626 }
2627 
2628 void VideoOutput::setGeometry (const IRect &rect) {
2629 #if QT_VERSION >= 0x050600
2630     int x = (int)(rect.x() / devicePixelRatioF());
2631     int y = (int)(rect.y() / devicePixelRatioF());
2632     int w = (int)(rect.width() / devicePixelRatioF());
2633     int h = (int)(rect.height() / devicePixelRatioF());
2634 #else
2635     int x = rect.x (), y = rect.y (), w = rect.width (), h = rect.height ();
2636 #endif
2637     if (m_view->keepSizeRatio ()) {
2638         // scale video widget inside region
2639         int hfw = heightForWidth (w);
2640         if (hfw > 0) {
2641             if (hfw > h) {
2642                 int old_w = w;
2643                 w = int ((1.0 * h * w)/(1.0 * hfw));
2644                 x += (old_w - w) / 2;
2645             } else {
2646                 y += (h - hfw) / 2;
2647                 h = hfw;
2648             }
2649         }
2650     }
2651     setGeometry (x, y, w, h);
2652     setVisible (true);
2653 }
2654 
2655 void VideoOutput::setAspect (float a) {
2656     m_aspect = a;
2657     QRect r = geometry ();
2658 #if QT_VERSION >= 0x050600
2659     int x = (int)(r.x() * devicePixelRatioF());
2660     int y = (int)(r.y() * devicePixelRatioF());
2661     int w = (int)(r.width() * devicePixelRatioF());
2662     int h = (int)(r.height() * devicePixelRatioF());
2663 #else
2664     int x = r.x();
2665     int y = r.y();
2666     int w = r.width();
2667     int h = r.height();
2668 #endif
2669     m_view->viewArea()->scheduleRepaint(IRect(x, y, w, h));
2670 }
2671 
2672 KDE_NO_EXPORT void VideoOutput::map () {
2673     setVisible (true);
2674 }
2675 
2676 KDE_NO_EXPORT void VideoOutput::unmap () {
2677     setVisible (false);
2678 }
2679 
2680 KDE_NO_EXPORT void VideoOutput::setMonitoring (Monitor m) {
2681     m_input_mask =
2682         //KeyPressMask | KeyReleaseMask |
2683         //EnterWindowMask | LeaveWindowMask |
2684         //FocusChangeMask |
2685         XCB_EVENT_MASK_EXPOSURE |
2686         //StructureNotifyMask |
2687         XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY;
2688     if (m & MonitorMouse)
2689         m_input_mask |= XCB_EVENT_MASK_POINTER_MOTION;
2690     if (m & MonitorKey)
2691         m_input_mask |= XCB_EVENT_MASK_KEY_PRESS;
2692     if (clientWinId ())
2693         setXSelectInput (clientWinId (), m_input_mask);
2694 }
2695 
2696 KDE_NO_EXPORT void VideoOutput::fullScreenChanged () {
2697     if (!(m_input_mask & XCB_EVENT_MASK_KEY_PRESS)) { // FIXME: store monitor when needed
2698         if (m_view->isFullScreen ())
2699             m_input_mask |= XCB_EVENT_MASK_POINTER_MOTION;
2700         else
2701             m_input_mask &= ~XCB_EVENT_MASK_POINTER_MOTION;
2702     }
2703     if (clientWinId ())
2704         setXSelectInput (clientWinId (), m_input_mask);
2705 }
2706 
2707 KDE_NO_EXPORT int VideoOutput::heightForWidth (int w) const {
2708     if (m_aspect <= 0.01)
2709         return 0;
2710     return int (w/m_aspect);
2711 }
2712 
2713 KDE_NO_EXPORT void VideoOutput::dropEvent (QDropEvent * de) {
2714     m_view->dropEvent (de);
2715 }
2716 
2717 KDE_NO_EXPORT void VideoOutput::dragEnterEvent (QDragEnterEvent* dee) {
2718     m_view->dragEnterEvent (dee);
2719 }
2720 
2721 KDE_NO_EXPORT void VideoOutput::contextMenuEvent (QContextMenuEvent * e) {
2722     m_view->controlPanel ()->popupMenu->exec (e->globalPos ());
2723 }
2724 
2725 KDE_NO_EXPORT void VideoOutput::setBackgroundColor (const QColor & c) {
2726     if (m_bgcolor != c.rgb ()) {
2727         m_bgcolor = c.rgb ();
2728         setCurrentBackgroundColor (c);
2729     }
2730 }
2731 
2732 KDE_NO_EXPORT void VideoOutput::resetBackgroundColor () {
2733     setCurrentBackgroundColor (m_bgcolor);
2734 }
2735 
2736 KDE_NO_EXPORT void VideoOutput::setCurrentBackgroundColor (const QColor & c) {
2737     QPalette palette;
2738     palette.setColor (backgroundRole(), c);
2739     setPalette (palette);
2740     if (clientWinId()) {
2741         xcb_connection_t* connection = QX11Info::connection();
2742         const uint32_t values[] = { c.rgb() };
2743         xcb_change_window_attributes(connection, clientWinId(), XCB_CW_BACK_PIXEL, values);
2744         xcb_flush(connection);
2745     }
2746 }
2747 
2748 #include "viewarea.moc"