diff --git a/plugins/adblock/CMakeLists.txt b/plugins/adblock/CMakeLists.txt index e0dc37dfd..7540168ed 100644 --- a/plugins/adblock/CMakeLists.txt +++ b/plugins/adblock/CMakeLists.txt @@ -1,18 +1,18 @@ ########### next target ############### add_definitions(-DTRANSLATION_DOMAIN=\"adblock\") set(adblock_PART_SRCS adblock.cpp adblockdialog.cpp ) add_library(adblock MODULE ${adblock_PART_SRCS}) -target_link_libraries(adblock KF5::Parts KF5::KHtml KF5::KCMUtils) +target_link_libraries(adblock KF5::Parts KF5::KHtml KF5::KCMUtils KF5::KDELibs4Support) install(TARGETS adblock DESTINATION ${KDE_INSTALL_PLUGINDIR} ) ########### install files ############### install( FILES plugin_adblock.desktop plugin_adblock.rc DESTINATION ${KDE_INSTALL_DATADIR}/khtml/kpartplugins ) diff --git a/plugins/adblock/adblock.cpp b/plugins/adblock/adblock.cpp index c98c4a6e2..2fc50980c 100644 --- a/plugins/adblock/adblock.cpp +++ b/plugins/adblock/adblock.cpp @@ -1,354 +1,355 @@ /* Copyright (c) 2008 Laurent Montel Copyright (C) 2006 Daniele Galdi This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* Project related */ #include "adblock.h" #include "adblockdialog.h" /* Kde related */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include using namespace DOM; #include #include #include K_PLUGIN_FACTORY(AdBlockFactory, registerPlugin< AdBlock >();) K_EXPORT_PLUGIN(AdBlockFactory("adblock")) AdBlock::AdBlock(QObject *parent, const QVariantList & /*args*/) : Plugin(parent), m_menu(0), m_elements(0) { m_part = dynamic_cast(parent); if (!m_part) { kDebug() << "couldn't get KHTMLPart"; return; } m_menu = new KActionMenu(KIcon("preferences-web-browser-adblock"), i18n("Adblock"), actionCollection()); actionCollection()->addAction("action adblock", m_menu); m_menu->setDelayed(false); QAction *a = actionCollection()->addAction("show_elements"); a->setText(i18n("Show Blockable Elements...")); connect(a, SIGNAL(triggered()), this, SLOT(slotConfigure())); m_menu->addAction(a); a = actionCollection()->addAction("configure"); a->setText(i18n("Configure Filters...")); connect(a, SIGNAL(triggered()), this, SLOT(showKCModule())); m_menu->addAction(a); a = actionCollection()->addAction("separator"); a->setSeparator(true); m_menu->addAction(a); a = actionCollection()->addAction("disable_for_this_page"); a->setText(i18n("No blocking for this page")); connect(a, SIGNAL(triggered()), this, SLOT(slotDisableForThisPage())); m_menu->addAction(a); a = actionCollection()->addAction("disable_for_this_site"); a->setText(i18n("No blocking for this site")); connect(a, SIGNAL(triggered()), this, SLOT(slotDisableForThisSite())); m_menu->addAction(a); connect(m_part, SIGNAL(completed()), this, SLOT(initLabel())); } AdBlock::~AdBlock() { KParts::StatusBarExtension *statusBarEx = KParts::StatusBarExtension::childObject(m_part); if (statusBarEx && m_label) { statusBarEx->removeStatusBarItem(m_label.data()); } delete m_label.data(); m_label.clear(); delete m_menu; m_menu = 0; delete m_elements; m_elements = 0; } void AdBlock::initLabel() { if (m_label) { return; } KParts::StatusBarExtension *statusBarEx = KParts::StatusBarExtension::childObject(m_part); if (!statusBarEx) { kDebug() << "couldn't get KParts::StatusBarExtension"; return; } KUrlLabel *label = new KUrlLabel(statusBarEx->statusBar()); KIconLoader *loader = KIconLoader::global(); label->setFixedHeight(loader->currentSize(KIconLoader::Small)); label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); label->setUseCursor(false); label->setPixmap(loader->loadIcon("preferences-web-browser-adblock", KIconLoader::Small)); statusBarEx->addStatusBarItem(label, 0, false); connect(label, SIGNAL(leftClickedUrl()), this, SLOT(slotConfigure())); connect(label, SIGNAL(rightClickedUrl()), this, SLOT(contextMenu())); m_label = label; } void AdBlock::disableForUrl(KUrl url) { url.setQuery(QString()); url.setRef(QString()); KHTMLSettings *settings = const_cast(m_part->settings()); settings->addAdFilter("@@" + url.url()); } void AdBlock::slotDisableForThisPage() { disableForUrl(m_part->toplevelURL().url()); } void AdBlock::slotDisableForThisSite() { KUrl u(m_part->toplevelURL().url()); u.setPath("/*"); disableForUrl(u); } void AdBlock::slotConfigure() { if (!m_part->settings()->isAdFilterEnabled()) { KMessageBox::error(0, i18n("Please enable Konqueror's Adblock"), i18nc("@title:window", "Adblock disabled")); return; } m_elements = new AdElementList; fillBlockableElements(); AdBlockDlg *dlg = new AdBlockDlg(m_part->widget(), m_elements, m_part); connect(dlg, SIGNAL(notEmptyFilter(QString)), this, SLOT(addAdFilter(QString))); connect(dlg, SIGNAL(configureFilters()), this, SLOT(showKCModule())); dlg->exec(); delete dlg; } void AdBlock::showKCModule() { KCMultiDialog *dialogue = new KCMultiDialog(m_part->widget()); dialogue->addModule("khtml_filter"); connect(dialogue, SIGNAL(cancelClicked()), dialogue, SLOT(delayedDestruct())); connect(dialogue, SIGNAL(closeClicked()), dialogue, SLOT(delayedDestruct())); dialogue->show(); } void AdBlock::contextMenu() { m_menu->menu()->exec(QCursor::pos()); } void AdBlock::fillBlockableElements() { fillWithHtmlTag("script", "src", i18n("script")); fillWithHtmlTag("embed", "src", i18n("object")); fillWithHtmlTag("object", "src", i18n("object")); // TODO: iframe's are not blocked by KHTML yet fillWithHtmlTag("iframe", "src", i18n("frame")); fillWithImages(); updateFilters(); } void AdBlock::fillWithImages() { HTMLDocument htmlDoc = m_part->htmlDocument(); HTMLCollection images = htmlDoc.images(); for (unsigned int i = 0; i < images.length(); i++) { HTMLImageElement image = static_cast(images.item(i)); DOMString src = image.src(); QString url = htmlDoc.completeURL(src).string(); if (!url.isEmpty() && url != m_part->baseURL().url()) { AdElement element(url, i18n("image"), "IMG", false, image); if (!m_elements->contains(element)) { m_elements->append(element); } } } } void AdBlock::fillWithHtmlTag(const DOMString &tagName, const DOMString &attrName, const QString &category) { Document doc = m_part->document(); NodeList nodes = doc.getElementsByTagName(tagName); for (unsigned int i = 0; i < nodes.length(); i++) { Node node = nodes.item(i); Node attr = node.attributes().getNamedItem(attrName); DOMString src = attr.nodeValue(); if (src.isNull()) { continue; } QString url = doc.completeURL(src).string(); if (!url.isEmpty() && url != m_part->baseURL().url()) { AdElement element(url, category, tagName.string().toUpper(), false, attr); if (!m_elements->contains(element)) { m_elements->append(element); } } } } void AdBlock::addAdFilter(const QString &url) { //FIXME hackish KHTMLSettings *settings = const_cast(m_part->settings()); settings->addAdFilter(url); updateFilters(); } void AdBlock::updateFilters() { const KHTMLSettings *settings = m_part->settings(); AdElementList::iterator it; for (it = m_elements->begin(); it != m_elements->end(); ++it) { AdElement &element = (*it); bool isWhitelist; QString filter = settings->adFilteredBy(element.url(), &isWhitelist); if (!filter.isEmpty()) { if (!isWhitelist) { element.setBlocked(true); element.setBlockedBy(i18n("Blocked by %1", filter)); } else { element.setBlockedBy(i18n("Allowed by %1", filter)); } } } } // ---------------------------------------------------------------------------- AdElement::AdElement() : m_blocked(false) {} AdElement::AdElement(const QString &url, const QString &category, const QString &type, bool blocked, const DOM::Node &node) : m_url(url), m_category(category), m_type(type), m_blocked(blocked), m_node(node) {} AdElement &AdElement::operator=(const AdElement &obj) { m_blocked = obj.m_blocked; m_blockedBy = obj.m_blockedBy; m_url = obj.m_url; m_category = obj.m_category; m_type = obj.m_type; m_node = obj.m_node; return *this; } bool AdElement::operator==(const AdElement &obj) { return m_url == obj.m_url; } bool AdElement::isBlocked() const { return m_blocked; } QString AdElement::blockedBy() const { return m_blockedBy; } void AdElement::setBlockedBy(const QString &by) { m_blockedBy = by; } void AdElement::setBlocked(bool blocked) { m_blocked = blocked; } QString AdElement::url() const { return m_url; } QString AdElement::category() const { return m_category; } QString AdElement::type() const { return m_type; } DOM::Node AdElement::node() const { return m_node; } #include "adblock.moc" diff --git a/plugins/adblock/adblockdialog.cpp b/plugins/adblock/adblockdialog.cpp index 2b23c4f29..74e771137 100644 --- a/plugins/adblock/adblockdialog.cpp +++ b/plugins/adblock/adblockdialog.cpp @@ -1,328 +1,329 @@ /* Copyright (c) 2008 Laurent Montel Copyright (C) 2006 Daniele Galdi This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "adblock.h" #include "adblockdialog.h" +#include "adblock.h" -#include -#include -#include +#include +#include +#include +#include #include #include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include #include #include #include // ---------------------------------------------------------------------------- class ListViewItem : public QTreeWidgetItem { public: ListViewItem(QTreeWidget *listView, const QStringList &lst, const AdElement *element) : QTreeWidgetItem(listView, lst), m_element(element), m_blocked(false) {}; bool isBlocked() const { return (m_blocked); }; void setBlocked(bool blocked); void setBlockedBy(const QString &reason); void setNode(const DOM::Node &node); DOM::Node node()const { return (m_node); } const AdElement *element() const { return (m_element); } private: const AdElement *m_element; bool m_blocked; DOM::Node m_node; }; void ListViewItem::setBlocked(bool blocked) { m_blocked = blocked; setData(0, Qt::TextColorRole, (blocked ? Qt::red : Qt::black)); QFont itemFont = font(0); itemFont.setItalic(blocked); itemFont.setBold(blocked); setData(0, Qt::FontRole, itemFont); } void ListViewItem::setBlockedBy(const QString &reason) { setToolTip(0, reason); } void ListViewItem::setNode(const DOM::Node &node) { m_node = node; } // ---------------------------------------------------------------------------- AdBlockDlg::AdBlockDlg(QWidget *parent, const AdElementList *elements, KHTMLPart *part) : KDialog(parent), m_part(part) { setModal(true); setCaption(i18nc("@title:window", "Blockable items on this page")); setButtons(KDialog::User1 | KDialog::User2 | KDialog::Close); setDefaultButton(KDialog::User2); setButtonText(KDialog::User1, i18n("Configure Filters...")); setButtonIcon(KDialog::User1, KIcon("preferences-web-browser-adblock")); setButtonText(KDialog::User2, i18n("Add filter")); setButtonIcon(KDialog::User2, KStandardGuiItem::add().icon()); QWidget *page = new QWidget(this); setMainWidget(page); QVBoxLayout *layout = new QVBoxLayout(page); layout->setMargin(0); QLabel *l = new QLabel(i18n("Search:"), page); layout->addWidget(l); KTreeWidgetSearchLine *searchLine = new KTreeWidgetSearchLine(page); layout->addWidget(searchLine); l->setBuddy(searchLine); l = new QLabel(i18n("Blockable items:"), page); layout->addWidget(l); m_list = new QTreeWidget(page); m_list->setAllColumnsShowFocus(true); layout->addWidget(m_list); l->setBuddy(m_list); QStringList lstHeader; lstHeader << i18n("Source") << i18n("Category") << i18n("Tag"); m_list->setHeaderLabels(lstHeader); m_list->setColumnWidth(0, 600); m_list->setColumnWidth(1, 90); m_list->setColumnWidth(2, 65); m_list->setRootIsDecorated(false); AdElementList::const_iterator it; for (it = elements->constBegin(); it != elements->constEnd(); ++it) { const AdElement &element = (*it); QStringList lst; lst << element.url() << element.category() << element.type(); ListViewItem *item = new ListViewItem(m_list, lst, &element); item->setBlocked(element.isBlocked()); item->setBlockedBy(element.blockedBy()); item->setNode(element.node()); } searchLine->setTreeWidget(m_list); layout->addSpacing(KDialog::spacingHint()); l = new QLabel(i18n("New filter (can use *?[] wildcards, /RE/ for regular expression, prefix with @@ for white list):"), page); layout->addWidget(l); m_filter = new QLineEdit(page); layout->addWidget(m_filter); connect(m_filter, SIGNAL(textChanged(QString)), SLOT(filterTextChanged(QString))); l->setBuddy(m_filter); filterTextChanged(QString::null); connect(this, SIGNAL(user1Clicked()), this, SLOT(slotConfigureFilters())); connect(this, SIGNAL(user2Clicked()), this, SLOT(slotAddFilter())); // Use itemActivated() signal instead of itemDoubleClicked() to honour // the KDE single/double click setting, and allow keyboard navigation. // Activating a item just copies its URL to the "new filter" box and // sets focus to there, it doesn't add the filter immediately. //connect(m_list, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(updateFilter(QTreeWidgetItem*)) ); connect(m_list, SIGNAL(itemActivated(QTreeWidgetItem*,int)), this, SLOT(updateFilter(QTreeWidgetItem*))); m_menu = new KMenu(this); m_menu->addAction(i18n("Filter this item"), this, SLOT(filterItem())); m_menu->addAction(i18n("Filter all items at same path"), this, SLOT(filterPath())); m_menu->addAction(i18n("Filter all items from same host"), this, SLOT(filterHost())); m_menu->addAction(i18n("Filter all items from same domain"), this, SLOT(filterDomain())); m_menu->addSeparator(); m_menu->addAction(i18n("Add this item to white list"), this, SLOT(addWhiteList())); m_menu->addSeparator(); m_menu->addAction(i18n("Copy Link Address"), this, SLOT(copyLinkAddress())); //comment for the moment //m_menu->addAction( i18n( "Highlight Element" ), this, SLOT(highLightElement()) ); m_menu->addAction(i18n("View item"), this, SLOT(showElement())); m_list->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_list, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint))); resize(800, 400); } AdBlockDlg::~AdBlockDlg() { } void AdBlockDlg::filterTextChanged(const QString &text) { enableButton(KDialog::User2, !text.isEmpty()); } void AdBlockDlg::setFilterText(const QString &text) { m_filter->setText(text); m_filter->setFocus(Qt::OtherFocusReason); } void AdBlockDlg::updateFilter(QTreeWidgetItem *selected) { ListViewItem *item = static_cast(selected); if (item->isBlocked()) { m_filter->clear(); return; } setFilterText(item->text(0)); } void AdBlockDlg::slotAddFilter() { const QString text = m_filter->text().trimmed(); if (text.isEmpty()) { return; } kDebug() << "adding filter" << text; emit notEmptyFilter(text); for (QTreeWidgetItemIterator it(m_list); (*it) != 0; ++it) { ListViewItem *item = static_cast(*it); item->setBlocked(item->element()->isBlocked()); item->setBlockedBy(item->element()->blockedBy()); } enableButton(KDialog::User2, false); // now that filter has been added } // until it is changed again void AdBlockDlg::slotConfigureFilters() { emit configureFilters(); delayedDestruct(); } void AdBlockDlg::showContextMenu(const QPoint &pos) { QPoint newPos = m_list->viewport()->mapToGlobal(pos); int column = m_list->columnAt(pos.x()); if (column == -1) { return; } m_menu->popup(newPos); } void AdBlockDlg::filterItem() { QTreeWidgetItem *item = m_list->currentItem(); setFilterText(item->text(0)); } KUrl AdBlockDlg::getItem() { QTreeWidgetItem *item = m_list->currentItem(); KUrl u(item->text(0)); u.setQuery(QString()); u.setRef(QString()); return (u); } void AdBlockDlg::filterPath() { KUrl u(getItem()); u.setFileName("*"); setFilterText(u.url()); } void AdBlockDlg::filterHost() { KUrl u(getItem()); u.setPath("/*"); setFilterText(u.url()); } void AdBlockDlg::filterDomain() { KUrl u(getItem()); QString host = u.host(); if (host.isEmpty()) { return; } int idx = host.indexOf('.'); if (idx < 0) { return; } QString t = u.protocol() + "://*" + host.mid(idx) + "/*"; setFilterText(t); } void AdBlockDlg::addWhiteList() { QTreeWidgetItem *item = m_list->currentItem(); setFilterText("@@" + item->text(0)); } void AdBlockDlg::copyLinkAddress() { QApplication::clipboard()->setText(m_list->currentItem()->text(0)); } void AdBlockDlg::highLightElement() { ListViewItem *item = static_cast(m_list->currentItem()); if (item) { DOM::Node handle = item->node(); kDebug() << " m_part :" << m_part; if (!handle.isNull()) { m_part->setActiveNode(handle); } } } void AdBlockDlg::showElement() { new KRun(m_list->currentItem()->text(0), 0); } diff --git a/plugins/adblock/adblockdialog.h b/plugins/adblock/adblockdialog.h index e8d0c1bf4..464eb6c45 100644 --- a/plugins/adblock/adblockdialog.h +++ b/plugins/adblock/adblockdialog.h @@ -1,67 +1,68 @@ /* Copyright (c) 2008 Laurent Montel Copyright (C) 2006 Daniele Galdi This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef KONQ_ADBLOCKDLG_H -#define KONQ_ADBLOCKDLG_H +#ifndef KONQ_ADBLOCKDIALOG_H +#define KONQ_ADBLOCKDIALOG_H +#include "adblock.h" #include #include #include class QLineEdit; class KMenu; class QTreeWidget; class AdBlockDlg : public KDialog { Q_OBJECT public: AdBlockDlg(QWidget *parent, const AdElementList *elements, KHTMLPart *part); ~AdBlockDlg(); private slots: void slotAddFilter(); void slotConfigureFilters(); void updateFilter(QTreeWidgetItem *item); void showContextMenu(const QPoint &); void filterItem(); void filterPath(); void filterHost(); void filterDomain(); void addWhiteList(); void copyLinkAddress(); void highLightElement(); void showElement(); void filterTextChanged(const QString &text); signals: void notEmptyFilter(const QString &url); void configureFilters(); private: KUrl getItem(); void setFilterText(const QString &text); QLineEdit *m_filter; QTreeWidget *m_list; KMenu *m_menu; KHTMLPart *m_part; }; #endif diff --git a/plugins/akregator/CMakeLists.txt b/plugins/akregator/CMakeLists.txt index 0734e5fa0..a80ca89c2 100644 --- a/plugins/akregator/CMakeLists.txt +++ b/plugins/akregator/CMakeLists.txt @@ -1,34 +1,34 @@ ecm_qt_declare_logging_category(akregatorplugin_DEBUG_SRCS HEADER akregatorplugindebug.h IDENTIFIER AKREGATORPLUGIN_LOG CATEGORY_NAME org.kde.konqueror.akregatorplugin) ########### next target ############### set(akregatorplugin_PART_SRCS akregatorplugin.cpp pluginutil.cpp ) kcoreaddons_add_plugin(akregatorplugin SOURCES ${akregatorplugin_PART_SRCS} ${akregatorplugin_DEBUG_SRCS} INSTALL_NAMESPACE "kf5/kfileitemaction") kcoreaddons_desktop_to_json(akregatorplugin akregator_konqplugin.desktop) target_compile_definitions(akregatorplugin PRIVATE TRANSLATION_DOMAIN="akregator_konqplugin") -target_link_libraries(akregatorplugin KF5::I18n KF5::KIOWidgets) +target_link_libraries(akregatorplugin KF5::I18n KF5::KIOWidgets Qt5::DBus) ########### next target ############### set(akregatorkonqfeedicon_PART_SRCS konqfeedicon.cpp feeddetector.cpp pluginutil.cpp ) add_library(akregatorkonqfeedicon MODULE ${akregatorkonqfeedicon_PART_SRCS} ${akregatorplugin_DEBUG_SRCS}) target_compile_definitions(akregatorkonqfeedicon PRIVATE TRANSLATION_DOMAIN="akregator_konqplugin") target_link_libraries(akregatorkonqfeedicon KF5::Parts KF5::IconThemes) install(TARGETS akregatorkonqfeedicon DESTINATION ${KDE_INSTALL_PLUGINDIR} ) ########### install files ############### install( FILES akregator_konqplugin.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} ) install( FILES akregator_konqfeedicon.desktop akregator_konqfeedicon.rc DESTINATION ${KDE_INSTALL_DATADIR}/khtml/kpartplugins ) install( FILES akregator_konqfeedicon.desktop akregator_konqfeedicon.rc DESTINATION ${KDE_INSTALL_DATADIR}/kwebkitpart/kpartplugins ) install( FILES feed.png DESTINATION ${KDE_INSTALL_DATADIR}/akregator/pics ) # contains list of debug categories, for kdebugsettings install(FILES akregatorplugin.categories DESTINATION ${KDE_INSTALL_CONFDIR}) diff --git a/plugins/akregator/feeddetector.cpp b/plugins/akregator/feeddetector.cpp index 1e9742fff..b4f1bceea 100644 --- a/plugins/akregator/feeddetector.cpp +++ b/plugins/akregator/feeddetector.cpp @@ -1,148 +1,148 @@ /* This file is part of Akregator. Copyright (C) 2004 Teemu Rytilahti This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ -#include -#include -#include +#include "feeddetector.h" +#include +#include +#include -#include +#include -#include "feeddetector.h" #include "akregatorplugindebug.h" using namespace Akregator; FeedDetectorEntryList FeedDetector::extractFromLinkTags(const QString &s) { //reduce all sequences of spaces, newlines etc. to one space: QString str = s.simplified(); // extracts tags QRegExp reLinkTag("<[\\s]?LINK[^>]*REL[\\s]?=[\\s]?\\\"[^\\\"]*(ALTERNATE|FEED|SERVICE\\.FEED)[^\\\"]*\\\"[^>]*>", Qt::CaseInsensitive); // extracts the URL (href="url") QRegExp reHref("HREF[\\s]?=[\\s]?\\\"([^\\\"]*)\\\"", Qt::CaseInsensitive); // extracts type attribute QRegExp reType("TYPE[\\s]?=[\\s]?\\\"([^\\\"]*)\\\"", Qt::CaseInsensitive); // extracts the title (title="title") QRegExp reTitle("TITLE[\\s]?=[\\s]?\\\"([^\\\"]*)\\\"", Qt::CaseInsensitive); int pos = 0; int matchpos = 0; // get all tags QStringList linkTags; //int strlength = str.length(); while (matchpos != -1) { matchpos = reLinkTag.indexIn(str, pos); if (matchpos != -1) { linkTags.append(str.mid(matchpos, reLinkTag.matchedLength())); pos = matchpos + reLinkTag.matchedLength(); } } FeedDetectorEntryList list; for (QStringList::Iterator it = linkTags.begin(); it != linkTags.end(); ++it) { QString type; int pos = reType.indexIn(*it, 0); if (pos != -1) { type = reType.cap(1).toLower(); } // we accept only type attributes indicating a feed if (type != QLatin1String("application/rss+xml") && type != QLatin1String("application/rdf+xml") && type != QLatin1String("application/atom+xml") && type != QLatin1String("application/xml")) { continue; } QString title; pos = reTitle.indexIn(*it, 0); if (pos != -1) { title = reTitle.cap(1); } title = KCharsets::resolveEntities(title); QString url; pos = reHref.indexIn(*it, 0); if (pos != -1) { url = reHref.cap(1); } url = KCharsets::resolveEntities(url); // if feed has no title, use the url as preliminary title (until feed is parsed) if (title.isEmpty()) { title = url; } if (!url.isEmpty()) { qCDebug(AKREGATORPLUGIN_LOG) << "found feed:" << url << title; list.append(FeedDetectorEntry(url, title)); } } return list; } QStringList FeedDetector::extractBruteForce(const QString &s) { QString str = s.simplified(); QRegExp reAhrefTag("<[\\s]?A[^>]?HREF=[\\s]?\\\"[^\\\"]*\\\"[^>]*>", Qt::CaseInsensitive); // extracts the URL (href="url") QRegExp reHref("HREF[\\s]?=[\\s]?\\\"([^\\\"]*)\\\"", Qt::CaseInsensitive); QRegExp rssrdfxml(".*(RSS|RDF|XML)", Qt::CaseInsensitive); int pos = 0; int matchpos = 0; // get all tags and capture url QStringList list; //int strlength = str.length(); while (matchpos != -1) { matchpos = reAhrefTag.indexIn(str, pos); if (matchpos != -1) { QString ahref = str.mid(matchpos, reAhrefTag.matchedLength()); int hrefpos = reHref.indexIn(ahref, 0); if (hrefpos != -1) { QString url = reHref.cap(1); url = KCharsets::resolveEntities(url); if (rssrdfxml.exactMatch(url)) { list.append(url); } } pos = matchpos + reAhrefTag.matchedLength(); } } return list; } diff --git a/plugins/autorefresh/autorefresh.cpp b/plugins/autorefresh/autorefresh.cpp index dd96ca542..96a162af6 100644 --- a/plugins/autorefresh/autorefresh.cpp +++ b/plugins/autorefresh/autorefresh.cpp @@ -1,128 +1,128 @@ // -*- c++ -*- /* Copyright 2003 by Richard J. Moore, rich@kde.org This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include #include "autorefresh.h" +#include #include #include #include #include #include #include #include #include #include AutoRefresh::AutoRefresh(QObject *parent, const QVariantList & /*args*/) : Plugin(parent) { timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &AutoRefresh::slotRefresh); refresher = actionCollection()->add(QStringLiteral("autorefresh")); refresher->setText(i18n("&Auto Refresh")); refresher->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh"))); connect(refresher, SIGNAL(triggered(QAction*)), this, SLOT(slotIntervalChanged())); QStringList sl; sl << i18n("None"); sl << i18n("Every 15 Seconds"); sl << i18n("Every 30 Seconds"); sl << i18n("Every Minute"); sl << i18n("Every 5 Minutes"); sl << i18n("Every 10 Minutes"); sl << i18n("Every 15 Minutes"); sl << i18n("Every 30 Minutes"); sl << i18n("Every 60 Minutes"); sl << i18n("Every 2 Hours"); sl << i18n("Every 6 Hours"); refresher->setItems(sl); refresher->setCurrentItem(0); } AutoRefresh::~AutoRefresh() { } void AutoRefresh::slotIntervalChanged() { int idx = refresher->currentItem(); int timeout = 0; switch (idx) { case 1: timeout = (15 * 1000); break; case 2: timeout = (30 * 1000); break; case 3: timeout = (60 * 1000); break; case 4: timeout = (5 * 60 * 1000); break; case 5: timeout = (10 * 60 * 1000); break; case 6: timeout = (15 * 60 * 1000); break; case 7: timeout = (30 * 60 * 1000); break; case 8: timeout = (60 * 60 * 1000); break; case 9: timeout = (2 * 60 * 60 * 1000); break; case 10: timeout = (6 * 60 * 60 * 1000); break; default: break; } timer->stop(); if (timeout) { timer->start(timeout); } } void AutoRefresh::slotRefresh() { KParts::ReadOnlyPart *part = qobject_cast< KParts::ReadOnlyPart * >(parent()); if (!part) { QString title = i18nc("@title:window", "Cannot Refresh Source"); QString text = i18n("This plugin cannot auto-refresh the current part."); KMessageBox::error(0, text, title); } else { // Get URL QUrl url = part->url(); part->openUrl(url); } } K_PLUGIN_FACTORY(AutoRefreshFactory, registerPlugin< AutoRefresh >();) #include "autorefresh.moc" diff --git a/plugins/fsview/fsview.cpp b/plugins/fsview/fsview.cpp index e2552064e..3e38c1975 100644 --- a/plugins/fsview/fsview.cpp +++ b/plugins/fsview/fsview.cpp @@ -1,592 +1,592 @@ /* This file is part of FSView. Copyright (C) 2002, 2003 Josef Weidendorfer KCachegrind is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* - * FSView specialisaton of TreeMap classes. + * FSView specialization of TreeMap classes. */ -#include -#include +#include "fsview.h" + +#include +#include #include #include #include #include #include #include #include #include #include -#include "fsview.h" - // FSView QMap FSView::_dirMetric; FSView::FSView(Inode *base, QWidget *parent) : TreeMapWidget(base, parent) { setFieldType(0, i18n("Name")); setFieldType(1, i18n("Size")); setFieldType(2, i18n("File Count")); setFieldType(3, i18n("Directory Count")); setFieldType(4, i18n("Last Modified")); setFieldType(5, i18n("Owner")); setFieldType(6, i18n("Group")); setFieldType(7, i18n("Mime Type")); // defaults setVisibleWidth(4, true); setSplitMode(TreeMapItem::Rows); setFieldForced(0, true); // show directory names setFieldForced(1, true); // show directory sizes setSelectionMode(TreeMapWidget::Extended); _colorMode = Depth; _pathDepth = 0; _allowRefresh = true; _progressPhase = 0; _chunkData1 = 0; _chunkData2 = 0; _chunkData3 = 0; _chunkSize1 = 0; _chunkSize2 = 0; _chunkSize3 = 0; _progressSize = 0; _progress = 0; _dirsFinished = 0; _lastDir = 0; _config = new KConfig(QStringLiteral("fsviewrc")); // restore TreeMap visualization options of last execution KConfigGroup tmconfig(_config, "TreeMap"); restoreOptions(&tmconfig); QString str = tmconfig.readEntry("ColorMode"); if (!str.isEmpty()) { setColorMode(str); } if (_dirMetric.count() == 0) { // restore metric cache KConfigGroup cconfig(_config, "MetricCache"); int ccount = cconfig.readEntry("Count", 0); int i, f, d; double s; QString str; for (i = 1; i <= ccount; i++) { str = QStringLiteral("Dir%1").arg(i); if (!cconfig.hasKey(str)) { continue; } str = cconfig.readPathEntry(str, QString()); s = cconfig.readEntry(QStringLiteral("Size%1").arg(i), 0.0); f = cconfig.readEntry(QStringLiteral("Files%1").arg(i), 0); d = cconfig.readEntry(QStringLiteral("Dirs%1").arg(i), 0); if (s == 0.0 || f == 0 || d == 0) { continue; } setDirMetric(str, s, f, d); } } _sm.setListener(this); } FSView::~FSView() { delete _config; } void FSView::stop() { _sm.stopScan(); } void FSView::setPath(const QString &p) { Inode *b = (Inode *)base(); if (!b) { return; } //kDebug(90100) << "FSView::setPath " << p; // stop any previous updating stop(); QFileInfo fi(p); _path = fi.absoluteFilePath(); if (!fi.isDir()) { _path = fi.absolutePath(); } _path = QDir::cleanPath(_path); _pathDepth = _path.count('/'); QUrl u; u.setPath(_path); if (!KUrlAuthorized::authorizeUrlAction(QStringLiteral("list"), QUrl(), u)) { QString msg = KIO::buildErrorString(KIO::ERR_ACCESS_DENIED, u.toDisplayString()); KMessageBox::sorry(this, msg); } ScanDir *d = _sm.setTop(_path); b->setPeer(d); setWindowTitle(QStringLiteral("%1 - FSView").arg(_path)); requestUpdate(b); } QList FSView::selectedUrls() { QList urls; foreach (TreeMapItem *i, selection()) { QUrl u = QUrl::fromLocalFile(((Inode *)i)->path()); urls.append(u); } return urls; } bool FSView::getDirMetric(const QString &k, double &s, unsigned int &f, unsigned int &d) { QMap::iterator it; it = _dirMetric.find(k); if (it == _dirMetric.end()) { return false; } s = (*it).size; f = (*it).fileCount; d = (*it).dirCount; if (0) { kDebug(90100) << "getDirMetric " << k; } if (0) { kDebug(90100) << " - got size " << s << ", files " << f; } return true; } void FSView::setDirMetric(const QString &k, double s, unsigned int f, unsigned int d) { if (0) kDebug(90100) << "setDirMetric '" << k << "': size " << s << ", files " << f << ", dirs " << d << endl; _dirMetric.insert(k, MetricEntry(s, f, d)); } void FSView::requestUpdate(Inode *i) { if (0) kDebug(90100) << "FSView::requestUpdate(" << i->path() << ")" << endl; ScanDir *peer = i->dirPeer(); if (!peer) { return; } peer->clear(); i->clear(); if (!_sm.scanRunning()) { QTimer::singleShot(0, this, SLOT(doUpdate())); QTimer::singleShot(100, this, SLOT(doRedraw())); /* start new progress chunk */ _progressPhase = 1; _chunkData1 += 3; _chunkData2 = _chunkData1 + 1; _chunkData3 = _chunkData1 + 2; _chunkSize1 = 0; _chunkSize2 = 0; _chunkSize3 = 0; peer->setData(_chunkData1); _progressSize = 0; _progress = 0; _dirsFinished = 0; _lastDir = 0; emit started(); } _sm.startScan(peer); } void FSView::scanFinished(ScanDir *d) { /* if finished directory was from last progress chunk, increment */ int data = d->data(); switch (_progressPhase) { case 1: if (data == _chunkData1) { _chunkSize1--; } break; case 2: if (data == _chunkData1) { _progress++; } if (data == _chunkData2) { _chunkSize2--; } break; case 3: if ((data == _chunkData1) || (data == _chunkData2)) { _progress++; } if (data == _chunkData3) { _chunkSize3--; } break; case 4: if ((data == _chunkData1) || (data == _chunkData2) || (data == _chunkData3)) { _progress++; } break; default: break; } _lastDir = d; _dirsFinished++; if (0) kDebug(90100) << "FSFiew::scanFinished: " << d->path() << ", Data " << data << ", Progress " << _progress << "/" << _progressSize << endl; } void FSView::selected(TreeMapItem *i) { setPath(((Inode *)i)->path()); } void FSView::contextMenu(TreeMapItem *i, const QPoint &p) { QMenu popup; QMenu *spopup = new QMenu(i18n("Go To")); QMenu *dpopup = new QMenu(i18n("Stop at Depth")); QMenu *apopup = new QMenu(i18n("Stop at Area")); QMenu *fpopup = new QMenu(i18n("Stop at Name")); // choosing from the selection menu will give a selectionChanged() signal addSelectionItems(spopup, 901, i); popup.addMenu(spopup); QAction *actionGoUp = popup.addAction(i18n("Go Up")); popup.addSeparator(); QAction *actionStopRefresh = popup.addAction(i18n("Stop Refresh")); actionStopRefresh->setEnabled(_sm.scanRunning()); QAction *actionRefresh = popup.addAction(i18n("Refresh")); actionRefresh->setEnabled(!_sm.scanRunning()); QAction *actionRefreshSelected = 0; if (i) { actionRefreshSelected = popup.addAction(i18n("Refresh '%1'", i->text(0))); } popup.addSeparator(); addDepthStopItems(dpopup, 1001, i); popup.addMenu(dpopup); addAreaStopItems(apopup, 1101, i); popup.addMenu(apopup); addFieldStopItems(fpopup, 1201, i); popup.addMenu(fpopup); popup.addSeparator(); QMenu *cpopup = new QMenu(i18n("Color Mode")); addColorItems(cpopup, 1401); popup.addMenu(cpopup); QMenu *vpopup = new QMenu(i18n("Visualization")); addVisualizationItems(vpopup, 1301); popup.addMenu(vpopup); _allowRefresh = false; QAction *action = popup.exec(mapToGlobal(p)); _allowRefresh = true; if (!action) { return; } if (action == actionGoUp) { Inode *i = (Inode *) base(); if (i) { setPath(i->path() + QLatin1String("/..")); } } else if (action == actionStopRefresh) { stop(); } else if (action == actionRefreshSelected) { //((Inode*)i)->refresh(); requestUpdate((Inode *)i); } else if (action == actionRefresh) { Inode *i = (Inode *) base(); if (i) { requestUpdate(i); } } } void FSView::saveMetric(KConfigGroup *g) { QMap::iterator it; int c = 1; for (it = _dirMetric.begin(); it != _dirMetric.end(); ++it) { g->writePathEntry(QStringLiteral("Dir%1").arg(c), it.key()); g->writeEntry(QStringLiteral("Size%1").arg(c), (*it).size); g->writeEntry(QStringLiteral("Files%1").arg(c), (*it).fileCount); g->writeEntry(QStringLiteral("Dirs%1").arg(c), (*it).dirCount); c++; } g->writeEntry("Count", c - 1); } void FSView::setColorMode(FSView::ColorMode cm) { if (_colorMode == cm) { return; } _colorMode = cm; redraw(); } bool FSView::setColorMode(const QString &mode) { if (mode == QLatin1String("None")) { setColorMode(None); } else if (mode == QLatin1String("Depth")) { setColorMode(Depth); } else if (mode == QLatin1String("Name")) { setColorMode(Name); } else if (mode == QLatin1String("Owner")) { setColorMode(Owner); } else if (mode == QLatin1String("Group")) { setColorMode(Group); } else if (mode == QLatin1String("Mime")) { setColorMode(Mime); } else { return false; } return true; } QString FSView::colorModeString() const { QString mode; switch (_colorMode) { case None: mode = QStringLiteral("None"); break; case Depth: mode = QStringLiteral("Depth"); break; case Name: mode = QStringLiteral("Name"); break; case Owner: mode = QStringLiteral("Owner"); break; case Group: mode = QStringLiteral("Group"); break; case Mime: mode = QStringLiteral("Mime"); break; default: mode = QStringLiteral("Unknown"); break; } return mode; } void FSView::addColorItems(QMenu *popup, int id) { _colorID = id; connect(popup, SIGNAL(triggered(QAction*)), this, SLOT(colorActivated(QAction*))); addPopupItem(popup, i18n("None"), colorMode() == None, id++); addPopupItem(popup, i18n("Depth"), colorMode() == Depth, id++); addPopupItem(popup, i18n("Name"), colorMode() == Name, id++); addPopupItem(popup, i18n("Owner"), colorMode() == Owner, id++); addPopupItem(popup, i18n("Group"), colorMode() == Group, id++); addPopupItem(popup, i18n("Mime Type"), colorMode() == Mime, id++); } void FSView::colorActivated(QAction *a) { const int id = a->data().toInt(); if (id == _colorID) { setColorMode(None); } else if (id == _colorID + 1) { setColorMode(Depth); } else if (id == _colorID + 2) { setColorMode(Name); } else if (id == _colorID + 3) { setColorMode(Owner); } else if (id == _colorID + 4) { setColorMode(Group); } else if (id == _colorID + 5) { setColorMode(Mime); } } void FSView::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Escape && !_pressed && (selection().size() > 0)) { // For consistency with Dolphin, deselect all on Escape if we're not dragging. TreeMapItem *changed = selection().commonParent(); if (changed) { clearSelection(changed); } } else { TreeMapWidget::keyPressEvent(e); } } void FSView::saveFSOptions() { KConfigGroup tmconfig(_config, "TreeMap"); saveOptions(&tmconfig); tmconfig.writeEntry("ColorMode", colorModeString()); KConfigGroup gconfig(_config, "General"); gconfig.writeEntry("Path", _path); KConfigGroup cconfig(_config, "MetricCache"); saveMetric(&cconfig); } void FSView::quit() { saveFSOptions(); qApp->quit(); } void FSView::doRedraw() { // we update progress every 1/4 second, and redraw every second static int redrawCounter = 0; bool redo = _sm.scanRunning(); if (!redo) { redrawCounter = 0; } if ((_progress > 0) && (_progressSize > 0) && _lastDir) { int percent = _progress * 100 / _progressSize; if (0) kDebug(90100) << "FSView::progress " << _progress << "/" << _progressSize << "= " << percent << "%, " << _dirsFinished << " dirs read, in " << _lastDir->path() << endl; emit progress(percent, _dirsFinished, _lastDir->path()); } if (_allowRefresh && ((redrawCounter % 4) == 0)) { if (0) { kDebug(90100) << "doRedraw " << _sm.scanLength(); } redraw(); } else { redo = true; } if (redo) { QTimer::singleShot(500, this, SLOT(doRedraw())); redrawCounter++; } } void FSView::doUpdate() { for (int i = 0; i < 5; i++) { switch (_progressPhase) { case 1: _chunkSize1 += _sm.scan(_chunkData1); if (_chunkSize1 > 100) { _progressPhase = 2; /* Go to maximally 33% by scaling with 3 */ _progressSize = 3 * _chunkSize1; if (1) { kDebug(90100) << "Phase 2: CSize " << _chunkSize1; } } break; case 2: /* progress phase 2 */ _chunkSize2 += _sm.scan(_chunkData2); /* switch to Phase 3 if we reach 80 % of Phase 2 */ if (_progress * 3 > _progressSize * 8 / 10) { _progressPhase = 3; /* Goal: Keep percentage equal from phase 2 to 3 */ double percent = (double)_progress / _progressSize; /* We scale by factor 2/3 aferwards */ percent = percent * 3 / 2; int todo = _chunkSize2 + (_progressSize / 3 - _progress); _progressSize = (int)((double)todo / (1.0 - percent)); _progress = _progressSize - todo; /* Go to maximally 66% by scaling with 1.5 */ _progressSize = _progressSize * 3 / 2; if (1) kDebug(90100) << "Phase 3: CSize " << _chunkSize2 << ", Todo " << todo << ", Progress " << _progress << "/" << _progressSize << endl; } break; case 3: /* progress phase 3 */ _chunkSize3 += _sm.scan(_chunkData3); /* switch to Phase 4 if we reach 80 % of Phase 3 */ if (_progress * 3 / 2 > _progressSize * 8 / 10) { _progressPhase = 4; /* Goal: Keep percentage equal from phase 2 to 3 */ double percent = (double)_progress / _progressSize; int todo = _chunkSize3 + (_progressSize * 2 / 3 - _progress); _progressSize = (int)((double)todo / (1.0 - percent) + .5); _progress = _progressSize - todo; if (1) kDebug(90100) << "Phase 4: CSize " << _chunkSize3 << ", Todo " << todo << ", Progress " << _progress << "/" << _progressSize << endl; } default: _sm.scan(-1); break; } } if (_sm.scanRunning()) { QTimer::singleShot(0, this, SLOT(doUpdate())); } else { emit completed(_dirsFinished); } } diff --git a/plugins/fsview/fsview_part.cpp b/plugins/fsview/fsview_part.cpp index ff584fd32..2b017aca7 100644 --- a/plugins/fsview/fsview_part.cpp +++ b/plugins/fsview/fsview_part.cpp @@ -1,556 +1,557 @@ /* This file is part of FSView. Copyright (C) 2002, 2003 Josef Weidendorfer Some file management code taken from the Dolphin file manager (C) 2006-2009, by Peter Penz KCachegrind is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * The KPart embedding the FSView widget */ -#include -#include +#include "fsview_part.h" + +#include +#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include "fsview_part.h" - K_PLUGIN_FACTORY(FSViewPartFactory, registerPlugin();) K_EXPORT_PLUGIN(FSViewPartFactory(KAboutData( "fsview", 0, ki18n("FSView"), "0.1.1", ki18n("Filesystem Utilization Viewer"), KAboutData::License_GPL, ki18n("(c) 2003-2005, Josef Weidendorfer")))) // FSJob, for progress FSJob::FSJob(FSView *v) : KIO::Job() { _view = v; QObject::connect(v, SIGNAL(progress(int,int,QString)), this, SLOT(progressSlot(int,int,QString))); } void FSJob::kill(bool /*quietly*/) { _view->stop(); Job::kill(); } void FSJob::progressSlot(int percent, int dirs, const QString &cDir) { if (percent < 100) { emitPercent(percent, 100); slotInfoMessage(this, i18np("Read 1 folder, in %2", "Read %1 folders, in %2", dirs, cDir), QString()); } else { slotInfoMessage(this, i18np("1 folder", "%1 folders", dirs), QString()); } } // FSViewPart FSViewPart::FSViewPart(QWidget *parentWidget, QObject *parent, const QList & /* args */) : KParts::ReadOnlyPart(parent) { KAboutData aboutData(QStringLiteral("fsview"), i18n("FSView"), QStringLiteral("0.1"), i18n("Filesystem Viewer"), KAboutLicense::GPL, i18n("(c) 2002, Josef Weidendorfer")); setComponentData(aboutData); _view = new FSView(new Inode(), parentWidget); _view->setWhatsThis(i18n("

