29 #include <KActionCollection>
32 #include <QCoreApplication>
33 #include <QContextMenuEvent>
35 #include <QWebElement>
48 using namespace boost;
49 using namespace MessageViewer;
53 if (element.hasAttribute(QLatin1String(
"href"))) {
54 const QUrl url = element.webFrame()->baseUrl().resolved(element.attribute(QLatin1String(
"href")));
55 QString linkKey (url.toString());
56 if (element.hasAttribute(QLatin1String(
"target"))) {
57 linkKey += QLatin1Char(
'+');
58 linkKey += element.attribute(QLatin1String(
"target"));
69 if (element.hasAttribute(QLatin1String(
"width")) && element.attribute(QLatin1String(
"width")).toInt() < 1) {
74 if (element.hasAttribute(QLatin1String(
"height")) && element.attribute(QLatin1String(
"height")).toInt() < 1) {
79 if (element.styleProperty(QLatin1String(
"visibility"),QWebElement::ComputedStyle).compare(QLatin1String(
"hidden"), Qt::CaseInsensitive) == 0) {
84 if (element.styleProperty(QLatin1String(
"display"),QWebElement::ComputedStyle).compare(QLatin1String(
"none"), Qt::CaseInsensitive) == 0) {
93 const QWebFrame* frame = (page ? page->currentFrame() : 0);
94 QWebElement element = (frame ? frame->findFirstElement(QLatin1String(
":focus")) : QWebElement());
95 if (!element.isNull()) {
96 const QString tagName(element.tagName());
97 if (tagName.compare(QLatin1String(
"textarea"), Qt::CaseInsensitive) == 0) {
100 const QString
type(element.attribute(QLatin1String(
"type")).toLower());
101 if (tagName.compare(QLatin1String(
"input"), Qt::CaseInsensitive) == 0
102 && (
type.isEmpty() ||
type == QLatin1String(
"text") ||
type == QLatin1String(
"password"))) {
105 if (element.evaluateJavaScript(QLatin1String(
"this.isContentEditable")).toBool()) {
114 if (element.tagName().compare(QLatin1String(
"A"), Qt::CaseInsensitive) == 0) {
117 if (dupLinkList->contains(linkKey)) {
119 *accessKey = dupLinkList->value(linkKey);
120 }
else if (!linkKey.isEmpty()) {
121 dupLinkList->insert(linkKey, *accessKey);
123 if (linkKey.isEmpty())
124 *accessKey = QChar();
129 MailWebView::MailWebView( KActionCollection *actionCollection,
QWidget *parent )
132 mActionCollection(actionCollection)
135 page()->setLinkDelegationPolicy( QWebPage::DelegateAllLinks );
136 settings()->setAttribute( QWebSettings::JavascriptEnabled,
false );
137 settings()->setAttribute( QWebSettings::JavaEnabled,
false );
138 settings()->setAttribute( QWebSettings::PluginsEnabled,
false );
139 connect( page(), SIGNAL(linkHovered(QString,QString,QString)),
140 this, SIGNAL(linkHovered(QString,QString,QString)) );
141 connect(
this, SIGNAL(loadStarted()),
this, SLOT(hideAccessKeys()));
142 connect(mScamDetection, SIGNAL(messageMayBeAScam()),
this, SIGNAL(messageMayBeAScam()));
143 connect(page(), SIGNAL(scrollRequested(
int,
int,QRect)),
this, SLOT(hideAccessKeys()));
146 MailWebView::~MailWebView()
148 delete mScamDetection;
153 if ( event->type() == QEvent::ContextMenu ) {
157 QContextMenuEvent
const *contextMenuEvent =
static_cast<QContextMenuEvent*
>(
event );
158 const QWebFrame *
const frame = page()->currentFrame();
159 const QWebHitTestResult hit = frame->hitTestContent( contextMenuEvent->pos() );
160 kDebug() <<
"Right-clicked URL:" << hit.linkUrl();
163 if ( !hit.linkUrl().isEmpty() )
165 emit
popupMenu( hit.linkUrl(), ((hit.pixmap().isNull()) ? QUrl() : hit.imageUrl()), mapToGlobal( contextMenuEvent->pos() ) );
169 return SuperClass::event( event );
174 QPoint point = page()->mainFrame()->scrollPosition();
175 point.ry() += pixels;
176 page()->mainFrame()->setScrollPosition( point );
186 const int pos = page()->mainFrame()->scrollBarValue( Qt::Vertical );
187 const int max = page()->mainFrame()->scrollBarMaximum( Qt::Vertical );
193 const qint64 height = page()->viewportSize().height();
194 const qint64 current = page()->mainFrame()->scrollBarValue( Qt::Vertical );
196 const qint64 newPosition = current + height * percent / 100;
197 if ( newPosition > std::numeric_limits<int>::max() )
198 kWarning() <<
"new position" << newPosition <<
"exceeds range of 'int'!";
199 page()->mainFrame()->setScrollBarValue( Qt::Vertical, newPosition );
218 return SuperClass::selectedText();
223 return page()->mainFrame()->scrollBarGeometry( Qt::Vertical ).isValid();
229 const double pos = page()->mainFrame()->scrollBarValue( Qt::Vertical );
230 const int height = page()->mainFrame()->scrollBarMaximum( Qt::Vertical );
231 return height ? pos / height : 0.0 ;
241 const int max = page()->mainFrame()->scrollBarMaximum( Qt::Vertical );
242 page()->currentFrame()->setScrollBarValue( Qt::Vertical, max * pos );
247 page()->triggerAction( QWebPage::SelectAll );
253 QMouseEvent
event(QEvent::MouseButtonPress, QPoint( 10, 10 ), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier );
254 QCoreApplication::sendEvent( page(), &event );
255 QMouseEvent event2(QEvent::MouseButtonRelease, QPoint( 10, 10 ), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier );
256 QCoreApplication::sendEvent( page(), &event2 );
263 if ( start.isNull() )
266 if ( start.tagName().toLower() == QLatin1String(
"div") ) {
267 if ( start.attribute( QLatin1String(
"id"), QString() ) == id )
277 const QPoint local = page()->view()->mapFromGlobal( global );
278 const QWebHitTestResult hit = page()->currentFrame()->hitTestContent( local );
285 QWebElement doc = page()->currentFrame()->documentElement();
286 QWebElement injectionPoint = doc.findFirst( QLatin1String(
"*#attachmentInjectionPoint") );
287 if( injectionPoint.isNull() )
290 const QString html = delayedHtml();
291 if ( html.isEmpty() )
294 assert( injectionPoint.tagName().toLower() == QLatin1String(
"div") );
295 injectionPoint.setInnerXml( html );
300 QWebElement doc = page()->mainFrame()->documentElement();
301 QWebElement link = doc.findFirst( QLatin1String(
"a[name=") + anchor +QLatin1Char(
']') );
302 if ( link.isNull() ) {
306 const int linkPos = link.geometry().bottom();
307 const int viewerPos = page()->mainFrame()->scrollPosition().y();
309 page()->mainFrame()->scroll(0, linkPos - viewerPos );
315 QWebElement doc = page()->mainFrame()->documentElement();
316 QWebElement attachmentDiv = doc.findFirst( QLatin1String(
"*#") +
id );
317 if ( attachmentDiv.isNull() )
319 attachmentDiv.removeAttribute( QLatin1String(
"style") );
325 QWebElement doc = page()->mainFrame()->documentElement();
326 QWebElement attachmentDiv = doc.findFirst( QLatin1String(
"*#") +
id );
327 if ( !attachmentDiv.isNull() ) {
328 attachmentDiv.setAttribute(QLatin1String(
"style"), style );
334 SuperClass::setHtml( html, base );
339 return page()->mainFrame()->documentElement().toOuterXml();
346 SuperClass::setAllowExternalContent( allow );
352 const QPoint local = page()->view()->mapFromGlobal( global );
353 const QWebHitTestResult hit = page()->currentFrame()->hitTestContent( local );
354 if ( !hit.linkUrl().isEmpty() )
355 return hit.linkUrl();
356 else if ( !hit.imageUrl().isEmpty() )
357 return hit.imageUrl();
365 page()->mainFrame()->setScrollBarPolicy( orientation, policy );
370 return page()->mainFrame()->scrollBarPolicy( orientation );
376 QWebElement doc = page()->currentFrame()->documentElement();
377 QWebElement tag = doc.findFirst( QLatin1String(
"*#") +
id );
378 if ( tag.isNull() ) {
381 tag.setInnerXml( delayedHtml() );
387 QWebElement doc = page()->currentFrame()->documentElement();
388 QWebElement e = doc.findFirst( QLatin1String(
"*#") +
id );
389 Q_ASSERT( !e.isNull() );
392 e.removeAttribute( QLatin1String(
"display") );
394 e.setStyleProperty( QLatin1String(
"display"), QLatin1String(
"none") );
400 QWebPage::FindFlags result;
402 result |= QWebPage::FindWrapsAroundDocument;
404 result |= QWebPage::FindBackward;
406 result |= QWebPage::FindCaseSensitively;
408 result |= QWebPage::HighlightAllOccurrences;
421 SuperClass::findText( QString(), QWebPage::HighlightAllOccurrences );
428 if (e->key() == Qt::Key_Control && e->modifiers() == Qt::NoModifier) {
430 mAccessKeyActivated = Activated;
432 mAccessKeyActivated = NotActivated;
435 SuperClass::keyReleaseEvent(e);
440 if (e && hasFocus()) {
442 if (mAccessKeyActivated == Activated) {
443 if (checkForAccessKey(e)) {
449 }
else if (e->key() == Qt::Key_Control && e->modifiers() == Qt::ControlModifier && !
isEditableElement(page())) {
450 mAccessKeyActivated = PreActivated;
454 SuperClass::keyPressEvent(e);
459 if (
GlobalSettings::self()->accessKeyEnabled() && mAccessKeyActivated == PreActivated && (e->modifiers() & Qt::ControlModifier)) {
460 mAccessKeyActivated = NotActivated;
462 SuperClass::wheelEvent(e);
465 bool MailWebView::checkForAccessKey(QKeyEvent *event)
467 if (mAccessKeyLabels.isEmpty())
469 QString text =
event->text();
472 QChar key = text.at(0).toUpper();
473 bool handled =
false;
474 if (mAccessKeyNodes.contains(key)) {
475 QWebElement element = mAccessKeyNodes[key];
476 QPoint p = element.geometry().center();
477 QWebFrame *frame = element.webFrame();
480 p -= frame->scrollPosition();
481 frame = frame->parentFrame();
482 }
while (frame && frame != page()->mainFrame());
483 QMouseEvent pevent(QEvent::MouseButtonPress, p, Qt::LeftButton, 0, 0);
484 QCoreApplication::sendEvent(
this, &pevent);
485 QMouseEvent revent(QEvent::MouseButtonRelease, p, Qt::LeftButton, 0, 0);
486 QCoreApplication::sendEvent(
this, &revent);
492 void MailWebView::hideAccessKeys()
494 if (!mAccessKeyLabels.isEmpty()) {
495 for (
int i = 0, count = mAccessKeyLabels.count(); i < count; ++i) {
496 QLabel *label = mAccessKeyLabels[i];
498 label->deleteLater();
500 mAccessKeyLabels.clear();
501 mAccessKeyNodes.clear();
502 mDuplicateLinkElements.clear();
503 mAccessKeyActivated = NotActivated;
509 void MailWebView::showAccessKeys()
511 QList<QChar> unusedKeys;
512 for (
char c =
'A'; c <=
'Z'; ++c) {
513 unusedKeys << QLatin1Char(c);
515 for (
char c =
'0'; c <=
'9'; ++c) {
516 unusedKeys << QLatin1Char(c);
518 if (mActionCollection) {
519 Q_FOREACH(QAction*act, mActionCollection->actions()) {
520 KAction *a = qobject_cast<KAction*>(act);
522 const KShortcut shortCut = a->shortcut();
523 if(!shortCut.isEmpty()) {
524 Q_FOREACH(
const QChar& c, unusedKeys) {
525 if(shortCut.conflictsWith(QKeySequence(c))) {
526 unusedKeys.removeOne(c);
534 QList<QWebElement> unLabeledElements;
535 QRect viewport = QRect(page()->mainFrame()->scrollPosition(), page()->viewportSize());
536 const QString selectorQuery (QLatin1String(
"a[href],"
538 "button:not([disabled]),"
539 "input:not([disabled]):not([hidden]),"
542 "select:not([disabled]),"
543 "textarea:not([disabled])"));
544 QList<QWebElement> result = page()->mainFrame()->findAllElements(selectorQuery).toList();
547 Q_FOREACH (
const QWebElement& element, result) {
548 const QRect geometry = element.geometry();
549 if (geometry.size().isEmpty() || !viewport.contains(geometry.topLeft())) {
555 const QString accessKeyAttribute (element.attribute(QLatin1String(
"accesskey")).toUpper());
556 if (accessKeyAttribute.isEmpty()) {
557 unLabeledElements.append(element);
561 for (
int i = 0; i < accessKeyAttribute.count(); i+=2) {
562 const QChar &possibleAccessKey = accessKeyAttribute[i];
563 if (unusedKeys.contains(possibleAccessKey)) {
564 accessKey = possibleAccessKey;
568 if (accessKey.isNull()) {
569 unLabeledElements.append(element);
574 if (!accessKey.isNull()) {
575 unusedKeys.removeOne(accessKey);
576 makeAccessKeyLabel(accessKey, element);
583 Q_FOREACH (
const QWebElement &element, unLabeledElements) {
584 const QRect geometry = element.geometry();
585 if (unusedKeys.isEmpty()
586 || geometry.size().isEmpty()
587 || !viewport.contains(geometry.topLeft()))
590 const QString text = element.toPlainText().toUpper();
591 for (
int i = 0; i < text.count(); ++i) {
592 const QChar &c = text.at(i);
593 if (unusedKeys.contains(c)) {
598 if (accessKey.isNull())
599 accessKey = unusedKeys.takeFirst();
602 if (!accessKey.isNull()) {
603 unusedKeys.removeOne(accessKey);
604 makeAccessKeyLabel(accessKey, element);
608 mAccessKeyActivated = (mAccessKeyLabels.isEmpty() ? Activated : NotActivated);
611 void MailWebView::makeAccessKeyLabel(
const QChar &accessKey,
const QWebElement &element)
614 QFont font (label->font());
616 label->setFont(font);
617 label->setText(accessKey);
618 label->setPalette(QToolTip::palette());
619 label->setAutoFillBackground(
true);
620 label->setFrameStyle(QFrame::Box | QFrame::Plain);
621 QPoint point = element.geometry().center();
622 point -= page()->mainFrame()->scrollPosition();
625 point.setX(point.x() - label->width() / 2);
627 mAccessKeyLabels.append(label);
628 mAccessKeyNodes.insertMulti(accessKey, element);
633 QWebFrame *mainFrame = page()->mainFrame();
634 mScamDetection->
scanPage(mainFrame);
644 QWebFrame *frame = page()->mainFrame();
645 QImage image(frame->contentsSize(), QImage::Format_ARGB32_Premultiplied);
646 image.fill(Qt::transparent);
648 QPainter painter(&image);
649 painter.setRenderHint(QPainter::Antialiasing,
true);
650 painter.setRenderHint(QPainter::TextAntialiasing,
true);
651 painter.setRenderHint(QPainter::SmoothPixmapTransform,
true);
652 frame->documentElement().render(&painter);
654 image.save(filename);
660 dlg->setWebFrame(page()->mainFrame());
677 #include "mailwebview.moc"
void expandedUrl(const KUrl &url)
static QWebPage::FindFlags convert_flags(MailWebView::FindFlags f)
QString selectedText() const
static bool has_parent_div_with_id(const QWebElement &start, const QString &id)
double relativePosition() const
static bool isHiddenElement(const QWebElement &element)
void scrollUp(int pixels)
void markAttachment(const QString &id, const QString &style)
static bool isEditableElement(QWebPage *page)
bool hasVerticalScrollBar() const
Qt::ScrollBarPolicy scrollBarPolicy(Qt::Orientation orientation) const
void setHtml(const QString &html, const QUrl &baseUrl)
virtual bool event(QEvent *event)
Reimplemented to catch context menu events and emit popupMenu()
static bool isShortUrl(const KUrl &url)
void openBlockableItemsDialog()
void clearFindSelection()
void scanPage(QWebFrame *frame)
void scrollToAnchor(const QString &anchor)
virtual void wheelEvent(QWheelEvent *e)
bool removeAttachmentMarking(const QString &id)
void scrollToRelativePosition(double pos)
virtual void keyReleaseEvent(QKeyEvent *)
Reimplement for access key.
static GlobalSettings * self()
bool isAShortUrl(const KUrl &url) const
static QString linkElementKey(const QWebElement &element)
QUrl linkOrImageUrlAt(const QPoint &global) const
void setScrollBarPolicy(Qt::Orientation orientation, Qt::ScrollBarPolicy policy)
ScamCheckShortUrl * scamCheckShortUrl() const
void injectAttachments(const boost::function< QString()> &delayedHtml)
bool isScrolledToBottom() const
void popupMenu(const QUrl &url, const QUrl &imageUrl, const QPoint &point)
Emitted when the user right-clicks somewhere.
void scrollPageDown(int percent)
void setElementByIdVisible(const QString &id, bool visible)
void setAllowExternalContent(bool allow)
bool replaceInnerHtml(const QString &id, const boost::function< QString()> &delayedHtml)
static void handleDuplicateLinkElements(const QWebElement &element, QHash< QString, QChar > *dupLinkList, QChar *accessKey)
void scrollPageUp(int percent)
void saveMainFrameScreenshotInFile(const QString &filename)
void expandUrl(const KUrl &url)
bool isAttachmentInjectionPoint(const QPoint &globalPos) const
QString htmlSource() const
virtual void keyPressEvent(QKeyEvent *)
bool findText(const QString &test, FindFlags flags)
void scrollDown(int pixels)