File indexing completed on 2024-04-21 16:31:57
0001 /** 0002 * SPDX-FileCopyrightText: (C) 2003 Sébastien Laoût <slaout@linux62.org> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "notecontent.h" 0008 0009 #include <QLocale> 0010 #include <QMimeData> 0011 #include <QMimeDatabase> 0012 #include <QTextBlock> 0013 #include <QTextCodec> 0014 #include <QWidget> 0015 #include <QtCore/QBuffer> 0016 #include <QtCore/QDateTime> 0017 #include <QtCore/QDir> 0018 #include <QtCore/QFile> 0019 #include <QtCore/QFileInfo> 0020 #include <QtCore/QRegExp> 0021 #include <QtCore/QStringList> 0022 #include <QtGui/QAbstractTextDocumentLayout> 0023 #include <QtGui/QBitmap> //For QPixmap::createHeuristicMask() 0024 #include <QtGui/QFontMetrics> 0025 #include <QtGui/QMovie> 0026 #include <QtGui/QPainter> 0027 #include <QtGui/QPixmap> 0028 #include <QtNetwork/QNetworkReply> 0029 #include <QtXml/QDomElement> 0030 0031 #include <KEncodingProber> 0032 #include <KFileItem> 0033 #include <KFileMetaData/KFileMetaData/Extractor> 0034 #include <KIO/PreviewJob> //For KIO::file_preview(...) 0035 #include <KLocalizedString> 0036 #include <KService> 0037 0038 #include <phonon/AudioOutput> 0039 #include <phonon/MediaObject> 0040 0041 #include "basketscene.h" 0042 #include "common.h" 0043 #include "config.h" 0044 #include "debugwindow.h" 0045 #include "file_metadata.h" 0046 #include "filter.h" 0047 #include "global.h" 0048 #include "htmlexporter.h" 0049 #include "note.h" 0050 #include "notefactory.h" 0051 #include "settings.h" 0052 #include "tools.h" 0053 #include "xmlwork.h" 0054 0055 /** 0056 * LinkDisplayItem definition 0057 * 0058 */ 0059 0060 QRectF LinkDisplayItem::boundingRect() const 0061 { 0062 if (m_note) { 0063 return QRect(0, 0, m_note->width() - m_note->contentX() - Note::NOTE_MARGIN, m_note->height() - 2 * Note::NOTE_MARGIN); 0064 } 0065 return QRectF(); 0066 } 0067 0068 void LinkDisplayItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) 0069 { 0070 if (!m_note) 0071 return; 0072 0073 QRectF rect = boundingRect(); 0074 m_linkDisplay.paint(painter, 0, 0, rect.width(), rect.height(), m_note->palette(), true, m_note->isSelected(), m_note->hovered(), m_note->hovered() && m_note->hoveredZone() == Note::Custom0); 0075 } 0076 0077 //** NoteType functions 0078 QString NoteType::typeToName(const NoteType::Id noteType) 0079 { 0080 switch (noteType) { 0081 case NoteType::Group: 0082 return i18n("Group"); 0083 case NoteType::Text: 0084 return i18n("Plain Text"); 0085 case NoteType::Html: 0086 return i18n("Text"); 0087 case NoteType::Image: 0088 return i18n("Image"); 0089 case NoteType::Animation: 0090 return i18n("Animation"); 0091 case NoteType::Sound: 0092 return i18n("Sound"); 0093 case NoteType::File: 0094 return i18n("File"); 0095 case NoteType::Link: 0096 return i18n("Link"); 0097 case NoteType::CrossReference: 0098 return i18n("Cross Reference"); 0099 case NoteType::Launcher: 0100 return i18n("Launcher"); 0101 case NoteType::Color: 0102 return i18n("Color"); 0103 case NoteType::Unknown: 0104 return i18n("Unknown"); 0105 } 0106 return i18n("Unknown"); 0107 } 0108 0109 QString NoteType::typeToLowerName(const NoteType::Id noteType) 0110 { 0111 switch (noteType) { 0112 case NoteType::Group: 0113 return "group"; 0114 case NoteType::Text: 0115 return "text"; 0116 case NoteType::Html: 0117 return "html"; 0118 case NoteType::Image: 0119 return "image"; 0120 case NoteType::Animation: 0121 return "animation"; 0122 case NoteType::Sound: 0123 return "sound"; 0124 case NoteType::File: 0125 return "file"; 0126 case NoteType::Link: 0127 return "link"; 0128 case NoteType::CrossReference: 0129 return "cross_reference"; 0130 case NoteType::Launcher: 0131 return "launcher"; 0132 case NoteType::Color: 0133 return "color"; 0134 case NoteType::Unknown: 0135 return "unknown"; 0136 } 0137 return "unknown"; 0138 } 0139 0140 NoteType::Id NoteType::typeFromLowerName(const QString& lowerTypeName) 0141 { 0142 if (lowerTypeName == "group") { 0143 return NoteType::Group; 0144 } else if (lowerTypeName == "text") { 0145 return NoteType::Text; 0146 } else if (lowerTypeName == "html") { 0147 return NoteType::Html; 0148 } else if (lowerTypeName == "image") { 0149 return NoteType::Image; 0150 } else if (lowerTypeName == "animation") { 0151 return NoteType::Animation; 0152 } else if (lowerTypeName == "sound") { 0153 return NoteType::Sound; 0154 } else if (lowerTypeName == "file") { 0155 return NoteType::File; 0156 } else if (lowerTypeName == "link") { 0157 return NoteType::Link; 0158 } else if (lowerTypeName == "cross_reference") { 0159 return NoteType::CrossReference; 0160 } else if (lowerTypeName == "launcher") { 0161 return NoteType::Launcher; 0162 } else if (lowerTypeName == "color") { 0163 return NoteType::Color; 0164 } else if (lowerTypeName == "unknown") { 0165 return NoteType::Unknown; 0166 } 0167 return NoteType::Unknown; 0168 } 0169 0170 0171 /** class NoteContent: 0172 */ 0173 0174 const int NoteContent::FEEDBACK_DARKING = 105; 0175 0176 NoteContent::NoteContent(Note *parent, const NoteType::Id type, const QString &fileName) 0177 : m_type(type) 0178 , m_note(parent) 0179 { 0180 if (parent) { 0181 parent->setContent(this); 0182 } 0183 NoteContent::setFileName(fileName); 0184 } 0185 0186 void NoteContent::saveToNode(QXmlStreamWriter &stream) 0187 { 0188 if (useFile()) { 0189 stream.writeStartElement("content"); 0190 stream.writeCharacters(fileName()); 0191 stream.writeEndElement(); 0192 } 0193 } 0194 0195 QRectF NoteContent::zoneRect(int zone, const QPointF & /*pos*/) 0196 { 0197 if (zone == Note::Content) 0198 return QRectF(0, 0, note()->width(), note()->height()); // Too wide and height, but it will be clipped by Note::zoneRect() 0199 else 0200 return QRectF(); 0201 } 0202 0203 QUrl NoteContent::urlToOpen(bool /*with*/) 0204 { 0205 return (useFile() ? QUrl::fromLocalFile(fullPath()) : QUrl()); 0206 } 0207 0208 void NoteContent::setFileName(const QString &fileName) 0209 { 0210 m_fileName = fileName; 0211 } 0212 0213 bool NoteContent::trySetFileName(const QString &fileName) 0214 { 0215 if (useFile() && fileName != m_fileName) { 0216 QString newFileName = Tools::fileNameForNewFile(fileName, basket()->fullPath()); 0217 QDir dir; 0218 dir.rename(fullPath(), basket()->fullPathForFileName(newFileName)); 0219 return true; 0220 } 0221 0222 return false; // !useFile() or unsuccessful rename 0223 } 0224 0225 QString NoteContent::fullPath() 0226 { 0227 if (note() && useFile()) 0228 return note()->fullPath(); 0229 else 0230 return QString(); 0231 } 0232 0233 QUrl NoteContent::fullPathUrl() 0234 { 0235 return QUrl::fromLocalFile(fullPath()); 0236 } 0237 0238 void NoteContent::contentChanged(qreal newMinWidth) 0239 { 0240 m_minWidth = newMinWidth; 0241 if (note()) { 0242 // note()->unbufferize(); 0243 note()->requestRelayout(); // TODO: It should re-set the width! m_width = 0 ? contentChanged: setWidth, geteight, if size havent changed, only repaint and not relayout 0244 } 0245 } 0246 0247 BasketScene *NoteContent::basket() 0248 { 0249 if (note()) 0250 return note()->basket(); 0251 else 0252 return nullptr; 0253 } 0254 0255 void NoteContent::setEdited() 0256 { 0257 note()->setLastModificationDate(QDateTime::currentDateTime()); 0258 basket()->save(); 0259 } 0260 0261 /** All the Content Classes: 0262 */ 0263 0264 QString NoteContent::toText(const QString &cuttedFullPath) 0265 { 0266 return (cuttedFullPath.isEmpty() ? fullPath() : cuttedFullPath); 0267 } 0268 0269 QString TextContent::toText(const QString & /*cuttedFullPath*/) 0270 { 0271 return text(); 0272 } 0273 QString HtmlContent::toText(const QString & /*cuttedFullPath*/) 0274 { 0275 return Tools::htmlToText(html()); 0276 } 0277 QString LinkContent::toText(const QString & /*cuttedFullPath*/) 0278 { 0279 if (autoTitle()) 0280 return url().toDisplayString(); 0281 else if (title().isEmpty() && url().isEmpty()) 0282 return QString(); 0283 else if (url().isEmpty()) 0284 return title(); 0285 else if (title().isEmpty()) 0286 return url().toDisplayString(); 0287 else 0288 return QString("%1 <%2>").arg(title(), url().toDisplayString()); 0289 } 0290 QString CrossReferenceContent::toText(const QString & /*cuttedFullPath*/) 0291 { 0292 if (title().isEmpty() && url().isEmpty()) 0293 return QString(); 0294 else if (url().isEmpty()) 0295 return title(); 0296 else if (title().isEmpty()) 0297 return url().toDisplayString(); 0298 else 0299 return QString("%1 <%2>").arg(title(), url().toDisplayString()); 0300 } 0301 QString ColorContent::toText(const QString & /*cuttedFullPath*/) 0302 { 0303 return color().name(); 0304 } 0305 QString UnknownContent::toText(const QString & /*cuttedFullPath*/) 0306 { 0307 return QString(); 0308 } 0309 0310 // TODO: If imageName.isEmpty() return fullPath() because it's for external use, else return fileName() because it's to display in a tooltip 0311 QString TextContent::toHtml(const QString & /*imageName*/, const QString & /*cuttedFullPath*/) 0312 { 0313 return Tools::textToHTMLWithoutP(text()); 0314 } 0315 0316 QString HtmlContent::toHtml(const QString & /*imageName*/, const QString & /*cuttedFullPath*/) 0317 { 0318 //extract HTML content exactly as is, with no further processing applied 0319 return m_graphicsTextItem.document()->toHtml("utf-8"); 0320 } 0321 0322 QString ImageContent::toHtml(const QString & /*imageName*/, const QString &cuttedFullPath) 0323 { 0324 return QString("<img src=\"%1\">").arg(cuttedFullPath.isEmpty() ? fullPath() : cuttedFullPath); 0325 } 0326 0327 QString AnimationContent::toHtml(const QString & /*imageName*/, const QString &cuttedFullPath) 0328 { 0329 return QString("<img src=\"%1\">").arg(cuttedFullPath.isEmpty() ? fullPath() : cuttedFullPath); 0330 } 0331 0332 QString SoundContent::toHtml(const QString & /*imageName*/, const QString &cuttedFullPath) 0333 { 0334 return QString("<a href=\"%1\">%2</a>").arg((cuttedFullPath.isEmpty() ? fullPath() : cuttedFullPath), fileName()); 0335 } // With the icon? 0336 0337 QString FileContent::toHtml(const QString & /*imageName*/, const QString &cuttedFullPath) 0338 { 0339 return QString("<a href=\"%1\">%2</a>").arg((cuttedFullPath.isEmpty() ? fullPath() : cuttedFullPath), fileName()); 0340 } // With the icon? 0341 0342 QString LinkContent::toHtml(const QString & /*imageName*/, const QString & /*cuttedFullPath*/) 0343 { 0344 return QString("<a href=\"%1\">%2</a>").arg(url().toDisplayString(), title()); 0345 } // With the icon? 0346 0347 QString CrossReferenceContent::toHtml(const QString & /*imageName*/, const QString & /*cuttedFullPath*/) 0348 { 0349 return QString("<a href=\"%1\">%2</a>").arg(url().toDisplayString(), title()); 0350 } // With the icon? 0351 0352 QString LauncherContent::toHtml(const QString & /*imageName*/, const QString &cuttedFullPath) 0353 { 0354 return QString("<a href=\"%1\">%2</a>").arg((cuttedFullPath.isEmpty() ? fullPath() : cuttedFullPath), name()); 0355 } // With the icon? 0356 0357 QString ColorContent::toHtml(const QString & /*imageName*/, const QString & /*cuttedFullPath*/) 0358 { 0359 return QString("<span style=\"color: %1\">%2</span>").arg(color().name(), color().name()); 0360 } 0361 0362 QString UnknownContent::toHtml(const QString & /*imageName*/, const QString & /*cuttedFullPath*/) 0363 { 0364 return QString(); 0365 } 0366 0367 QPixmap ImageContent::toPixmap() 0368 { 0369 return pixmap(); 0370 } 0371 QPixmap AnimationContent::toPixmap() 0372 { 0373 return m_movie->currentPixmap(); 0374 } 0375 0376 void NoteContent::toLink(QUrl *url, QString *title, const QString &cuttedFullPath) 0377 { 0378 *url = QUrl(); 0379 title->clear(); 0380 } 0381 0382 void LinkContent::toLink(QUrl *url, QString *title, const QString & /*cuttedFullPath*/) 0383 { 0384 *url = this->url(); 0385 *title = this->title(); 0386 } 0387 0388 void CrossReferenceContent::toLink(QUrl *url, QString *title, const QString & /*cuttedFullPath*/) 0389 { 0390 *url = this->url(); 0391 *title = this->title(); 0392 } 0393 0394 void LauncherContent::toLink(QUrl *url, QString *title, const QString &cuttedFullPath) 0395 { 0396 *url = QUrl::fromUserInput(cuttedFullPath.isEmpty() ? fullPath() : cuttedFullPath); 0397 *title = name(); 0398 } 0399 0400 bool TextContent::useFile() const 0401 { 0402 return true; 0403 } 0404 bool HtmlContent::useFile() const 0405 { 0406 return true; 0407 } 0408 bool ImageContent::useFile() const 0409 { 0410 return true; 0411 } 0412 bool AnimationContent::useFile() const 0413 { 0414 return true; 0415 } 0416 bool SoundContent::useFile() const 0417 { 0418 return true; 0419 } 0420 bool FileContent::useFile() const 0421 { 0422 return true; 0423 } 0424 bool LinkContent::useFile() const 0425 { 0426 return false; 0427 } 0428 bool CrossReferenceContent::useFile() const 0429 { 0430 return false; 0431 } 0432 bool LauncherContent::useFile() const 0433 { 0434 return true; 0435 } 0436 bool ColorContent::useFile() const 0437 { 0438 return false; 0439 } 0440 bool UnknownContent::useFile() const 0441 { 0442 return true; 0443 } 0444 0445 bool TextContent::canBeSavedAs() const 0446 { 0447 return true; 0448 } 0449 bool HtmlContent::canBeSavedAs() const 0450 { 0451 return true; 0452 } 0453 bool ImageContent::canBeSavedAs() const 0454 { 0455 return true; 0456 } 0457 bool AnimationContent::canBeSavedAs() const 0458 { 0459 return true; 0460 } 0461 bool SoundContent::canBeSavedAs() const 0462 { 0463 return true; 0464 } 0465 bool FileContent::canBeSavedAs() const 0466 { 0467 return true; 0468 } 0469 bool LinkContent::canBeSavedAs() const 0470 { 0471 return true; 0472 } 0473 bool CrossReferenceContent::canBeSavedAs() const 0474 { 0475 return true; 0476 } 0477 bool LauncherContent::canBeSavedAs() const 0478 { 0479 return true; 0480 } 0481 bool ColorContent::canBeSavedAs() const 0482 { 0483 return false; 0484 } 0485 bool UnknownContent::canBeSavedAs() const 0486 { 0487 return false; 0488 } 0489 0490 QString TextContent::saveAsFilters() const 0491 { 0492 return "text/plain"; 0493 } 0494 QString HtmlContent::saveAsFilters() const 0495 { 0496 return "text/html"; 0497 } 0498 QString ImageContent::saveAsFilters() const 0499 { 0500 return "image/png"; 0501 } // TODO: Offer more types 0502 QString AnimationContent::saveAsFilters() const 0503 { 0504 return "image/gif"; 0505 } // TODO: MNG... 0506 QString SoundContent::saveAsFilters() const 0507 { 0508 return "audio/mp3 audio/ogg"; 0509 } // TODO: OGG... 0510 QString FileContent::saveAsFilters() const 0511 { 0512 return "*"; 0513 } // TODO: Get MIME type of the url target 0514 QString LinkContent::saveAsFilters() const 0515 { 0516 return "*"; 0517 } // TODO: idem File + If isDir() const: return 0518 QString CrossReferenceContent::saveAsFilters() const 0519 { 0520 return "*"; 0521 } // TODO: idem File + If isDir() const: return 0522 QString LauncherContent::saveAsFilters() const 0523 { 0524 return "application/x-desktop"; 0525 } 0526 QString ColorContent::saveAsFilters() const 0527 { 0528 return QString(); 0529 } 0530 QString UnknownContent::saveAsFilters() const 0531 { 0532 return QString(); 0533 } 0534 0535 bool TextContent::match(const FilterData &data) 0536 { 0537 return text().contains(data.string); 0538 } 0539 bool HtmlContent::match(const FilterData &data) 0540 { 0541 return m_textEquivalent.contains(data.string, Qt::CaseInsensitive); 0542 } // OPTIM_FILTER 0543 bool ImageContent::match(const FilterData & /*data*/) 0544 { 0545 return false; 0546 } 0547 bool AnimationContent::match(const FilterData & /*data*/) 0548 { 0549 return false; 0550 } 0551 bool SoundContent::match(const FilterData &data) 0552 { 0553 return fileName().contains(data.string); 0554 } 0555 bool FileContent::match(const FilterData &data) 0556 { 0557 return fileName().contains(data.string); 0558 } 0559 bool LinkContent::match(const FilterData &data) 0560 { 0561 return title().contains(data.string) || url().toDisplayString().contains(data.string); 0562 } 0563 bool CrossReferenceContent::match(const FilterData &data) 0564 { 0565 return title().contains(data.string) || url().toDisplayString().contains(data.string); 0566 } 0567 bool LauncherContent::match(const FilterData &data) 0568 { 0569 return exec().contains(data.string) || name().contains(data.string); 0570 } 0571 bool ColorContent::match(const FilterData &data) 0572 { 0573 return color().name().contains(data.string); 0574 } 0575 bool UnknownContent::match(const FilterData &data) 0576 { 0577 return mimeTypes().contains(data.string); 0578 } 0579 0580 QString TextContent::editToolTipText() const 0581 { 0582 return i18n("Edit this plain text"); 0583 } 0584 QString HtmlContent::editToolTipText() const 0585 { 0586 return i18n("Edit this text"); 0587 } 0588 QString ImageContent::editToolTipText() const 0589 { 0590 return i18n("Edit this image"); 0591 } 0592 QString AnimationContent::editToolTipText() const 0593 { 0594 return i18n("Edit this animation"); 0595 } 0596 QString SoundContent::editToolTipText() const 0597 { 0598 return i18n("Edit the file name of this sound"); 0599 } 0600 QString FileContent::editToolTipText() const 0601 { 0602 return i18n("Edit the name of this file"); 0603 } 0604 QString LinkContent::editToolTipText() const 0605 { 0606 return i18n("Edit this link"); 0607 } 0608 QString CrossReferenceContent::editToolTipText() const 0609 { 0610 return i18n("Edit this cross reference"); 0611 } 0612 QString LauncherContent::editToolTipText() const 0613 { 0614 return i18n("Edit this launcher"); 0615 } 0616 QString ColorContent::editToolTipText() const 0617 { 0618 return i18n("Edit this color"); 0619 } 0620 QString UnknownContent::editToolTipText() const 0621 { 0622 return i18n("Edit this unknown object"); 0623 } 0624 0625 QString TextContent::cssClass() const 0626 { 0627 return QString(); 0628 } 0629 QString HtmlContent::cssClass() const 0630 { 0631 return QString(); 0632 } 0633 QString ImageContent::cssClass() const 0634 { 0635 return QString(); 0636 } 0637 QString AnimationContent::cssClass() const 0638 { 0639 return QString(); 0640 } 0641 QString SoundContent::cssClass() const 0642 { 0643 return "sound"; 0644 } 0645 QString FileContent::cssClass() const 0646 { 0647 return "file"; 0648 } 0649 QString LinkContent::cssClass() const 0650 { 0651 return (LinkLook::lookForURL(m_url) == LinkLook::localLinkLook ? "local" : "network"); 0652 } 0653 QString CrossReferenceContent::cssClass() const 0654 { 0655 return "cross_reference"; 0656 } 0657 QString LauncherContent::cssClass() const 0658 { 0659 return "launcher"; 0660 } 0661 QString ColorContent::cssClass() const 0662 { 0663 return QString(); 0664 } 0665 QString UnknownContent::cssClass() const 0666 { 0667 return QString(); 0668 } 0669 0670 void TextContent::fontChanged() 0671 { 0672 setText(text()); 0673 } 0674 void HtmlContent::fontChanged() 0675 { 0676 QTextDocument *richDoc = m_graphicsTextItem.document(); 0677 // This check is important when applying style to a note which is not loaded yet. Example: 0678 // Filter all -> open some basket for the first time -> close filter: if a note was tagged as TODO, then it would display no text 0679 if (!richDoc->isEmpty()) 0680 setHtml(Tools::textDocumentToMinimalHTML(richDoc)); 0681 } 0682 void ImageContent::fontChanged() 0683 { 0684 setPixmap(pixmap()); 0685 } 0686 void AnimationContent::fontChanged() 0687 { 0688 /*startMovie();*/ 0689 } 0690 void FileContent::fontChanged() 0691 { 0692 setFileName(fileName()); 0693 } 0694 void LinkContent::fontChanged() 0695 { 0696 setLink(url(), title(), icon(), autoTitle(), autoIcon()); 0697 } 0698 void CrossReferenceContent::fontChanged() 0699 { 0700 setCrossReference(url(), title(), icon()); 0701 } 0702 void LauncherContent::fontChanged() 0703 { 0704 setLauncher(name(), icon(), exec()); 0705 } 0706 void ColorContent::fontChanged() 0707 { 0708 setColor(color()); 0709 } 0710 void UnknownContent::fontChanged() 0711 { 0712 loadFromFile(/*lazyLoad=*/false); 0713 } // TODO: Optimize: setMimeTypes() 0714 0715 // QString TextContent::customOpenCommand() { return (Settings::isTextUseProg() && ! Settings::textProg().isEmpty() ? Settings::textProg() : QString()); } 0716 QString HtmlContent::customOpenCommand() 0717 { 0718 return (Settings::isHtmlUseProg() && !Settings::htmlProg().isEmpty() ? Settings::htmlProg() : QString()); 0719 } 0720 QString ImageContent::customOpenCommand() 0721 { 0722 return (Settings::isImageUseProg() && !Settings::imageProg().isEmpty() ? Settings::imageProg() : QString()); 0723 } 0724 QString AnimationContent::customOpenCommand() 0725 { 0726 return (Settings::isAnimationUseProg() && !Settings::animationProg().isEmpty() ? Settings::animationProg() : QString()); 0727 } 0728 QString SoundContent::customOpenCommand() 0729 { 0730 return (Settings::isSoundUseProg() && !Settings::soundProg().isEmpty() ? Settings::soundProg() : QString()); 0731 } 0732 0733 void LinkContent::serialize(QDataStream &stream) 0734 { 0735 stream << url() << title() << icon() << (quint64)autoTitle() << (quint64)autoIcon(); 0736 } 0737 void CrossReferenceContent::serialize(QDataStream &stream) 0738 { 0739 stream << url() << title() << icon(); 0740 } 0741 void ColorContent::serialize(QDataStream &stream) 0742 { 0743 stream << color(); 0744 } 0745 0746 QPixmap TextContent::feedbackPixmap(qreal width, qreal height) 0747 { 0748 QRectF textRect = QFontMetrics(note()->font()).boundingRect(0, 0, width, height, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, text()); 0749 QPixmap pixmap(qMin(width, textRect.width()), qMin(height, textRect.height())); 0750 pixmap.fill(note()->backgroundColor().darker(FEEDBACK_DARKING)); 0751 QPainter painter(&pixmap); 0752 painter.setPen(note()->textColor()); 0753 painter.setFont(note()->font()); 0754 painter.drawText(0, 0, pixmap.width(), pixmap.height(), Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, text()); 0755 painter.end(); 0756 0757 return pixmap; 0758 } 0759 0760 QPixmap HtmlContent::feedbackPixmap(qreal width, qreal height) 0761 { 0762 QTextDocument richText; 0763 richText.setHtml(html()); 0764 richText.setDefaultFont(note()->font()); 0765 richText.setTextWidth(width); 0766 QPalette palette; 0767 palette = basket()->palette(); 0768 palette.setColor(QPalette::Text, note()->textColor()); 0769 palette.setColor(QPalette::Background, note()->backgroundColor().darker(FEEDBACK_DARKING)); 0770 QPixmap pixmap(qMin(width, richText.idealWidth()), qMin(height, richText.size().height())); 0771 pixmap.fill(note()->backgroundColor().darker(FEEDBACK_DARKING)); 0772 QPainter painter(&pixmap); 0773 painter.setPen(note()->textColor()); 0774 painter.translate(0, 0); 0775 richText.drawContents(&painter, QRectF(0, 0, pixmap.width(), pixmap.height())); 0776 painter.end(); 0777 0778 return pixmap; 0779 } 0780 0781 QPixmap ImageContent::feedbackPixmap(qreal width, qreal height) 0782 { 0783 if (width >= m_pixmapItem.pixmap().width() && height >= m_pixmapItem.pixmap().height()) { // Full size 0784 if (m_pixmapItem.pixmap().hasAlpha()) { 0785 QPixmap opaque(m_pixmapItem.pixmap().width(), m_pixmapItem.pixmap().height()); 0786 opaque.fill(note()->backgroundColor().darker(FEEDBACK_DARKING)); 0787 QPainter painter(&opaque); 0788 painter.drawPixmap(0, 0, m_pixmapItem.pixmap()); 0789 painter.end(); 0790 return opaque; 0791 } else { 0792 return m_pixmapItem.pixmap(); 0793 } 0794 } else { // Scaled down 0795 QImage imageToScale = m_pixmapItem.pixmap().toImage(); 0796 QPixmap pmScaled; 0797 pmScaled = QPixmap::fromImage(imageToScale.scaled(width, height, Qt::KeepAspectRatio)); 0798 if (pmScaled.hasAlpha()) { 0799 QPixmap opaque(pmScaled.width(), pmScaled.height()); 0800 opaque.fill(note()->backgroundColor().darker(FEEDBACK_DARKING)); 0801 QPainter painter(&opaque); 0802 painter.drawPixmap(0, 0, pmScaled); 0803 painter.end(); 0804 return opaque; 0805 } else { 0806 return pmScaled; 0807 } 0808 } 0809 } 0810 0811 QPixmap AnimationContent::feedbackPixmap(qreal width, qreal height) 0812 { 0813 QPixmap pixmap = m_movie->currentPixmap(); 0814 if (width >= pixmap.width() && height >= pixmap.height()) // Full size 0815 return pixmap; 0816 else { // Scaled down 0817 QImage imageToScale = pixmap.toImage(); 0818 QPixmap pmScaled; 0819 pmScaled = QPixmap::fromImage(imageToScale.scaled(width, height, Qt::KeepAspectRatio)); 0820 return pmScaled; 0821 } 0822 } 0823 0824 QPixmap LinkContent::feedbackPixmap(qreal width, qreal height) 0825 { 0826 QPalette palette; 0827 palette = basket()->palette(); 0828 palette.setColor(QPalette::WindowText, note()->textColor()); 0829 palette.setColor(QPalette::Background, note()->backgroundColor().darker(FEEDBACK_DARKING)); 0830 return m_linkDisplayItem.linkDisplay().feedbackPixmap(width, height, palette, /*isDefaultColor=*/note()->textColor() == basket()->textColor()); 0831 } 0832 0833 QPixmap CrossReferenceContent::feedbackPixmap(qreal width, qreal height) 0834 { 0835 QPalette palette; 0836 palette = basket()->palette(); 0837 palette.setColor(QPalette::WindowText, note()->textColor()); 0838 palette.setColor(QPalette::Background, note()->backgroundColor().darker(FEEDBACK_DARKING)); 0839 return m_linkDisplayItem.linkDisplay().feedbackPixmap(width, height, palette, /*isDefaultColor=*/note()->textColor() == basket()->textColor()); 0840 } 0841 0842 QPixmap ColorContent::feedbackPixmap(qreal width, qreal height) 0843 { 0844 // TODO: Duplicate code: make a rect() method! 0845 QRectF boundingRect = m_colorItem.boundingRect(); 0846 0847 QPalette palette; 0848 palette = basket()->palette(); 0849 palette.setColor(QPalette::WindowText, note()->textColor()); 0850 palette.setColor(QPalette::Background, note()->backgroundColor().darker(FEEDBACK_DARKING)); 0851 0852 QPixmap pixmap(qMin(width, boundingRect.width()), qMin(height, boundingRect.height())); 0853 pixmap.fill(note()->backgroundColor().darker(FEEDBACK_DARKING)); 0854 QPainter painter(&pixmap); 0855 m_colorItem.paint(&painter, nullptr, nullptr); //, pixmap.width(), pixmap.height(), palette, false, false, false); // We don't care of the three last boolean parameters. 0856 painter.end(); 0857 0858 return pixmap; 0859 } 0860 0861 QPixmap FileContent::feedbackPixmap(qreal width, qreal height) 0862 { 0863 QPalette palette; 0864 palette = basket()->palette(); 0865 palette.setColor(QPalette::WindowText, note()->textColor()); 0866 palette.setColor(QPalette::Background, note()->backgroundColor().darker(FEEDBACK_DARKING)); 0867 return m_linkDisplayItem.linkDisplay().feedbackPixmap(width, height, palette, /*isDefaultColor=*/note()->textColor() == basket()->textColor()); 0868 } 0869 0870 QPixmap LauncherContent::feedbackPixmap(qreal width, qreal height) 0871 { 0872 QPalette palette; 0873 palette = basket()->palette(); 0874 palette.setColor(QPalette::WindowText, note()->textColor()); 0875 palette.setColor(QPalette::Background, note()->backgroundColor().darker(FEEDBACK_DARKING)); 0876 return m_linkDisplayItem.linkDisplay().feedbackPixmap(width, height, palette, /*isDefaultColor=*/note()->textColor() == basket()->textColor()); 0877 } 0878 0879 QPixmap UnknownContent::feedbackPixmap(qreal width, qreal height) 0880 { 0881 QRectF boundingRect = m_unknownItem.boundingRect(); 0882 0883 QPalette palette; 0884 palette = basket()->palette(); 0885 palette.setColor(QPalette::WindowText, note()->textColor()); 0886 palette.setColor(QPalette::Background, note()->backgroundColor().darker(FEEDBACK_DARKING)); 0887 0888 QPixmap pixmap(qMin(width, boundingRect.width()), qMin(height, boundingRect.height())); 0889 QPainter painter(&pixmap); 0890 m_unknownItem.paint(&painter, nullptr, nullptr); //, pixmap.width() + 1, pixmap.height(), palette, false, false, false); // We don't care of the three last boolean parameters. 0891 painter.setPen(note()->backgroundColor().darker(FEEDBACK_DARKING)); 0892 painter.drawPoint(0, 0); 0893 painter.drawPoint(pixmap.width() - 1, 0); 0894 painter.drawPoint(0, pixmap.height() - 1); 0895 painter.drawPoint(pixmap.width() - 1, pixmap.height() - 1); 0896 painter.end(); 0897 0898 return pixmap; 0899 } 0900 0901 /** class TextContent: 0902 */ 0903 0904 TextContent::TextContent(Note *parent, const QString &fileName, bool lazyLoad) 0905 : NoteContent(parent, NoteType::Text, fileName) 0906 , m_graphicsTextItem(parent) 0907 { 0908 if (parent) { 0909 parent->addToGroup(&m_graphicsTextItem); 0910 m_graphicsTextItem.setPos(parent->contentX(), Note::NOTE_MARGIN); 0911 } 0912 0913 basket()->addWatchedFile(fullPath()); 0914 TextContent::loadFromFile(lazyLoad); 0915 } 0916 0917 TextContent::~TextContent() 0918 { 0919 if (note()) 0920 note()->removeFromGroup(&m_graphicsTextItem); 0921 } 0922 0923 qreal TextContent::setWidthAndGetHeight(qreal /*width*/) 0924 { 0925 return m_graphicsTextItem.boundingRect().height(); 0926 } 0927 0928 bool TextContent::loadFromFile(bool lazyLoad) 0929 { 0930 DEBUG_WIN << "Loading TextContent From " + basket()->folderName() + fileName(); 0931 0932 QString content; 0933 bool success = FileStorage::loadFromFile(fullPath(), &content); 0934 0935 if (success) 0936 setText(content, lazyLoad); 0937 else { 0938 qDebug() << "FAILED TO LOAD TextContent: " << fullPath(); 0939 setText(QString(), lazyLoad); 0940 if (!QFile::exists(fullPath())) 0941 TextContent::saveToFile(); // Reserve the fileName so no new note will have the same name! 0942 } 0943 return success; 0944 } 0945 0946 bool TextContent::finishLazyLoad() 0947 { 0948 m_graphicsTextItem.setFont(note()->font()); 0949 contentChanged(m_graphicsTextItem.boundingRect().width() + 1); 0950 return true; 0951 } 0952 0953 bool TextContent::saveToFile() 0954 { 0955 return FileStorage::saveToFile(fullPath(), text()); 0956 } 0957 0958 QString TextContent::linkAt(const QPointF & /*pos*/) 0959 { 0960 return QString(); 0961 } 0962 0963 QString TextContent::messageWhenOpening(OpenMessage where) 0964 { 0965 switch (where) { 0966 case OpenOne: 0967 return i18n("Opening plain text..."); 0968 case OpenSeveral: 0969 return i18n("Opening plain texts..."); 0970 case OpenOneWith: 0971 return i18n("Opening plain text with..."); 0972 case OpenSeveralWith: 0973 return i18n("Opening plain texts with..."); 0974 case OpenOneWithDialog: 0975 return i18n("Open plain text with:"); 0976 case OpenSeveralWithDialog: 0977 return i18n("Open plain texts with:"); 0978 default: 0979 return QString(); 0980 } 0981 } 0982 0983 void TextContent::setText(const QString &text, bool lazyLoad) 0984 { 0985 m_graphicsTextItem.setText(text); 0986 if (!lazyLoad) 0987 TextContent::finishLazyLoad(); 0988 else 0989 contentChanged(m_graphicsTextItem.boundingRect().width()); 0990 } 0991 0992 void TextContent::exportToHTML(HTMLExporter *exporter, int indent) 0993 { 0994 QString spaces; 0995 QString html = "<html><head><meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"><meta name=\"qrichtext\" content=\"1\" /></head><body>" + 0996 Tools::detectCrossReferences( 0997 Tools::detectURLs(Tools::textToHTMLWithoutP(text().replace(QChar('\t'), " "))), 0998 false, exporter); // Don't collapse multiple spaces! 0999 exporter->stream << html.replace(" ", " ").replace(QChar('\n'), '\n' + spaces.fill(' ', indent + 1)); 1000 } 1001 1002 /** class HtmlContent: 1003 */ 1004 1005 HtmlContent::HtmlContent(Note *parent, const QString &fileName, bool lazyLoad) 1006 : NoteContent(parent, NoteType::Html, fileName) 1007 , m_graphicsTextItem(parent) 1008 { 1009 if (parent) { 1010 parent->addToGroup(&m_graphicsTextItem); 1011 m_graphicsTextItem.setPos(parent->contentX(), Note::NOTE_MARGIN); 1012 } 1013 basket()->addWatchedFile(fullPath()); 1014 HtmlContent::loadFromFile(lazyLoad); 1015 } 1016 1017 HtmlContent::~HtmlContent() 1018 { 1019 if (note()) 1020 note()->removeFromGroup(&m_graphicsTextItem); 1021 } 1022 1023 qreal HtmlContent::setWidthAndGetHeight(qreal width) 1024 { 1025 width -= 1; 1026 m_graphicsTextItem.setTextWidth(width); 1027 return m_graphicsTextItem.boundingRect().height(); 1028 } 1029 1030 bool HtmlContent::loadFromFile(bool lazyLoad) 1031 { 1032 DEBUG_WIN << "Loading HtmlContent From " + basket()->folderName() + fileName(); 1033 1034 QString content; 1035 bool success = FileStorage::loadFromFile(fullPath(), &content); 1036 1037 if (success) 1038 setHtml(content, lazyLoad); 1039 else { 1040 setHtml(QString(), lazyLoad); 1041 if (!QFile::exists(fullPath())) 1042 HtmlContent::saveToFile(); // Reserve the fileName so no new note will have the same name! 1043 } 1044 return success; 1045 } 1046 1047 bool HtmlContent::finishLazyLoad() 1048 { 1049 qreal width = m_graphicsTextItem.document()->idealWidth(); 1050 1051 m_graphicsTextItem.setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable); 1052 m_graphicsTextItem.setTextInteractionFlags(Qt::TextEditorInteraction); 1053 1054 /*QString css = ".cross_reference { display: block; width: 100%; text-decoration: none; color: #336600; }" 1055 "a:hover.cross_reference { text-decoration: underline; color: #ff8000; }"; 1056 m_graphicsTextItem.document()->setDefaultStyleSheet(css);*/ 1057 QString convert = Tools::detectURLs(m_html); 1058 if (note()->allowCrossReferences()) 1059 convert = Tools::detectCrossReferences(convert); 1060 m_graphicsTextItem.setHtml(convert); 1061 m_graphicsTextItem.setDefaultTextColor(note()->textColor()); 1062 m_graphicsTextItem.setFont(note()->font()); 1063 m_graphicsTextItem.setTextWidth(1); // We put a width of 1 pixel, so usedWidth() is equal to the minimum width 1064 int minWidth = m_graphicsTextItem.document()->idealWidth(); 1065 m_graphicsTextItem.setTextWidth(width); 1066 contentChanged(minWidth + 1); 1067 1068 return true; 1069 } 1070 1071 bool HtmlContent::saveToFile() 1072 { 1073 return FileStorage::saveToFile(fullPath(), html()); 1074 } 1075 1076 QString HtmlContent::linkAt(const QPointF &pos) 1077 { 1078 return m_graphicsTextItem.document()->documentLayout()->anchorAt(pos); 1079 } 1080 1081 QString HtmlContent::messageWhenOpening(OpenMessage where) 1082 { 1083 switch (where) { 1084 case OpenOne: 1085 return i18n("Opening text..."); 1086 case OpenSeveral: 1087 return i18n("Opening texts..."); 1088 case OpenOneWith: 1089 return i18n("Opening text with..."); 1090 case OpenSeveralWith: 1091 return i18n("Opening texts with..."); 1092 case OpenOneWithDialog: 1093 return i18n("Open text with:"); 1094 case OpenSeveralWithDialog: 1095 return i18n("Open texts with:"); 1096 default: 1097 return QString(); 1098 } 1099 } 1100 1101 void HtmlContent::setHtml(const QString &html, bool lazyLoad) 1102 { 1103 m_html = html; 1104 /* The code was commented, so now non-Latin text is stored directly in Unicode. 1105 * If testing doesn't show any bugs, this block should be deleted 1106 QRegExp rx("([^\\x00-\\x7f])"); 1107 while (m_html.contains(rx)) { 1108 m_html.replace( rx.cap().unicode()[0], QString("&#%1;").arg(rx.cap().unicode()[0].unicode()) ); 1109 }*/ 1110 m_textEquivalent = HtmlContent::toText(QString()); // OPTIM_FILTER 1111 if (!lazyLoad) 1112 HtmlContent::finishLazyLoad(); 1113 else 1114 contentChanged(10); 1115 } 1116 1117 void HtmlContent::exportToHTML(HTMLExporter *exporter, int indent) 1118 { 1119 QString spaces; 1120 QString convert = Tools::detectURLs(html().replace("\t", " ")); 1121 if (note()->allowCrossReferences()) 1122 convert = Tools::detectCrossReferences(convert, false, exporter); 1123 1124 exporter->stream << Tools::htmlToParagraph(convert).replace(" ", " ").replace("\n", '\n' + spaces.fill(' ', indent + 1)); 1125 } 1126 1127 /** class ImageContent: 1128 */ 1129 1130 ImageContent::ImageContent(Note *parent, const QString &fileName, bool lazyLoad) 1131 : NoteContent(parent, NoteType::Image, fileName) 1132 , m_pixmapItem(parent) 1133 , m_format() 1134 { 1135 if (parent) { 1136 parent->addToGroup(&m_pixmapItem); 1137 m_pixmapItem.setPos(parent->contentX(), Note::NOTE_MARGIN); 1138 } 1139 1140 basket()->addWatchedFile(fullPath()); 1141 ImageContent::loadFromFile(lazyLoad); 1142 } 1143 1144 ImageContent::~ImageContent() 1145 { 1146 if (note()) 1147 note()->removeFromGroup(&m_pixmapItem); 1148 } 1149 1150 qreal ImageContent::setWidthAndGetHeight(qreal width) 1151 { 1152 width -= 1; 1153 // Don't store width: we will get it on paint! 1154 if (width >= m_pixmapItem.pixmap().width()) // Full size 1155 { 1156 m_pixmapItem.setScale(1.0); 1157 return m_pixmapItem.boundingRect().height(); 1158 } else { // Scaled down 1159 qreal scaleFactor = width / m_pixmapItem.pixmap().width(); 1160 m_pixmapItem.setScale(scaleFactor); 1161 return m_pixmapItem.boundingRect().height() * scaleFactor; 1162 } 1163 } 1164 1165 bool ImageContent::loadFromFile(bool lazyLoad) 1166 { 1167 if (lazyLoad) 1168 return true; 1169 else 1170 return ImageContent::finishLazyLoad(); 1171 } 1172 1173 bool ImageContent::finishLazyLoad() 1174 { 1175 DEBUG_WIN << "Loading ImageContent From " + basket()->folderName() + fileName(); 1176 1177 QByteArray content; 1178 QPixmap pixmap; 1179 1180 if (FileStorage::loadFromFile(fullPath(), &content)) { 1181 QBuffer buffer(&content); 1182 1183 buffer.open(QIODevice::ReadOnly); 1184 m_format = QImageReader::imageFormat(&buffer); // See QImageIO to know what formats can be supported. 1185 buffer.close(); 1186 if (!m_format.isNull()) { 1187 pixmap.loadFromData(content); 1188 setPixmap(pixmap); 1189 return true; 1190 } 1191 } 1192 1193 qDebug() << "FAILED TO LOAD ImageContent: " << fullPath(); 1194 m_format = "PNG"; // If the image is set later, it should be saved without destruction, so we use PNG by default. 1195 pixmap = QPixmap(1, 1); // Create a 1x1 pixels image instead of an undefined one. 1196 pixmap.fill(); 1197 pixmap.setMask(pixmap.createHeuristicMask()); 1198 setPixmap(pixmap); 1199 if (!QFile::exists(fullPath())) 1200 ImageContent::saveToFile(); // Reserve the fileName so no new note will have the same name! 1201 return false; 1202 } 1203 1204 bool ImageContent::saveToFile() 1205 { 1206 QByteArray ba; 1207 QBuffer buffer(&ba); 1208 1209 buffer.open(QIODevice::WriteOnly); 1210 m_pixmapItem.pixmap().save(&buffer, m_format); 1211 return FileStorage::saveToFile(fullPath(), ba); 1212 } 1213 1214 QMap<QString, QString> ImageContent::toolTipInfos() 1215 { 1216 return { 1217 {i18n("Size"), i18n("%1 by %2 pixels", QString::number(m_pixmapItem.pixmap().width()), 1218 QString::number(m_pixmapItem.pixmap().height()))} 1219 }; 1220 } 1221 1222 QString ImageContent::messageWhenOpening(OpenMessage where) 1223 { 1224 switch (where) { 1225 case OpenOne: 1226 return i18n("Opening image..."); 1227 case OpenSeveral: 1228 return i18n("Opening images..."); 1229 case OpenOneWith: 1230 return i18n("Opening image with..."); 1231 case OpenSeveralWith: 1232 return i18n("Opening images with..."); 1233 case OpenOneWithDialog: 1234 return i18n("Open image with:"); 1235 case OpenSeveralWithDialog: 1236 return i18n("Open images with:"); 1237 default: 1238 return QString(); 1239 } 1240 } 1241 1242 void ImageContent::setPixmap(const QPixmap &pixmap) 1243 { 1244 m_pixmapItem.setPixmap(pixmap); 1245 // Since it's scaled, the height is always greater or equal to the size of the tag emblems (16) 1246 contentChanged(16 + 1); // TODO: always good? I don't think... 1247 } 1248 1249 void ImageContent::exportToHTML(HTMLExporter *exporter, int /*indent*/) 1250 { 1251 qreal width = m_pixmapItem.pixmap().width(); 1252 qreal height = m_pixmapItem.pixmap().height(); 1253 qreal contentWidth = note()->width() - note()->contentX() - 1 - Note::NOTE_MARGIN; 1254 1255 QString imageName = exporter->copyFile(fullPath(), /*createIt=*/true); 1256 1257 if (contentWidth <= m_pixmapItem.pixmap().width()) { // Scaled down 1258 qreal scale = contentWidth / m_pixmapItem.pixmap().width(); 1259 width = m_pixmapItem.pixmap().width() * scale; 1260 height = m_pixmapItem.pixmap().height() * scale; 1261 exporter->stream << "<a href=\"" << exporter->dataFolderName << imageName << "\" title=\"" << i18n("Click for full size view") << "\">"; 1262 } 1263 1264 exporter->stream << "<img src=\"" << exporter->dataFolderName << imageName << "\" width=\"" << width << "\" height=\"" << height << "\" alt=\"\">"; 1265 1266 if (contentWidth <= m_pixmapItem.pixmap().width()) // Scaled down 1267 exporter->stream << "</a>"; 1268 } 1269 1270 /** class AnimationContent: 1271 */ 1272 1273 AnimationContent::AnimationContent(Note *parent, const QString &fileName, bool lazyLoad) 1274 : NoteContent(parent, NoteType::Animation, fileName) 1275 , m_buffer(new QBuffer(this)) 1276 , m_movie(new QMovie(this)) 1277 , m_currentWidth(0) 1278 , m_graphicsPixmap(parent) 1279 { 1280 if (parent) { 1281 parent->addToGroup(&m_graphicsPixmap); 1282 m_graphicsPixmap.setPos(parent->contentX(), Note::NOTE_MARGIN); 1283 connect(parent->basket(), SIGNAL(activated()), m_movie, SLOT(start())); 1284 connect(parent->basket(), SIGNAL(closed()), m_movie, SLOT(stop())); 1285 } 1286 1287 basket()->addWatchedFile(fullPath()); 1288 connect(m_movie, SIGNAL(resized(QSize)), this, SLOT(movieResized())); 1289 connect(m_movie, SIGNAL(frameChanged(int)), this, SLOT(movieFrameChanged())); 1290 1291 AnimationContent::loadFromFile(lazyLoad); 1292 } 1293 1294 AnimationContent::~AnimationContent() 1295 { 1296 note()->removeFromGroup(&m_graphicsPixmap); 1297 } 1298 1299 qreal AnimationContent::setWidthAndGetHeight(qreal width) 1300 { 1301 m_currentWidth = width; 1302 QPixmap pixmap = m_graphicsPixmap.pixmap(); 1303 if (pixmap.width() > m_currentWidth) { 1304 qreal scaleFactor = m_currentWidth / pixmap.width(); 1305 m_graphicsPixmap.setScale(scaleFactor); 1306 return pixmap.height() * scaleFactor; 1307 } else { 1308 m_graphicsPixmap.setScale(1.0); 1309 return pixmap.height(); 1310 } 1311 1312 return 0; 1313 } 1314 1315 bool AnimationContent::loadFromFile(bool lazyLoad) 1316 { 1317 if (lazyLoad) 1318 return true; 1319 else 1320 return AnimationContent::finishLazyLoad(); 1321 } 1322 1323 bool AnimationContent::finishLazyLoad() 1324 { 1325 QByteArray content; 1326 if (FileStorage::loadFromFile(fullPath(), &content)) { 1327 m_buffer->setData(content); 1328 startMovie(); 1329 contentChanged(16); 1330 return true; 1331 } 1332 m_buffer->setData(nullptr); 1333 return false; 1334 } 1335 1336 bool AnimationContent::saveToFile() 1337 { 1338 // Impossible! 1339 return false; 1340 } 1341 1342 QString AnimationContent::messageWhenOpening(OpenMessage where) 1343 { 1344 switch (where) { 1345 case OpenOne: 1346 return i18n("Opening animation..."); 1347 case OpenSeveral: 1348 return i18n("Opening animations..."); 1349 case OpenOneWith: 1350 return i18n("Opening animation with..."); 1351 case OpenSeveralWith: 1352 return i18n("Opening animations with..."); 1353 case OpenOneWithDialog: 1354 return i18n("Open animation with:"); 1355 case OpenSeveralWithDialog: 1356 return i18n("Open animations with:"); 1357 default: 1358 return QString(); 1359 } 1360 } 1361 1362 bool AnimationContent::startMovie() 1363 { 1364 if (m_buffer->data().isEmpty()) 1365 return false; 1366 m_movie->setDevice(m_buffer); 1367 m_movie->start(); 1368 return true; 1369 } 1370 1371 void AnimationContent::movieUpdated() 1372 { 1373 m_graphicsPixmap.setPixmap(m_movie->currentPixmap()); 1374 } 1375 1376 void AnimationContent::movieResized() 1377 { 1378 m_graphicsPixmap.setPixmap(m_movie->currentPixmap()); 1379 } 1380 1381 void AnimationContent::movieFrameChanged() 1382 { 1383 m_graphicsPixmap.setPixmap(m_movie->currentPixmap()); 1384 } 1385 1386 void AnimationContent::exportToHTML(HTMLExporter *exporter, int /*indent*/) 1387 { 1388 exporter->stream << QString("<img src=\"%1\" width=\"%2\" height=\"%3\" alt=\"\">") 1389 .arg(exporter->dataFolderName + exporter->copyFile(fullPath(), /*createIt=*/true), QString::number(m_movie->currentPixmap().size().width()), QString::number(m_movie->currentPixmap().size().height())); 1390 } 1391 1392 /** class FileContent: 1393 */ 1394 1395 FileContent::FileContent(Note *parent, const QString &fileName) 1396 : NoteContent(parent, NoteType::File, fileName) 1397 , m_linkDisplayItem(parent) 1398 , m_previewJob(nullptr) 1399 { 1400 basket()->addWatchedFile(fullPath()); 1401 FileContent::setFileName(fileName); // FIXME: TO THAT HERE BECAUSE NoteContent() constructor seems to don't be able to call virtual methods??? 1402 if (parent) { 1403 parent->addToGroup(&m_linkDisplayItem); 1404 m_linkDisplayItem.setPos(parent->contentX(), Note::NOTE_MARGIN); 1405 } 1406 } 1407 1408 FileContent::~FileContent() 1409 { 1410 if (note()) 1411 note()->removeFromGroup(&m_linkDisplayItem); 1412 } 1413 1414 qreal FileContent::setWidthAndGetHeight(qreal width) 1415 { 1416 m_linkDisplayItem.linkDisplay().setWidth(width); 1417 return m_linkDisplayItem.linkDisplay().height(); 1418 } 1419 1420 bool FileContent::loadFromFile(bool /*lazyLoad*/) 1421 { 1422 setFileName(fileName()); // File changed: get new file preview! 1423 return true; 1424 } 1425 1426 QMap<QString, QString> FileContent::toolTipInfos() 1427 { 1428 QMap<QString, QString> toolTip; 1429 1430 // Get the size of the file: 1431 uint size = QFileInfo(fullPath()).size(); 1432 QString humanFileSize = KIO::convertSize((KIO::filesize_t)size); 1433 toolTip.insert(i18n("Size"), humanFileSize); 1434 1435 QMimeDatabase db; 1436 QMimeType mime = db.mimeTypeForUrl(QUrl::fromLocalFile(fullPath())); 1437 if (mime.isValid()) { 1438 toolTip.insert(i18n("Type"), mime.comment()); 1439 } 1440 1441 MetaDataExtractionResult result(fullPath(), mime.name()); 1442 1443 KFileMetaData::ExtractorCollection extractorCollection; 1444 const QList<KFileMetaData::Extractor*> exList = extractorCollection.fetchExtractors(mime.name()); 1445 for (KFileMetaData::Extractor *ex : exList) { 1446 ex->extract(&result); 1447 const auto groups = result.preferredGroups(); 1448 DEBUG_WIN << "Metadata Extractor result has " << QString::number(groups.count()) << " groups"; 1449 1450 for (const auto &group : groups) { 1451 if (!group.second.isEmpty()) { 1452 toolTip.insert(group.first, group.second); 1453 } 1454 } 1455 } 1456 1457 return toolTip; 1458 } 1459 1460 int FileContent::zoneAt(const QPointF &pos) 1461 { 1462 return (m_linkDisplayItem.linkDisplay().iconButtonAt(pos) ? 0 : Note::Custom0); 1463 } 1464 1465 QRectF FileContent::zoneRect(int zone, const QPointF & /*pos*/) 1466 { 1467 QRectF linkRect = m_linkDisplayItem.linkDisplay().iconButtonRect(); 1468 1469 if (zone == Note::Custom0) 1470 return QRectF(linkRect.width(), 0, note()->width(), note()->height()); // Too wide and height, but it will be clipped by Note::zoneRect() 1471 else if (zone == Note::Content) 1472 return linkRect; 1473 else 1474 return QRectF(); 1475 } 1476 1477 QString FileContent::zoneTip(int zone) 1478 { 1479 return (zone == Note::Custom0 ? i18n("Open this file") : QString()); 1480 } 1481 1482 Qt::CursorShape FileContent::cursorFromZone(int zone) const 1483 { 1484 if (zone == Note::Custom0) 1485 return Qt::PointingHandCursor; 1486 return Qt::ArrowCursor; 1487 } 1488 1489 int FileContent::xEditorIndent() 1490 { 1491 return m_linkDisplayItem.linkDisplay().iconButtonRect().width() + 2; 1492 } 1493 1494 QString FileContent::messageWhenOpening(OpenMessage where) 1495 { 1496 switch (where) { 1497 case OpenOne: 1498 return i18n("Opening file..."); 1499 case OpenSeveral: 1500 return i18n("Opening files..."); 1501 case OpenOneWith: 1502 return i18n("Opening file with..."); 1503 case OpenSeveralWith: 1504 return i18n("Opening files with..."); 1505 case OpenOneWithDialog: 1506 return i18n("Open file with:"); 1507 case OpenSeveralWithDialog: 1508 return i18n("Open files with:"); 1509 default: 1510 return QString(); 1511 } 1512 } 1513 1514 void FileContent::setFileName(const QString &fileName) 1515 { 1516 NoteContent::setFileName(fileName); 1517 QUrl url = QUrl::fromLocalFile(fullPath()); 1518 if (FileContent::linkLook()->previewEnabled()) 1519 m_linkDisplayItem.linkDisplay().setLink(fileName, NoteFactory::iconForURL(url), FileContent::linkLook(), note()->font()); // FIXME: move iconForURL outside of NoteFactory !!!!! 1520 else 1521 m_linkDisplayItem.linkDisplay().setLink(fileName, NoteFactory::iconForURL(url), QPixmap(), FileContent::linkLook(), note()->font()); 1522 startFetchingUrlPreview(); 1523 contentChanged(m_linkDisplayItem.linkDisplay().minWidth()); 1524 } 1525 1526 void FileContent::linkLookChanged() 1527 { 1528 fontChanged(); 1529 // setFileName(fileName()); 1530 // startFetchingUrlPreview(); 1531 } 1532 1533 void FileContent::newPreview(const KFileItem &, const QPixmap &preview) 1534 { 1535 LinkLook *linkLook = this->linkLook(); 1536 m_linkDisplayItem.linkDisplay().setLink(fileName(), NoteFactory::iconForURL(QUrl::fromLocalFile(fullPath())), (linkLook->previewEnabled() ? preview : QPixmap()), linkLook, note()->font()); 1537 contentChanged(m_linkDisplayItem.linkDisplay().minWidth()); 1538 } 1539 1540 void FileContent::removePreview(const KFileItem &ki) 1541 { 1542 newPreview(ki, QPixmap()); 1543 } 1544 1545 void FileContent::startFetchingUrlPreview() 1546 { 1547 /* 1548 KUrl url(fullPath()); 1549 LinkLook *linkLook = this->linkLook(); 1550 1551 // delete m_previewJob; 1552 if (!url.isEmpty() && linkLook->previewSize() > 0) { 1553 QUrl filteredUrl = NoteFactory::filteredURL(url);//KURIFilter::self()->filteredURI(url); 1554 KUrl::List urlList; 1555 urlList.append(filteredUrl); 1556 m_previewJob = KIO::filePreview(urlList, linkLook->previewSize(), linkLook->previewSize(), linkLook->iconSize()); 1557 connect(m_previewJob, SIGNAL(gotPreview(const KFileItem&, const QPixmap&)), this, SLOT(newPreview(const KFileItem&, const QPixmap&))); 1558 connect(m_previewJob, SIGNAL(failed(const KFileItem&)), this, SLOT(removePreview(const KFileItem&))); 1559 } 1560 */ 1561 } 1562 1563 void FileContent::exportToHTML(HTMLExporter *exporter, int indent) 1564 { 1565 QString spaces; 1566 QString fileName = exporter->copyFile(fullPath(), true); 1567 exporter->stream << m_linkDisplayItem.linkDisplay().toHtml(exporter, QUrl::fromLocalFile(exporter->dataFolderName + fileName), QString()).replace("\n", '\n' + spaces.fill(' ', indent + 1)); 1568 } 1569 1570 /** class SoundContent: 1571 */ 1572 1573 SoundContent::SoundContent(Note *parent, const QString &fileName) 1574 : FileContent(parent, fileName) 1575 { 1576 SoundContent::setFileName(fileName); 1577 music = new Phonon::MediaObject(this); 1578 music->setCurrentSource(Phonon::MediaSource(fullPathUrl())); 1579 auto *audioOutput = new Phonon::AudioOutput(Phonon::MusicCategory, this); 1580 Phonon::createPath(music, audioOutput); 1581 connect(music, &Phonon::MediaObject::stateChanged, this, &SoundContent::stateChanged); 1582 } 1583 1584 void SoundContent::stateChanged(Phonon::State newState, Phonon::State oldState) 1585 { 1586 qDebug() << "stateChanged " << oldState << " to " << newState; 1587 } 1588 1589 QString SoundContent::zoneTip(int zone) 1590 { 1591 return (zone == Note::Custom0 ? i18n("Open this sound") : QString()); 1592 } 1593 1594 void SoundContent::setHoveredZone(int oldZone, int newZone) 1595 { 1596 if (newZone == Note::Custom0 || newZone == Note::Content) { 1597 // Start the sound preview: 1598 if (oldZone != Note::Custom0 && oldZone != Note::Content) { // Don't restart if it was already in one of those zones 1599 if (music->state() == 1) { 1600 music->play(); 1601 } 1602 } 1603 } else { 1604 // Stop the sound preview, if it was started: 1605 if (music->state() != 1) { 1606 music->stop(); 1607 // delete music;//TODO implement this in slot connected with music alted signal 1608 // music = 0; 1609 } 1610 } 1611 } 1612 1613 QString SoundContent::messageWhenOpening(OpenMessage where) 1614 { 1615 switch (where) { 1616 case OpenOne: 1617 return i18n("Opening sound..."); 1618 case OpenSeveral: 1619 return i18n("Opening sounds..."); 1620 case OpenOneWith: 1621 return i18n("Opening sound with..."); 1622 case OpenSeveralWith: 1623 return i18n("Opening sounds with..."); 1624 case OpenOneWithDialog: 1625 return i18n("Open sound with:"); 1626 case OpenSeveralWithDialog: 1627 return i18n("Open sounds with:"); 1628 default: 1629 return QString(); 1630 } 1631 } 1632 1633 1634 void SoundContent::setFileName(const QString &fileName) 1635 { 1636 NoteContent::setFileName(fileName); 1637 QUrl url = QUrl::fromLocalFile(fullPath()); 1638 if (SoundContent::linkLook()->previewEnabled()) { 1639 m_linkDisplayItem.linkDisplay().setLink(fileName, NoteFactory::iconForURL(url), SoundContent::linkLook(), note()->font()); // FIXME: move iconForURL outside of NoteFactory !!!!! 1640 } else { 1641 m_linkDisplayItem.linkDisplay().setLink(fileName, NoteFactory::iconForURL(url), QPixmap(), SoundContent::linkLook(), note()->font()); 1642 } 1643 startFetchingUrlPreview(); 1644 contentChanged(m_linkDisplayItem.linkDisplay().minWidth()); 1645 } 1646 1647 /** class LinkContent: 1648 */ 1649 1650 LinkContent::LinkContent(Note *parent, const QUrl &url, const QString &title, const QString &icon, bool autoTitle, bool autoIcon) 1651 : NoteContent(parent, NoteType::Link) 1652 , m_linkDisplayItem(parent) 1653 , m_access_manager(nullptr) 1654 , m_acceptingData(false) 1655 , m_previewJob(nullptr) 1656 { 1657 setLink(url, title, icon, autoTitle, autoIcon); 1658 if (parent) { 1659 parent->addToGroup(&m_linkDisplayItem); 1660 m_linkDisplayItem.setPos(parent->contentX(), Note::NOTE_MARGIN); 1661 } 1662 } 1663 LinkContent::~LinkContent() 1664 { 1665 if (note()) 1666 note()->removeFromGroup(&m_linkDisplayItem); 1667 } 1668 1669 qreal LinkContent::setWidthAndGetHeight(qreal width) 1670 { 1671 m_linkDisplayItem.linkDisplay().setWidth(width); 1672 return m_linkDisplayItem.linkDisplay().height(); 1673 } 1674 1675 void LinkContent::saveToNode(QXmlStreamWriter &stream) 1676 { 1677 stream.writeStartElement("content"); 1678 stream.writeAttribute("title", title()); 1679 stream.writeAttribute("icon", icon()); 1680 stream.writeAttribute("autoIcon", (autoIcon() ? "true" : "false")); 1681 stream.writeAttribute("autoTitle", (autoTitle() ? "true" : "false")); 1682 stream.writeCharacters(url().toDisplayString()); 1683 stream.writeEndElement(); 1684 } 1685 1686 QMap<QString, QString> LinkContent::toolTipInfos() 1687 { 1688 return { 1689 { i18n("Target"), m_url.toDisplayString() } 1690 }; 1691 } 1692 1693 int LinkContent::zoneAt(const QPointF &pos) 1694 { 1695 return (m_linkDisplayItem.linkDisplay().iconButtonAt(pos) ? 0 : Note::Custom0); 1696 } 1697 1698 QRectF LinkContent::zoneRect(int zone, const QPointF & /*pos*/) 1699 { 1700 QRectF linkRect = m_linkDisplayItem.linkDisplay().iconButtonRect(); 1701 1702 if (zone == Note::Custom0) 1703 return QRectF(linkRect.width(), 0, note()->width(), note()->height()); // Too wide and height, but it will be clipped by Note::zoneRect() 1704 else if (zone == Note::Content) 1705 return linkRect; 1706 else 1707 return QRectF(); 1708 } 1709 1710 QString LinkContent::zoneTip(int zone) 1711 { 1712 return (zone == Note::Custom0 ? i18n("Open this link") : QString()); 1713 } 1714 1715 Qt::CursorShape LinkContent::cursorFromZone(int zone) const 1716 { 1717 if (zone == Note::Custom0) 1718 return Qt::PointingHandCursor; 1719 return Qt::ArrowCursor; 1720 } 1721 1722 QString LinkContent::statusBarMessage(int zone) 1723 { 1724 if (zone == Note::Custom0 || zone == Note::Content) 1725 return m_url.toDisplayString(); 1726 else 1727 return QString(); 1728 } 1729 1730 QUrl LinkContent::urlToOpen(bool /*with*/) 1731 { 1732 return NoteFactory::filteredURL(url()); // KURIFilter::self()->filteredURI(url()); 1733 } 1734 1735 QString LinkContent::messageWhenOpening(OpenMessage where) 1736 { 1737 if (url().isEmpty()) 1738 return i18n("Link have no URL to open."); 1739 1740 switch (where) { 1741 case OpenOne: 1742 return i18n("Opening link target..."); 1743 case OpenSeveral: 1744 return i18n("Opening link targets..."); 1745 case OpenOneWith: 1746 return i18n("Opening link target with..."); 1747 case OpenSeveralWith: 1748 return i18n("Opening link targets with..."); 1749 case OpenOneWithDialog: 1750 return i18n("Open link target with:"); 1751 case OpenSeveralWithDialog: 1752 return i18n("Open link targets with:"); 1753 default: 1754 return QString(); 1755 } 1756 } 1757 1758 void LinkContent::setLink(const QUrl &url, const QString &title, const QString &icon, bool autoTitle, bool autoIcon) 1759 { 1760 m_autoTitle = autoTitle; 1761 m_autoIcon = autoIcon; 1762 m_url = NoteFactory::filteredURL(url); // KURIFilter::self()->filteredURI(url); 1763 m_title = (autoTitle ? NoteFactory::titleForURL(m_url) : title); 1764 m_icon = (autoIcon ? NoteFactory::iconForURL(m_url) : icon); 1765 1766 LinkLook *look = LinkLook::lookForURL(m_url); 1767 if (look->previewEnabled()) 1768 m_linkDisplayItem.linkDisplay().setLink(m_title, m_icon, look, note()->font()); 1769 else 1770 m_linkDisplayItem.linkDisplay().setLink(m_title, m_icon, QPixmap(), look, note()->font()); 1771 startFetchingUrlPreview(); 1772 if (autoTitle) 1773 startFetchingLinkTitle(); 1774 contentChanged(m_linkDisplayItem.linkDisplay().minWidth()); 1775 } 1776 1777 void LinkContent::linkLookChanged() 1778 { 1779 fontChanged(); 1780 } 1781 1782 void LinkContent::newPreview(const KFileItem &, const QPixmap &preview) 1783 { 1784 LinkLook *linkLook = LinkLook::lookForURL(url()); 1785 m_linkDisplayItem.linkDisplay().setLink(title(), icon(), (linkLook->previewEnabled() ? preview : QPixmap()), linkLook, note()->font()); 1786 contentChanged(m_linkDisplayItem.linkDisplay().minWidth()); 1787 } 1788 1789 void LinkContent::removePreview(const KFileItem &ki) 1790 { 1791 newPreview(ki, QPixmap()); 1792 } 1793 1794 // QHttp slots for getting link title 1795 void LinkContent::httpReadyRead() 1796 { 1797 if (!m_acceptingData) 1798 return; 1799 1800 // Check for availability 1801 qint64 bytesAvailable = m_reply->bytesAvailable(); 1802 if (bytesAvailable <= 0) 1803 return; 1804 1805 QByteArray buf = m_reply->read(bytesAvailable); 1806 m_httpBuff.append(buf); 1807 1808 // Stop at 10k bytes 1809 if (m_httpBuff.length() > 10000) { 1810 m_acceptingData = false; 1811 m_reply->abort(); 1812 endFetchingLinkTitle(); 1813 } 1814 } 1815 1816 void LinkContent::httpDone(QNetworkReply *reply) 1817 { 1818 if (m_acceptingData) { 1819 m_acceptingData = false; 1820 endFetchingLinkTitle(); 1821 } 1822 1823 // If all done, close and delete the reply. 1824 reply->deleteLater(); 1825 } 1826 1827 void LinkContent::startFetchingLinkTitle() 1828 { 1829 QUrl newUrl = this->url(); 1830 1831 // If this is not an HTTP request, just ignore it. 1832 if (newUrl.scheme() != QLatin1String("http") && newUrl.scheme() != QLatin1String("https")) { 1833 return; 1834 } 1835 1836 // If we have no access_manager, create one. 1837 if (m_access_manager == nullptr) { 1838 m_access_manager = new QNetworkAccessManager(this); 1839 connect(m_access_manager, &QNetworkAccessManager::finished, this, &LinkContent::httpDone); 1840 } 1841 1842 // If no explicit port, default to port 80. 1843 if (newUrl.port() == 0) { 1844 newUrl.setPort(80); 1845 } 1846 1847 // If no path or query part, default to / 1848 if ((newUrl.path() + newUrl.query()).isEmpty()) { 1849 newUrl = QUrl::fromLocalFile("/"); 1850 } 1851 1852 // Issue request 1853 m_reply = m_access_manager->get(QNetworkRequest(newUrl)); 1854 m_acceptingData = true; 1855 connect(m_reply, &QNetworkReply::readyRead, this, &LinkContent::httpReadyRead); 1856 } 1857 1858 // Code duplicated from FileContent::startFetchingUrlPreview() 1859 void LinkContent::startFetchingUrlPreview() 1860 { 1861 QUrl url = this->url(); 1862 LinkLook *linkLook = LinkLook::lookForURL(this->url()); 1863 1864 // delete m_previewJob; 1865 if (!url.isEmpty() && linkLook->previewSize() > 0) { 1866 KFileItem itemUrl(NoteFactory::filteredURL(url)); // KURIFilter::self()->filteredURI(url); 1867 m_previewJob = KIO::filePreview({itemUrl}, QSize(linkLook->previewSize(), linkLook->previewSize()), nullptr); 1868 m_previewJob->setOverlayIconSize(linkLook->iconSize()); 1869 connect(m_previewJob, SIGNAL(gotPreview(const KFileItem &, const QPixmap &)), this, SLOT(newPreview(const KFileItem &, const QPixmap &))); 1870 connect(m_previewJob, SIGNAL(failed(const KFileItem &)), this, SLOT(removePreview(const KFileItem &))); 1871 } 1872 } 1873 1874 void LinkContent::endFetchingLinkTitle() 1875 { 1876 if (m_httpBuff.length() > 0) { 1877 decodeHtmlTitle(); 1878 m_httpBuff.clear(); 1879 } else 1880 DEBUG_WIN << "LinkContent: empty buffer on endFetchingLinkTitle for " + m_url.toString(); 1881 } 1882 1883 void LinkContent::exportToHTML(HTMLExporter *exporter, int indent) 1884 { 1885 QString linkTitle = title(); 1886 1887 // TODO: 1888 // // Append address (useful for print version of the page/basket): 1889 // if (exportData.formatForImpression && (!autoTitle() && title() != NoteFactory::titleForURL(url().toDisplayString()))) { 1890 // // The address is on a new line, unless title is empty (empty lines was replaced by ): 1891 // if (linkTitle == " "/*" "*/) 1892 // linkTitle = url().toDisplayString()/*QString()*/; 1893 // else 1894 // linkTitle = linkTitle + " <" + url().toDisplayString() + ">"/*+ "<br>"*/; 1895 // //linkTitle += "<i>" + url().toDisplayString() + "</i>"; 1896 // } 1897 1898 QUrl linkURL; 1899 /* 1900 QFileInfo fInfo(url().path()); 1901 // DEBUG_WIN << url().path() 1902 // << "IsFile:" + QString::number(fInfo.isFile()) 1903 // << "IsDir:" + QString::number(fInfo.isDir()); 1904 if (exportData.embedLinkedFiles && fInfo.isFile()) { 1905 // DEBUG_WIN << "Embed file"; 1906 linkURL = exportData.dataFolderName + BasketScene::copyFile(url().path(), exportData.dataFolderPath, true); 1907 } else if (exportData.embedLinkedFolders && fInfo.isDir()) { 1908 // DEBUG_WIN << "Embed folder"; 1909 linkURL = exportData.dataFolderName + BasketScene::copyFile(url().path(), exportData.dataFolderPath, true); 1910 } else { 1911 // DEBUG_WIN << "Embed LINK"; 1912 */ 1913 linkURL = url(); 1914 /* 1915 } 1916 */ 1917 1918 QString spaces; 1919 exporter->stream << m_linkDisplayItem.linkDisplay().toHtml(exporter, linkURL, linkTitle).replace("\n", '\n' + spaces.fill(' ', indent + 1)); 1920 } 1921 1922 /** class CrossReferenceContent: 1923 */ 1924 1925 CrossReferenceContent::CrossReferenceContent(Note *parent, const QUrl &url, const QString &title, const QString &icon) 1926 : NoteContent(parent, NoteType::CrossReference) 1927 , m_linkDisplayItem(parent) 1928 { 1929 this->setCrossReference(url, title, icon); 1930 if (parent) 1931 parent->addToGroup(&m_linkDisplayItem); 1932 } 1933 1934 CrossReferenceContent::~CrossReferenceContent() 1935 { 1936 if (note()) 1937 note()->removeFromGroup(&m_linkDisplayItem); 1938 } 1939 1940 qreal CrossReferenceContent::setWidthAndGetHeight(qreal width) 1941 { 1942 m_linkDisplayItem.linkDisplay().setWidth(width); 1943 return m_linkDisplayItem.linkDisplay().height(); 1944 } 1945 1946 void CrossReferenceContent::saveToNode(QXmlStreamWriter &stream) 1947 { 1948 stream.writeStartElement("content"); 1949 stream.writeAttribute("title", title()); 1950 stream.writeAttribute("icon", icon()); 1951 stream.writeCharacters(url().toDisplayString()); 1952 stream.writeEndElement(); 1953 } 1954 1955 QMap<QString, QString> CrossReferenceContent::toolTipInfos() 1956 { 1957 return { 1958 { i18n("Target"), m_url.toDisplayString() } 1959 }; 1960 } 1961 1962 int CrossReferenceContent::zoneAt(const QPointF &pos) 1963 { 1964 return (m_linkDisplayItem.linkDisplay().iconButtonAt(pos) ? 0 : Note::Custom0); 1965 } 1966 1967 QRectF CrossReferenceContent::zoneRect(int zone, const QPointF & /*pos*/) 1968 { 1969 QRectF linkRect = m_linkDisplayItem.linkDisplay().iconButtonRect(); 1970 1971 if (zone == Note::Custom0) 1972 return QRectF(linkRect.width(), 0, note()->width(), note()->height()); // Too wide and height, but it will be clipped by Note::zoneRect() 1973 else if (zone == Note::Content) 1974 return linkRect; 1975 else 1976 return QRectF(); 1977 } 1978 1979 QString CrossReferenceContent::zoneTip(int zone) 1980 { 1981 return (zone == Note::Custom0 ? i18n("Open this link") : QString()); 1982 } 1983 1984 Qt::CursorShape CrossReferenceContent::cursorFromZone(int zone) const 1985 { 1986 if (zone == Note::Custom0) 1987 return Qt::PointingHandCursor; 1988 return Qt::ArrowCursor; 1989 } 1990 1991 QString CrossReferenceContent::statusBarMessage(int zone) 1992 { 1993 if (zone == Note::Custom0 || zone == Note::Content) 1994 return i18n("Link to %1", this->title()); 1995 else 1996 return QString(); 1997 } 1998 1999 QUrl CrossReferenceContent::urlToOpen(bool /*with*/) 2000 { 2001 return m_url; 2002 } 2003 2004 QString CrossReferenceContent::messageWhenOpening(OpenMessage where) 2005 { 2006 if (url().isEmpty()) 2007 return i18n("Link has no basket to open."); 2008 2009 switch (where) { 2010 case OpenOne: 2011 return i18n("Opening basket..."); 2012 default: 2013 return QString(); 2014 } 2015 } 2016 2017 void CrossReferenceContent::setLink(const QUrl &url, const QString &title, const QString &icon) 2018 { 2019 this->setCrossReference(url, title, icon); 2020 } 2021 2022 void CrossReferenceContent::setCrossReference(const QUrl &url, const QString &title, const QString &icon) 2023 { 2024 m_url = url; 2025 m_title = (title.isEmpty() ? url.url() : title); 2026 m_icon = icon; 2027 2028 LinkLook *look = LinkLook::crossReferenceLook; 2029 m_linkDisplayItem.linkDisplay().setLink(m_title, m_icon, look, note()->font()); 2030 2031 contentChanged(m_linkDisplayItem.linkDisplay().minWidth()); 2032 } 2033 2034 void CrossReferenceContent::linkLookChanged() 2035 { 2036 fontChanged(); 2037 } 2038 2039 void CrossReferenceContent::exportToHTML(HTMLExporter *exporter, int /*indent*/) 2040 { 2041 QString url = m_url.url(); 2042 QString title; 2043 2044 if (url.startsWith(QLatin1String("basket://"))) 2045 url = url.mid(9, url.length() - 9); 2046 if (url.endsWith('/')) 2047 url = url.left(url.length() - 1); 2048 2049 BasketScene *basket = Global::bnpView->basketForFolderName(url); 2050 2051 if (!basket) 2052 title = "unknown basket"; 2053 else 2054 title = basket->basketName(); 2055 2056 // if the basket we're trying to link to is the basket that was exported then 2057 // we have to use a special way to refer to it for the links. 2058 if (basket == exporter->exportedBasket) 2059 url = "../../" + exporter->fileName; 2060 else { 2061 // if we're in the exported basket then the links have to include 2062 // the sub directories. 2063 if (exporter->currentBasket == exporter->exportedBasket) 2064 url.prepend(exporter->basketsFolderName); 2065 url.append(".html"); 2066 } 2067 2068 QString linkIcon = exporter->iconsFolderName + exporter->copyIcon(m_icon, LinkLook::crossReferenceLook->iconSize()); 2069 linkIcon = QString("<img src=\"%1\" alt=\"\">").arg(linkIcon); 2070 2071 exporter->stream << QString("<a href=\"%1\">%2 %3</a>").arg(url, linkIcon, title); 2072 } 2073 2074 /** class LauncherContent: 2075 */ 2076 2077 LauncherContent::LauncherContent(Note *parent, const QString &fileName) 2078 : NoteContent(parent, NoteType::Launcher, fileName) 2079 , m_linkDisplayItem(parent) 2080 { 2081 basket()->addWatchedFile(fullPath()); 2082 LauncherContent::loadFromFile(/*lazyLoad=*/false); 2083 if (parent) { 2084 parent->addToGroup(&m_linkDisplayItem); 2085 m_linkDisplayItem.setPos(parent->contentX(), Note::NOTE_MARGIN); 2086 } 2087 } 2088 2089 LauncherContent::~LauncherContent() 2090 { 2091 if (note()) 2092 note()->removeFromGroup(&m_linkDisplayItem); 2093 } 2094 2095 qreal LauncherContent::setWidthAndGetHeight(qreal width) 2096 { 2097 m_linkDisplayItem.linkDisplay().setWidth(width); 2098 return m_linkDisplayItem.linkDisplay().height(); 2099 } 2100 2101 bool LauncherContent::loadFromFile(bool /*lazyLoad*/) // TODO: saveToFile() ?? Is it possible? 2102 { 2103 DEBUG_WIN << "Loading LauncherContent From " + basket()->folderName() + fileName(); 2104 KService service(fullPath()); 2105 setLauncher(service.name(), service.icon(), service.exec()); 2106 return true; 2107 } 2108 2109 QMap<QString, QString> LauncherContent::toolTipInfos() 2110 { 2111 QMap<QString, QString> toolTip; 2112 KService service(fullPath()); 2113 2114 QString exec = service.exec(); 2115 if (service.terminal()) 2116 exec = i18n("%1 <i>(run in terminal)</i>", exec); 2117 2118 if (!service.comment().isEmpty() && service.comment() != service.name()) { 2119 toolTip.insert(i18n("Comment"), service.comment()); 2120 } 2121 toolTip.insert(i18n("Command"), exec); 2122 2123 return toolTip; 2124 } 2125 2126 int LauncherContent::zoneAt(const QPointF &pos) 2127 { 2128 return (m_linkDisplayItem.linkDisplay().iconButtonAt(pos) ? 0 : Note::Custom0); 2129 } 2130 2131 QRectF LauncherContent::zoneRect(int zone, const QPointF & /*pos*/) 2132 { 2133 QRectF linkRect = m_linkDisplayItem.linkDisplay().iconButtonRect(); 2134 2135 if (zone == Note::Custom0) 2136 return QRectF(linkRect.width(), 0, note()->width(), note()->height()); // Too wide and height, but it will be clipped by Note::zoneRect() 2137 else if (zone == Note::Content) 2138 return linkRect; 2139 else 2140 return QRectF(); 2141 } 2142 2143 QString LauncherContent::zoneTip(int zone) 2144 { 2145 return (zone == Note::Custom0 ? i18n("Launch this application") : QString()); 2146 } 2147 2148 Qt::CursorShape LauncherContent::cursorFromZone(int zone) const 2149 { 2150 if (zone == Note::Custom0) 2151 return Qt::PointingHandCursor; 2152 return Qt::ArrowCursor; 2153 } 2154 2155 QUrl LauncherContent::urlToOpen(bool with) 2156 { 2157 if (KService(fullPath()).exec().isEmpty()) 2158 return QUrl(); 2159 2160 return (with ? QUrl() : QUrl::fromLocalFile(fullPath())); // Can open the application, but not with another application :-) 2161 } 2162 2163 QString LauncherContent::messageWhenOpening(OpenMessage where) 2164 { 2165 if (KService(fullPath()).exec().isEmpty()) 2166 return i18n("The launcher have no command to run."); 2167 2168 switch (where) { 2169 case OpenOne: 2170 return i18n("Launching application..."); 2171 case OpenSeveral: 2172 return i18n("Launching applications..."); 2173 case OpenOneWith: 2174 case OpenSeveralWith: 2175 case OpenOneWithDialog: 2176 case OpenSeveralWithDialog: // TODO: "Open this application with this file as parameter"? 2177 default: 2178 return QString(); 2179 } 2180 } 2181 2182 void LauncherContent::setLauncher(const QString &name, const QString &icon, const QString &exec) 2183 { 2184 m_name = name; 2185 m_icon = icon; 2186 m_exec = exec; 2187 2188 m_linkDisplayItem.linkDisplay().setLink(name, icon, LinkLook::launcherLook, note()->font()); 2189 contentChanged(m_linkDisplayItem.linkDisplay().minWidth()); 2190 } 2191 2192 void LauncherContent::exportToHTML(HTMLExporter *exporter, int indent) 2193 { 2194 QString spaces; 2195 QString fileName = exporter->copyFile(fullPath(), /*createIt=*/true); 2196 exporter->stream << m_linkDisplayItem.linkDisplay().toHtml(exporter, QUrl::fromLocalFile(exporter->dataFolderName + fileName), QString()).replace("\n", '\n' + spaces.fill(' ', indent + 1)); 2197 } 2198 2199 /** class ColorItem: 2200 */ 2201 const int ColorItem::RECT_MARGIN = 2; 2202 2203 ColorItem::ColorItem(Note *parent, const QColor &color) 2204 : QGraphicsItem(parent) 2205 , m_note(parent) 2206 { 2207 ColorItem::setColor(color); 2208 } 2209 2210 void ColorItem::setColor(const QColor &color) 2211 { 2212 m_color = color; 2213 m_textRect = QFontMetrics(m_note->font()).boundingRect(m_color.name()); 2214 } 2215 2216 QRectF ColorItem::boundingRect() const 2217 { 2218 qreal rectHeight = (m_textRect.height() + 2) * 3 / 2; 2219 qreal rectWidth = rectHeight * 14 / 10; // 1.4 times the height, like A4 papers. 2220 return QRectF(0, 0, rectWidth + RECT_MARGIN + m_textRect.width() + RECT_MARGIN, rectHeight); 2221 } 2222 2223 void ColorItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) 2224 { 2225 QRectF boundingRect = this->boundingRect(); 2226 qreal rectHeight = (m_textRect.height() + 2) * 3 / 2; 2227 qreal rectWidth = rectHeight * 14 / 10; // 1.4 times the height, like A4 papers. 2228 2229 // FIXME: Duplicate from CommonColorSelector::drawColorRect: 2230 // Fill: 2231 painter->fillRect(1, 1, rectWidth - 2, rectHeight - 2, color()); 2232 // Stroke: 2233 QColor stroke = color().darker(125); 2234 painter->setPen(stroke); 2235 painter->drawLine(1, 0, rectWidth - 2, 0); 2236 painter->drawLine(0, 1, 0, rectHeight - 2); 2237 painter->drawLine(1, rectHeight - 1, rectWidth - 2, rectHeight - 1); 2238 painter->drawLine(rectWidth - 1, 1, rectWidth - 1, rectHeight - 2); 2239 // Round corners: 2240 painter->setPen(Tools::mixColor(color(), stroke)); 2241 painter->drawPoint(1, 1); 2242 painter->drawPoint(1, rectHeight - 2); 2243 painter->drawPoint(rectWidth - 2, rectHeight - 2); 2244 painter->drawPoint(rectWidth - 2, 1); 2245 2246 // Draw the text: 2247 painter->setFont(m_note->font()); 2248 painter->setPen(m_note->palette().color(QPalette::Active, QPalette::WindowText)); 2249 painter->drawText(rectWidth + RECT_MARGIN, 0, m_textRect.width(), boundingRect.height(), Qt::AlignLeft | Qt::AlignVCenter, color().name()); 2250 } 2251 2252 /** class ColorContent: 2253 */ 2254 2255 ColorContent::ColorContent(Note *parent, const QColor &color) 2256 : NoteContent(parent, NoteType::Color) 2257 , m_colorItem(parent, color) 2258 { 2259 if (parent) { 2260 parent->addToGroup(&m_colorItem); 2261 m_colorItem.setPos(parent->contentX(), Note::NOTE_MARGIN); 2262 } 2263 } 2264 2265 ColorContent::~ColorContent() 2266 { 2267 if (note()) 2268 note()->removeFromGroup(&m_colorItem); 2269 } 2270 2271 qreal ColorContent::setWidthAndGetHeight(qreal /*width*/) // We do not need width because we can't word-break, and width is always >= minWidth() 2272 { 2273 return m_colorItem.boundingRect().height(); 2274 } 2275 2276 void ColorContent::saveToNode(QXmlStreamWriter &stream) 2277 { 2278 stream.writeStartElement("content"); 2279 stream.writeCharacters(color().name()); 2280 stream.writeEndElement(); 2281 } 2282 2283 QMap<QString, QString> ColorContent::toolTipInfos() 2284 { 2285 QMap<QString, QString> toolTip; 2286 2287 int hue, saturation, value; 2288 color().getHsv(&hue, &saturation, &value); 2289 2290 toolTip.insert(i18nc("RGB Colorspace: Red/Green/Blue", "RGB"), 2291 i18n("<i>Red</i>: %1, <i>Green</i>: %2, <i>Blue</i>: %3,", QString::number(color().red()), QString::number(color().green()), QString::number(color().blue()))); 2292 toolTip.insert(i18nc("HSV Colorspace: Hue/Saturation/Value", "HSV"), 2293 i18n("<i>Hue</i>: %1, <i>Saturation</i>: %2, <i>Value</i>: %3,", QString::number(hue), QString::number(saturation), QString::number(value))); 2294 2295 const QString colorName = Tools::cssColorName(color().name()); 2296 if (!colorName.isEmpty()) { 2297 toolTip.insert(i18n("CSS Color Name"), colorName); 2298 } 2299 2300 toolTip.insert(i18n("Is Web Color"), Tools::isWebColor(color()) ? i18n("Yes") : i18n("No")); 2301 2302 return toolTip; 2303 } 2304 2305 void ColorContent::setColor(const QColor &color) 2306 { 2307 m_colorItem.setColor(color); 2308 contentChanged(m_colorItem.boundingRect().width()); 2309 } 2310 2311 void ColorContent::addAlternateDragObjects(QMimeData *dragObject) 2312 { 2313 dragObject->setColorData(color()); 2314 } 2315 2316 void ColorContent::exportToHTML(HTMLExporter *exporter, int /*indent*/) 2317 { 2318 // FIXME: Duplicate from setColor(): TODO: rectSize() 2319 QRectF textRect = QFontMetrics(note()->font()).boundingRect(color().name()); 2320 int rectHeight = (textRect.height() + 2) * 3 / 2; 2321 int rectWidth = rectHeight * 14 / 10; // 1.4 times the height, like A4 papers. 2322 2323 QString fileName = /*Tools::fileNameForNewFile(*/ QString("color_%1.png").arg(color().name().toLower().mid(1)) /*, exportData.iconsFolderPath)*/; 2324 QString fullPath = exporter->iconsFolderPath + fileName; 2325 QPixmap colorIcon(rectWidth, rectHeight); 2326 QPainter painter(&colorIcon); 2327 painter.setBrush(color()); 2328 painter.drawRoundedRect(0, 0, rectWidth, rectHeight, 2, 2); 2329 colorIcon.save(fullPath, "PNG"); 2330 QString iconHtml = QString("<img src=\"%1\" width=\"%2\" height=\"%3\" alt=\"\">").arg(exporter->iconsFolderName + fileName, QString::number(colorIcon.width()), QString::number(colorIcon.height())); 2331 2332 exporter->stream << iconHtml + ' ' + color().name(); 2333 } 2334 2335 /** class UnknownItem: 2336 */ 2337 2338 const qreal UnknownItem::DECORATION_MARGIN = 2; 2339 2340 UnknownItem::UnknownItem(Note *parent) 2341 : QGraphicsItem(parent) 2342 , m_note(parent) 2343 { 2344 } 2345 2346 QRectF UnknownItem::boundingRect() const 2347 { 2348 return QRectF(0, 0, m_textRect.width() + 2 * DECORATION_MARGIN, m_textRect.height() + 2 * DECORATION_MARGIN); 2349 } 2350 2351 void UnknownItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) 2352 { 2353 QPalette palette = m_note->basket()->palette(); 2354 qreal width = boundingRect().width(); 2355 qreal height = boundingRect().height(); 2356 painter->setPen(palette.color(QPalette::Active, QPalette::WindowText)); 2357 2358 // Stroke: 2359 QColor stroke = Tools::mixColor(palette.color(QPalette::Active, QPalette::Background), palette.color(QPalette::Active, QPalette::WindowText)); 2360 painter->setPen(stroke); 2361 painter->drawLine(1, 0, width - 2, 0); 2362 painter->drawLine(0, 1, 0, height - 2); 2363 painter->drawLine(1, height - 1, width - 2, height - 1); 2364 painter->drawLine(width - 1, 1, width - 1, height - 2); 2365 // Round corners: 2366 painter->setPen(Tools::mixColor(palette.color(QPalette::Active, QPalette::Background), stroke)); 2367 painter->drawPoint(1, 1); 2368 painter->drawPoint(1, height - 2); 2369 painter->drawPoint(width - 2, height - 2); 2370 painter->drawPoint(width - 2, 1); 2371 2372 painter->setPen(palette.color(QPalette::Active, QPalette::WindowText)); 2373 painter->drawText(DECORATION_MARGIN, DECORATION_MARGIN, width - 2 * DECORATION_MARGIN, height - 2 * DECORATION_MARGIN, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextWordWrap, m_mimeTypes); 2374 } 2375 2376 void UnknownItem::setMimeTypes(QString mimeTypes) 2377 { 2378 m_mimeTypes = mimeTypes; 2379 m_textRect = QFontMetrics(m_note->font()).boundingRect(0, 0, 1, 500000, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, m_mimeTypes); 2380 } 2381 2382 void UnknownItem::setWidth(qreal width) 2383 { 2384 prepareGeometryChange(); 2385 m_textRect = QFontMetrics(m_note->font()).boundingRect(0, 0, width, 500000, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, m_mimeTypes); 2386 } 2387 2388 /** class UnknownContent: 2389 */ 2390 2391 UnknownContent::UnknownContent(Note *parent, const QString &fileName) 2392 : NoteContent(parent, NoteType::Unknown, fileName) 2393 , m_unknownItem(parent) 2394 { 2395 if (parent) { 2396 parent->addToGroup(&m_unknownItem); 2397 m_unknownItem.setPos(parent->contentX(), Note::NOTE_MARGIN); 2398 } 2399 2400 basket()->addWatchedFile(fullPath()); 2401 UnknownContent::loadFromFile(/*lazyLoad=*/false); 2402 } 2403 2404 UnknownContent::~UnknownContent() 2405 { 2406 if (note()) 2407 note()->removeFromGroup(&m_unknownItem); 2408 } 2409 2410 qreal UnknownContent::setWidthAndGetHeight(qreal width) 2411 { 2412 m_unknownItem.setWidth(width); 2413 return m_unknownItem.boundingRect().height(); 2414 } 2415 2416 bool UnknownContent::loadFromFile(bool /*lazyLoad*/) 2417 { 2418 DEBUG_WIN << "Loading UnknownContent From " + basket()->folderName() + fileName(); 2419 QString mimeTypes; 2420 QFile file(fullPath()); 2421 if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { 2422 QTextStream stream(&file); 2423 QString line; 2424 // Get the MIME-types names: 2425 do { 2426 if (!stream.atEnd()) { 2427 line = stream.readLine(); 2428 if (!line.isEmpty()) { 2429 if (mimeTypes.isEmpty()) 2430 mimeTypes += line; 2431 else 2432 mimeTypes += QString("\n") + line; 2433 } 2434 } 2435 } while (!line.isEmpty() && !stream.atEnd()); 2436 file.close(); 2437 } 2438 2439 m_unknownItem.setMimeTypes(mimeTypes); 2440 contentChanged(m_unknownItem.boundingRect().width() + 1); 2441 2442 return true; 2443 } 2444 2445 void UnknownContent::addAlternateDragObjects(QMimeData *dragObject) 2446 { 2447 QFile file(fullPath()); 2448 if (file.open(QIODevice::ReadOnly)) { 2449 QDataStream stream(&file); 2450 // Get the MIME types names: 2451 QStringList mimes; 2452 QString line; 2453 do { 2454 if (!stream.atEnd()) { 2455 stream >> line; 2456 if (!line.isEmpty()) 2457 mimes.append(line); 2458 } 2459 } while (!line.isEmpty() && !stream.atEnd()); 2460 // Add the streams: 2461 quint64 size; // TODO: It was quint32 in version 0.5.0 ! 2462 QByteArray *array; 2463 for (int i = 0; i < mimes.count(); ++i) { 2464 // Get the size: 2465 stream >> size; 2466 // Allocate memory to retrieve size bytes and store them: 2467 array = new QByteArray; 2468 array->resize(size); 2469 stream.readRawData(array->data(), size); 2470 // Creata and add the QDragObject: 2471 dragObject->setData(mimes.at(i).toLatin1(), *array); 2472 delete array; // FIXME: Should we? 2473 } 2474 file.close(); 2475 } 2476 } 2477 2478 void UnknownContent::exportToHTML(HTMLExporter *exporter, int indent) 2479 { 2480 QString spaces; 2481 exporter->stream << "<div class=\"unknown\">" << mimeTypes().replace("\n", '\n' + spaces.fill(' ', indent + 1 + 1)) << "</div>"; 2482 } 2483 2484 void LinkContent::decodeHtmlTitle() 2485 { 2486 KEncodingProber prober; 2487 prober.feed(m_httpBuff); 2488 2489 // Fallback scheme: KEncodingProber - QTextCodec::codecForHtml - UTF-8 2490 QTextCodec *textCodec; 2491 if (prober.confidence() > 0.5) 2492 textCodec = QTextCodec::codecForName(prober.encoding()); 2493 else 2494 textCodec = QTextCodec::codecForHtml(m_httpBuff, QTextCodec::codecForName("utf-8")); 2495 2496 QString httpBuff = textCodec->toUnicode(m_httpBuff.data(), m_httpBuff.size()); 2497 2498 // todo: this should probably strip odd html tags like etc 2499 QRegExp reg("<title>[\\s]*( )?([^<]+)[\\s]*</title>", Qt::CaseInsensitive); 2500 reg.setMinimal(true); 2501 // qDebug() << *m_httpBuff << " bytes: " << bytes_read; 2502 2503 if (reg.indexIn(httpBuff) >= 0) { 2504 m_title = reg.cap(2); 2505 m_autoTitle = false; 2506 setEdited(); 2507 2508 // refresh the title 2509 setLink(url(), title(), icon(), autoTitle(), autoIcon()); 2510 } 2511 } 2512 2513 #include "moc_notecontent.cpp"