This is the FSView plugin, a graphical " "browsing mode showing filesystem utilization " "by using a tree map visualization.

" "

Note that in this mode, automatic updating " "when filesystem changes are made " "is intentionally not done.

" "

For details on usage and options available, " "see the online help under " "menu 'Help/FSView Manual'.

")); _view->show(); setWidget(_view); _ext = new FSViewBrowserExtension(this); _job = 0; _areaMenu = new KActionMenu(i18n("Stop at Area"), actionCollection()); actionCollection()->addAction(QStringLiteral("treemap_areadir"), _areaMenu); _depthMenu = new KActionMenu(i18n("Stop at Depth"), actionCollection()); actionCollection()->addAction(QStringLiteral("treemap_depthdir"), _depthMenu); _visMenu = new KActionMenu(i18n("Visualization"), actionCollection()); actionCollection()->addAction(QStringLiteral("treemap_visdir"), _visMenu); _colorMenu = new KActionMenu(i18n("Color Mode"), actionCollection()); actionCollection()->addAction(QStringLiteral("treemap_colordir"), _colorMenu); QAction *action; action = actionCollection()->addAction(QStringLiteral("help_fsview")); action->setText(i18n("&FSView Manual")); action->setIcon(QIcon::fromTheme(QStringLiteral("fsview"))); action->setToolTip(i18n("Show FSView manual")); action->setWhatsThis(i18n("Opens the help browser with the " "FSView documentation")); connect(action, SIGNAL(triggered()), this, SLOT(showHelp())); QObject::connect(_visMenu->menu(), SIGNAL(aboutToShow()), SLOT(slotShowVisMenu())); QObject::connect(_areaMenu->menu(), SIGNAL(aboutToShow()), SLOT(slotShowAreaMenu())); QObject::connect(_depthMenu->menu(), SIGNAL(aboutToShow()), SLOT(slotShowDepthMenu())); QObject::connect(_colorMenu->menu(), SIGNAL(aboutToShow()), SLOT(slotShowColorMenu())); slotSettingsChanged(KGlobalSettings::SETTINGS_MOUSE); connect(KGlobalSettings::self(), SIGNAL(settingsChanged(int)), SLOT(slotSettingsChanged(int))); QObject::connect(_view, SIGNAL(returnPressed(TreeMapItem*)), _ext, SLOT(selected(TreeMapItem*))); QObject::connect(_view, SIGNAL(selectionChanged()), this, SLOT(updateActions())); QObject::connect(_view, SIGNAL(contextMenuRequested(TreeMapItem*,QPoint)), this, SLOT(contextMenu(TreeMapItem*,QPoint))); QObject::connect(_view, SIGNAL(started()), this, SLOT(startedSlot())); QObject::connect(_view, SIGNAL(completed(int)), this, SLOT(completedSlot(int))); // Create common file management actions - this is necessary in KDE4 // as these common actions are no longer automatically part of KParts. // Much of this is taken from Dolphin. // FIXME: Renaming didn't even seem to work in KDE3! Implement (non-inline) renaming // functionality. //QAction* renameAction = m_actionCollection->addAction("rename"); //rename->setText(i18nc("@action:inmenu Edit", "Rename...")); //rename->setShortcut(Qt::Key_F2); QAction *moveToTrashAction = actionCollection()->addAction(QStringLiteral("move_to_trash")); moveToTrashAction->setText(i18nc("@action:inmenu File", "Move to Trash")); moveToTrashAction->setIcon(QIcon::fromTheme(QStringLiteral("user-trash"))); actionCollection()->setDefaultShortcut(moveToTrashAction, QKeySequence(QKeySequence::Delete)); connect(moveToTrashAction, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), _ext, SLOT(trash(Qt::MouseButtons,Qt::KeyboardModifiers))); QAction *deleteAction = actionCollection()->addAction(QStringLiteral("delete")); deleteAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"))); deleteAction->setText(i18nc("@action:inmenu File", "Delete")); actionCollection()->setDefaultShortcut(deleteAction, QKeySequence(Qt::SHIFT | Qt::Key_Delete)); connect(deleteAction, SIGNAL(triggered()), _ext, SLOT(del())); QAction *editMimeTypeAction = actionCollection()->addAction(QStringLiteral("editMimeType")); editMimeTypeAction->setText(i18nc("@action:inmenu Edit", "&Edit File Type...")); connect(editMimeTypeAction, SIGNAL(triggered()), _ext, SLOT(editMimeType())); QAction *propertiesAction = actionCollection()->addAction(QStringLiteral("properties")); propertiesAction->setText(i18nc("@action:inmenu File", "Properties")); propertiesAction->setIcon(QIcon::fromTheme(QStringLiteral("document-properties"))); propertiesAction->setShortcut(Qt::ALT | Qt::Key_Return); connect(propertiesAction, SIGNAL(triggered()), SLOT(slotProperties())); QTimer::singleShot(1, this, SLOT(showInfo())); updateActions(); setXMLFile(QStringLiteral("fsview_part.rc")); } FSViewPart::~FSViewPart() { kDebug(90100) << "FSViewPart Destructor"; delete _job; _view->saveFSOptions(); } void FSViewPart::slotSettingsChanged(int category) { if (category != KGlobalSettings::SETTINGS_MOUSE) { return; } QObject::disconnect(_view, SIGNAL(clicked(TreeMapItem*)), _ext, SLOT(selected(TreeMapItem*))); QObject::disconnect(_view, SIGNAL(doubleClicked(TreeMapItem*)), _ext, SLOT(selected(TreeMapItem*))); - if (KGlobalSettings::singleClick()) + if (_view->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick)) QObject::connect(_view, SIGNAL(clicked(TreeMapItem*)), _ext, SLOT(selected(TreeMapItem*))); else QObject::connect(_view, SIGNAL(doubleClicked(TreeMapItem*)), _ext, SLOT(selected(TreeMapItem*))); } void FSViewPart::showInfo() { QString info; info = i18n("FSView intentionally does not support automatic updates " "when changes are made to files or directories, " "currently visible in FSView, from the outside.\n" "For details, see the 'Help/FSView Manual'."); KMessageBox::information(_view, info, QString(), QStringLiteral("ShowFSViewInfo")); } void FSViewPart::showHelp() { KToolInvocation::startServiceByDesktopName(QStringLiteral("khelpcenter"), QStringLiteral("help:/konqueror/index.html#fsview")); } void FSViewPart::startedSlot() { _job = new FSJob(_view); _job->setUiDelegate(new KIO::JobUiDelegate()); emit started(_job); } void FSViewPart::completedSlot(int dirs) { if (_job) { _job->progressSlot(100, dirs, QString()); delete _job; _job = 0; } KConfigGroup cconfig(_view->config(), "MetricCache"); _view->saveMetric(&cconfig); emit completed(); } void FSViewPart::slotShowVisMenu() { _visMenu->menu()->clear(); _view->addVisualizationItems(_visMenu->menu(), 1301); } void FSViewPart::slotShowAreaMenu() { _areaMenu->menu()->clear(); _view->addAreaStopItems(_areaMenu->menu(), 1001, 0); } void FSViewPart::slotShowDepthMenu() { _depthMenu->menu()->clear(); _view->addDepthStopItems(_depthMenu->menu(), 1501, 0); } void FSViewPart::slotShowColorMenu() { _colorMenu->menu()->clear(); _view->addColorItems(_colorMenu->menu(), 1401); } bool FSViewPart::openFile() // never called since openUrl is reimplemented { kDebug(90100) << "FSViewPart::openFile " << localFilePath(); _view->setPath(localFilePath()); return true; } bool FSViewPart::openUrl(const QUrl &url) { kDebug(90100) << "FSViewPart::openUrl " << url.path(); if (!url.isValid()) { return false; } if (!url.isLocalFile()) { return false; } setUrl(url); emit setWindowCaption(this->url().toDisplayString(QUrl::PreferLocalFile)); _view->setPath(this->url().path()); return true; } bool FSViewPart::closeUrl() { kDebug(90100) << "FSViewPart::closeUrl "; _view->stop(); return true; } void FSViewPart::setNonStandardActionEnabled(const char *actionName, bool enabled) { QAction *action = actionCollection()->action(actionName); action->setEnabled(enabled); } void FSViewPart::updateActions() { int canDel = 0, canCopy = 0, canMove = 0; QList urls; foreach (TreeMapItem *i, _view->selection()) { QUrl u = QUrl::fromLocalFile(((Inode *)i)->path()); urls.append(u); canCopy++; if (KProtocolManager::supportsDeleting(u)) { canDel++; } if (KProtocolManager::supportsMoving(u)) { canMove++; } } // Standard KBrowserExtension actions. emit _ext->enableAction("copy", canCopy > 0); emit _ext->enableAction("cut", canMove > 0); // Custom actions. //setNonStandardActionEnabled("rename", canMove > 0 ); // FIXME setNonStandardActionEnabled("move_to_trash", (canDel > 0 && canMove > 0)); setNonStandardActionEnabled("delete", canDel > 0); setNonStandardActionEnabled("editMimeType", _view->selection().count() == 1); setNonStandardActionEnabled("properties", _view->selection().count() == 1); emit _ext->selectionInfo(urls); if (canCopy > 0) { stateChanged(QStringLiteral("has_selection")); } else { stateChanged(QStringLiteral("has_no_selection")); } kDebug(90100) << "FSViewPart::updateActions, deletable " << canDel; } void FSViewPart::contextMenu(TreeMapItem * /*item*/, const QPoint &p) { int canDel = 0, canCopy = 0, canMove = 0; KFileItemList items; foreach (TreeMapItem *i, _view->selection()) { QUrl u = QUrl::fromLocalFile(((Inode *)i)->path()); QString mimetype = ((Inode *)i)->mimeType().name(); const QFileInfo &info = ((Inode *)i)->fileInfo(); mode_t mode = info.isFile() ? S_IFREG : info.isDir() ? S_IFDIR : info.isSymLink() ? S_IFLNK : (mode_t) - 1; items.append(KFileItem(u, mimetype, mode)); canCopy++; if (KProtocolManager::supportsDeleting(u)) { canDel++; } if (KProtocolManager::supportsMoving(u)) { canMove++; } } QList editActions; KParts::BrowserExtension::ActionGroupMap actionGroups; KParts::BrowserExtension::PopupFlags flags = KParts::BrowserExtension::ShowUrlOperations | KParts::BrowserExtension::ShowProperties; bool addTrash = (canMove > 0); bool addDel = false; if (canDel == 0) { flags |= KParts::BrowserExtension::NoDeletion; } else { if (!url().isLocalFile()) { addDel = true; } else if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { addTrash = false; addDel = true; } else { KSharedConfig::Ptr globalConfig = KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::IncludeGlobals); KConfigGroup configGroup(globalConfig, "KDE"); addDel = configGroup.readEntry("ShowDeleteCommand", false); } } if (addTrash) { editActions.append(actionCollection()->action(QStringLiteral("move_to_trash"))); } if (addDel) { editActions.append(actionCollection()->action(QStringLiteral("delete"))); } // FIXME: rename is currently unavailable. Requires popup renaming. // if (canMove) // editActions.append(actionCollection()->action("rename")); actionGroups.insert(QStringLiteral("editactions"), editActions); if (items.count() > 0) emit _ext->popupMenu(_view->mapToGlobal(p), items, KParts::OpenUrlArguments(), KParts::BrowserArguments(), flags, actionGroups); } void FSViewPart::slotProperties() { QList urlList; if (view()) { urlList = view()->selectedUrls(); } if (!urlList.isEmpty()) { KPropertiesDialog::showDialog(urlList.first(), view()); } } // FSViewBrowserExtension FSViewBrowserExtension::FSViewBrowserExtension(FSViewPart *viewPart) : KParts::BrowserExtension(viewPart) { _view = viewPart->view(); } FSViewBrowserExtension::~FSViewBrowserExtension() {} void FSViewBrowserExtension::del() { const QList urls = _view->selectedUrls(); KIO::JobUiDelegate uiDelegate; uiDelegate.setWindow(_view); if (uiDelegate.askDeleteConfirmation(urls, KIO::JobUiDelegate::Delete, KIO::JobUiDelegate::DefaultConfirmation)) { KIO::Job *job = KIO::del(urls); KJobWidgets::setWindow(job, _view); job->ui()->setAutoErrorHandlingEnabled(true); connect(job, SIGNAL(result(KJob*)), this, SLOT(refresh())); } } void FSViewBrowserExtension::trash(Qt::MouseButtons, Qt::KeyboardModifiers modifiers) { bool deleteNotTrash = ((modifiers & Qt::ShiftModifier) != 0); if (deleteNotTrash) { del(); } else { KIO::JobUiDelegate uiDelegate; uiDelegate.setWindow(_view); const QList urls = _view->selectedUrls(); if (uiDelegate.askDeleteConfirmation(urls, KIO::JobUiDelegate::Trash, KIO::JobUiDelegate::DefaultConfirmation)) { KIO::Job *job = KIO::trash(urls); KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Trash, urls, QUrl("trash:/"), job); KJobWidgets::setWindow(job, _view); job->ui()->setAutoErrorHandlingEnabled(true); connect(job, SIGNAL(result(KJob*)), this, SLOT(refresh())); } } } void FSViewBrowserExtension::copySelection(bool move) { QMimeData *data = new QMimeData; data->setUrls(_view->selectedUrls()); KIO::setClipboardDataCut(data, move); QApplication::clipboard()->setMimeData(data); } void FSViewBrowserExtension::editMimeType() { Inode *i = (Inode *) _view->selection().first(); if (i) { KMimeTypeEditor::editMimeType(i->mimeType().name(), _view); } } // refresh treemap at end of KIO jobs void FSViewBrowserExtension::refresh() { // only need to refresh common ancestor for all selected items TreeMapItem *commonParent = _view->selection().commonParent(); if (!commonParent) { return; } /* if commonParent is a file, update parent directory */ if (!((Inode *)commonParent)->isDir()) { commonParent = commonParent->parent(); if (!commonParent) { return; } } kDebug(90100) << "FSViewPart::refreshing " << ((Inode *)commonParent)->path() << endl; _view->requestUpdate((Inode *)commonParent); } void FSViewBrowserExtension::selected(TreeMapItem *i) { if (!i) { return; } QUrl url = QUrl::fromLocalFile(((Inode *)i)->path()); emit openUrlRequest(url); } #include "fsview_part.moc" diff --git a/plugins/fsview/inode.cpp b/plugins/fsview/inode.cpp index 5fa39cc8f..ab2efe4dd 100644 --- a/plugins/fsview/inode.cpp +++ b/plugins/fsview/inode.cpp @@ -1,429 +1,429 @@ /* This file is part of FSView. Copyright (C) 2002, 2003 Josef Weidendorfer KCachegrind is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * FSView specialisaton of TreeMapItem class. */ +#include "inode.h" #include #include #include #include #include #include -#include "inode.h" #include "scan.h" #include "fsview.h" // Inode Inode::Inode() { _dirPeer = 0; _filePeer = 0; init(QString()); } Inode::Inode(ScanDir *d, Inode *parent) : TreeMapItem(parent) { QString absPath; if (parent) { absPath = parent->path(); if (!absPath.endsWith(QLatin1Char('/'))) { absPath += QLatin1Char('/'); } } absPath += d->name(); _dirPeer = d; _filePeer = 0; init(absPath); } Inode::Inode(ScanFile *f, Inode *parent) : TreeMapItem(parent) { QString absPath; if (parent) { absPath = parent->path() + QLatin1Char('/'); } absPath += f->name(); _dirPeer = 0; _filePeer = f; init(absPath); } Inode::~Inode() { if (0) kDebug(90100) << "~Inode [" << path() << "]" << endl; /* reset Listener of old Peer */ if (_dirPeer) { _dirPeer->setListener(0); } if (_filePeer) { _filePeer->setListener(0); } } void Inode::setPeer(ScanDir *d) { /* reset Listener of old Peer */ if (_dirPeer) { _dirPeer->setListener(0); } if (_filePeer) { _filePeer->setListener(0); } _dirPeer = d; _filePeer = 0; init(d->name()); } QString Inode::path() const { return _info.absoluteFilePath(); } void Inode::init(const QString &path) { if (0) kDebug(90100) << "Inode::init [" << path << "]" << endl; _info = QFileInfo(path); if (!FSView::getDirMetric(path, _sizeEstimation, _fileCountEstimation, _dirCountEstimation)) { _sizeEstimation = 0.0; _fileCountEstimation = 0; _dirCountEstimation = 0; } _mimeSet = false; _mimePixmapSet = false; _resortNeeded = false; clear(); /* we want to get notifications about dir changes */ if (_dirPeer) { _dirPeer->setListener(this); } if (_filePeer) { _filePeer->setListener(this); } if (_dirPeer && _dirPeer->scanFinished()) { scanFinished(_dirPeer); } } /* ScanListener interface */ void Inode::sizeChanged(ScanDir *d) { if (0) kDebug(90100) << "Inode::sizeChanged [" << path() << "] in " << d->name() << ": size " << d->size() << endl; _resortNeeded = true; } void Inode::scanFinished(ScanDir *d) { if (0) kDebug(90100) << "Inode::scanFinished [" << path() << "] in " << d->name() << ": size " << d->size() << endl; _resortNeeded = true; /* no estimation any longer */ _sizeEstimation = 0.0; _fileCountEstimation = 0; _dirCountEstimation = 0; // cache metrics if "important" (for "/usr" is dd==3) int dd = ((FSView *)widget())->pathDepth() + depth(); int files = d->fileCount(); int dirs = d->dirCount(); if ((files < 500) && (dirs < 50)) { if (dd > 4 && (files < 50) && (dirs < 5)) { return; } } FSView::setDirMetric(path(), d->size(), files, dirs); } void Inode::destroyed(ScanDir *d) { if (_dirPeer == d) { _dirPeer = 0; } // remove children clear(); } void Inode::destroyed(ScanFile *f) { if (_filePeer == f) { _filePeer = 0; } } TreeMapItemList *Inode::children() { if (!_dirPeer) { return 0; } if (!_children) { if (!_dirPeer->scanStarted()) { return 0; } _children = new TreeMapItemList; setSorting(-1); ScanFileVector &files = _dirPeer->files(); if (files.count() > 0) { ScanFileVector::iterator it; for (it = files.begin(); it != files.end(); ++it) { new Inode(&(*it), this); } } ScanDirVector &dirs = _dirPeer->dirs(); if (dirs.count() > 0) { ScanDirVector::iterator it; for (it = dirs.begin(); it != dirs.end(); ++it) { new Inode(&(*it), this); } } setSorting(-2); _resortNeeded = false; } if (_resortNeeded) { resort(); _resortNeeded = false; } return _children; } double Inode::size() const { // sizes of files are always correct if (_filePeer) { return _filePeer->size(); } if (!_dirPeer) { return 0; } double size = _dirPeer->size(); return (_sizeEstimation > size) ? _sizeEstimation : size; } double Inode::value() const { return size(); } unsigned int Inode::fileCount() const { unsigned int fileCount = 1; if (_dirPeer) { fileCount = _dirPeer->fileCount(); } if (_fileCountEstimation > fileCount) { fileCount = _fileCountEstimation; } return fileCount; } unsigned int Inode::dirCount() const { unsigned int dirCount = 0; if (_dirPeer) { dirCount = _dirPeer->dirCount(); } if (_dirCountEstimation > dirCount) { dirCount = _dirCountEstimation; } return dirCount; } QColor Inode::backColor() const { QString n; int id = 0; switch (((FSView *)widget())->colorMode()) { case FSView::Depth: { int d = ((FSView *)widget())->pathDepth() + depth(); return QColor::fromHsv((100 * d) % 360, 192, 128); } case FSView::Name: n = text(0); break; case FSView::Owner: id = _info.ownerId(); break; case FSView::Group: id = _info.groupId(); break; case FSView::Mime: n = text(7); break; default: break; } if (id > 0) { n = QString::number(id); } if (n.isEmpty()) { return widget()->palette().button().color(); } QByteArray tmpBuf = n.toLocal8Bit(); const char *str = tmpBuf.data(); int h = 0, s = 100; while (*str) { h = (h * 37 + s * (unsigned) * str) % 256; s = (s * 17 + h * (unsigned) * str) % 192; str++; } return QColor::fromHsv(h, 64 + s, 192); } QMimeType Inode::mimeType() const { if (!_mimeSet) { QMimeDatabase db; _mimeType = db.mimeTypeForUrl(QUrl::fromLocalFile(path())); _mimeSet = true; } return _mimeType; } QString Inode::text(int i) const { if (i == 0) { QString name; if (_dirPeer) { name = _dirPeer->name(); if (!name.endsWith(QLatin1Char('/'))) { name += QLatin1Char('/'); } } else if (_filePeer) { name = _filePeer->name(); } return name; } if (i == 1) { QString text; double s = size(); if (s < 1000) { text = QStringLiteral("%1 B").arg((int)(s + .5)); } else if (s < 10 * 1024) { text = QStringLiteral("%1 kB").arg(KGlobal::locale()->formatNumber(s / 1024 + .005, 2)); } else if (s < 100 * 1024) { text = QStringLiteral("%1 kB").arg(KGlobal::locale()->formatNumber(s / 1024 + .05, 1)); } else if (s < 1000 * 1024) { text = QStringLiteral("%1 kB").arg((int)(s / 1024 + .5)); } else if (s < 10 * 1024 * 1024) { text = QStringLiteral("%1 MB").arg(KGlobal::locale()->formatNumber(s / 1024 / 1024 + .005, 2)); } else if (s < 100 * 1024 * 1024) { text = QStringLiteral("%1 MB").arg(KGlobal::locale()->formatNumber(s / 1024 / 1024 + .05, 1)); } else if (s < 1000 * 1024 * 1024) { text = QStringLiteral("%1 MB").arg((int)(s / 1024 / 1024 + .5)); } else { text = QStringLiteral("%1 GB").arg(KGlobal::locale()->formatNumber(s / 1024 / 1024 / 1024 + .005, 2)); } if (_sizeEstimation > 0) { text += '+'; } return text; } if ((i == 2) || (i == 3)) { /* file/dir count makes no sense for files */ if (_filePeer) { return QString(); } QString text; unsigned int f = (i == 2) ? fileCount() : dirCount(); if (f > 0) { while (f > 1000) { text = QStringLiteral("%1 %2").arg(QString::number(f).right(3)).arg(text); f /= 1000; } text = QStringLiteral("%1 %2").arg(QString::number(f)).arg(text); if (_fileCountEstimation > 0) { text += '+'; } } return text; } if (i == 4) { return _info.lastModified().toString(); } if (i == 5) { return _info.owner(); } if (i == 6) { return _info.group(); } if (i == 7) { return mimeType().comment(); } return QString(); } QPixmap Inode::pixmap(int i) const { if (i != 0) { return QPixmap(); } if (!_mimePixmapSet) { QUrl u = QUrl::fromLocalFile(path()); _mimePixmap = KIconLoader::global()->loadMimeTypeIcon(KIO::iconNameForUrl(u), KIconLoader::Small); _mimePixmapSet = true; } return _mimePixmap; } diff --git a/plugins/fsview/scan.cpp b/plugins/fsview/scan.cpp index 700cfe882..f654ae5a9 100644 --- a/plugins/fsview/scan.cpp +++ b/plugins/fsview/scan.cpp @@ -1,436 +1,437 @@ /* This file is part of FSView. Copyright (C) 2002, 2003 Josef Weidendorfer KCachegrind is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include -#include -#include +#include "scan.h" + +#include +#include +#include #include #include #include #include -#include "scan.h" #include "inode.h" // ScanManager ScanManager::ScanManager() { _topDir = 0; _listener = 0; } ScanManager::ScanManager(const QString &path) { _topDir = 0; _listener = 0; setTop(path); } ScanManager::~ScanManager() { stopScan(); delete _topDir; } void ScanManager::setListener(ScanListener *l) { _listener = l; } ScanDir *ScanManager::setTop(const QString &path, int data) { stopScan(); if (_topDir) { delete _topDir; _topDir = 0; } if (!path.isEmpty()) { _topDir = new ScanDir(path, this, 0, data); } return _topDir; } bool ScanManager::scanRunning() { if (!_topDir) { return false; } return _topDir->scanRunning(); } void ScanManager::startScan(ScanDir *from) { if (!_topDir) { return; } if (!from) { from = _topDir; } if (scanRunning()) { stopScan(); } from->clear(); if (from->parent()) { from->parent()->setupChildRescan(); } _list.append(new ScanItem(from->path(), from)); } void ScanManager::stopScan() { if (!_topDir) { return; } if (0) kDebug(90100) << "ScanManager::stopScan, scanLength " << _list.count() << endl; while (!_list.isEmpty()) { ScanItem *si = _list.takeFirst(); si->dir->finish(); delete si; } } int ScanManager::scan(int data) { if (_list.isEmpty()) { return false; } ScanItem *si = _list.takeFirst(); int newCount = si->dir->scan(si, _list, data); delete si; return newCount; } // ScanFile ScanFile::ScanFile() { _size = 0; _listener = 0; } ScanFile::ScanFile(const QString &n, KIO::fileoffset_t s) { _name = n; _size = s; _listener = 0; } ScanFile::~ScanFile() { if (_listener) { _listener->destroyed(this); } } // ScanDir ScanDir::ScanDir() { _dirty = true; _dirsFinished = -1; /* scan not started */ _parent = Q_NULLPTR; _manager = 0; _listener = 0; _data = 0; } ScanDir::ScanDir(const QString &n, ScanManager *m, ScanDir *p, int data) : _name(n) { _dirty = true; _dirsFinished = -1; /* scan not started */ _parent = p; _manager = m; _listener = 0; _data = data; } ScanDir::~ScanDir() { if (_listener) { _listener->destroyed(this); } } void ScanDir::setListener(ScanListener *l) { _listener = l; } QString ScanDir::path() { if (_parent) { QString p = _parent->path(); if (!p.endsWith(QLatin1Char('/'))) { p += QLatin1Char('/'); } return p + _name; } return _name; } void ScanDir::clear() { _dirty = true; _dirsFinished = -1; /* scan not started */ _files.clear(); _dirs.clear(); } void ScanDir::update() { if (!_dirty) { return; } _dirty = false; _fileCount = 0; _dirCount = 0; _size = 0; if (_dirsFinished == -1) { return; } if (_files.count() > 0) { _fileCount += _files.count(); _size = _fileSize; } if (_dirs.count() > 0) { _dirCount += _dirs.count(); ScanDirVector::iterator it; for (it = _dirs.begin(); it != _dirs.end(); ++it) { (*it).update(); _fileCount += (*it)._fileCount; _dirCount += (*it)._dirCount; _size += (*it)._size; } } } bool ScanDir::isForbiddenDir(QString &d) { static QSet *s = 0; if (!s) { s = new QSet; // directories without real files on Linux // TODO: should be OS specific s->insert(QStringLiteral("/proc")); s->insert(QStringLiteral("/dev")); s->insert(QStringLiteral("/sys")); } return (s->contains(d)); } int ScanDir::scan(ScanItem *si, ScanItemList &list, int data) { clear(); _dirsFinished = 0; _fileSize = 0; _dirty = true; if (isForbiddenDir(si->absPath)) { if (_parent) { _parent->subScanFinished(); } return 0; } QUrl u = QUrl::fromLocalFile(si->absPath); if (!KUrlAuthorized::authorizeUrlAction(QStringLiteral("list"), QUrl(), u)) { if (_parent) { _parent->subScanFinished(); } return 0; } QDir d(si->absPath); const QStringList fileList = d.entryList(QDir::Files | QDir::Hidden | QDir::NoSymLinks); if (fileList.count() > 0) { QT_STATBUF buff; _files.reserve(fileList.count()); QStringList::ConstIterator it; for (it = fileList.constBegin(); it != fileList.constEnd(); ++it) { QString tmp(si->absPath + QLatin1Char('/') + (*it)); if (QT_LSTAT(tmp.toStdString().c_str(), &buff) != 0) { continue; } _files.append(ScanFile(*it, buff.st_size)); _fileSize += buff.st_size; } } const QStringList dirList = d.entryList(QDir::Dirs | QDir::Hidden | QDir::NoSymLinks | QDir::NoDotAndDotDot); if (dirList.count() > 0) { _dirs.reserve(dirList.count()); QStringList::ConstIterator it; for (it = dirList.constBegin(); it != dirList.constEnd(); ++it) { _dirs.append(ScanDir(*it, _manager, this, data)); QString newpath = si->absPath; if (!newpath.endsWith(QChar('/'))) { newpath.append("/"); } newpath.append(*it); list.append(new ScanItem(newpath, &(_dirs.last()))); } _dirCount += _dirs.count(); } callScanStarted(); callSizeChanged(); if (_dirs.count() == 0) { callScanFinished(); if (_parent) { _parent->subScanFinished(); } } return _dirs.count(); } void ScanDir::subScanFinished() { _dirsFinished++; callSizeChanged(); if (0) kDebug(90100) << "ScanDir::subScanFinished [" << path() << "]: " << _dirsFinished << "/" << _dirs.count() << endl; if (_dirsFinished < _dirs.count()) { return; } /* all subdirs read */ callScanFinished(); if (_parent) { _parent->subScanFinished(); } } void ScanDir::finish() { if (scanRunning()) { _dirsFinished = _dirs.count(); callScanFinished(); } if (_parent) { _parent->finish(); } } void ScanDir::setupChildRescan() { if (_dirs.count() == 0) { return; } _dirsFinished = 0; ScanDirVector::iterator it; for (it = _dirs.begin(); it != _dirs.end(); ++it) if ((*it).scanFinished()) { _dirsFinished++; } if (_parent && (_dirsFinished < _dirs.count())) { _parent->setupChildRescan(); } callScanStarted(); } void ScanDir::callScanStarted() { if (0) kDebug(90100) << "ScanDir:Started [" << path() << "]: size " << size() << ", files " << fileCount() << endl; ScanListener *mListener = _manager ? _manager->listener() : 0; if (_listener) { _listener->scanStarted(this); } if (mListener) { mListener->scanStarted(this); } } void ScanDir::callSizeChanged() { if (0) kDebug(90100) << ". [" << path() << "]: size " << size() << ", files " << fileCount() << endl; _dirty = true; if (_parent) { _parent->callSizeChanged(); } ScanListener *mListener = _manager ? _manager->listener() : 0; if (_listener) { _listener->sizeChanged(this); } if (mListener) { mListener->sizeChanged(this); } } void ScanDir::callScanFinished() { if (0) kDebug(90100) << "ScanDir:Finished [" << path() << "]: size " << size() << ", files " << fileCount() << endl; ScanListener *mListener = _manager ? _manager->listener() : 0; if (_listener) { _listener->scanFinished(this); } if (mListener) { mListener->scanFinished(this); } } diff --git a/plugins/kimgalleryplugin/imgallerydialog.cpp b/plugins/kimgalleryplugin/imgallerydialog.cpp index 872a4b65b..16a49476a 100644 --- a/plugins/kimgalleryplugin/imgallerydialog.cpp +++ b/plugins/kimgalleryplugin/imgallerydialog.cpp @@ -1,484 +1,482 @@ /* This file is part of the KDE project Copyright (C) 2001, 2003 Lukas Tinkl Andreas Schlapbach This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include -#include -#include - -#include -#include -#include -//Added by qt3to4: +#include "imgallerydialog.h" + +#include +#include +#include +#include +#include +#include #include #include #include #include +#include #include #include #include #include #include #include #include #include -#include -#include "imgallerydialog.h" - KIGPDialog::KIGPDialog(QWidget *parent, const QString &path) : KPageDialog(parent) { setStandardButtons(QDialogButtonBox::RestoreDefaults | QDialogButtonBox::Ok | QDialogButtonBox::Cancel); buttonBox()->button(QDialogButtonBox::Ok)->setDefault(true); setModal(true); setFaceType(List); m_path = path; setWindowTitle(i18nc("@title:window", "Create Image Gallery")); KGuiItem::assign(buttonBox()->button(QDialogButtonBox::Ok), KGuiItem(i18n("Create"), QStringLiteral("imagegallery"))); m_config = new KConfig(QStringLiteral("kimgallerypluginrc"), KConfig::NoGlobals); setupLookPage(path); setupDirectoryPage(path); setupThumbnailPage(path); connect(buttonBox()->button(QDialogButtonBox::RestoreDefaults), SIGNAL(clicked()), this, SLOT(slotDefault())); } void KIGPDialog::slotDefault() { m_title->setText(i18n("Image Gallery for %1", m_path)); m_imagesPerRow->setValue(4); m_imageName->setChecked(true); m_imageSize->setChecked(false); m_imageProperty->setChecked(false); m_fontName->setItemText(m_fontName->currentIndex(), QFontDatabase::systemFont(QFontDatabase::GeneralFont).family()); m_fontSize->setValue(14); m_foregroundColor->setColor(QColor(QStringLiteral("#d0ffd0"))); m_backgroundColor->setColor(QColor(QStringLiteral("#333333"))); m_imageNameReq->setUrl(QUrl::fromLocalFile(m_path + "images.html")); m_recurseSubDir->setChecked(false); m_recursionLevel->setEnabled(false); m_recursionLevel->setValue(0); m_copyOriginalFiles->setChecked(false); m_useCommentFile->setChecked(false); m_commentFileReq->setUrl(QUrl::fromLocalFile(m_path + "comments")); m_commentFileReq->setEnabled(false); m_imageFormat->setItemText(m_imageFormat->currentIndex(), QStringLiteral("JPEG")); m_thumbnailSize->setValue(140); m_colorDepthSet->setChecked(false); m_colorDepth->setItemText(m_colorDepth->currentIndex(), QStringLiteral("8")); } void KIGPDialog::setupLookPage(const QString &path) { QFrame *page = new QFrame(); KPageWidgetItem *pageItem = new KPageWidgetItem(page, i18n("Look")); pageItem->setHeader(i18n("Page Look")); pageItem->setIcon(QIcon::fromTheme("fill-color")); addPage(pageItem); KConfigGroup look = m_config->group("Look"); QVBoxLayout *vlay = new QVBoxLayout(page); vlay->setMargin(0); QLabel *label = new QLabel(i18n("&Page title:"), page); vlay->addWidget(label); m_title = new QLineEdit(i18n("Image Gallery for %1", path), page); vlay->addWidget(m_title); label->setBuddy(m_title); m_imagesPerRow = new KIntNumInput(look.readEntry("ImagesPerRow", 4), page); m_imagesPerRow->setRange(1, 8, 1); m_imagesPerRow->setSliderEnabled(true); m_imagesPerRow->setLabel(i18n("I&mages per row:")); vlay->addWidget(m_imagesPerRow); QGridLayout *grid = new QGridLayout(); grid->setMargin(2); grid->setSpacing(2); vlay->addLayout(grid); m_imageName = new QCheckBox(i18n("Show image file &name"), page); m_imageName->setChecked(look.readEntry("ImageName", true)); grid->addWidget(m_imageName, 0, 0); m_imageSize = new QCheckBox(i18n("Show image file &size"), page); m_imageSize->setChecked(look.readEntry("ImageSize", false)); grid->addWidget(m_imageSize, 0, 1); m_imageProperty = new QCheckBox(i18n("Show image &dimensions"), page); m_imageProperty->setChecked(look.readEntry("ImageProperty", false)); grid->addWidget(m_imageProperty, 1, 0); QHBoxLayout *hlay11 = new QHBoxLayout(); vlay->addLayout(hlay11); m_fontName = new QComboBox(page); QStringList standardFonts; KFontChooser::getFontList(standardFonts, 0); m_fontName->addItems(standardFonts); m_fontName->setItemText(m_fontName->currentIndex(), look.readEntry("FontName", QFontDatabase::systemFont(QFontDatabase::GeneralFont).family())); label = new QLabel(i18n("Fon&t name:"), page); label->setBuddy(m_fontName); hlay11->addWidget(label); hlay11->addStretch(1); hlay11->addWidget(m_fontName); QHBoxLayout *hlay12 = new QHBoxLayout(); vlay->addLayout(hlay12); m_fontSize = new QSpinBox(page); m_fontSize->setMinimum(6); m_fontSize->setMaximum(15); m_fontSize->setSingleStep(1); m_fontSize->setValue(look.readEntry("FontSize", 14)); label = new QLabel(i18n("Font si&ze:"), page); label->setBuddy(m_fontSize); hlay12->addWidget(label); hlay12->addStretch(1); hlay12->addWidget(m_fontSize); QHBoxLayout *hlay1 = new QHBoxLayout(); vlay->addLayout(hlay1); m_foregroundColor = new KColorButton(page); m_foregroundColor->setColor(QColor(look.readEntry("ForegroundColor", "#d0ffd0"))); label = new QLabel(i18n("&Foreground color:"), page); label->setBuddy(m_foregroundColor); hlay1->addWidget(label); hlay1->addStretch(1); hlay1->addWidget(m_foregroundColor); QHBoxLayout *hlay2 = new QHBoxLayout(); vlay->addLayout(hlay2); m_backgroundColor = new KColorButton(page); m_backgroundColor->setColor(QColor(look.readEntry("BackgroundColor", "#333333"))); label = new QLabel(i18n("&Background color:"), page); hlay2->addWidget(label); label->setBuddy(m_backgroundColor); hlay2->addStretch(1); hlay2->addWidget(m_backgroundColor); vlay->addStretch(1); } void KIGPDialog::setupDirectoryPage(const QString &path) { QFrame *page = new QFrame(); KPageWidgetItem *pageItem = new KPageWidgetItem(page, i18n("Folders")); pageItem->setHeader(i18n("Folders")); pageItem->setIcon(QIcon("folder")); addPage(pageItem); KConfigGroup group = m_config->group("Directory"); QVBoxLayout *dvlay = new QVBoxLayout(page); dvlay->setMargin(0); QLabel *label; label = new QLabel(i18n("&Save to HTML file:"), page); dvlay->addWidget(label); QString whatsThis; whatsThis = i18n("

The name of the HTML file this gallery will be saved to.

"); label->setWhatsThis(whatsThis); m_imageNameReq = new KUrlRequester(QUrl::fromLocalFile(QString(path + "images.html")), page); label->setBuddy(m_imageNameReq); dvlay->addWidget(m_imageNameReq); connect(m_imageNameReq, SIGNAL(textChanged(QString)), this, SLOT(imageUrlChanged(QString))); m_imageNameReq->setWhatsThis(whatsThis); const bool recurseSubDir = group.readEntry("RecurseSubDirectories", false); m_recurseSubDir = new QCheckBox(i18n("&Recurse subfolders"), page); m_recurseSubDir->setChecked(recurseSubDir); whatsThis = i18n("

Whether subfolders should be included for the " "image gallery creation or not.

"); m_recurseSubDir->setWhatsThis(whatsThis); const int recursionLevel = group.readEntry("RecursionLevel", 0); m_recursionLevel = new KIntNumInput(recursionLevel, page); m_recursionLevel->setRange(0, 99, 1); m_recursionLevel->setSliderEnabled(true); m_recursionLevel->setLabel(i18n("Rec&ursion depth:")); if (recursionLevel == 0) { m_recursionLevel->setSpecialValueText(i18n("Endless")); } m_recursionLevel->setEnabled(recurseSubDir); whatsThis = i18n("

You can limit the number of folders the " "image gallery creator will traverse to by setting an " "upper bound for the recursion depth.

"); m_recursionLevel->setWhatsThis(whatsThis); connect(m_recurseSubDir, SIGNAL(toggled(bool)), m_recursionLevel, SLOT(setEnabled(bool))); dvlay->addWidget(m_recurseSubDir); dvlay->addWidget(m_recursionLevel); m_copyOriginalFiles = new QCheckBox(i18n("Copy or&iginal files"), page); m_copyOriginalFiles->setChecked(group.readEntry("CopyOriginalFiles", false)); dvlay->addWidget(m_copyOriginalFiles); whatsThis = i18n("

This makes a copy of all images and the gallery will refer " "to these copies instead of the original images.

"); m_copyOriginalFiles->setWhatsThis(whatsThis); const bool useCommentFile = group.readEntry("UseCommentFile", false); m_useCommentFile = new QCheckBox(i18n("Use &comment file"), page); m_useCommentFile->setChecked(useCommentFile); dvlay->addWidget(m_useCommentFile); whatsThis = i18n("

If you enable this option you can specify " "a comment file which will be used for generating " "subtitles for the images.

" "

For details about the file format please see " "the \"What's This?\" help below.

"); m_useCommentFile->setWhatsThis(whatsThis); label = new QLabel(i18n("Comments &file:"), page); label->setEnabled(useCommentFile); dvlay->addWidget(label); whatsThis = i18n("

You can specify the name of the comment file here. " "The comment file contains the subtitles for the images. " "The format of this file is:

" "

FILENAME1:" "
Description" "
" "
FILENAME2:" "
Description" "
" "
and so on

"); label->setWhatsThis(whatsThis); m_commentFileReq = new KUrlRequester(QUrl::fromLocalFile(QString(path + "comments")), page); m_commentFileReq->setEnabled(useCommentFile); label->setBuddy(m_commentFileReq); dvlay->addWidget(m_commentFileReq); m_commentFileReq->setWhatsThis(whatsThis); connect(m_useCommentFile, SIGNAL(toggled(bool)), label, SLOT(setEnabled(bool))); connect(m_useCommentFile, SIGNAL(toggled(bool)), m_commentFileReq, SLOT(setEnabled(bool))); dvlay->addStretch(1); } void KIGPDialog::setupThumbnailPage(const QString &path) { Q_UNUSED(path); QFrame *page = new QFrame(); KPageWidgetItem *pageItem = new KPageWidgetItem(page, i18n("Thumbnails")); pageItem->setHeader(i18n("Thumbnails")); pageItem->setIcon(QIcon::fromTheme("view-preview")); addPage(pageItem); KConfigGroup group = m_config->group("Thumbnails"); QLabel *label; QVBoxLayout *vlay = new QVBoxLayout(page); vlay->setMargin(0); QHBoxLayout *hlay3 = new QHBoxLayout(); vlay->addLayout(hlay3); m_imageFormat = new QComboBox(page); QStringList lstImgageFormat; lstImgageFormat << QStringLiteral("JPEG") << QStringLiteral("PNG"); m_imageFormat->addItems(lstImgageFormat); m_imageFormat->setItemText(m_imageFormat->currentIndex(), group.readEntry("ImageFormat", "JPEG")); label = new QLabel(i18n("Image format f&or the thumbnails:"), page); hlay3->addWidget(label); label->setBuddy(m_imageFormat); hlay3->addStretch(1); hlay3->addWidget(m_imageFormat); m_thumbnailSize = new KIntNumInput(group.readEntry("ThumbnailSize", 140), page); m_thumbnailSize->setRange(10, 1000, 1); m_thumbnailSize->setLabel(i18n("Thumbnail size:")); m_thumbnailSize->setSliderEnabled(true); vlay->addWidget(m_thumbnailSize); QGridLayout *grid = new QGridLayout(); grid->setMargin(2); grid->setSpacing(2); vlay->addLayout(grid); QHBoxLayout *hlay4 = new QHBoxLayout(); vlay->addLayout(hlay4); const bool colorDepthSet = group.readEntry("ColorDepthSet", false); m_colorDepthSet = new QCheckBox(i18n("&Set different color depth:"), page); m_colorDepthSet->setChecked(colorDepthSet); hlay4->addWidget(m_colorDepthSet); m_colorDepth = new QComboBox(page); QStringList lst; lst << QStringLiteral("1") << QStringLiteral("8") << QStringLiteral("16") << QStringLiteral("32"); m_colorDepth->addItems(lst); m_colorDepth->setItemText(m_colorDepth->currentIndex(), group.readEntry("ColorDepth", "8")); m_colorDepth->setEnabled(colorDepthSet); hlay4->addWidget(m_colorDepth); connect(m_colorDepthSet, SIGNAL(toggled(bool)), m_colorDepth, SLOT(setEnabled(bool))); vlay->addStretch(1); } void KIGPDialog::writeConfig() { KConfigGroup group = m_config->group("Look"); group.writeEntry("ImagesPerRow", getImagesPerRow()); group.writeEntry("ImageName", printImageName()); group.writeEntry("ImageSize", printImageSize()); group.writeEntry("ImageProperty", printImageProperty()); group.writeEntry("FontName", getFontName()); group.writeEntry("FontSize", getFontSize()); group.writeEntry("ForegroundColor", getForegroundColor().name()); group.writeEntry("BackgroundColor", getBackgroundColor().name()); group = m_config->group("Directory"); group.writeEntry("RecurseSubDirectories", recurseSubDirectories()); group.writeEntry("RecursionLevel", recursionLevel()); group.writeEntry("CopyOriginalFiles", copyOriginalFiles()); group.writeEntry("UseCommentFile", useCommentFile()); group = m_config->group("Thumbnails"); group.writeEntry("ThumbnailSize", getThumbnailSize()); group.writeEntry("ColorDepth", getColorDepth()); group.writeEntry("ColorDepthSet", colorDepthSet()); group.writeEntry("ImageFormat", getImageFormat()); group.sync(); } KIGPDialog::~KIGPDialog() { delete m_config; } void KIGPDialog::imageUrlChanged(const QString &url) { buttonBox()->button(QDialogButtonBox::Ok)->setEnabled(!url.isEmpty()); } bool KIGPDialog::printImageName() const { return m_imageName->isChecked(); } bool KIGPDialog::printImageSize() const { return m_imageSize->isChecked(); } bool KIGPDialog::printImageProperty() const { return m_imageProperty->isChecked(); } bool KIGPDialog::recurseSubDirectories() const { return m_recurseSubDir->isChecked(); } int KIGPDialog::recursionLevel() const { return m_recursionLevel->value(); } bool KIGPDialog::copyOriginalFiles() const { return m_copyOriginalFiles->isChecked(); } bool KIGPDialog::useCommentFile() const { return m_useCommentFile->isChecked(); } int KIGPDialog::getImagesPerRow() const { return m_imagesPerRow->value(); } int KIGPDialog::getThumbnailSize() const { return m_thumbnailSize->value(); } int KIGPDialog::getColorDepth() const { return m_colorDepth->currentText().toInt(); } bool KIGPDialog::colorDepthSet() const { return m_colorDepthSet->isChecked(); } const QString KIGPDialog::getTitle() const { return m_title->text(); } const QUrl KIGPDialog::getImageUrl() const { return m_imageNameReq->url(); } const QString KIGPDialog::getCommentFile() const { return m_commentFileReq->url().toLocalFile(); } const QString KIGPDialog::getFontName() const { return m_fontName->currentText(); } const QString KIGPDialog::getFontSize() const { return m_fontSize->text(); } const QColor KIGPDialog::getBackgroundColor() const { return m_backgroundColor->color(); } const QColor KIGPDialog::getForegroundColor() const { return m_foregroundColor->color(); } const QString KIGPDialog::getImageFormat() const { return m_imageFormat->currentText(); } diff --git a/plugins/kimgalleryplugin/imgalleryplugin.cpp b/plugins/kimgalleryplugin/imgalleryplugin.cpp index 0d3244535..e9e311e10 100644 --- a/plugins/kimgalleryplugin/imgalleryplugin.cpp +++ b/plugins/kimgalleryplugin/imgalleryplugin.cpp @@ -1,540 +1,541 @@ /* This file is part of the KDE project Copyright (C) 2001, 2003 Lukas Tinkl Andreas Schlapbach This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include -#include -#include -#include -#include -#include -#include +#include "imgalleryplugin.h" + +#include +#include +#include +#include +#include +#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "imgallerydialog.h" -#include "imgalleryplugin.h" //KDELibs4Support #include K_PLUGIN_FACTORY(KImGalleryPluginFactory, registerPlugin();) static QString directory(const QUrl &url) { return url.adjusted(QUrl::StripTrailingSlash).adjusted(QUrl::RemoveFilename).toLocalFile(); } KImGalleryPlugin::KImGalleryPlugin(QObject *parent, const QVariantList &) : KParts::Plugin(parent), m_commentMap(0) { QAction *a = actionCollection()->addAction(QStringLiteral("create_img_gallery")); a->setText(i18n("&Create Image Gallery...")); a->setIcon(QIcon::fromTheme(QStringLiteral("imagegallery"))); actionCollection()->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::Key_I)); connect(a, SIGNAL(triggered()), this, SLOT(slotExecute())); } void KImGalleryPlugin::slotExecute() { m_progressDlg = 0L; if (!parent()) { KMessageBox::sorry(0L, i18n("Could not create the plugin, please report a bug.")); return; } m_part = qobject_cast(parent()); if (!m_part || !m_part->url().isLocalFile()) { //TODO support remote URLs too? KMessageBox::sorry(m_part->widget(), i18n("Creating an image gallery works only on local folders.")); return; } QString path = m_part->url().adjusted(QUrl::StripTrailingSlash).toLocalFile() + '/'; m_configDlg = new KIGPDialog(m_part->widget(), path); if (m_configDlg->exec() == QDialog::Accepted) { kDebug(90170) << "dialog is ok"; m_configDlg->writeConfig(); m_copyFiles = m_configDlg->copyOriginalFiles(); m_recurseSubDirectories = m_configDlg->recurseSubDirectories(); m_useCommentFile = m_configDlg->useCommentFile(); m_imagesPerRow = m_configDlg->getImagesPerRow(); QUrl url(m_configDlg->getImageUrl()); if (!url.isEmpty() && url.isValid()) { m_progressDlg = new QProgressDialog(m_part->widget()); QObject::connect(m_progressDlg, SIGNAL(canceled()), this, SLOT(slotCancelled())); m_progressDlg->setLabelText(i18n("Creating thumbnails")); QPushButton *button = new QPushButton(m_progressDlg); KGuiItem::assign(button, KStandardGuiItem::cancel()); m_progressDlg->setCancelButton(button); m_cancelled = false; m_progressDlg->show(); if (createHtml(url, m_part->url().path(), m_configDlg->recursionLevel() > 0 ? m_configDlg->recursionLevel() + 1 : 0, m_configDlg->getImageFormat())) { KToolInvocation::invokeBrowser(url.url()); // Open a browser to show the result } else { deleteCancelledGallery(url, m_part->url().path(), m_configDlg->recursionLevel() > 0 ? m_configDlg->recursionLevel() + 1 : 0, m_configDlg->getImageFormat()); } } } delete m_progressDlg; } bool KImGalleryPlugin::createDirectory(const QDir &dir, const QString &imgGalleryDir, const QString &dirName) { QDir thumb_dir(dir); if (!thumb_dir.exists()) { thumb_dir.setPath(imgGalleryDir); if (!(thumb_dir.mkdir(dirName/*, false*/))) { KMessageBox::sorry(m_part->widget(), i18n("Could not create folder: %1", thumb_dir.path())); return false; } else { thumb_dir.setPath(imgGalleryDir + '/' + dirName + '/'); return true; } } else { return true; } } void KImGalleryPlugin::createHead(QTextStream &stream) { const QString chsetName = QTextCodec::codecForLocale()->name(); stream << "" << endl; stream << "" << endl; stream << "" << endl; stream << "" << endl; stream << "" << m_configDlg->getTitle().toHtmlEscaped() << "" << endl; stream << "" << endl; stream << "" << endl; createCSSSection(stream); stream << "" << endl; } void KImGalleryPlugin::createCSSSection(QTextStream &stream) { const QString backgroundColor = m_configDlg->getBackgroundColor().name(); const QString foregroundColor = m_configDlg->getForegroundColor().name(); //adding a touch of style stream << "" << endl; } QString KImGalleryPlugin::extension(const QString &imageFormat) { if (imageFormat == QLatin1String("PNG")) { return QStringLiteral(".png"); } if (imageFormat == QLatin1String("JPEG")) { return QStringLiteral(".jpg"); } Q_ASSERT(false); return QString(); } void KImGalleryPlugin::createBody(QTextStream &stream, const QString &sourceDirName, const QStringList &subDirList, const QDir &imageDir, const QUrl &url, const QString &imageFormat) { int numOfImages = imageDir.count(); const QString imgGalleryDir = directory(url); const QString today(KLocale::global()->formatDate(QDate::currentDate())); stream << "\n

" << m_configDlg->getTitle().toHtmlEscaped() << "

" << endl; stream << i18n("Number of images: %1", numOfImages) << "
" << endl; stream << i18n("Created on: %1", today) << "

" << endl; stream << "
" << endl; if (m_recurseSubDirectories && subDirList.count() > 2) { //subDirList.count() is always >= 2 because of the "." and ".." directories stream << i18n("Subfolders:") << "
" << endl; for (QStringList::ConstIterator it = subDirList.constBegin(); it != subDirList.constEnd(); it++) { if (*it == QLatin1String(".") || *it == QLatin1String("..")) { continue; //disregard the "." and ".." directories } stream << "
" << *it << "
" << endl; } stream << "
" << endl; } stream << "" << endl; //table with images int imgIndex; QFileInfo imginfo; QPixmap imgProp; for (imgIndex = 0; !m_cancelled && (imgIndex < numOfImages);) { stream << "" << endl; for (int col = 0; !m_cancelled && (col < m_imagesPerRow) && (imgIndex < numOfImages); col++) { const QString imgName = imageDir[imgIndex]; if (m_copyFiles) { stream << "" << endl; m_progressDlg->setMaximum(numOfImages); m_progressDlg->setValue(imgIndex); qApp->processEvents(); imgIndex++; } stream << "" << endl; } //close the HTML stream << "
\n"; } else { stream << "\n"; } if (createThumb(imgName, sourceDirName, imgGalleryDir, imageFormat)) { const QString imgPath("thumbs/" + imgName + extension(imageFormat)); stream << "\"""; m_progressDlg->setLabelText(i18n("Created thumbnail for: \n%1", imgName)); } else { kDebug(90170) << "Creating thumbnail for " << imgName << " failed"; m_progressDlg->setLabelText(i18n("Creating thumbnail for: \n%1\n failed", imgName)); } stream << "" << endl; if (m_configDlg->printImageName()) { stream << "
" << imgName << "
" << endl; } if (m_configDlg->printImageProperty()) { #ifdef __GNUC__ #warning "kde4: verify it : imageDir.absoluteFilePath(imgName,true)"; #endif imgProp.load(imageDir.absoluteFilePath(imgName)); stream << "
" << imgProp.width() << " x " << imgProp.height() << "
" << endl; } if (m_configDlg->printImageSize()) { imginfo.setFile(imageDir, imgName); stream << "
(" << (imginfo.size() / 1024) << " " << i18n("KiB") << ")" << "
" << endl; } if (m_useCommentFile) { QString imgComment = (*m_commentMap)[imgName]; stream << "
" << imgComment.toHtmlEscaped() << "
" << endl; } stream << "
\n\n" << endl; } bool KImGalleryPlugin::createHtml(const QUrl &url, const QString &sourceDirName, int recursionLevel, const QString &imageFormat) { if (m_cancelled) { return false; } if (!parent() || !parent()->inherits("DolphinPart")) { return false; } QStringList subDirList; if (m_recurseSubDirectories && (recursionLevel >= 0)) { //recursionLevel == 0 means endless QDir toplevel_dir = QDir(sourceDirName); toplevel_dir.setFilter(QDir::Dirs | QDir::Readable | QDir::Writable); subDirList = toplevel_dir.entryList(); for (QStringList::ConstIterator it = subDirList.constBegin(); it != subDirList.constEnd() && !m_cancelled; it++) { const QString currentDir = *it; if (currentDir == QLatin1String(".") || currentDir == QLatin1String("..")) { continue; //disregard the "." and ".." directories } QDir subDir = QDir(directory(url) + '/' + currentDir); if (!subDir.exists()) { subDir.setPath(directory(url)); if (!(subDir.mkdir(currentDir/*, false*/))) { KMessageBox::sorry(m_part->widget(), i18n("Could not create folder: %1", subDir.path())); continue; } else { subDir.setPath(directory(url) + '/' + currentDir); } } if (!createHtml(QUrl::fromLocalFile(subDir.path() + '/' + url.fileName()), sourceDirName + '/' + currentDir, recursionLevel > 1 ? recursionLevel - 1 : 0, imageFormat)) { return false; } } } if (m_useCommentFile) { loadCommentFile(); } kDebug(90170) << "sourceDirName: " << sourceDirName; //We're interested in only the patterns, so look for the first | //#### perhaps an accessor should be added to KImageIO instead? QString filter = KImageIO::pattern(KImageIO::Reading).section('|', 0, 0); QDir imageDir(sourceDirName, filter.toLatin1(), QDir::Name | QDir::IgnoreCase, QDir::Files | QDir::Readable); const QString imgGalleryDir = directory(url); kDebug(90170) << "imgGalleryDir: " << imgGalleryDir; // Create the "thumbs" subdirectory if necessary QDir thumb_dir(imgGalleryDir + QLatin1String("/thumbs/")); if (createDirectory(thumb_dir, imgGalleryDir, QStringLiteral("thumbs")) == false) { return false; } // Create the "images" subdirectory if necessary QDir images_dir(imgGalleryDir + QLatin1String("/images/")); if (m_copyFiles) { if (createDirectory(images_dir, imgGalleryDir, QStringLiteral("images")) == false) { return false; } } QFile file(url.path()); kDebug(90170) << "url.path(): " << url.path() << ", thumb_dir: " << thumb_dir.path() << ", imageDir: " << imageDir.path() << endl; if (imageDir.exists() && file.open(QIODevice::WriteOnly)) { QTextStream stream(&file); stream.setCodec(QTextCodec::codecForLocale()); createHead(stream); createBody(stream, sourceDirName, subDirList, imageDir, url, imageFormat); //ugly file.close(); return !m_cancelled; } else { QString path = url.toLocalFile(); if (!path.endsWith("/")) { path += '/'; } KMessageBox::sorry(m_part->widget(), i18n("Could not open file: %1", path)); return false; } } void KImGalleryPlugin::deleteCancelledGallery(const QUrl &url, const QString &sourceDirName, int recursionLevel, const QString &imageFormat) { if (m_recurseSubDirectories && (recursionLevel >= 0)) { QStringList subDirList; QDir toplevel_dir = QDir(sourceDirName); toplevel_dir.setFilter(QDir::Dirs); subDirList = toplevel_dir.entryList(); for (QStringList::ConstIterator it = subDirList.constBegin(); it != subDirList.constEnd(); it++) { if (*it == QLatin1String(".") || *it == QLatin1String("..") || *it == QLatin1String("thumbs") || (m_copyFiles && *it == QLatin1String("images"))) { continue; //disregard the "." and ".." directories } deleteCancelledGallery(QUrl::fromLocalFile(directory(url) + '/' + *it + '/' + url.fileName()), sourceDirName + '/' + *it, recursionLevel > 1 ? recursionLevel - 1 : 0, imageFormat); } } const QString imgGalleryDir = directory(url); QDir thumb_dir(imgGalleryDir + QLatin1String("/thumbs/")); QDir images_dir(imgGalleryDir + QLatin1String("/images/")); QDir imageDir(sourceDirName, QStringLiteral("*.png *.PNG *.gif *.GIF *.jpg *.JPG *.jpeg *.JPEG *.bmp *.BMP"), QDir::Name | QDir::IgnoreCase, QDir::Files | QDir::Readable); QFile file(url.path()); // Remove the image file .. file.remove(); // ..all the thumbnails .. for (uint i = 0; i < imageDir.count(); i++) { const QString imgName = imageDir[i]; const QString imgNameFormat = imgName + extension(imageFormat); bool isRemoved = thumb_dir.remove(imgNameFormat); kDebug(90170) << "removing: " << thumb_dir.path() << "/" << imgNameFormat << "; " << isRemoved; } // ..and the thumb directory thumb_dir.rmdir(thumb_dir.path()); // ..and the images directory if images were to be copied if (m_copyFiles) { for (uint i = 0; i < imageDir.count(); i++) { const QString imgName = imageDir[i]; bool isRemoved = images_dir.remove(imgName); kDebug(90170) << "removing: " << images_dir.path() << "/" << imgName << "; " << isRemoved; } images_dir.rmdir(images_dir.path()); } } void KImGalleryPlugin::loadCommentFile() { QFile file(m_configDlg->getCommentFile()); if (file.open(QIODevice::ReadOnly)) { kDebug(90170) << "File opened."; QTextStream *m_textStream = new QTextStream(&file); m_textStream->setCodec(QTextCodec::codecForLocale()); delete m_commentMap; m_commentMap = new CommentMap; QString picName, picComment, curLine, curLineStripped; while (!m_textStream->atEnd()) { curLine = m_textStream->readLine(); curLineStripped = curLine.trimmed(); // Lines starting with '#' are comment if (!(curLineStripped.isEmpty()) && !curLineStripped.startsWith(QLatin1String("#"))) { if (curLineStripped.endsWith(QLatin1String(":"))) { picComment.clear(); picName = curLineStripped.left(curLineStripped.length() - 1); kDebug(90170) << "picName: " << picName; } else { do { //kDebug(90170) << "picComment"; picComment += curLine + '\n'; curLine = m_textStream->readLine(); } while (!m_textStream->atEnd() && !(curLine.trimmed().isEmpty()) && !curLine.trimmed().startsWith(QLatin1String("#"))); //kDebug(90170) << "Pic comment: " << picComment; m_commentMap->insert(picName, picComment); } } } CommentMap::ConstIterator it; for (it = m_commentMap->constBegin(); it != m_commentMap->constEnd(); ++it) { kDebug(90170) << "picName: " << it.key() << ", picComment: " << it.value(); } file.close(); kDebug(90170) << "File closed."; delete m_textStream; } else { KMessageBox::sorry(m_part->widget(), i18n("Could not open file: %1", m_configDlg->getCommentFile())); m_useCommentFile = false; } } bool KImGalleryPlugin::createThumb(const QString &imgName, const QString &sourceDirName, const QString &imgGalleryDir, const QString &imageFormat) { QImage img; const QString pixPath = sourceDirName + QLatin1String("/") + imgName; if (m_copyFiles) { QUrl srcURL = QUrl::fromLocalFile(pixPath); //kDebug(90170) << "srcURL: " << srcURL; QUrl destURL = QUrl::fromLocalFile(imgGalleryDir + QLatin1String("/images/") + imgName); //kDebug(90170) << "destURL: " << destURL; KIO::NetAccess::file_copy(srcURL, destURL, static_cast(parent())->widget()); } const QString imgNameFormat = imgName + extension(imageFormat); const QString thumbDir = imgGalleryDir + QLatin1String("/thumbs/"); int extent = m_configDlg->getThumbnailSize(); // this code is stolen from kdebase/kioslave/thumbnail/imagecreator.cpp // (c) 2000 gis and malte m_imgWidth = 120; // Setting the size of the images is m_imgHeight = 90; // required to generate faster 'loading' pages if (img.load(pixPath)) { int w = img.width(), h = img.height(); // scale to pixie size // kDebug(90170) << "w: " << w << " h: " << h; // Resizing if to big if (w > extent || h > extent) { if (w > h) { h = (int)((double)(h * extent) / w); if (h == 0) { h = 1; } w = extent; Q_ASSERT(h <= extent); } else { w = (int)((double)(w * extent) / h); if (w == 0) { w = 1; } h = extent; Q_ASSERT(w <= extent); } const QImage scaleImg(img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); if (scaleImg.width() != w || scaleImg.height() != h) { kDebug(90170) << "Resizing failed. Aborting."; return false; } img = scaleImg; if (m_configDlg->colorDepthSet() == true) { QImage::Format format; switch (m_configDlg->getColorDepth()) { case 1: format = QImage::Format_Mono; break; case 8: format = QImage::Format_Indexed8; break; case 16: format = QImage::Format_RGB16; break; case 32: default: format = QImage::Format_RGB32; break; } const QImage depthImg(img.convertToFormat(format)); img = depthImg; } } kDebug(90170) << "Saving thumbnail to: " << thumbDir + imgNameFormat; if (!img.save(thumbDir + imgNameFormat, imageFormat.toLatin1())) { kDebug(90170) << "Saving failed. Aborting."; return false; } m_imgWidth = w; m_imgHeight = h; return true; } return false; } void KImGalleryPlugin::slotCancelled() { m_cancelled = true; } #include "imgalleryplugin.moc" diff --git a/plugins/microformat/pluginbase.cpp b/plugins/microformat/pluginbase.cpp index b5bbda0ff..d71c6ba45 100644 --- a/plugins/microformat/pluginbase.cpp +++ b/plugins/microformat/pluginbase.cpp @@ -1,43 +1,43 @@ /* Copyright (C) 2004 Teemu Rytilahti Copyright (C) 2005 George Staikos This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ +#include "pluginbase.h" + #include #include -#include "pluginbase.h" - #include PluginBase::PluginBase() { } PluginBase::~PluginBase() { } void PluginBase::addVCardViaDCOP(const QString &card) { QDBusInterface kaddressbook("org.kde.kaddressbook", "/AddressBookService", "org.kde.adressbook.service"); kaddressbook.call("importVCardFromData", card); } diff --git a/plugins/minitools/minitoolsplugin.cpp b/plugins/minitools/minitoolsplugin.cpp index 2f15575e1..de864dc1e 100644 --- a/plugins/minitools/minitoolsplugin.cpp +++ b/plugins/minitools/minitoolsplugin.cpp @@ -1,170 +1,169 @@ /* Copyright (c) 2003 Alexander Kellett This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License (LGPL) as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include - +#include "minitoolsplugin.h" #include "minitoolsplugindebug.h" +#include + #include #include #include #include #include #include -#include #include #include #include #include #include #include -#include "minitoolsplugin.h" K_PLUGIN_FACTORY(MinitoolsPluginFactory, registerPlugin();) MinitoolsPlugin::MinitoolsPlugin(QObject *parent, const QVariantList &) : KParts::Plugin(parent) { m_part = (parent && parent->inherits("KHTMLPart")) ? static_cast(parent) : 0L; m_pMinitoolsMenu = new KActionMenu(QIcon::fromTheme(QStringLiteral("minitools")), i18n("&Minitools"), actionCollection()); actionCollection()->addAction(QStringLiteral("minitools"), m_pMinitoolsMenu); m_pMinitoolsMenu->setDelayed(false); m_pMinitoolsMenu->setEnabled(true); connect(m_pMinitoolsMenu->menu(), SIGNAL(aboutToShow()), this, SLOT(slotAboutToShow())); } MinitoolsPlugin::~MinitoolsPlugin() { ; } void MinitoolsPlugin::slotAboutToShow() { m_minitoolsList.clear(); KXBELBookmarkImporterImpl importer; connect(&importer, SIGNAL(newBookmark(QString,QString,QString)), SLOT(newBookmarkCallback(QString,QString,QString))); connect(&importer, SIGNAL(endFolder()), SLOT(endFolderCallback())); QString filename = minitoolsFilename(true); if (!filename.isEmpty() && QFile::exists(filename)) { importer.setFilename(filename); importer.parse(); } filename = minitoolsFilename(false); if (!filename.isEmpty() && QFile::exists(filename)) { importer.setFilename(filename); importer.parse(); } m_pMinitoolsMenu->menu()->clear(); int count = m_pMinitoolsMenu->menu()->actions().count(); // why not 0??? bool gotSep = true; // don't start with a sep if (m_minitoolsList.count() > 0) { MinitoolsList::ConstIterator e = m_minitoolsList.constBegin(); for (; e != m_minitoolsList.constEnd(); ++e) { if (((*e).first == QLatin1String("-")) && ((*e).second == QLatin1String("-")) ) { if (!gotSep) { m_pMinitoolsMenu->menu()->addSeparator(); } gotSep = true; count++; } else { QString str = (*e).first; // emsquieezzy thingy? if (str.length() > 48) { str.truncate(48); str.append("..."); } QAction *action = m_pMinitoolsMenu->menu()->addAction( str, this, SLOT(slotItemSelected())); action->setData(qVariantFromValue(++count)); gotSep = false; } } } if (!gotSep) { // don't have an extra sep m_pMinitoolsMenu->menu()->addSeparator(); } m_pMinitoolsMenu->menu() ->addAction(i18n("&Edit Minitools"), this, SLOT(slotEditBookmarks())); } void MinitoolsPlugin::newBookmarkCallback( const QString &text, const QString &url, const QString & ) { qCDebug(MINITOOLSPLUGIN_LOG) << "MinitoolsPlugin::newBookmarkCallback" << text << url; m_minitoolsList.prepend(qMakePair(text, url)); } void MinitoolsPlugin::endFolderCallback() { qCDebug(MINITOOLSPLUGIN_LOG) << "MinitoolsPlugin::endFolderCallback"; m_minitoolsList.prepend(qMakePair(QStringLiteral("-"), QStringLiteral("-"))); } QString MinitoolsPlugin::minitoolsFilename(bool local) { return local ? KStandardDirs::locateLocal("data", QStringLiteral("konqueror/minitools.xml")) : KStandardDirs::locateLocal("data", QStringLiteral("konqueror/minitools-global.xml")); } void MinitoolsPlugin::slotEditBookmarks() { KBookmarkManager *manager = KBookmarkManager::managerForFile(minitoolsFilename(true), QStringLiteral("minitools")); manager->slotEditBookmarks(); } void MinitoolsPlugin::slotItemSelected() { bool ok = false; int id = sender() ? qobject_cast(sender())->data().toInt(&ok) : 0; if (!ok) { return; } if (m_minitoolsList.count() == 0) { return; } QString tmp = m_minitoolsList[id - 1].second; QString script = QUrl::fromPercentEncoding(tmp.right(tmp.length() - 11).toLatin1()); // sizeof("javascript:") connect(this, SIGNAL(executeScript(QString)), m_part, SLOT(executeScript(QString))); emit executeScript(script); disconnect(this, SIGNAL(executeScript(QString)), 0, 0); } #include "minitoolsplugin.moc" diff --git a/plugins/rellinks/plugin_rellinks.cpp b/plugins/rellinks/plugin_rellinks.cpp index 639492b02..e9e1456e4 100644 --- a/plugins/rellinks/plugin_rellinks.cpp +++ b/plugins/rellinks/plugin_rellinks.cpp @@ -1,642 +1,642 @@ /*************************************************************************** * Copyright (C) 2002, Anders Lund * * Copyright (C) 2003, 2004, Franck Quélain * * Copyright (C) 2004, Kevin Krammer * * Copyright (C) 2004, 2006, Olivier Goffart * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ +// local includes +#include "plugin_rellinks.h" + // Qt includes -#include -#include +#include +#include #include #include // KDE include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -// local includes -#include "plugin_rellinks.h" - /** Rellinks factory */ K_PLUGIN_FACTORY(RelLinksFactory, registerPlugin();) #include static QUrl resolvedUrl(const QUrl &base, const QString& rel) { return QUrl(base).resolved(QUrl(rel)); } Q_DECLARE_METATYPE(DOM::Element); /** Constructor of the plugin. */ RelLinksPlugin::RelLinksPlugin(QObject *parent, const QVariantList &) : KParts::Plugin(parent), m_part(0), m_viewVisible(false), m_linksFound(false) { setComponentData(KAboutData(QStringLiteral("rellinks"), i18n("Rellinks"), QStringLiteral("1.0"))); QActionGroup *grp = new QActionGroup(this); connect(grp, SIGNAL(triggered(QAction*)), this, SLOT(actionTriggered(QAction*))); QAction *a; // ------------- Navigation links -------------- a = actionCollection()->addAction(QStringLiteral("rellinks_home")); a->setText(i18n("&Top")); a->setIcon(QIcon::fromTheme(QStringLiteral("go-top"))); actionCollection()->setDefaultShortcut(a, QKeySequence(QStringLiteral("Ctrl+Alt+T"))); a->setWhatsThis(i18n("

This link references a home page or the top of some hierarchy.

")); grp->addAction(a); a = actionCollection()->addAction(QStringLiteral("rellinks_up")); a->setText(i18n("&Up")); a->setIcon(QIcon::fromTheme(QStringLiteral("go-up"))); actionCollection()->setDefaultShortcut(a, QKeySequence(QStringLiteral("Ctrl+Alt+U"))); a->setWhatsThis(i18n("

This link references the immediate parent of the current document.

")); grp->addAction(a); bool isRTL = QApplication::isRightToLeft(); a = actionCollection()->addAction(QStringLiteral("rellinks_begin")); a->setText(i18n("&First")); a->setIcon(QIcon::fromTheme(isRTL ? "go-last" : "go-first")); actionCollection()->setDefaultShortcut(a, QKeySequence(QStringLiteral("Ctrl+Alt+F"))); a->setWhatsThis(i18n("

This link type tells search engines which document is considered by the author to be the starting point of the collection.

")); grp->addAction(a); a = actionCollection()->addAction(QStringLiteral("rellinks_prev")); a->setText(i18n("&Previous")); a->setIcon(QIcon::fromTheme(isRTL ? "go-next" : "go-previous")); actionCollection()->setDefaultShortcut(a, QKeySequence(QStringLiteral("Ctrl+Alt+P"))); a->setWhatsThis(i18n("

This link references the previous document in an ordered series of documents.

")); grp->addAction(a); a = actionCollection()->addAction(QStringLiteral("rellinks_next")); a->setText(i18n("&Next")); a->setIcon(QIcon::fromTheme(isRTL ? "go-previous" : "go-next")); actionCollection()->setDefaultShortcut(a, QKeySequence(QStringLiteral("Ctrl+Alt+N"))); a->setWhatsThis(i18n("

This link references the next document in an ordered series of documents.

")); grp->addAction(a); a = actionCollection()->addAction(QStringLiteral("rellinks_last")); a->setText(i18n("&Last")); a->setIcon(QIcon::fromTheme(isRTL ? "go-first" : "go-last")); actionCollection()->setDefaultShortcut(a, QKeySequence(QStringLiteral("Ctrl+Alt+L"))); a->setWhatsThis(i18n("

This link references the end of a sequence of documents.

")); grp->addAction(a); // ------------ special items -------------------------- a = actionCollection()->addAction(QStringLiteral("rellinks_search")); a->setText(i18n("&Search")); a->setIcon(QIcon::fromTheme(QStringLiteral("edit-find"))); actionCollection()->setDefaultShortcut(a, QKeySequence(QStringLiteral("Ctrl+Alt+S"))); a->setWhatsThis(i18n("

This link references the search.

")); grp->addAction(a); // ------------ Document structure links --------------- m_document = new KActionMenu(QIcon::fromTheme(QStringLiteral("go-jump")), i18n("Document"), actionCollection()); actionCollection()->addAction(QStringLiteral("rellinks_document"), m_document); m_document->setWhatsThis(i18n("

This menu contains the links referring the document information.

")); m_document->setDelayed(false); a = actionCollection()->addAction(QStringLiteral("rellinks_contents")); a->setText(i18n("Table of &Contents")); actionCollection()->setDefaultShortcut(a, QKeySequence(QStringLiteral("Ctrl+Alt+C"))); a->setWhatsThis(i18n("

This link references the table of contents.

")); m_document->addAction(a); grp->addAction(a); kactionmenu_map[QStringLiteral("chapter")] = new KActionMenu(i18n("Chapters"), actionCollection()); actionCollection()->addAction(QStringLiteral("rellinks_chapters"), kactionmenu_map[QStringLiteral("chapter") ]); m_document->addAction(kactionmenu_map[QStringLiteral("chapter")]); connect(kactionmenu_map[QStringLiteral("chapter")]->menu(), &QMenu::triggered, this, &RelLinksPlugin::actionTriggered); kactionmenu_map[QStringLiteral("chapter")]->setWhatsThis(i18n("

This menu references the chapters of the document.

")); kactionmenu_map[QStringLiteral("chapter")]->setDelayed(false); kactionmenu_map[QStringLiteral("section")] = new KActionMenu(i18n("Sections"), actionCollection()); actionCollection()->addAction(QStringLiteral("rellinks_sections"), kactionmenu_map[QStringLiteral("section")]); m_document->addAction(kactionmenu_map[QStringLiteral("section")]); connect(kactionmenu_map[QStringLiteral("section")]->menu(), &QMenu::triggered, this, &RelLinksPlugin::actionTriggered); kactionmenu_map[QStringLiteral("section")]->setWhatsThis(i18n("

This menu references the sections of the document.

")); kactionmenu_map[QStringLiteral("section")]->setDelayed(false); kactionmenu_map[QStringLiteral("subsection")] = new KActionMenu(i18n("Subsections"), actionCollection()); m_document->addAction(kactionmenu_map[QStringLiteral("subsection")]); actionCollection()->addAction(QStringLiteral("rellinks_subsections"), kactionmenu_map[QStringLiteral("subsection")]); connect(kactionmenu_map[QStringLiteral("subsection")]->menu(), &QMenu::triggered, this, &RelLinksPlugin::actionTriggered); kactionmenu_map[QStringLiteral("subsection")]->setWhatsThis(i18n("

This menu references the subsections of the document.

")); kactionmenu_map[QStringLiteral("subsection")]->setDelayed(false); kactionmenu_map[QStringLiteral("appendix")] = new KActionMenu(i18n("Appendix"), actionCollection()); actionCollection()->addAction(QStringLiteral("rellinks_appendix"), kactionmenu_map[QStringLiteral("appendix")]); m_document->addAction(kactionmenu_map[QStringLiteral("appendix")]); connect(kactionmenu_map[QStringLiteral("appendix")]->menu(), &QMenu::triggered, this, &RelLinksPlugin::actionTriggered); kactionmenu_map[QStringLiteral("appendix")]->setWhatsThis(i18n("

This link references the appendix.

")); kactionmenu_map[QStringLiteral("appendix")]->setDelayed(false); a = actionCollection()->addAction(QStringLiteral("rellinks_glossary")); a->setText(i18n("&Glossary")); actionCollection()->setDefaultShortcut(a, QKeySequence(QStringLiteral("Ctrl+Alt+G"))); a->setWhatsThis(i18n("

This link references the glossary.

")); m_document->addAction(a); grp->addAction(a); a = actionCollection()->addAction(QStringLiteral("rellinks_index")); a->setText(i18n("&Index")); actionCollection()->setDefaultShortcut(a, QKeySequence(QStringLiteral("Ctrl+Alt+I"))); a->setWhatsThis(i18n("

This link references the index.

")); m_document->addAction(a); grp->addAction(a); // Other links m_more = new KActionMenu(i18n("More"), actionCollection()); actionCollection()->addAction(QStringLiteral("rellinks_more"), m_more); m_more->setWhatsThis(i18n("

This menu contains other important links.

")); m_more->setDelayed(false); a = actionCollection()->addAction(QStringLiteral("rellinks_help")); a->setText(i18n("&Help")); a->setIcon(QIcon::fromTheme(QStringLiteral("help-contents"))); actionCollection()->setDefaultShortcut(a, QKeySequence(QStringLiteral("Ctrl+Alt+H"))); a->setWhatsThis(i18n("

This link references the help.

")); m_more->addAction(a); grp->addAction(a); a = actionCollection()->addAction(QStringLiteral("rellinks_author")); a->setText(i18n("&Authors")); a->setIcon(QIcon::fromTheme(QStringLiteral("x-office-contact"))); actionCollection()->setDefaultShortcut(a, QKeySequence(QStringLiteral("Ctrl+Alt+A"))); a->setWhatsThis(i18n("

This link references the author.

")); m_more->addAction(a); grp->addAction(a); a = actionCollection()->addAction(QStringLiteral("rellinks_copyright")); a->setText(i18n("Copy&right")); a->setIcon(QIcon::fromTheme(QStringLiteral("help-about"))); actionCollection()->setDefaultShortcut(a, QKeySequence(QStringLiteral("Ctrl+Alt+R"))); a->setWhatsThis(i18n("

This link references the copyright.

")); m_more->addAction(a); grp->addAction(a); kactionmenu_map[QStringLiteral("bookmark")] = new KActionMenu(QIcon::fromTheme(QStringLiteral("bookmarks")), i18n("Bookmarks"), actionCollection()); actionCollection()->addAction(QStringLiteral("rellinks_bookmarks"), kactionmenu_map[QStringLiteral("bookmark")]); m_more->addAction(kactionmenu_map[QStringLiteral("bookmark")]); kactionmenu_map[QStringLiteral("bookmark")]->setWhatsThis(i18n("

This menu references the bookmarks.

")); connect(kactionmenu_map[QStringLiteral("bookmark")]->menu(), &QMenu::triggered, this, &RelLinksPlugin::actionTriggered); kactionmenu_map[QStringLiteral("bookmark")]->setDelayed(false); kactionmenu_map[QStringLiteral("alternate")] = new KActionMenu(i18n("Other Versions"), actionCollection()); actionCollection()->addAction(QStringLiteral("rellinks_other_versions"), kactionmenu_map[QStringLiteral("alternate")]); m_more->addAction(kactionmenu_map[QStringLiteral("alternate")]); kactionmenu_map[QStringLiteral("alternate")]->setWhatsThis(i18n("

This link references the alternate versions of this document.

")); connect(kactionmenu_map[QStringLiteral("alternate")]->menu(), &QMenu::triggered, this, &RelLinksPlugin::actionTriggered); kactionmenu_map[QStringLiteral("alternate")]->setDelayed(false); // Unclassified menu m_links = new KActionMenu(QIcon::fromTheme(QStringLiteral("rellinks")), i18n("Miscellaneous"), actionCollection()); actionCollection()->addAction(QStringLiteral("rellinks_links"), m_links); kactionmenu_map[QStringLiteral("unclassified")] = m_links; kactionmenu_map[QStringLiteral("unclassified")]->setWhatsThis(i18n("

Miscellaneous links.

")); connect(kactionmenu_map[QStringLiteral("unclassified")]->menu(), &QMenu::triggered, this, &RelLinksPlugin::actionTriggered); kactionmenu_map[QStringLiteral("unclassified")]->setDelayed(false); // We unactivate all the possible actions disableAll(); // When the rendering of the HTML is done, we update the site navigation bar m_part = qobject_cast(parent); if (!m_part) { return; } connect(m_part, SIGNAL(docCreated()), this, SLOT(newDocument())); connect(m_part, SIGNAL(completed()), this, SLOT(loadingFinished())); // create polling timer and connect it m_pollTimer = new QTimer(this); m_pollTimer->setObjectName(QStringLiteral("polling timer")); connect(m_pollTimer, &QTimer::timeout, this, &RelLinksPlugin::updateToolbar); // delay access to our part's members until it has finished its initialisation QTimer::singleShot(0, this, SLOT(delayedSetup())); } /** Destructor */ RelLinksPlugin::~RelLinksPlugin() { } bool RelLinksPlugin::eventFilter(QObject *watched, QEvent *event) { if (m_part == 0) { return false; } if (watched == 0 || event == 0) { return false; } if (watched == m_view) { switch (event->type()) { case QEvent::Show: m_viewVisible = true; updateToolbar(); break; case QEvent::Hide: m_viewVisible = false; updateToolbar(); break; case QEvent::Close: m_pollTimer->stop(); m_view->removeEventFilter(this); break; default: break; } } // we never filter an event, we just want to know about it return false; } void RelLinksPlugin::delayedSetup() { if (m_part == 0) { return; } m_view = m_part->view(); m_view->installEventFilter(this); m_viewVisible = m_view->isVisible(); } void RelLinksPlugin::newDocument() { // start calling upateToolbar periodically to get the new links as soon as possible m_pollTimer->start(500); //kDebug(90210) << "newDocument()"; updateToolbar(); } void RelLinksPlugin::loadingFinished() { m_pollTimer->stop(); //kDebug(90210) << "loadingFinished()"; updateToolbar(); guessRelations(); } /* Update the site navigation bar */ void RelLinksPlugin::updateToolbar() { // If we have a part if (!m_part) { return; } // We disable all disableAll(); // get a list of LINK nodes in document DOM::NodeList linkNodes = m_part->document().getElementsByTagName("link"); //kDebug(90210) << "Rellinks: Link nodes =" << linkNodes.length(); bool showBar = false; unsigned long nodeLength = linkNodes.length(); m_linksFound = nodeLength > 0; for (unsigned int i = 0; i < nodeLength; i++) { // create a entry for each one DOM::Element e(linkNodes.item(i)); // --- Retrieve of the relation type -- QString rel = e.getAttribute("rel").string(); rel = rel.simplified(); if (rel.isEmpty()) { // If the "rel" attribut is null then use the "rev" attribute... QString rev = e.getAttribute("rev").string(); rev = rev.simplified(); if (rev.isEmpty()) { // if "rev" attribut is also empty => ignore continue; } // Determine the "rel" equivalent of "rev" type rel = transformRevToRel(rev); } // Determin the name used internally QString lrel = getLinkType(rel.toLower()); // relation to ignore if (lrel.isEmpty()) { continue; } // kDebug() << "lrel=" << lrel; // -- Retrieve of other useful information -- QString href = e.getAttribute("href").string(); // if nowhere to go, ignore the link if (href.isEmpty()) { continue; } QString title = e.getAttribute("title").string(); QString hreflang = e.getAttribute("hreflang").string(); QUrl ref(resolvedUrl(m_part->url(), href)); if (title.isEmpty()) { title = ref.toDisplayString(); } // escape ampersand before settings as action title, otherwise the menu entry will interpret it as an // accelerator title.replace('&', QLatin1String("&&")); // -- Menus activation -- // Activation of "Document" menu ? if (lrel == QLatin1String("contents") || lrel == QLatin1String("glossary") || lrel == QLatin1String("index") || lrel == QLatin1String("appendix")) { m_document->setEnabled(true); } // Activation of "More" menu ? if (lrel == QLatin1String("help") || lrel == QLatin1String("author") || lrel == QLatin1String("copyright")) { m_more->setEnabled(true); } // -- Buttons or menu items activation / creation -- if (lrel == QLatin1String("bookmark") || lrel == QLatin1String("alternate")) { QAction *a = kactionmenu_map[lrel]->menu()->addAction(title); a->setData(QVariant::fromValue(e)); m_more->setEnabled(true); kactionmenu_map[lrel]->setEnabled(true); } else if (lrel == QLatin1String("appendix") || lrel == QLatin1String("chapter") || lrel == QLatin1String("section") || lrel == QLatin1String("subsection")) { QAction *a = kactionmenu_map[lrel]->menu()->addAction(title); m_document->setEnabled(true); kactionmenu_map[lrel]->setEnabled(true); a->setData(QVariant::fromValue(e)); } else { // It is a unique action QAction *a = actionCollection()->action("rellinks_" + lrel); if (a) { a->setData(QVariant::fromValue(e)); a->setEnabled(true); // Tooltip if (hreflang.isEmpty()) { a->setToolTip(title); } else { a->setToolTip(title + " [" + hreflang + ']'); } } else { // For the moment all the elements are reference in a separated menu // TODO : reference the unknown ? QAction *a = kactionmenu_map[QStringLiteral("unclassified")]->menu()->addAction(lrel + " : " + title); kactionmenu_map[QStringLiteral("unclassified")]->setEnabled(true); a->setData(QVariant::fromValue(e)); } } showBar = true; } } void RelLinksPlugin::guessRelations() { m_part = qobject_cast(parent()); if (!m_part || m_part->document().isNull()) { return; } //If the page already contains some link, that mean the webmaster is aware //of the meaning of so we can consider that if prev/next was possible //they are already there. if (m_linksFound) { return; } // - The number of didgit may not be more of 3, or this is certenly an id. // - We make sure that the number is followed by a dot, a &, or the end, we // don't want to match stuff like that: page.html?id=A14E12FD // - We make also sure the number is not preceded dirrectly by others number QRegExp rx("^(.*[=/?&][^=/?&.\\-0-9]*)([\\d]{1,3})([.&][^/0-9]{0,15})?$"); const QString zeros(QStringLiteral("0000")); QString url = m_part->url().url(); if (rx.indexIn(url) != -1) { uint val = rx.cap(2).toUInt(); int lenval = rx.cap(2).length(); QString nval_str = QString::number(val + 1); //prepend by zeros if the original also contains zeros. if (nval_str.length() < lenval && rx.cap(2)[0] == '0') { nval_str.prepend(zeros.left(lenval - nval_str.length())); } QString href = rx.cap(1) + nval_str + rx.cap(3); QUrl ref(resolvedUrl(m_part->url(), href)); QString title = i18n("[Autodetected] %1", ref.toDisplayString()); DOM::Element e = m_part->document().createElement("link"); e.setAttribute("href", href); QAction *a = actionCollection()->action(QStringLiteral("rellinks_next")); a->setEnabled(true); a->setToolTip(title); a->setData(QVariant::fromValue(e)); if (val > 1) { nval_str = QString::number(val - 1); if (nval_str.length() < lenval && rx.cap(2)[0] == '0') { nval_str.prepend(zeros.left(lenval - nval_str.length())); } QString href = rx.cap(1) + nval_str + rx.cap(3); QUrl ref(resolvedUrl(m_part->url(), href)); QString title = i18n("[Autodetected] %1", ref.toDisplayString()); e = m_part->document().createElement("link"); e.setAttribute("href", href); QAction *a = actionCollection()->action(QStringLiteral("rellinks_prev")); a->setEnabled(true); a->setToolTip(title); a->setData(QVariant::fromValue(e)); } } } void RelLinksPlugin::goToLink(DOM::Element e) { // have the KHTML part open it KHTMLPart *part = qobject_cast(parent()); if (!part) { return; } QString href = e.getAttribute("href").string(); QUrl url(resolvedUrl(part->url(), href)); QString target = e.getAttribute("target").string(); // URL arguments KParts::OpenUrlArguments arguments; KParts::BrowserArguments browserArguments; browserArguments.frameName = target; // Add base url if not valid if (url.isValid()) { part->browserExtension()->openUrlRequest(url, arguments, browserArguments); } else { QUrl baseURL = part->baseURL(); QString endURL = url.toDisplayString(); QUrl realURL = resolvedUrl(baseURL, endURL); part->browserExtension()->openUrlRequest(realURL, arguments, browserArguments); } } void RelLinksPlugin::disableAll() { m_linksFound = false; foreach (QAction *a, actionCollection()->actionGroups()[0]->actions()) { a->setEnabled(false); a->setToolTip(a->text().remove('&')); } // Clear actions KActionMenuMap::Iterator itmenu; for (itmenu = kactionmenu_map.begin(); itmenu != kactionmenu_map.end(); ++itmenu) { // If I don't test it crash :( if (itmenu.value()) { itmenu.value()->menu()->clear(); itmenu.value()->setEnabled(false); itmenu.value()->setToolTip(itmenu.value()->text().remove('&')); } } // Unactivate menus m_more->setEnabled(false); m_document->setEnabled(false); } QString RelLinksPlugin::getLinkType(const QString &lrel) { // Relations to ignore... if (lrel.contains(QLatin1String("stylesheet")) || lrel == QLatin1String("script") || lrel == QLatin1String("icon") || lrel == QLatin1String("shortcut icon") || lrel == QLatin1String("prefetch")) { return QString(); } // ...known relations... if (lrel == QLatin1String("top") || lrel == QLatin1String("origin") || lrel == QLatin1String("start")) { return QStringLiteral("home"); } if (lrel == QLatin1String("parent")) { return QStringLiteral("up"); } if (lrel == QLatin1String("first")) { return QStringLiteral("begin"); } if (lrel == QLatin1String("previous")) { return QStringLiteral("prev"); } if (lrel == QLatin1String("child")) { return QStringLiteral("next"); } if (lrel == QLatin1String("end")) { return QStringLiteral("last"); } if (lrel == QLatin1String("toc")) { return QStringLiteral("contents"); } if (lrel == QLatin1String("find")) { return QStringLiteral("search"); } if (lrel == QLatin1String("alternative stylesheet")) { return QStringLiteral("alternate stylesheet"); } if (lrel == QLatin1String("authors")) { return QStringLiteral("author"); } if (lrel == QLatin1String("toc")) { return QStringLiteral("contents"); } //...unknown relations or name that don't need to change return lrel; } QString RelLinksPlugin::transformRevToRel(const QString &rev) { QString altRev = getLinkType(rev); // Known relations if (altRev == QLatin1String("prev")) { return getLinkType(QStringLiteral("next")); } if (altRev == QLatin1String("next")) { return getLinkType(QStringLiteral("prev")); } if (altRev == QLatin1String("made")) { return getLinkType(QStringLiteral("author")); } if (altRev == QLatin1String("up")) { return getLinkType(QStringLiteral("child")); } if (altRev == QLatin1String("sibling")) { return getLinkType(QStringLiteral("sibling")); } //...unknown inverse relation => ignore for the moment return QString(); } void RelLinksPlugin::actionTriggered(QAction *action) { if (action->data().isValid()) { goToLink(action->data().value()); } } #include "plugin_rellinks.moc" diff --git a/plugins/uachanger/uachangerplugin.cpp b/plugins/uachanger/uachangerplugin.cpp index 7aa7cdf14..705e8cd55 100644 --- a/plugins/uachanger/uachangerplugin.cpp +++ b/plugins/uachanger/uachangerplugin.cpp @@ -1,462 +1,463 @@ /* Copyright (c) 2001 Dawit Alemayehu This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License (LGPL) as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "uachangerplugin.h" + #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include "uachangerplugin.h" static const KAboutData aboutdata("uachangerplugin", 0, ki18n("Change Browser Identification"), "1.0"); K_PLUGIN_FACTORY(UAChangerPluginFactory, registerPlugin();) K_EXPORT_PLUGIN(UAChangerPluginFactory(aboutdata)) #define UA_PTOS(x) (*it)->property(x).toString() #define QFL1(x) QLatin1String(x) UAChangerPlugin::UAChangerPlugin(QObject *parent, const QVariantList &) : KParts::Plugin(parent), m_bSettingsLoaded(false), m_part(0L), m_config(0L) { setComponentData(UAChangerPlugin::componentData()); m_pUAMenu = new KActionMenu(KIcon("preferences-web-browser-identification"), i18n("Change Browser &Identification"), actionCollection()); actionCollection()->addAction("changeuseragent", m_pUAMenu); m_pUAMenu->setDelayed(false); connect(m_pUAMenu->menu(), SIGNAL(aboutToShow()), this, SLOT(slotAboutToShow())); if (parent) { m_part = qobject_cast(parent); connect(m_part, SIGNAL(started(KIO::Job*)), this, SLOT(slotEnableMenu())); connect(m_part, SIGNAL(completed()), this, SLOT(slotEnableMenu())); connect(m_part, SIGNAL(completed(bool)), this, SLOT(slotEnableMenu())); } } UAChangerPlugin::~UAChangerPlugin() { saveSettings(); slotReloadDescriptions(); } void UAChangerPlugin::slotReloadDescriptions() { delete m_config; m_config = 0L; } void UAChangerPlugin::parseDescFiles() { const KService::List list = KServiceTypeTrader::self()->query("UserAgentStrings"); if (list.isEmpty()) { return; } m_mapAlias.clear(); m_lstAlias.clear(); m_lstIdentity.clear(); struct utsname utsn; uname(&utsn); QStringList languageList = KLocale::global()->languageList(); if (!languageList.isEmpty()) { const int index = languageList.indexOf(QFL1("C")); if (index > -1) { if (languageList.contains(QFL1("en"))) { languageList.removeAt(index); } else { languageList[index] = QFL1("en"); } } } KService::List::ConstIterator it = list.constBegin(); KService::List::ConstIterator lastItem = list.constEnd(); for (; it != lastItem; ++it) { QString ua = UA_PTOS("X-KDE-UA-FULL"); QString tag = UA_PTOS("X-KDE-UA-TAG"); // The menu groups thing by tag, with the menu name being the X-KDE-UA-NAME by default. We make groups for // IE, NS, Firefox, Safari, and Opera, and put everything else into "Other" QString menuName; MenuGroupSortKey menuKey; // key for the group.. if (tag != "IE" && tag != "NN" && tag != "FF" && tag != "SAF" && tag != "OPR") { tag = "OTHER"; menuName = i18n("Other"); menuKey = MenuGroupSortKey(tag, true); } else { menuName = UA_PTOS("X-KDE-UA-NAME"); menuKey = MenuGroupSortKey(tag, false); } if ((*it)->property("X-KDE-UA-DYNAMIC-ENTRY").toBool()) { ua.replace(QFL1("appSysName"), QFL1(utsn.sysname)); ua.replace(QFL1("appSysRelease"), QFL1(utsn.release)); ua.replace(QFL1("appMachineType"), QFL1(utsn.machine)); ua.replace(QFL1("appLanguage"), languageList.join(QFL1(", "))); ua.replace(QFL1("appPlatform"), QFL1("X11")); } if (m_lstIdentity.contains(ua)) { continue; // Ignore dups! } m_lstIdentity << ua; // Compute what to display for our menu entry --- including platform name if it's available, // and avoiding repeating the browser name in categories other than 'other'. QString platform = QString("%1 %2").arg(UA_PTOS("X-KDE-UA-SYSNAME")).arg(UA_PTOS("X-KDE-UA-SYSRELEASE")); QString alias; if (platform.trimmed().isEmpty()) { if (!menuKey.isOther) { alias = i18nc("%1 = browser version (e.g. 2.0)", "Version %1", UA_PTOS("X-KDE-UA-VERSION")); } else alias = i18nc("%1 = browser name, %2 = browser version (e.g. Firefox, 2.0)", "%1 %2", UA_PTOS("X-KDE-UA-NAME"), UA_PTOS("X-KDE-UA-VERSION")); } else { if (!menuKey.isOther) alias = i18nc("%1 = browser version, %2 = platform (e.g. 2.0, Windows XP)", "Version %1 on %2", UA_PTOS("X-KDE-UA-VERSION"), platform); else alias = i18nc("%1 = browser name, %2 = browser version, %3 = platform (e.g. Firefox, 2.0, Windows XP)", "%1 %2 on %3", UA_PTOS("X-KDE-UA-NAME"), UA_PTOS("X-KDE-UA-VERSION"), platform); } m_lstAlias << alias; /* sort in this UA Alias alphabetically */ BrowserGroup ualist = m_mapAlias[menuKey]; BrowserGroup::Iterator e = ualist.begin(); while (!alias.isEmpty() && e != ualist.end()) { if (m_lstAlias[(*e)] > alias) { ualist.insert(e, m_lstAlias.count() - 1); alias.clear(); } ++e; } if (!alias.isEmpty()) { ualist.append(m_lstAlias.count() - 1); } m_mapAlias[menuKey] = ualist; m_mapBrowser[menuKey] = menuName; } } void UAChangerPlugin::slotEnableMenu() { m_currentURL = m_part->url(); // This plugin works on local files, http[s], and webdav[s]. QString proto = m_currentURL.protocol(); if (m_currentURL.isLocalFile() || proto.startsWith("http") || proto.startsWith("webdav")) { if (!m_pUAMenu->isEnabled()) { m_pUAMenu->setEnabled(true); } } else { m_pUAMenu->setEnabled(false); } } void UAChangerPlugin::slotAboutToShow() { if (!m_config) { m_config = new KConfig("kio_httprc"); parseDescFiles(); } if (!m_bSettingsLoaded) { loadSettings(); } if (m_pUAMenu->menu()->actions().isEmpty()) { // need to create the actions m_pUAMenu->menu()->addTitle(i18n("Identify As")); // imho title doesn't need colon.. m_defaultAction = new QAction(i18n("Default Identification"), this); m_defaultAction->setCheckable(true); connect(m_defaultAction, SIGNAL(triggered()), this, SLOT(slotDefault())); m_pUAMenu->menu()->addAction(m_defaultAction); m_pUAMenu->menu()->addSeparator(); m_actionGroup = new QActionGroup(m_pUAMenu->menu()); AliasConstIterator map = m_mapAlias.constBegin(); for (; map != m_mapAlias.constEnd(); ++map) { QMenu *browserMenu = m_pUAMenu->menu()->addMenu(m_mapBrowser.value(map.key())); BrowserGroup::ConstIterator e = map.value().begin(); for (; e != map.value().end(); ++e) { QAction *action = new QAction(m_lstAlias[*e], m_actionGroup); action->setCheckable(true); action->setData(*e); browserMenu->addAction(action); } } connect(m_actionGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotItemSelected(QAction*))); m_pUAMenu->menu()->addSeparator(); /* useless here, imho.. m_pUAMenu->menu()->insertItem( i18n("Reload Identifications"), this, SLOT(slotReloadDescriptions()), 0, ++count );*/ m_applyEntireSiteAction = new QAction(i18n("Apply to Entire Site"), this); m_applyEntireSiteAction->setCheckable(true); connect(m_applyEntireSiteAction, SIGNAL(triggered()), this, SLOT(slotApplyToDomain())); m_pUAMenu->menu()->addAction(i18n("Apply to Entire Site")); m_pUAMenu->menu()->addAction(i18n("Configure..."), this, SLOT(slotConfigure())); } // Reflect current settings in the actions QString host = m_currentURL.isLocalFile() ? QFL1("localhost") : m_currentURL.host(); m_currentUserAgent = KProtocolManager::userAgentForHost(host); //kDebug(90130) << "User Agent: " << m_currentUserAgent; m_defaultAction->setChecked(m_currentUserAgent == KProtocolManager::defaultUserAgent()); m_applyEntireSiteAction->setChecked(m_bApplyToDomain); Q_FOREACH (QAction *action, m_actionGroup->actions()) { const int id = action->data().toInt(); action->setChecked(m_lstIdentity[id] == m_currentUserAgent); } } void UAChangerPlugin::slotConfigure() { KService::Ptr service = KService::serviceByDesktopName("useragent"); if (service) { KRun::runCommand(service->exec(), m_part->widget()); } } void UAChangerPlugin::slotItemSelected(QAction *action) { const int id = action->data().toInt(); if (m_lstIdentity[id] == m_currentUserAgent) { return; } m_currentUserAgent = m_lstIdentity[id]; QString host = m_currentURL.isLocalFile() ? QFL1("localhost") : filterHost(m_currentURL.host()); KConfigGroup grp = m_config->group(host.toLower()); grp.writeEntry("UserAgent", m_currentUserAgent); //kDebug(90130) << "Writing out UserAgent=" << m_currentUserAgent << "for host=" << host; grp.sync(); // Reload the page with the new user-agent string reloadPage(); } void UAChangerPlugin::slotDefault() { if (m_currentUserAgent == KProtocolManager::defaultUserAgent()) { return; // don't flicker! } // We have no choice but delete all higher domain level settings here since it // affects what will be matched. QStringList partList = m_currentURL.host().split(' ', QString::SkipEmptyParts); if (!partList.isEmpty()) { partList.removeFirst(); QStringList domains; // Remove the exact name match... domains << m_currentURL.host(); while (partList.count()) { if (partList.count() == 2) if (partList[0].length() <= 2 && partList[1].length() == 2) { break; } if (partList.count() == 1) { break; } domains << partList.join(QFL1(".")); partList.removeFirst(); } KConfigGroup grp(m_config, QString()); for (QStringList::Iterator it = domains.begin(); it != domains.end(); it++) { //kDebug () << "Domain to remove: " << *it; if (grp.hasGroup(*it)) { grp.deleteGroup(*it); } else if (grp.hasKey(*it)) { grp.deleteEntry(*it); } } } else if (m_currentURL.isLocalFile() && m_config->hasGroup("localhost")) { m_config->deleteGroup("localhost"); } m_config->sync(); // Reset some internal variables and inform the http io-slaves of the changes. m_currentUserAgent = KProtocolManager::defaultUserAgent(); // Reload the page with the default user-agent reloadPage(); } void UAChangerPlugin::reloadPage() { // Inform running http(s) io-slaves about the change... KIO::Scheduler::emitReparseSlaveConfiguration(); KParts::OpenUrlArguments args = m_part->arguments(); args.setReload(true); m_part->setArguments(args); m_part->openUrl(m_currentURL); } QString UAChangerPlugin::filterHost(const QString &hostname) { QRegExp rx; // Check for IPv4 address rx.setPattern("[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}"); if (rx.exactMatch(hostname)) { return hostname; } // Check for IPv6 address here... rx.setPattern("^\\[.*\\]$"); if (rx.exactMatch(hostname)) { return hostname; } // Return the TLD if apply to domain or return (m_bApplyToDomain ? findTLD(hostname) : hostname); } QString UAChangerPlugin::findTLD(const QString &hostname) { QStringList domains; QStringList partList = hostname.split(' ', QString::SkipEmptyParts); if (partList.count()) { partList.removeFirst(); // Remove hostname } while (partList.count()) { // We only have a TLD left. if (partList.count() == 1) { break; } if (partList.count() == 2) { // The .name domain uses ..name // Although the TLD is striclty speaking .name, for our purpose // it should be .name since people should not be able // to set cookies for everyone with the same surname. // Matches .name if (partList[1].toLower() == QFL1("name")) { break; } else if (partList[1].length() == 2) { // If this is a TLD, we should stop. (e.g. co.uk) // We assume this is a TLD if it ends with .xx.yy or .x.yy if (partList[0].length() <= 2) { break; // This is a TLD. } // Catch some TLDs that we miss with the previous check // e.g. com.au, org.uk, mil.co QByteArray t = partList[0].toLower().toUtf8(); if ((t == "com") || (t == "net") || (t == "org") || (t == "gov") || (t == "edu") || (t == "mil") || (t == "int")) { break; } } } domains.append(partList.join(QFL1("."))); partList.removeFirst(); // Remove part } if (domains.isEmpty()) { return hostname; } return domains[0]; } void UAChangerPlugin::saveSettings() { if (!m_bSettingsLoaded) { return; } KConfig cfg("uachangerrc", KConfig::NoGlobals); KConfigGroup grp = cfg.group("General"); grp.writeEntry("applyToDomain", m_bApplyToDomain); } void UAChangerPlugin::loadSettings() { KConfig cfg("uachangerrc", KConfig::NoGlobals); KConfigGroup grp = cfg.group("General"); m_bApplyToDomain = grp.readEntry("applyToDomain", true); m_bSettingsLoaded = true; } void UAChangerPlugin::slotApplyToDomain() { m_bApplyToDomain = !m_bApplyToDomain; } #include "uachangerplugin.moc" diff --git a/plugins/webarchiver/archivedialog.cpp b/plugins/webarchiver/archivedialog.cpp index 4a734445b..ee05dfcc8 100644 --- a/plugins/webarchiver/archivedialog.cpp +++ b/plugins/webarchiver/archivedialog.cpp @@ -1,1374 +1,1375 @@ /* Copyright (C) 2001 Andreas Schlapbach Copyright (C) 2003 Antonio Larrosa Copyright (C) 2008 Matthias Grimrath This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // The DOM-tree is recursed twice. The first run gathers all URLs while the second // run writes out all HTML frames and CSS stylesheets. These two distinct runs are // necessary, because some frames and/or stylesheets may be dropped (for example // a frame currently not displayed or deemed insecure). In that case an URL that // points to such a frame/stylesheet has to be removed. Since the URL may be mentioned // earlier before recursing to the to-be-removed frame, two runs are necessary to get // a complete list of URLs that should be archived. // Changelog // * replace dynamic_cast<> and ->inherits() with qobject_cast<> // * use QHash instead of QMap; get rid of Ordered<> class // * fixed crash / assertion on Konqueror exit after a webpage was archived // See comment about KHTMLView parent widget in plugin_webarchiver.cpp // * Using KDE4/Qt4 QUrl::equals() and QUrl::fragment() to compare Urls // * KHTML stores comment with a trailing '-'. Looks like some off-by-one bug. // * Add mimetype indicating suffix to downloaded files. // DONE CSS mentioned in elements that are not parsed by Konqueror did not get their // href='' resolved/removed // TODO if href= etc links in a frameset refer to frames currently displayed, make links relative // to archived page instead of absolute // TODO KDE4 webarchiver: look at m_bPreserveWS // TODO KDE4 webarchiver: look at closing tags // TODO check if PartFrameData::framesWithName get a 'KHTMLPart *' if any // TODO KHTMLPart::frames(): Is it possible to have NULL pointers in returned list? // TODO If downloaded object need no data conversion, use KIO::file_copy or signal data() // TODO KDE4 check what KHTMLPart is doing on job->addMetaData() // TODO KDE4 use HTMLScriptElementImpl::charset() to get charset="" attribute of elements +#include "archivedialog.h" + #include -#include -#include +#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include "archivedialog.h" #include "webarchiverdebug.h" //KDELibs4Support #include // Set to true if you have a patched http-io-slave that has // improved offline-browsing functionality. static const bool patchedHttpSlave = false; #define CONTENT_TYPE "" // // Qt 4.x offers a @c foreach pseudo keyword. This is however slightly slower than FOR_ITER // because @c foreach makes a shared copy of the container. // #define FOR_ITER(type,var,it) for (type::iterator it(var.begin()), it##end(var.end()); it != it##end; ++it) #define FOR_CONST_ITER(type,var,it) for (type::const_iterator it(var.begin()), it##end(var.end()); it != it##end; ++it) #define FOR_ITER_TEMPLATE(type,var,it) for (typename type::iterator it(var.begin()), it##end(var.end()); it != it##end; ++it) static const mode_t archivePerms = S_IFREG | 0644; typedef QList ROPartList; // // functions needed for storing certain DOM elements in a QHash<> // namespace DOM { inline uint qHash(const CSSStyleSheet &a) { return ::qHash(static_cast(a.handle())); } inline bool operator==(const DOM::CSSStyleSheet &a, const DOM::CSSStyleSheet &b) { return a.handle() == b.handle(); } inline uint qHash(const Node &a) { return ::qHash(static_cast(a.handle())); } }// namespace DOM // // elems with 'type' attr: object, param, link, script, style // // TODO convert to bsearch? probably more time and memory efficient ArchiveDialog::NonCDataAttr::NonCDataAttr() { static const char *const non_cdata[] = { "id", "dir", "shape", "tabindex", "align", "nohref", "clear" // Unfinished... }; for (int i = 0; i != (sizeof(non_cdata) / sizeof(non_cdata[0])); ++i) { insert(non_cdata[i]); } } // TODO lazy init? ArchiveDialog::NonCDataAttr ArchiveDialog::non_cdata_attr; ArchiveDialog::RecurseData::RecurseData(KHTMLPart *_part, QTextStream *_textStream, PartFrameData *pfd) : part(_part), textStream(_textStream), partFrameData(pfd), document(_part->htmlDocument()), baseSeen(false) { Q_ASSERT(!document.isNull()); } static KHTMLPart *isArchivablePart(KParts::ReadOnlyPart *part) { KHTMLPart *cp = qobject_cast(part); if (! cp) { return NULL; } DOM::HTMLDocument domdoc(cp->htmlDocument()); if (domdoc.isNull()) { return NULL; } return cp; } ArchiveDialog::ArchiveDialog(QWidget *parent, const QString &filename, KHTMLPart *part) : KDialog(parent), m_top(part), m_job(NULL), m_uniqId(2), m_tarBall(NULL), m_filename(filename), m_widget(NULL) { setCaption(i18nc("@title:window", "Web Archiver")); setButtons(KDialog::Ok | KDialog::Cancel); setButtonGuiItem(KDialog::Ok, KStandardGuiItem::close()); setModal(false); enableButtonOk(false); setDefaultButton(KDialog::NoDefault); m_widget = new ArchiveViewBase(this); { QTreeWidgetItem *twi = m_widget->progressView->headerItem(); twi->setText(0, i18n("Status")); twi->setText(1, i18n("Url")); } setMainWidget(m_widget); QUrl srcURL = part->url(); m_widget->urlLabel->setText(QStringLiteral("" + KStringHandler::csqueeze(srcURL.toDisplayString(), 80) + ""); m_widget->targetLabel->setText(QStringLiteral("" + KStringHandler::csqueeze(filename, 80) + ""); //if(part->document().ownerDocument().isNull()) // m_document = part->document(); //else // m_document = part->document().ownerDocument(); m_tarBall = new KTar(filename, QStringLiteral("application/x-gzip")); m_archiveTime = QDateTime::currentDateTime(); } ArchiveDialog::~ArchiveDialog() { // TODO cancel outstanding download jobs? qCDebug(WEBARCHIVERPLUGIN_LOG) << "destroying"; if (m_job) { m_job->kill(); m_job = NULL; } delete m_tarBall; m_tarBall = NULL; } void ArchiveDialog::archive() { if (m_tarBall->open(QIODevice::WriteOnly)) { obtainURLs(); // Assign unique tarname to URLs // Split m_url2tar into Stylesheets / non stylesheets m_objects.clear(); assert(static_cast(m_url2tar.size()) - static_cast(m_cssURLs.size()) >= 0); // m_objects.reserve(m_url2tar.size() - m_cssURLs.size()); FOR_ITER(UrlTarMap, m_url2tar, u2t_it) { const QUrl &url = u2t_it.key(); DownloadInfo &info = u2t_it.value(); assert(info.tarName.isNull()); // info.tarName = uniqTarName( url.fileName(), 0 ); // To able to append mimetype hinting suffixes to tarnames, for instance adding '.gif' to a // webbug '87626734' adding the name to the url-to-tarname map is deferred. // This cannot be done with CSS because CSS may reference each other so when URLS // of the first CSS are changed all tarnames need to be there. // if (m_cssURLs.find(url) == m_cssURLs.end()) { m_objects.append(u2t_it); } else { info.tarName = uniqTarName(url.fileName(), 0); } } QProgressBar *pb = m_widget->progressBar; pb->setMaximum(m_url2tar.count() + 1); pb->setValue(0); m_objects_it = m_objects.begin(); downloadObjects(); } else { const QString title = i18nc("@title:window", "Unable to Open Web-Archive"); const QString text = i18n("Unable to open \n %1 \n for writing.", m_tarBall->fileName()); KMessageBox::sorry(NULL, text, title); } } void ArchiveDialog::downloadObjects() { if (m_objects_it == m_objects.end()) { m_styleSheets_it = m_cssURLs.begin(); downloadStyleSheets(); } else { m_dlurl2tar_it = (*m_objects_it); const QUrl &url = m_dlurl2tar_it.key(); DownloadInfo &info = m_dlurl2tar_it.value(); assert(m_dlurl2tar_it != m_url2tar.end()); Q_ASSERT(m_job == NULL); m_job = startDownload(url, info.part); connect(m_job, SIGNAL(result(KJob*)), SLOT(slotObjectFinished(KJob*))); } } void ArchiveDialog::slotObjectFinished(KJob *_job) { KIO::StoredTransferJob *job = qobject_cast(_job); Q_ASSERT(job == m_job); m_job = NULL; const QUrl &url = m_dlurl2tar_it.key(); DownloadInfo &info = m_dlurl2tar_it.value(); assert(info.tarName.isNull()); bool error = job->error(); if (!error) { const QString &mimetype(job->mimetype()); info.tarName = uniqTarName(appendMimeTypeSuffix(url.fileName(), mimetype), 0); QByteArray data(job->data()); const QString &tarName = info.tarName; // qCDebug(WEBARCHIVERPLUGIN_LOG) << "downloaded " << url.toDisplayString() << "size=" << data.size() << "mimetype" << mimetype; error = ! m_tarBall->writeFile(tarName, data, archivePerms, QString::null, QString::null, m_archiveTime, m_archiveTime, m_archiveTime); if (error) { qCDebug(WEBARCHIVERPLUGIN_LOG) << "Error writing to archive file"; finishedArchiving(true); return; } } else { info.tarName.clear(); qCDebug(WEBARCHIVERPLUGIN_LOG) << "download error for url='" << url; } endProgressInfo(error); ++m_objects_it; downloadObjects(); } void ArchiveDialog::downloadStyleSheets() { if (m_styleSheets_it == m_cssURLs.end()) { saveWebpages(); } else { // QTimer::singleShot(3000, this, SLOT(slotDownloadStyleSheetsDelay())); const QUrl &url = m_styleSheets_it.key(); m_dlurl2tar_it = m_url2tar.find(url); assert(m_dlurl2tar_it != m_url2tar.end()); DownloadInfo &info = m_dlurl2tar_it.value(); Q_ASSERT(m_job == NULL); m_job = startDownload(url, info.part); connect(m_job, SIGNAL(result(KJob*)), SLOT(slotStyleSheetFinished(KJob*))); } } void ArchiveDialog::slotStyleSheetFinished(KJob *_job) { KIO::StoredTransferJob *job = qobject_cast(_job); Q_ASSERT(job == m_job); m_job = NULL; const QUrl &url = m_dlurl2tar_it.key(); DownloadInfo &info = m_dlurl2tar_it.value(); bool error = job->error(); if (! error) { QByteArray data(job->data()); const QString &tarName = info.tarName; URLsInStyleSheet::Iterator uss_it = m_URLsInStyleSheet.find(m_styleSheets_it.value()); assert(uss_it != m_URLsInStyleSheet.end()); DOM::DOMString ds(uss_it.key().charset()); QString cssCharSet(ds.string()); bool ok; QTextCodec *codec = KCharsets::charsets()->codecForName(cssCharSet, ok); qCDebug(WEBARCHIVERPLUGIN_LOG) << "translating URLs in CSS" << url << "charset=" << cssCharSet << " found=" << ok; assert(codec); QString css_text = codec->toUnicode(data); data.clear(); // Do *NOT* delete 'codec'! These are allocated by Qt changeCSSURLs(css_text, uss_it.value()); data = codec->fromUnicode(css_text); css_text.clear(); error = ! m_tarBall->writeFile(tarName, data, archivePerms, QString::null, QString::null, m_archiveTime, m_archiveTime, m_archiveTime); if (error) { qCDebug(WEBARCHIVERPLUGIN_LOG) << "Error writing to archive file"; finishedArchiving(true); return; } } else { info.tarName.clear(); qCDebug(WEBARCHIVERPLUGIN_LOG) << "download error for css url='" << url; } endProgressInfo(error); ++m_styleSheets_it; downloadStyleSheets(); } KIO::Job *ArchiveDialog::startDownload(const QUrl &url, KHTMLPart *part) { QTreeWidgetItem *twi = new QTreeWidgetItem; twi->setText(0, i18n("Downloading")); twi->setText(1, url.toDisplayString()); QTreeWidget *tw = m_widget->progressView; tw->insertTopLevelItem(0, twi); KIO::Job *job = KIO::storedGet(url, KIO::NoReload, KIO::HideProgressInfo); // Use entry from cache only. Avoids re-downloading. Requires modified kio_http slave. job->addMetaData(QStringLiteral("cache"), patchedHttpSlave ? "cacheonly" : "cache"); // This is a duplication of the code in loader.cpp: Loader::servePendingRequests() //job->addMetaData("accept", req->object->accept()); job->addMetaData(QStringLiteral("referrer"), part->url().url()); job->addMetaData(QStringLiteral("cross-domain"), part->toplevelURL().url()); return job; } void ArchiveDialog::endProgressInfo(bool error) { QTreeWidget *tw = m_widget->progressView; tw->topLevelItem(0)->setText(0, error ? i18n("Error") : i18n("OK")); QProgressBar *pb = m_widget->progressBar; pb->setValue(pb->value() + 1); } void ArchiveDialog::saveWebpages() { bool error = saveTopFrame(); if (error) { qCDebug(WEBARCHIVERPLUGIN_LOG) << "Error writing to archive file"; finishedArchiving(true); return; } QProgressBar *pb = m_widget->progressBar; pb->setValue(pb->value() + 1); // KMessageBox::information(0, i18n( "Archiving webpage completed." ), QString::null, QString::null, false); finishedArchiving(false); } void ArchiveDialog::finishedArchiving(bool tarerror) { if (tarerror) { KMessageBox::error(this, i18n("I/O error occurred while writing to web archive file %1.", m_tarBall->fileName())); } m_tarBall->close(); m_widget->progressView->sortItems(0, Qt::AscendingOrder); setDefaultButton(KDialog::Ok); setEscapeButton(KDialog::Ok); enableButtonOk(true); enableButtonCancel(false); } void ArchiveDialog::slotButtonClicked(int) { deleteLater(); // Keep memory consumption low } // This is the mess you get because C++ lacks a lambda generator // // The whole purpose of the Get* classes is to parametrize what // attribute of a KHTMLPart object should be fetched. // // GetName and GetURL are used for the 'class FuncObj' parameter // class in the template function filterFrameMappings below struct GetFromPart { const KHTMLPart *child; GetFromPart(const KHTMLPart *_child) : child(_child) { } }; struct GetName : public GetFromPart { GetName(const KHTMLPart *child) : GetFromPart(child) { } operator QString() { return child->objectName(); } }; struct GetURL : public GetFromPart { GetURL(const KHTMLPart *child) : GetFromPart(child) { } operator QUrl() { return child->url(); } }; template< class Id2Part, class FuncObj > static void filterFrameMappings(KHTMLPart *part, Id2Part &result) { Id2Part existing_frames; // TODO this can probably be optimized: no temp of existing, directly store to be removed parts. ROPartList childParts(part->frames()); FOR_ITER(ROPartList, childParts, child_it) { // TODO It is not clear from browsing the source code of KHTML if *child_it may be NULL Q_ASSERT(*child_it); KHTMLPart *cp = isArchivablePart(*child_it); if (cp) { existing_frames.insert(FuncObj(cp), cp); } } typedef QList< typename Id2Part::Iterator > IdRemoveList; IdRemoveList beRemoved; FOR_ITER_TEMPLATE(Id2Part, result, it) { typename Id2Part::Iterator exists_it = existing_frames.find(it.key()); if (exists_it == existing_frames.end()) { beRemoved.append(it); } else { it.value() = exists_it.value(); } } FOR_ITER_TEMPLATE(IdRemoveList, beRemoved, rem_it) { qCDebug(WEBARCHIVERPLUGIN_LOG) << "removing insecure(?) frame='" << (*rem_it).key(); result.erase((*rem_it)); } } template void filterFrameMappings< ArchiveDialog::Name2Part, GetName >(KHTMLPart *, ArchiveDialog::Name2Part &); template void filterFrameMappings< ArchiveDialog::URL2Part, GetURL >(KHTMLPart *, ArchiveDialog::URL2Part &); /** * Recursively traverses the DOM-Tree extracting all URLs that need to be downloaded */ void ArchiveDialog::obtainURLs() { m_url2tar.clear(); m_tarName2part.clear(); m_framesInPart.clear(); m_cssURLs.clear(); m_URLsInStyleSheet.clear(); m_URLsInStyleElement.clear(); m_topStyleSheets.clear(); obtainURLsLower(m_top, 0); FOR_ITER(FramesInPart, m_framesInPart, fip_it) { KHTMLPart *part = fip_it.key(); PartFrameData &pfd = fip_it.value(); // Remove all frames obtained from the DOM tree parse // that do not have a corresponding KHTMLPart as a direct child. // Do NOT use KHTMLPart::findFrame()! This one searches recursively all subframes as well! filterFrameMappings< Name2Part, GetName >(part, pfd.framesWithName); filterFrameMappings< URL2Part, GetURL >(part, pfd.framesWithURLOnly); } assert(! m_framesInPart.empty()); #if 0 FOR_ITER(CSSURLSet, m_cssURLs, it) { qCDebug(WEBARCHIVERPLUGIN_LOG) << "to be downloaded stylesheet='" << it.key(); } FOR_ITER(URLsInStyleSheet, m_URLsInStyleSheet, ss2u_it) { qCDebug(WEBARCHIVERPLUGIN_LOG) << "raw URLs in sheet='" << ss2u_it.key().href(); FOR_ITER(RawHRef2FullURL, ss2u_it.data(), c2f_it) { qCDebug(WEBARCHIVERPLUGIN_LOG) << " url='" << c2f_it.key() << "' -> '" << c2f_it.data().toDisplayString(); } } FOR_ITER(URLsInStyleElement, m_URLsInStyleElement, e2u_it) { qCDebug(WEBARCHIVERPLUGIN_LOG) << "raw URLs in style-element:"; FOR_ITER(RawHRef2FullURL, e2u_it.data(), c2f_it) { qCDebug(WEBARCHIVERPLUGIN_LOG) << " url='" << c2f_it.key() << "' -> '" << c2f_it.data().toDisplayString(); } } #endif } void ArchiveDialog::obtainStyleSheetURLsLower(DOM::CSSStyleSheet css, RecurseData &data) { //qCDebug(WEBARCHIVERPLUGIN_LOG) << "stylesheet title='" << styleSheet.title().string() << "' " // "type='" << styleSheet.type().string(); RawHRef2FullURL &raw2full = m_URLsInStyleSheet.insert(css, RawHRef2FullURL()).value(); DOM::CSSRuleList crl = css.cssRules(); for (int j = 0; j != static_cast(crl.length()); ++j) { DOM::CSSRule cr = crl.item(j); switch (cr.type()) { case DOM::CSSRule::STYLE_RULE: { const DOM::CSSStyleRule &csr = static_cast(cr); //qCDebug(WEBARCHIVERPLUGIN_LOG) << "found selector '" << csr.selectorText(); parseStyleDeclaration(css.baseUrl(), csr.style(), raw2full, data); } break; case DOM::CSSRule::IMPORT_RULE: { const DOM::CSSImportRule &cir = static_cast(cr); DOM::CSSStyleSheet importSheet = cir.styleSheet(); if (importSheet.isNull()) { // Given stylesheet was not downloaded / parsed by KHTML // Remove that URL from the stylesheet qCDebug(WEBARCHIVERPLUGIN_LOG) << "stylesheet: invalid @import url('" << cir.href() << "')"; raw2full.insert(cir.href().string(), QUrl()); } else { qCDebug(WEBARCHIVERPLUGIN_LOG) << "stylesheet: @import url('" << cir.href() << "') found"; QString href = cir.href().string(); Q_ASSERT(!href.isNull()); QUrl fullURL = importSheet.baseUrl(); bool inserted = insertHRefFromStyleSheet(href, raw2full, fullURL, data); if (inserted) { m_cssURLs.insert(fullURL, importSheet); obtainStyleSheetURLsLower(importSheet, data); } } } break; default: qCDebug(WEBARCHIVERPLUGIN_LOG) << " unknown/unsupported rule=" << cr.type(); } } } void ArchiveDialog::obtainURLsLower(KHTMLPart *part, int level) { //QString indent; //indent.fill(' ', level*2); QString htmlFileName = (level == 0) ? QStringLiteral("index.html") : part->url().fileName(); // Add .html extension if not found already. This works around problems with frames, // where the frame is for example "framead.php". The http-io-slave gets the mimetype // from the webserver, but files in a tar archive do not have such metadata. The result // is that Konqueror asks "save 'adframe.php' to file?" without this measure. htmlFileName = appendMimeTypeSuffix(htmlFileName, QStringLiteral("text/html")); // If level == 0, the m_tarName2part map is empty and so uniqTarName will return "index.html" unchanged. uniqTarName(htmlFileName, part); assert(m_framesInPart.find(part) == m_framesInPart.end()); FramesInPart::Iterator fip_it = m_framesInPart.insert(part, PartFrameData()); RecurseData data(part, 0, &(fip_it.value())); data.document.documentElement(); obtainPartURLsLower(data.document.documentElement(), 1, data); { // Limit lifetime of @c childParts ROPartList childParts(part->frames()); FOR_ITER(ROPartList, childParts, child_it) { KHTMLPart *cp = isArchivablePart(*child_it); if (cp) { obtainURLsLower(cp, level + 1); } } } DOM::StyleSheetList styleSheetList = data.document.styleSheets(); //qCDebug(WEBARCHIVERPLUGIN_LOG) << "# of stylesheets=" << styleSheetList.length(); for (int i = 0; i != static_cast(styleSheetList.length()); ++i) { DOM::StyleSheet ss = styleSheetList.item(i); if (ss.isCSSStyleSheet()) { DOM::CSSStyleSheet &css = static_cast(ss); QString href = css.href().string(); if (! href.isNull()) { QString href = css.href().string(); QUrl fullUrl = css.baseUrl(); qCDebug(WEBARCHIVERPLUGIN_LOG) << "top-level stylesheet='" << href; bool inserted = insertTranslateURL(fullUrl, data); if (inserted) { m_cssURLs.insert(fullUrl, css); } } else { DOM::Node node = css.ownerNode(); if (! node.isNull()) { assert(! m_topStyleSheets.contains(node)); qCDebug(WEBARCHIVERPLUGIN_LOG) << "top-level inline stylesheet '" << node.nodeName(); // TODO I think there can be more than one