diff options
Diffstat (limited to 'libkonq')
64 files changed, 13862 insertions, 0 deletions
diff --git a/libkonq/AUTHORS b/libkonq/AUTHORS new file mode 100644 index 000000000..e33d1edd6 --- /dev/null +++ b/libkonq/AUTHORS @@ -0,0 +1,7 @@ +The classes in this library were written by: +Torben Weis <[email protected]> +David Faure <[email protected]> +Simon Hausmann <[email protected]> +Holger Freyther <[email protected]> +and are all available under the LGPL license. +See the individual files for more. diff --git a/libkonq/DESIGN b/libkonq/DESIGN new file mode 100644 index 000000000..0f9fa893b --- /dev/null +++ b/libkonq/DESIGN @@ -0,0 +1,46 @@ +libkonq is the construction kit for a file manager +(together with libkio, which is at a lower level) + +It is used by konqueror, of course, but also by kdesktop, which is +another file manager in fact, and by apps that want to use +the properties dialog boxes (like kpanel and kfind) or the bookmark +classes. + +libkonq contents : +================== + +1) menus +kbookmark.* : general purpose bookmark class +kbookmarkmenu.* : bookmark menu +kbookmarkbar.* : bookmark bar +konq_popupmenu.*: popupmenu for URLs +konq_xmlguiclient.* : general purpose xmlgui manipulating class +knewmenu.* : implements the 'new' menu (file templates) + +2) files +konq_operations.*: common operations to all views, like deleting files, and + dropping files. +konq_undo.* : undo feature for file operations + +3) icons +kfileivi.* : icon representing a file (inherits QListViewItem and + uses KFileItem for holding file information) +konq_iconviewwidget.* : the specialisation of QIconView that knows about files. + Basic widget for icon views in konqueror and kdesktop. +konq_drag.* : the drag object for konqiconviewwidget (adds text/uri-list + support to QIconView's drag object). + +4) configuration +konq_propsview.* : view properties (global and per-directory) +konq_settings.* : settings +konq_defaults.h : default values, shared with kcmkonq + +5) directory views +konq_dirpart.* : base class for directory views +konq_bgnddlg.* : background chosing dialog + +6) other +konq_events.* : events sent by konqueror, for use by the views [& metaviews] + +7) plugin interface +konq_popupmenu.h : class KonqPopupMenuPlugin diff --git a/libkonq/Makefile.am b/libkonq/Makefile.am new file mode 100644 index 000000000..b93f3395a --- /dev/null +++ b/libkonq/Makefile.am @@ -0,0 +1,72 @@ +# This file is part of the KDE libraries +# Copyright (C) 1997 David Faure <[email protected]> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# 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. + +INCLUDES = -I$(kde_includes)/arts $(all_includes) +SUBDIRS = pics favicons + +lib_LTLIBRARIES = libkonq.la +libkonq_la_LDFLAGS = $(all_libraries) -version-info 6:0:2 -no-undefined +libkonq_la_LIBADD = $(LIB_KPARTS) + +libkonq_la_SOURCES = konq_popupmenu.cc knewmenu.cc \ + konq_xmlguiclient.cc\ + kfileivi.cc konq_iconviewwidget.cc konq_settings.cc konq_drag.cc \ + konq_operations.cc \ + konq_dirpart.cc konq_propsview.cc konq_events.cc konq_bgnddlg.cc \ + konq_undo.cc konq_undo.skel \ + konq_historymgr.cc konq_historycomm.cc konq_historycomm.skel \ + konq_pixmapprovider.cc \ + kivdirectoryoverlay.cc \ + konq_faviconmgr.cc konq_faviconmgr.skel konq_filetip.cc + +directory_DATA = directory_bookmarkbar.desktop +directorydir = $(kde_datadir)/kbookmark + +servicetype_DATA = konqpopupmenuplugin.desktop +servicetypedir = $(kde_servicetypesdir) + +METASOURCES = AUTO + +include_HEADERS = konq_popupmenu.h knewmenu.h \ + kfileivi.h konq_drag.h konq_iconviewwidget.h \ + konq_defaults.h konq_settings.h \ + konq_operations.h libkonq_export.h \ + konq_dirpart.h konq_propsview.h konq_events.h \ + konq_undo.h konq_historymgr.h konq_historycomm.h \ + konq_pixmapprovider.h \ + kivdirectoryoverlay.h \ + konq_faviconmgr.h konq_xmlguiclient.h konqbookmarkmanager.h konq_filetip.h + + +if include_ARTS +ARTS_MODULE = konq_sound.la +endif + +kde_module_LTLIBRARIES = $(ARTS_MODULE) +konq_sound_la_SOURCES = konq_sound.cc +konq_sound_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) +konq_sound_la_LIBADD = -lsoundserver_idl -lartskde + +noinst_HEADERS = konq_sound.h + +messages: + $(XGETTEXT) *.cc *.h -o $(podir)/libkonq.pot + +DOXYGEN_REFERENCES = dcop kdecore kio/bookmarks +include ../admin/Doxyfile.am + diff --git a/libkonq/PLUGINS b/libkonq/PLUGINS new file mode 100644 index 000000000..4eb535a59 --- /dev/null +++ b/libkonq/PLUGINS @@ -0,0 +1,13 @@ +Thus file explains how to add plugins into the konqpopupmenu +used by konqueror and kdesktop. + +Why? +Why do we need this kind of functionality? We do have SERVICEMENUS. +A plugin can be much more dynamic. If you want to add features that +are runtime specific or need some interaction with different things +a plugin comes in handy + +How? +Please look at ?no_location? to get the latest plugin template + +Holger Freyther 30th October 2001
\ No newline at end of file diff --git a/libkonq/SERVICEMENUS b/libkonq/SERVICEMENUS new file mode 100644 index 000000000..1419b86e0 --- /dev/null +++ b/libkonq/SERVICEMENUS @@ -0,0 +1,52 @@ +This file explains how to add an item in the popupmenu (for both +konqueror and kdesktop), without using the file associations. + +Why +=== +One reason for doing this is being able to associate +some action with all files without this action becoming a default handler +(called on left click). +Another is that for text-based programs and tools (e.g. gzip) it's faster +than defining a desktop file for the application, making it hidden, and +associate it with the relevant file types. + +How +=== +Create a file ~/.kde/share/apps/konqueror/servicemenus/something.desktop +and write into it something like (without the comments) : + +[Desktop Entry] +ServiceTypes=text/html,text/plain # use all/all for all entries + # all/allfiles for files only + # and use inode/directory for dirs only + # you can also do things like image/* for all + # image mimetypes +Actions=gzip;mail # those are ';' separated, per the standard ! +X-KDE-Submenu=Menuname # this optional entry allows grouping the + # entries in this servicemenu file into a + # common submenu, in this case "Menuname" +TryExec=gzip # Find if executable exist, if it doesn't exist + # menu entry is not displaying +ExcludeServiceTypes=application/x-zip # This entry is used to avoid to display menu + # when it's a specific servicetype + # for exemple when we use all/allfiles and zip + # them, we don't want to zip a zip file + + +[Desktop Action gzip] # One "Desktop Action <name>" group per Action +Name=GZip this file +Name[fr]=... +Icon=tgz +Exec=gzip %f + +[Desktop Action mail] +Name=Mail this file +Name[fr]=... +Icon=kmail +Exec=kmail --there-is-no-such-option-yet %f + + +See also the "desktop entry standard", which defines more formally the same +concept of actions but for desktop files (e.g. eject on a device desktop file, +etc.) + diff --git a/libkonq/directory_bookmarkbar.desktop b/libkonq/directory_bookmarkbar.desktop new file mode 100644 index 000000000..93c7e9012 --- /dev/null +++ b/libkonq/directory_bookmarkbar.desktop @@ -0,0 +1,83 @@ +[Desktop Entry] +Icon=bookmark_toolbar +Name=Bookmark Toolbar +Name[af]=Boekmerk Nutsbalk +Name[ar]=شريط أدوات علامات المواقع +Name[az]=Vasitə Çubuğu Dəftəri +Name[be]=Панэль закладак +Name[bg]=Лента с отметки +Name[bn]=বুকমার্ক টুলবার +Name[br]=Barrenn ostilhoù sined +Name[bs]=Traka s zabilješkama +Name[ca]=Barra d'eines de punts +Name[cs]=Lišta se záložkami +Name[csb]=Lëstew załóżków +Name[cy]=Bar Offer Nodau Tudalen +Name[da]=Bogmærkeværktøjslinje +Name[de]=Lesezeichenleiste +Name[el]=Γραμμή εργαλείων Σελιδοδεικτών +Name[eo]=Legosigno-ilobreto +Name[es]=Barra de herramientas de marcadores +Name[et]=Järjehoidjate tööriistariba +Name[eu]=Laster-marka barra +Name[fa]=میله ابزار چوب الف +Name[fi]=Kirjanmerkkien työkalurivi +Name[fo]=Bókamerkisamboðsbjálki +Name[fr]=Barre de signets +Name[fy]=Blêdwizerbalke +Name[ga]=Barra Uirlisí Leabharmharcanna +Name[gl]=Barra de Marcadores +Name[he]=סרגל הסימניות +Name[hi]=औज़ार-पट्टी पसंद +Name[hr]=Traka s oznakama +Name[hu]=Könyvjelző-eszköztár +Name[id]=Toolbar Bookmark +Name[is]=Bókamerkjaslá +Name[it]=Barra dei segnalibri +Name[ja]=ブックマークツールバー +Name[ka]=სანიშნეთა პანელი +Name[kk]=Бетбелгі панелі +Name[km]=របារឧបករណ៍ចំណាំ +Name[ko]=책갈피 도구 모음 +Name[lo]=ແຖບເຄື່ອງມືທີ່ຄັ້ນປື້ນ +Name[lt]=Žymelių įrankių juosta +Name[lv]=Grāmatzīmju Rīkjosla +Name[mk]=Алатник за обележувачи +Name[mn]=Хавчуурга-самбар +Name[ms]=Bar Alatan Penandabuku +Name[mt]=Toolbar tal-Favoriti +Name[nb]=Bokmerkeverktøy +Name[nds]=Leesteken-Warktüüchbalken +Name[ne]=पुस्तकचिनो उपकरणपट्टी +Name[nl]=Bladwijzer - werkbalk +Name[nn]=Bokmerke-verktøylinje +Name[nso]=Bar ya Sebereka sa Tshwao ya Buka +Name[oc]=Otilh de puents +Name[pa]=ਬੁੱਕਮਾਰਕ ਸੰਦਪੱਟੀ +Name[pl]=Pasek zakładek +Name[pt]=Barra de Favoritos +Name[pt_BR]=Barra de Ferramenta de Favoritos +Name[ro]=Bară semne de carte +Name[ru]=Панель закладок +Name[rw]=Umwanyabikoresho w'Akamenyetso +Name[se]=Girjemearkaholga +Name[sk]=Panel záložiek +Name[sl]=Orodna vrstica z zaznamki +Name[sr]=Трака са маркерима +Name[sr@Latn]=Traka sa markerima +Name[sv]=Verktygsrad med bokmärken +Name[ta]=புத்தகக்குறிக் கருவிப்பட்டை +Name[tg]=Панели поягузорӣ +Name[th]=แถบเครื่องมือที่คั่นหน้า +Name[tr]=Araç Çubuğu Defteri +Name[tt]=Bitbilge Tiräse +Name[uk]=Панель закладок +Name[uz]=Xatchoʻp paneli +Name[uz@cyrillic]=Хатчўп панели +Name[ven]=Bara ya tshishumiswa tsha tswayo ya bugu +Name[vi]=Thanh công cụ Lưu địa chỉ +Name[wa]=Bår ås usteyes des rmåkes +Name[xh]=Ibar yesixhobo Senqaku lencwadi +Name[zh_CN]=书签工具栏 +Name[zh_TW]=書籤工具列 +Name[zu]=Ibha yamathuluzi yomaka bencwadi diff --git a/libkonq/favicons/Makefile.am b/libkonq/favicons/Makefile.am new file mode 100644 index 000000000..a53c7faf1 --- /dev/null +++ b/libkonq/favicons/Makefile.am @@ -0,0 +1,15 @@ +kde_module_LTLIBRARIES = kded_favicons.la + +INCLUDES = $(all_includes) +kded_favicons_la_SOURCES = favicons.cpp favicons.skel +kded_favicons_la_LDFLAGS = $(all_libraries) -module -avoid-version +kded_favicons_la_LIBADD = $(LIB_KSYCOCA) + +METASOURCES = AUTO + +servicesdir = $(kde_servicesdir)/kded +services_DATA = favicons.desktop + +update_DATA = favicons.upd +update_SCRIPTS = move_favicons.sh +updatedir = $(kde_datadir)/kconf_update diff --git a/libkonq/favicons/favicons.cpp b/libkonq/favicons/favicons.cpp new file mode 100644 index 000000000..9419b4b03 --- /dev/null +++ b/libkonq/favicons/favicons.cpp @@ -0,0 +1,275 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Malte Starostik <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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 <string.h> +#include <time.h> + +#include <qbuffer.h> +#include <qfile.h> +#include <qcache.h> +#include <qimage.h> +#include <qtimer.h> + +#include <kdatastream.h> // DO NOT REMOVE, otherwise bool marshalling breaks +#include <kicontheme.h> +#include <kimageio.h> +#include <ksimpleconfig.h> +#include <kstandarddirs.h> +#include <kio/job.h> + +#include "favicons.moc" + +struct FaviconsModulePrivate +{ + virtual ~FaviconsModulePrivate() { delete config; } + + struct DownloadInfo + { + QString hostOrURL; + bool isHost; + QByteArray iconData; + }; + QMap<KIO::Job *, DownloadInfo> downloads; + QStringList failedDownloads; + KSimpleConfig *config; + QPtrList<KIO::Job> killJobs; + KIO::MetaData metaData; + QString faviconsDir; + QCache<QString> faviconsCache; +}; + +FaviconsModule::FaviconsModule(const QCString &obj) + : KDEDModule(obj) +{ + // create our favicons folder so that KIconLoader knows about it + d = new FaviconsModulePrivate; + d->faviconsDir = KGlobal::dirs()->saveLocation( "cache", "favicons/" ); + d->faviconsDir.truncate(d->faviconsDir.length()-9); // Strip off "favicons/" + d->metaData.insert("ssl_no_client_cert", "TRUE"); + d->metaData.insert("ssl_militant", "TRUE"); + d->metaData.insert("UseCache", "false"); + d->metaData.insert("cookies", "none"); + d->metaData.insert("no-auth", "true"); + d->config = new KSimpleConfig(locateLocal("data", "konqueror/faviconrc")); + d->killJobs.setAutoDelete(true); + d->faviconsCache.setAutoDelete(true); +} + +FaviconsModule::~FaviconsModule() +{ + delete d; +} + +QString removeSlash(QString result) +{ + for (unsigned int i = result.length() - 1; i > 0; --i) + if (result[i] != '/') + { + result.truncate(i + 1); + break; + } + + return result; +} + + +QString FaviconsModule::iconForURL(const KURL &url) +{ + if (url.host().isEmpty()) + return QString::null; + + QString icon; + QString simplifiedURL = simplifyURL(url); + + QString *iconURL = d->faviconsCache.find( removeSlash(simplifiedURL) ); + if (iconURL) + icon = *iconURL; + else + icon = d->config->readEntry( removeSlash(simplifiedURL) ); + + if (!icon.isEmpty()) + icon = iconNameFromURL(KURL( icon )); + else + icon = url.host(); + + icon = "favicons/" + icon; + + if (QFile::exists(d->faviconsDir+icon+".png")) + return icon; + + return QString::null; +} + +QString FaviconsModule::simplifyURL(const KURL &url) +{ + // splat any = in the URL so it can be safely used as a config key + QString result = url.host() + url.path(); + for (unsigned int i = 0; i < result.length(); ++i) + if (result[i] == '=') + result[i] = '_'; + return result; +} + +QString FaviconsModule::iconNameFromURL(const KURL &iconURL) +{ + if (iconURL.path() == "/favicon.ico") + return iconURL.host(); + + QString result = simplifyURL(iconURL); + // splat / so it can be safely used as a file name + for (unsigned int i = 0; i < result.length(); ++i) + if (result[i] == '/') + result[i] = '_'; + + QString ext = result.right(4); + if (ext == ".ico" || ext == ".png" || ext == ".xpm") + result.remove(result.length() - 4, 4); + + return result; +} + +bool FaviconsModule::isIconOld(const QString &icon) +{ + struct stat st; + if (stat(QFile::encodeName(icon), &st) != 0) + return true; // Trigger a new download on error + + return (time(0) - st.st_mtime) > 604800; // arbitrary value (one week) +} + +void FaviconsModule::setIconForURL(const KURL &url, const KURL &iconURL) +{ + QString simplifiedURL = simplifyURL(url); + + d->faviconsCache.insert(removeSlash(simplifiedURL), new QString(iconURL.url()) ); + + QString iconName = "favicons/" + iconNameFromURL(iconURL); + QString iconFile = d->faviconsDir + iconName + ".png"; + + if (!isIconOld(iconFile)) { + emit iconChanged(false, simplifiedURL, iconName); + return; + } + + startDownload(simplifiedURL, false, iconURL); +} + +void FaviconsModule::downloadHostIcon(const KURL &url) +{ + QString iconFile = d->faviconsDir + "favicons/" + url.host() + ".png"; + if (!isIconOld(iconFile)) + return; + + startDownload(url.host(), true, KURL(url, "/favicon.ico")); +} + +void FaviconsModule::startDownload(const QString &hostOrURL, bool isHost, const KURL &iconURL) +{ + if (d->failedDownloads.contains(iconURL.url())) + return; + + KIO::Job *job = KIO::get(iconURL, false, false); + job->addMetaData(d->metaData); + connect(job, SIGNAL(data(KIO::Job *, const QByteArray &)), SLOT(slotData(KIO::Job *, const QByteArray &))); + connect(job, SIGNAL(result(KIO::Job *)), SLOT(slotResult(KIO::Job *))); + connect(job, SIGNAL(infoMessage(KIO::Job *, const QString &)), SLOT(slotInfoMessage(KIO::Job *, const QString &))); + FaviconsModulePrivate::DownloadInfo download; + download.hostOrURL = hostOrURL; + download.isHost = isHost; + d->downloads.insert(job, download); +} + +void FaviconsModule::slotData(KIO::Job *job, const QByteArray &data) +{ + FaviconsModulePrivate::DownloadInfo &download = d->downloads[job]; + unsigned int oldSize = download.iconData.size(); + if (oldSize > 0x10000) + { + d->killJobs.append(job); + QTimer::singleShot(0, this, SLOT(slotKill())); + } + download.iconData.resize(oldSize + data.size()); + memcpy(download.iconData.data() + oldSize, data.data(), data.size()); +} + +void FaviconsModule::slotResult(KIO::Job *job) +{ + FaviconsModulePrivate::DownloadInfo download = d->downloads[job]; + d->downloads.remove(job); + KURL iconURL = static_cast<KIO::TransferJob *>(job)->url(); + QString iconName; + if (!job->error()) + { + QBuffer buffer(download.iconData); + buffer.open(IO_ReadOnly); + QImageIO io; + io.setIODevice(&buffer); + io.setParameters("size=16"); + // Check here too, the job might have had no error, but the downloaded + // file contains just a 404 message sent with a 200 status. + // microsoft.com does that... (malte) + if (io.read()) + { + // Some sites have nasty 32x32 icons, according to the MS docs + // IE ignores them, well, we scale them, otherwise the location + // combo / menu will look quite ugly + if (io.image().width() != KIcon::SizeSmall || io.image().height() != KIcon::SizeSmall) + io.setImage(io.image().smoothScale(KIcon::SizeSmall, KIcon::SizeSmall)); + + if (download.isHost) + iconName = download.hostOrURL; + else + iconName = iconNameFromURL(iconURL); + + iconName = "favicons/" + iconName; + + io.setIODevice(0); + io.setFileName(d->faviconsDir + iconName + ".png"); + io.setFormat("PNG"); + if (!io.write()) + iconName = QString::null; + else if (!download.isHost) + d->config->writeEntry( removeSlash(download.hostOrURL), iconURL.url()); + } + } + if (iconName.isEmpty()) + d->failedDownloads.append(iconURL.url()); + + emit iconChanged(download.isHost, download.hostOrURL, iconName); +} + +void FaviconsModule::slotInfoMessage(KIO::Job *job, const QString &msg) +{ + emit infoMessage(static_cast<KIO::TransferJob *>( job )->url(), msg); +} + +void FaviconsModule::slotKill() +{ + d->killJobs.clear(); +} + +extern "C" { + KDE_EXPORT KDEDModule *create_favicons(const QCString &obj) + { + KImageIO::registerFormats(); + return new FaviconsModule(obj); + } +} + +// vim: ts=4 sw=4 et diff --git a/libkonq/favicons/favicons.desktop b/libkonq/favicons/favicons.desktop new file mode 100644 index 000000000..7a7042057 --- /dev/null +++ b/libkonq/favicons/favicons.desktop @@ -0,0 +1,148 @@ +[Desktop Entry] +Type=Service +Name=KDED Favicon Module +Name[af]=Kded Favicon Module +Name[ar]=وحدة KDED لأيقونة الموقع +Name[az]=KDED Favikon Modulu +Name[be]=Модуль любімай значкі KDED +Name[bn]=KDED ফ্যাভ-আইকন মডিউল +Name[br]=Mollad Favicon evit KDED +Name[bs]=KDED Favicon modul +Name[ca]=Mòdul KDED per a favicon +Name[cs]=Modul KDEDu Favicon +Name[csb]=Ikònczi serwerów +Name[cy]=Modiwl KDED Favicon +Name[da]=KDED Favicon-modul +Name[de]=Webseitensymbol-Verwaltung +Name[el]=KDED άρθρωμα Favicon +Name[eo]=Modulo por interretaj piktogramoj +Name[es]=Módulo Favicon de KDED +Name[et]=KDED favicon moodul +Name[eu]=KDED Favicon modulua +Name[fa]=پیمانۀ شمایل پسندان KDED +Name[fi]=KDED-sivustokuvakemoduuli +Name[fr]=Icône préférée +Name[gl]=Módulo de Faviconas de KDED +Name[he]=מודול סמלים מועדפים של KDED +Name[hi]=केडीईडी फेविकॉन मॉड्यूल +Name[hr]=KDED Favicon modul +Name[hu]=KDED favicon-kezelő +Name[id]=Modul Favicon KDE +Name[is]=KDED Favicon íhlutur +Name[it]=Modulo Favicon di KDED +Name[ja]=KDED Favicon モジュール +Name[ka]=KDED Favicon მოდული +Name[kk]=KDED сайт белгішілер модулі +Name[km]=ម៉ូឌុល KDED Favicon +Name[ko]=KDED 파비콘 모듈 +Name[lo]=ໂມດລູໄອຄອນ KDED +Name[lt]=KDED favicon modulis +Name[lv]=KDED Favikon Modulis +Name[mk]=KDED Favicon Модул +Name[mn]=Favicon-Модул +Name[ms]=Modul KDED Favicon +Name[mt]=Modulu "favicon" KDED +Name[nb]=KDED favicon-modul +Name[nds]=KDED-Moduul för Nettsiet-Lüttbiller +Name[ne]=KDED फेभिकन मोड्युल +Name[nn]=KDED favicon-modul +Name[nso]=Seripa sa Favicon ya KDE +Name[pa]=KDED ਫਾਵੀਕੋਨ ਮੋਡੁਲੀ +Name[pl]=Ikony serwerów +Name[pt]=Módulo Favicon do KDED +Name[pt_BR]=Módulo Favicon do KDE +Name[ro]=Module favicon KDED +Name[ru]=Служба значков +Name[rw]=Igice KDED Favicon +Name[se]=KDED favicon-moduvla +Name[sk]=Modul KDED Favicon +Name[sl]=Modul KDED za favicon +Name[sr]=Favicon модул, KDED +Name[sr@Latn]=Favicon modul, KDED +Name[sv]=KDED-favoritikonmodul +Name[ta]=KDED பெவிகான் பகுதி +Name[tg]=Бахши пиктограммаи KDED +Name[th]=โมดูลไอคอนเว็บของ KDED +Name[tr]=KDE Favicon Modülü +Name[tt]=KDED İkon Module +Name[uk]=Модуль KDED Favicon +Name[uz]=KDED Favicon moduli +Name[uz@cyrillic]=KDED Favicon модули +Name[ven]=Modulu wa KDED wa Favikhono +Name[vi]=Mô đun KDED Favicon +Name[wa]=Module imådjetes KDED +Name[xh]=KDED Isichatshulwa se Favicon +Name[zh_CN]=KDED 收藏图标模块 +Name[zh_TW]=KDED Favicon 測試模組 +Name[zu]=Ingxenye ye-Favicon ye-KDED +Comment=Shortcut icon support +Comment[af]=Kortpad ikoon ondersteuning +Comment[az]=Qısa yol timsal dəstəyi +Comment[be]=Падтрымка значак скаротаў +Comment[bg]=Поддръжка на препратки с икони +Comment[bn]=শর্টকাট আইকন সাপোর্ট +Comment[bs]=Podrška za ikone sa prečicama +Comment[ca]=Suport de dreceres d'icona +Comment[cs]=Podpora ikon zkratek +Comment[csb]=Òbsłużënk ikònów serwerów +Comment[da]=Genvejsikonstøtte +Comment[de]=Unterstützung für Webseitensymbole ("Favicons") in KDE +Comment[el]=υποστήριξη εικονιδίων Συντόμευσης +Comment[eo]=Subteno por klavkombinaj piktogramoj +Comment[es]=Soporte para iconos de acceso directo +Comment[et]=Kiirkorralduse ikooni toetus +Comment[eu]=Lasterbide-laukitxoen euskarria +Comment[fa]=پشتیبانی شمایل میانبر +Comment[fi]=Sivustojen kuvakkeet +Comment[fr]=Gestion d'icône de raccourci +Comment[fy]=Stipe foar byldkaikes +Comment[gl]=Soporte para Icona de Atallo Directo +Comment[he]=תמיכה בסמלי קיצור דרך +Comment[hi]=शॉर्टकट प्रतीक आधार +Comment[hr]=Podrška za ikonske prečace +Comment[hu]=Website-ikonok támogatása +Comment[is]=Stuðningur fyrir flýtitáknmyndir +Comment[it]=Supporto icone scorciatoia +Comment[ja]=ショートカットアイコンサポート +Comment[ka]=მალმხმობი ხატულების მხარდაჭერა +Comment[kk]=Жарлықтарды қолдау +Comment[km]=ការគាំទ្ររូបតំណាងផ្លូវកាត់ +Comment[ko]=단축 아이콘 지원 +Comment[lt]=Nuorodų ženkliukų palaikymas +Comment[lv]=Īsinājumikonu atbalsts +Comment[mk]=Поддршка за икони за кратенки +Comment[ms]=Sokongan ikon pintasan +Comment[mt]=Sapport għall-ikoni "shortcut" +Comment[nb]=Støtte for snarveisikoner +Comment[nds]=Ünnerstütten för Link-Lüttbiller +Comment[ne]=सर्टकट प्रतिमा समर्थन +Comment[nl]=Ondersteuning voor pictogrammen +Comment[nn]=Støtte for snarvegikon +Comment[pa]=ਸ਼ਾਰਟਕੱਟ ਆਈਕਾਨ ਸਹਿਯੋਗ +Comment[pl]=Obsługa ikon serwerów +Comment[pt]=Suporte de ícone de atalho +Comment[pt_BR]=Suporte ao ícone de atalho +Comment[ro]=Suport pentru iconițe accelerator +Comment[ru]=Показ настраиваемых значков в KDE +Comment[rw]=Iyemera ry'Ihinanzira ry'Agashushondanga +Comment[se]=Govašlávkestagaid doarjja +Comment[sk]=Podpora ikon skratiek +Comment[sl]=Podpora ikonam za bližnjice +Comment[sr]=Подршка за икону пречице +Comment[sr@Latn]=Podrška za ikonu prečice +Comment[sv]=Stöd för genvägsikon +Comment[ta]=குறுக்கு வழி சின்ன ஆதரவு +Comment[th]=สนับสนุนไอคอนทางลัด +Comment[tr]=Kısayol simge desteği +Comment[tt]=KDE'da ikon kürsätü +Comment[uk]=Підтримка скорочень піктограм +Comment[vi]=Hỗ trợ biểu tượng giúp truy cập nhanh +Comment[wa]=Sopoirt imådjetes rascourtis +Comment[zh_CN]=快捷图标支持 +Comment[zh_TW]=捷徑圖示支援 +ServiceTypes=KDEDModule +X-KDE-ModuleType=Library +X-KDE-Library=favicons +X-KDE-FactoryName=favicons +X-KDE-Kded-autoload=false +X-KDE-Kded-load-on-demand=true diff --git a/libkonq/favicons/favicons.h b/libkonq/favicons/favicons.h new file mode 100644 index 000000000..021ea3b9b --- /dev/null +++ b/libkonq/favicons/favicons.h @@ -0,0 +1,105 @@ +/* This file is part of the KDE Project + Copyright (c) 2001 Malte Starostik <[email protected]> + + 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. +*/ + +#ifndef _FAVICONS_H_ +#define _FAVICONS_H_ + +#include <kdedmodule.h> +#include <kurl.h> + +namespace KIO { class Job; } + +/** + * KDED Module to handle shortcut icons ("favicons") + * FaviconsModule implements a KDED Module that handles the association of + * URLs and hosts with shortcut icons and the icons' downloads in a central + * place. + * + * After a successful download, the DCOP signal iconChanged() is emitted. + * It has the signature void iconChanged(bool, QString, QString); + * The first parameter is true if the icon is a "host" icon, that is it is + * the default icon for all URLs on the given host. In this case, the + * second parameter is a host name, otherwise the second parameter is the + * URL which is associated with the icon. The third parameter is the + * @ref KIconLoader friendly name of the downloaded icon, the same as + * @ref iconForURL will from now on return for any matching URL. + * + * @short KDED Module for favicons + * @author Malte Starostik <[email protected]> + */ +class FaviconsModule : public KDEDModule +{ + Q_OBJECT + K_DCOP +public: + FaviconsModule(const QCString &obj); + virtual ~FaviconsModule(); + +k_dcop: + /** + * Looks up an icon name for a given URL. This function does not + * initiate any download. If no icon for the URL or its host has + * been downloaded yet, QString::null is returned. + * + * @param url the URL for which the icon is queried + * @return the icon name suitable to pass to @ref KIconLoader or + * QString::null if no icon for this URL was found. + */ + QString iconForURL(const KURL &url); + /** + * Assiciates an icon with the given URL. If the icon was not + * downloaded before or the downloaded was too long ago, a + * download attempt will be started and the iconChanged() DCOP + * signal is emitted after the download finished successfully. + * + * @param url the URL which will be associated with the icon + * @param iconURL the URL of the icon to be downloaded + */ + ASYNC setIconForURL(const KURL &url, const KURL &iconURL); + /** + * Downloads the icon for a given host if it was not downloaded before + * or the download was too long ago. If the download finishes + * successfully, the iconChanged() DCOP signal is emitted. + * + * @param url any URL on the host for which the icon is to be downloaded + */ + ASYNC downloadHostIcon(const KURL &url); + +k_dcop_signals: + void iconChanged(bool isHost, QString hostOrURL, QString iconName); + void infoMessage(KURL iconURL, QString msg); + +private: + void startDownload(const QString &, bool, const KURL &); + QString simplifyURL(const KURL &); + QString iconNameFromURL(const KURL &); + bool isIconOld(const QString &); + +private slots: + void slotData(KIO::Job *, const QByteArray &); + void slotResult(KIO::Job *); + void slotInfoMessage(KIO::Job *, const QString &); + void slotKill(); + +private: + struct FaviconsModulePrivate *d; +}; + +#endif + +// vim: ts=4 sw=4 et diff --git a/libkonq/favicons/favicons.upd b/libkonq/favicons/favicons.upd new file mode 100644 index 000000000..b5f693b3b --- /dev/null +++ b/libkonq/favicons/favicons.upd @@ -0,0 +1,3 @@ +# Move favicons from $KDEHOME/share/icons and $KDEHOME/share/cache to $KDEHOME/cache-$HOST +Id=kde3_2 +Script=move_favicons.sh,sh diff --git a/libkonq/favicons/move_favicons.sh b/libkonq/favicons/move_favicons.sh new file mode 100755 index 000000000..a06d7d2bd --- /dev/null +++ b/libkonq/favicons/move_favicons.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +prefix=$(kde-config --localprefix) +source1="$prefix/share/icons/favicons" +source2="$prefix/share/cache/favicons" +dest="$(kde-config --path cache)/favicons" + +if [ -n "$prefix" -a -d "$source1" ]; then + while [ ! -d "$dest" ]; do + dir="$dest" + while [ ! -d `dirname "$dir"` ]; do + dir=`dirname "$dir"` + done + mkdir "$dir" || exit 1 + done + + icons=`ls "$source1" 2>/dev/null` + if [ -n "$icons" ]; then + for i in $icons; do + mv -f "$source1/$i" "$dest/$i" + done + fi + rmdir "$source1" +fi +if [ -n "$prefix" -a -d "$source2" ]; then + while [ ! -d "$dest" ]; do + dir="$dest" + while [ ! -d `dirname "$dir"` ]; do + dir=`dirname "$dir"` + done + mkdir "$dir" || exit 1 + done + + icons=`ls "$source2" 2>/dev/null` + if [ -n "$icons" ]; then + for i in $icons; do + mv -f "$source2/$i" "$dest/$i" + done + fi + rmdir "$source2" +fi diff --git a/libkonq/kfileivi.cc b/libkonq/kfileivi.cc new file mode 100644 index 000000000..6775f115a --- /dev/null +++ b/libkonq/kfileivi.cc @@ -0,0 +1,477 @@ +/* This file is part of the KDE project + Copyright (C) 1999, 2000, 2001, 2002 David Faure <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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 "kfileivi.h" +#include "kivdirectoryoverlay.h" +#include "konq_iconviewwidget.h" +#include "konq_operations.h" +#include "konq_settings.h" + +#include <qpainter.h> + +#include <kurldrag.h> +#include <kiconeffect.h> +#include <kfileitem.h> +#include <kdebug.h> +#include <krun.h> + +#undef Bool + +/** + * Private data for KFileIVI + */ +struct KFileIVI::Private +{ + QIconSet icons; // Icon states (cached to prevent re-applying icon effects + // every time) + QPixmap thumb; // Raw unprocessed thumbnail + QString m_animatedIcon; // Name of animation + bool m_animated; // Animation currently running ? + KIVDirectoryOverlay* m_directoryOverlay; + QPixmap m_overlay; + QString m_overlayName; +}; + +KFileIVI::KFileIVI( KonqIconViewWidget *iconview, KFileItem* fileitem, int size ) + : KIconViewItem( iconview, fileitem->text() ), + m_size( size ), m_state( KIcon::DefaultState ), + m_bDisabled( false ), m_bThumbnail( false ), m_fileitem( fileitem ) +{ + d = new KFileIVI::Private; + + updatePixmapSize(); + setPixmap( m_fileitem->pixmap( m_size, m_state ) ); + setDropEnabled( S_ISDIR( m_fileitem->mode() ) ); + + // Cache entry for the icon effects + d->icons.reset( *pixmap(), QIconSet::Large ); + d->m_animated = false; + + // iconName() requires the mimetype to be known + if ( fileitem->isMimeTypeKnown() ) + { + QString icon = fileitem->iconName(); + if ( !icon.isEmpty() ) + setMouseOverAnimation( icon ); + else + setMouseOverAnimation( "unknown" ); + } + d->m_directoryOverlay = 0; +} + +KFileIVI::~KFileIVI() +{ + delete d->m_directoryOverlay; + delete d; +} + +void KFileIVI::invalidateThumb( int state, bool redraw ) +{ + QIconSet::Mode mode; + switch( state ) + { + case KIcon::DisabledState: + mode = QIconSet::Disabled; + break; + case KIcon::ActiveState: + mode = QIconSet::Active; + break; + case KIcon::DefaultState: + default: + mode = QIconSet::Normal; + break; + } + d->icons = QIconSet(); + d->icons.setPixmap( KGlobal::iconLoader()->iconEffect()-> + apply( d->thumb, KIcon::Desktop, state ), + QIconSet::Large, mode ); + m_state = state; + + QIconViewItem::setPixmap( d->icons.pixmap( QIconSet::Large, mode ), + false, redraw ); +} + +void KFileIVI::setIcon( int size, int state, bool recalc, bool redraw ) +{ + m_size = size; + m_bThumbnail = false; + if ( m_bDisabled ) + m_state = KIcon::DisabledState; + else + m_state = state; + + if ( d->m_overlayName.isNull() ) + d->m_overlay = QPixmap(); + else { + int halfSize; + if (m_size == 0) { + halfSize = IconSize(KIcon::Desktop) / 2; + } else { + halfSize = m_size / 2; + } + d->m_overlay = DesktopIcon(d->m_overlayName, halfSize); + } + + setPixmapDirect(m_fileitem->pixmap( m_size, m_state ) , recalc, redraw ); +} + +void KFileIVI::setOverlay( const QString& iconName ) +{ + d->m_overlayName = iconName; + + refreshIcon(true); +} + +KIVDirectoryOverlay* KFileIVI::setShowDirectoryOverlay( bool show ) +{ + if ( !m_fileitem->isDir() || m_fileitem->iconName() != "folder" ) + return 0; + + if (show) { + if (!d->m_directoryOverlay) + d->m_directoryOverlay = new KIVDirectoryOverlay(this); + return d->m_directoryOverlay; + } else { + delete d->m_directoryOverlay; + d->m_directoryOverlay = 0; + setOverlay(QString()); + return 0; + } +} + +bool KFileIVI::showDirectoryOverlay( ) +{ + return (bool)d->m_directoryOverlay; +} + +void KFileIVI::setPixmapDirect( const QPixmap& pixmap, bool recalc, bool redraw ) +{ + QIconSet::Mode mode; + switch( m_state ) + { + case KIcon::DisabledState: + mode = QIconSet::Disabled; + break; + case KIcon::ActiveState: + mode = QIconSet::Active; + break; + case KIcon::DefaultState: + default: + mode = QIconSet::Normal; + break; + } + + // We cannot just reset() the iconset here, because setIcon can be + // called with any state and not just normal state. So we just + // create a dummy empty iconset as base object. + d->icons = QIconSet(); + d->icons.setPixmap( pixmap, QIconSet::Large, mode ); + + updatePixmapSize(); + QIconViewItem::setPixmap( d->icons.pixmap( QIconSet::Large, mode ), + recalc, redraw ); +} + +void KFileIVI::setDisabled( bool disabled ) +{ + if ( m_bDisabled != disabled ) + { + m_bDisabled = disabled; + bool active = ( m_state == KIcon::ActiveState ); + setEffect( m_bDisabled ? KIcon::DisabledState : + ( active ? KIcon::ActiveState : KIcon::DefaultState ) ); + } +} + +void KFileIVI::setThumbnailPixmap( const QPixmap & pixmap ) +{ + m_bThumbnail = true; + d->thumb = pixmap; + // QIconSet::reset() doesn't seem to clear the other generated pixmaps, + // so we just create a blank QIconSet here + d->icons = QIconSet(); + d->icons.setPixmap( KGlobal::iconLoader()->iconEffect()-> + apply( pixmap, KIcon::Desktop, KIcon::DefaultState ), + QIconSet::Large, QIconSet::Normal ); + + m_state = KIcon::DefaultState; + + // Recalc when setting this pixmap! + updatePixmapSize(); + QIconViewItem::setPixmap( d->icons.pixmap( QIconSet::Large, + QIconSet::Normal ), true ); +} + +void KFileIVI::setActive( bool active ) +{ + if ( active ) + setEffect( KIcon::ActiveState ); + else + setEffect( m_bDisabled ? KIcon::DisabledState : KIcon::DefaultState ); +} + +void KFileIVI::setEffect( int state ) +{ + QIconSet::Mode mode; + switch( state ) + { + case KIcon::DisabledState: + mode = QIconSet::Disabled; + break; + case KIcon::ActiveState: + mode = QIconSet::Active; + break; + case KIcon::DefaultState: + default: + mode = QIconSet::Normal; + break; + } + // Do not update if the fingerprint is identical (prevents flicker)! + + KIconEffect *effect = KGlobal::iconLoader()->iconEffect(); + + bool haveEffect = effect->hasEffect( KIcon::Desktop, m_state ) != + effect->hasEffect( KIcon::Desktop, state ); + + //kdDebug(1203) << "desktop;defaultstate=" << + // effect->fingerprint(KIcon::Desktop, KIcon::DefaultState) << + // endl; + //kdDebug(1203) << "desktop;activestate=" << + // effect->fingerprint(KIcon::Desktop, KIcon::ActiveState) << + // endl; + + if( haveEffect && + effect->fingerprint( KIcon::Desktop, m_state ) != + effect->fingerprint( KIcon::Desktop, state ) ) + { + // Effects on are not applied until they are first accessed to + // save memory. Do this now when needed + if( m_bThumbnail ) + { + if( d->icons.isGenerated( QIconSet::Large, mode ) ) + d->icons.setPixmap( effect->apply( d->thumb, KIcon::Desktop, state ), + QIconSet::Large, mode ); + } + else + { + if( d->icons.isGenerated( QIconSet::Large, mode ) ) + d->icons.setPixmap( m_fileitem->pixmap( m_size, state ), + QIconSet::Large, mode ); + } + QIconViewItem::setPixmap( d->icons.pixmap( QIconSet::Large, mode ) ); + } + m_state = state; +} + +void KFileIVI::refreshIcon( bool redraw ) +{ + if (!isThumbnail()) + setIcon( m_size, m_state, true, redraw ); +} + +void KFileIVI::invalidateThumbnail() +{ + d->thumb = QPixmap(); +} + +bool KFileIVI::isThumbnailInvalid() const +{ + return d->thumb.isNull(); +} + +bool KFileIVI::acceptDrop( const QMimeSource *mime ) const +{ + if ( mime->provides( "text/uri-list" ) ) // We're dragging URLs + { + if ( m_fileitem->acceptsDrops() ) // Directory, executables, ... + return true; + + // Use cache + KURL::List uris = ( static_cast<KonqIconViewWidget*>(iconView()) )->dragURLs(); + + // Check if we want to drop something on itself + // (Nothing will happen, but it's a convenient way to move icons) + KURL::List::Iterator it = uris.begin(); + for ( ; it != uris.end() ; it++ ) + { + if ( m_fileitem->url().equals( *it, true /*ignore trailing slashes*/ ) ) + return true; + } + } + return QIconViewItem::acceptDrop( mime ); +} + +void KFileIVI::setKey( const QString &key ) +{ + QString theKey = key; + + QVariant sortDirProp = iconView()->property( "sortDirectoriesFirst" ); + + bool isdir = ( S_ISDIR( m_fileitem->mode() ) && ( !sortDirProp.isValid() || ( sortDirProp.type() == QVariant::Bool && sortDirProp.toBool() ) ) ); + + // The order is: .dir (0), dir (1), .file (2), file (3) + int sortChar = isdir ? 1 : 3; + if ( m_fileitem->text()[0] == '.' ) + --sortChar; + + if ( !iconView()->sortDirection() ) // reverse sorting + sortChar = 3 - sortChar; + + theKey.prepend( QChar( sortChar + '0' ) ); + + QIconViewItem::setKey( theKey ); +} + +void KFileIVI::dropped( QDropEvent *e, const QValueList<QIconDragItem> & ) +{ + KonqOperations::doDrop( item(), item()->url(), e, iconView() ); +} + +void KFileIVI::returnPressed() +{ + if ( static_cast<KonqIconViewWidget*>(iconView())->isDesktop() ) { + KURL url = m_fileitem->url(); + // When clicking on a link to e.g. $HOME from the desktop, we want to open $HOME + // Symlink resolution must only happen on the desktop though (#63014) + if ( m_fileitem->isLink() && url.isLocalFile() ) + url = KURL( url, m_fileitem->linkDest() ); + + (void) new KRun( url, m_fileitem->mode(), m_fileitem->isLocalFile() ); + } else { + m_fileitem->run(); + } +} + + +void KFileIVI::paintItem( QPainter *p, const QColorGroup &c ) +{ + QColorGroup cg = updateColors(c); + paintFontUpdate( p ); + + //*** TEMPORARY CODE - MUST BE MADE CONFIGURABLE FIRST - Martijn + // SET UNDERLINE ON HOVER ONLY + /*if ( ( ( KonqIconViewWidget* ) iconView() )->m_pActiveItem == this ) + { + QFont f( p->font() ); + f.setUnderline( TRUE ); + p->setFont( f ); + }*/ + + KIconViewItem::paintItem( p, cg ); + paintOverlay(p); + +} + +void KFileIVI::paintOverlay( QPainter *p ) const +{ + if ( !d->m_overlay.isNull() ) { + QRect rect = pixmapRect(true); + p->drawPixmap(x() + rect.x() , y() + pixmapRect().height() - d->m_overlay.height(), d->m_overlay); + } +} + +void KFileIVI::paintFontUpdate( QPainter *p ) const +{ + if ( m_fileitem->isLink() ) + { + QFont f( p->font() ); + f.setItalic( TRUE ); + p->setFont( f ); + } +} + +QColorGroup KFileIVI::updateColors( const QColorGroup &c ) const +{ + QColorGroup cg( c ); + cg.setColor( QColorGroup::Text, static_cast<KonqIconViewWidget*>(iconView())->itemColor() ); + return cg; +} + +bool KFileIVI::move( int x, int y ) +{ + if ( static_cast<KonqIconViewWidget*>(iconView())->isDesktop() ) { + if ( x < 5 ) + x = 5; + if ( x > iconView()->viewport()->width() - ( width() + 5 ) ) + x = iconView()->viewport()->width() - ( width() + 5 ); + if ( y < 5 ) + y = 5; + if ( y > iconView()->viewport()->height() - ( height() + 5 ) ) + y = iconView()->viewport()->height() - ( height() + 5 ); + } + return QIconViewItem::move( x, y ); +} + +bool KFileIVI::hasAnimation() const +{ + return !d->m_animatedIcon.isEmpty() && !m_bThumbnail; +} + +void KFileIVI::setMouseOverAnimation( const QString& movieFileName ) +{ + if ( !movieFileName.isEmpty() ) + { + //kdDebug(1203) << "KIconViewItem::setMouseOverAnimation " << movieFileName << endl; + d->m_animatedIcon = movieFileName; + } +} + +QString KFileIVI::mouseOverAnimation() const +{ + return d->m_animatedIcon; +} + +bool KFileIVI::isAnimated() const +{ + return d->m_animated; +} + +void KFileIVI::setAnimated( bool a ) +{ + d->m_animated = a; +} + +int KFileIVI::compare( QIconViewItem *i ) const +{ + KonqIconViewWidget* view = static_cast<KonqIconViewWidget*>(iconView()); + if ( view->caseInsensitiveSort() ) + return key().localeAwareCompare( i->key() ); + else + return view->m_pSettings->caseSensitiveCompare( key(), i->key() ); +} + +void KFileIVI::updatePixmapSize() +{ + int size = m_size ? m_size : + KGlobal::iconLoader()->currentSize( KIcon::Desktop ); + + KonqIconViewWidget* view = static_cast<KonqIconViewWidget*>( iconView() ); + + if ( view && view->canPreview( item() ) ) { + int previewSize = view->previewIconSize( size ); + setPixmapSize( QSize( previewSize, previewSize ) ); + } + else { + QSize pixSize = QSize( size, size ); + if ( pixSize != pixmapSize() ) + setPixmapSize( pixSize ); + } +} + +/* vim: set noet sw=4 ts=8 softtabstop=4: */ diff --git a/libkonq/kfileivi.h b/libkonq/kfileivi.h new file mode 100644 index 000000000..d1f1d9ab7 --- /dev/null +++ b/libkonq/kfileivi.h @@ -0,0 +1,239 @@ +/* This file is part of the KDE project + Copyright (C) 1999, 2000, 2001, 2002 David Faure <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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. +*/ + +#ifndef __kfileivi_h__ +#define __kfileivi_h__ + +#include <kiconview.h> +#include <kiconloader.h> +#include <libkonq_export.h> + +class KFileItem; +class KonqIconViewWidget; +class KIVDirectoryOverlay; + +/** + * KFileIVI (short form of "Konq - File - IconViewItem") + * is, as expected, an improved KIconViewItem, because + * it represents a file. + * All the information about the file is contained in the KFileItem + * pointer. + */ +class LIBKONQ_EXPORT KFileIVI : public KIconViewItem +{ +public: + /** + * Create an icon, within a qlistview, representing a file + * @param iconview the parent widget + * @param fileitem the file item created by KDirLister + * @param size the icon size + */ + KFileIVI( KonqIconViewWidget *iconview, KFileItem* fileitem, int size ); + virtual ~KFileIVI(); + + /** + * Handler for return (or single/double click) on ONE icon. + * Runs the file through KRun. + */ + virtual void returnPressed(); + + /** + * @return the file item held by this instance + */ + KFileItem * item() const { return m_fileitem; } + + /** + * @return true if dropping on this file is allowed + * Overloads QIconView::acceptDrop() + */ + virtual bool acceptDrop( const QMimeSource *mime ) const; + + /** + * Changes the icon for this item. + * @param size the icon size (0 for default, otherwise size in pixels) + * @param state the state of the icon (enum in KIcon) + * @param recalc whether to update the layout of the icon view when setting the icon + * @param redraw whether to redraw the item after setting the icon + */ + virtual void setIcon( int size, + int state=KIcon::DefaultState, + bool recalc=false, + bool redraw=false); + + /** + * Bypass @ref setIcon. This is for animated icons, you should use setIcon + * in all other cases. + * @param pixmap the pixmap to set - it SHOULD really have the right icon size! + * @param recalc whether to update the layout of the icon view when setting the icon + * @param redraw whether to redraw the item after setting the icon + */ + void setPixmapDirect( const QPixmap & pixmap, + bool recalc=false, + bool redraw=false); + + /** + * Notifies that all icon effects on thumbs should be invalidated, + * e.g. because the effect settings have been changed. The thumb itself + * is assumed to be still valid (use setThumbnailPixmap() instead + * otherwise). + * @param state the state of the icon (enum in KIcon) + * @param redraw whether to redraw the item after setting the icon + */ + void invalidateThumb( int state, bool redraw = false ); + + /** + * Our current thumbnail is not longer "current". + * Called when the file contents have changed. + */ + void invalidateThumbnail(); + bool isThumbnailInvalid() const; + + bool hasValidThumbnail() const { return isThumbnail() && !isThumbnailInvalid(); } + + /** + * Return the current state of the icon + * (KIcon::DefaultState, KIcon::ActiveState etc.) + */ + int state() const { return m_state; } + + /** + * Return the theorical size of the icon + */ + int iconSize() const { return m_size; } + + /** + * Set to true when this icon is 'cut' + */ + void setDisabled( bool disabled ); + + /** + * Set this when the thumbnail was loaded + */ + void setThumbnailPixmap( const QPixmap & pixmap ); + + /** + * Set the icon to use the specified KIconEffect + * See the docs for KIconEffect for details. + */ + void setEffect( /*int group,*/ int state ); + + /** + * @return true if this item is a thumbnail + */ + bool isThumbnail() const { return m_bThumbnail; } + + /** + * Sets an icon to be shown over the bottom left corner of the icon. + * Currently used for directory overlays. + * setOverlay(QString::null) to remove icon. + */ + void setOverlay( const QString & iconName); + + /** + * Redetermines the icon (useful if KFileItem might return another icon). + * Does nothing with thumbnails + */ + virtual void refreshIcon( bool redraw ); + + virtual void setKey( const QString &key ); + + /** + * Paints this item. Takes care of using the normal or alpha + * blending methods depending on the configuration. + */ + virtual void paintItem( QPainter *p, const QColorGroup &cg ); + + virtual bool move( int x, int y ); + + /** + * Enable an animation on mouseover, if there is an available mng. + * @param movieFileName the base name for the mng, e.g. "folder". + * Nothing happens if there is no animation available. + */ + void setMouseOverAnimation( const QString& movieFileName ); + QString mouseOverAnimation() const; + + /** + * Return true if the icon _might_ have an animation available. + * This doesn't mean the .mng exists (only determined when hovering on the + * icon - and if it doesn't exist setMouseOverAnimation(QString::null) is called), + * and it doesn't mean that it's currently running either. + */ + bool hasAnimation() const; + + /** Return true if we are currently animating this icon */ + bool isAnimated() const; + void setAnimated( bool ); + + /** Called when the mouse is over the icon */ + void setActive( bool active ); + + /** + * Sets showing of directory overlays. Does nothing if this does + * not represent a folder. + */ + KIVDirectoryOverlay* setShowDirectoryOverlay( bool ); + bool showDirectoryOverlay( ); + + virtual int compare( QIconViewItem *i ) const; + +protected: + virtual void dropped( QDropEvent *e, const QValueList<QIconDragItem> & ); + + /** + * Contains the logic and code for painting the overlay pixmap. + */ + void paintOverlay( QPainter *p ) const; + + /** + * Updates the colorgroup. + */ + QColorGroup updateColors(const QColorGroup &c) const; + + /** + * Contains the logic and code for painting links. + */ + void paintFontUpdate( QPainter *p ) const; + +private: + /** You are not supposed to call this on a KFileIVI, from the outside, + * it bypasses the icons cache */ + virtual void setPixmap ( const QPixmap & icon ) { KIconViewItem::setPixmap( icon ); } + virtual void setPixmap ( const QPixmap & icon, bool recalc, bool redraw = TRUE ) + { KIconViewItem::setPixmap( icon, recalc, redraw ); } + + /** Check if a thumbnail will be generated and calc the size of the icon */ + void updatePixmapSize(); + + int m_size, m_state; + bool m_bDisabled; + bool m_bThumbnail; + /** Pointer to the file item in KDirLister's list */ + KFileItem* m_fileitem; + + /** + * Private data for KFileIVI + * Implementation in kfileivi.cc + */ + struct Private; + + Private *d; +}; + +#endif diff --git a/libkonq/kivdirectoryoverlay.cc b/libkonq/kivdirectoryoverlay.cc new file mode 100644 index 000000000..5c9d14783 --- /dev/null +++ b/libkonq/kivdirectoryoverlay.cc @@ -0,0 +1,141 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002 Simon MacMullen + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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 <qdict.h> +#include <qpixmap.h> +#include <qpainter.h> +#include <qbitmap.h> +#include <qimage.h> + +#include <kfileivi.h> +#include <kfileitem.h> +#include <kapplication.h> +#include <kdirlister.h> +#include <kstandarddirs.h> +#include <kiconloader.h> +#include <konq_settings.h> +#include <klocale.h> +#include <kdebug.h> + +#include "kivdirectoryoverlay.h" + +KIVDirectoryOverlay::KIVDirectoryOverlay(KFileIVI* directory) +: m_lister(0), m_foundItems(false), + m_containsFolder(false), m_popularIcons(0) +{ + if (!m_lister) + { + m_lister = new KDirLister; + m_lister->setAutoErrorHandlingEnabled(false, 0); + connect(m_lister, SIGNAL(completed()), SLOT(slotCompleted())); + connect(m_lister, SIGNAL(newItems( const KFileItemList& )), SLOT(slotNewItems( const KFileItemList& ))); + m_lister->setShowingDotFiles(false); + } + m_directory = directory; +} + +KIVDirectoryOverlay::~KIVDirectoryOverlay() +{ + if (m_lister) m_lister->stop(); + delete m_lister; + delete m_popularIcons; +} + +void KIVDirectoryOverlay::start() +{ + if ( m_directory->item()->isReadable() ) { + m_popularIcons = new QDict<int>; + m_popularIcons->setAutoDelete(true); + m_lister->openURL(m_directory->item()->url()); + } else { + emit finished(); + } +} + +void KIVDirectoryOverlay::timerEvent(QTimerEvent *) +{ + m_lister->stop(); +} + +void KIVDirectoryOverlay::slotCompleted() +{ + if (!m_popularIcons) return; + + // Look through the histogram for the most popular mimetype + QDictIterator<int> currentIcon( (*m_popularIcons) ); + unsigned int best = 0; + unsigned int total = 0; + for ( ; currentIcon.current(); ++currentIcon ) { + unsigned int currentCount = (*currentIcon.current()); + total += currentCount; + if ( best < currentCount ) { + best = currentCount; + m_bestIcon = currentIcon.currentKey(); + } + } + + // Only show folder if there's no other candidate. Most folders contain + // folders. We know this. + if ( m_bestIcon.isNull() && m_containsFolder ) { + m_bestIcon = "folder"; + } + + if ( best * 2 < total ) { + m_bestIcon = "kmultiple"; + } + + if (!m_bestIcon.isNull()) { + m_directory->setOverlay(m_bestIcon); + } + + delete m_popularIcons; + m_popularIcons = 0; + + emit finished(); +} + +void KIVDirectoryOverlay::slotNewItems( const KFileItemList& items ) +{ + if ( !m_popularIcons) return; + + KFileItemListIterator files( items ); + + KFileItem* file; + for ( ; (file = files.current()) != 0; ++files ) { + if ( file -> isFile() ) { + + QString iconName = file -> iconName(); + if (!iconName) continue; + + int* iconCount = m_popularIcons -> find( file -> iconName() ); + if (!iconCount) { + iconCount = new int(0); + Q_ASSERT(file); + m_popularIcons -> insert(file -> iconName(), iconCount); + } + (*iconCount)++; + } else if ( file -> isDir() ) { + m_containsFolder = true; + } + } + + m_foundItems = true; +} + +#include "kivdirectoryoverlay.moc" diff --git a/libkonq/kivdirectoryoverlay.h b/libkonq/kivdirectoryoverlay.h new file mode 100644 index 000000000..5ebd07959 --- /dev/null +++ b/libkonq/kivdirectoryoverlay.h @@ -0,0 +1,58 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002 Simon MacMullen + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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. +*/ + +#ifndef _KIVDIRECTORYOVERLAY_H_ +#define _KIVDIRECTORYOVERLAY_H_ + +#include <kfileitem.h> +#include <libkonq_export.h> + +#include <qdict.h> + +class KDirLister; +class KFileIVI; + +class LIBKONQ_EXPORT KIVDirectoryOverlay : public QObject +{ + Q_OBJECT +public: + KIVDirectoryOverlay(KFileIVI* directory); + virtual ~KIVDirectoryOverlay(); + void start(); + +signals: + void finished(); + +protected: + virtual void timerEvent(QTimerEvent *); + +private slots: + void slotCompleted(); + void slotNewItems( const KFileItemList& items ); + +private: + KDirLister* m_lister; + bool m_foundItems; + bool m_containsFolder; + QDict<int>* m_popularIcons; + QString m_bestIcon; + KFileIVI* m_directory; +}; + +#endif diff --git a/libkonq/knewmenu.cc b/libkonq/knewmenu.cc new file mode 100644 index 000000000..168ade6f7 --- /dev/null +++ b/libkonq/knewmenu.cc @@ -0,0 +1,621 @@ +/* This file is part of the KDE project + Copyright (C) 1998, 1999 David Faure <[email protected]> + 2003 Sven Leiber <[email protected]> + + 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 <qdir.h> + +#include <kdebug.h> +#include <kdesktopfile.h> +#include <kdirwatch.h> +#include <kinstance.h> +#include <kinputdialog.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kstandarddirs.h> +#include <kprotocolinfo.h> +#include <kpopupmenu.h> +#include <krun.h> + +#include <kio/job.h> +#include <kio/renamedlg.h> + +#include <kpropertiesdialog.h> +#include "konq_operations.h" +#include "konq_undo.h" +#include "knewmenu.h" +#include <utime.h> + +// For KURLDesktopFileDlg +#include <qlayout.h> +#include <qhbox.h> +#include <klineedit.h> +#include <kurlrequester.h> +#include <qlabel.h> +#include <qpopupmenu.h> + +QValueList<KNewMenu::Entry> * KNewMenu::s_templatesList = 0L; +int KNewMenu::s_templatesVersion = 0; +bool KNewMenu::s_filesParsed = false; +KDirWatch * KNewMenu::s_pDirWatch = 0L; + +class KNewMenu::KNewMenuPrivate +{ +public: + KNewMenuPrivate() : m_parentWidget(0) {} + KActionCollection * m_actionCollection; + QString m_destPath; + QWidget *m_parentWidget; + KActionMenu *m_menuDev; +}; + +KNewMenu::KNewMenu( KActionCollection * _collec, const char *name ) : + KActionMenu( i18n( "Create New" ), "filenew", _collec, name ), + menuItemsVersion( 0 ) +{ + //kdDebug(1203) << "KNewMenu::KNewMenu " << this << endl; + // Don't fill the menu yet + // We'll do that in slotCheckUpToDate (should be connected to abouttoshow) + d = new KNewMenuPrivate; + d->m_actionCollection = _collec; + makeMenus(); +} + +KNewMenu::KNewMenu( KActionCollection * _collec, QWidget *parentWidget, const char *name ) : + KActionMenu( i18n( "Create New" ), "filenew", _collec, name ), + menuItemsVersion( 0 ) +{ + d = new KNewMenuPrivate; + d->m_actionCollection = _collec; + d->m_parentWidget = parentWidget; + makeMenus(); +} + +KNewMenu::~KNewMenu() +{ + //kdDebug(1203) << "KNewMenu::~KNewMenu " << this << endl; + delete d; +} + +void KNewMenu::makeMenus() +{ + d->m_menuDev = new KActionMenu( i18n( "Link to Device" ), "kcmdevices", d->m_actionCollection, "devnew" ); +} + +void KNewMenu::slotCheckUpToDate( ) +{ + //kdDebug(1203) << "KNewMenu::slotCheckUpToDate() " << this + // << " : menuItemsVersion=" << menuItemsVersion + // << " s_templatesVersion=" << s_templatesVersion << endl; + if (menuItemsVersion < s_templatesVersion || s_templatesVersion == 0) + { + //kdDebug(1203) << "KNewMenu::slotCheckUpToDate() : recreating actions" << endl; + // We need to clean up the action collection + // We look for our actions using the group + QValueList<KAction*> actions = d->m_actionCollection->actions( "KNewMenu" ); + for( QValueListIterator<KAction*> it = actions.begin(); it != actions.end(); ++it ) + { + remove( *it ); + d->m_actionCollection->remove( *it ); + } + + if (!s_templatesList) { // No templates list up to now + s_templatesList = new QValueList<Entry>(); + slotFillTemplates(); + parseFiles(); + } + + // This might have been already done for other popupmenus, + // that's the point in s_filesParsed. + if ( !s_filesParsed ) + parseFiles(); + + fillMenu(); + + menuItemsVersion = s_templatesVersion; + } +} + +void KNewMenu::parseFiles() +{ + //kdDebug(1203) << "KNewMenu::parseFiles()" << endl; + s_filesParsed = true; + QValueList<Entry>::Iterator templ = s_templatesList->begin(); + for ( /*++templ*/; templ != s_templatesList->end(); ++templ) + { + QString iconname; + QString filePath = (*templ).filePath; + if ( !filePath.isEmpty() ) + { + QString text; + QString templatePath; + // If a desktop file, then read the name from it. + // Otherwise (or if no name in it?) use file name + if ( KDesktopFile::isDesktopFile( filePath ) ) { + KSimpleConfig config( filePath, true ); + config.setDesktopGroup(); + text = config.readEntry("Name"); + (*templ).icon = config.readEntry("Icon"); + (*templ).comment = config.readEntry("Comment"); + QString type = config.readEntry( "Type" ); + if ( type == "Link" ) + { + templatePath = config.readPathEntry("URL"); + if ( templatePath[0] != '/' ) + { + if ( templatePath.startsWith("file:/") ) + templatePath = KURL(templatePath).path(); + else + { + // A relative path, then (that's the default in the files we ship) + QString linkDir = filePath.left( filePath.findRev( '/' ) + 1 /*keep / */ ); + //kdDebug(1203) << "linkDir=" << linkDir << endl; + templatePath = linkDir + templatePath; + } + } + } + if ( templatePath.isEmpty() ) + { + // No dest, this is an old-style template + (*templ).entryType = TEMPLATE; + (*templ).templatePath = (*templ).filePath; // we'll copy the file + } else { + (*templ).entryType = LINKTOTEMPLATE; + (*templ).templatePath = templatePath; + } + + } + if (text.isEmpty()) + { + text = KURL(filePath).fileName(); + if ( text.endsWith(".desktop") ) + text.truncate( text.length() - 8 ); + else if ( text.endsWith(".kdelnk") ) + text.truncate( text.length() - 7 ); + } + (*templ).text = text; + /*kdDebug(1203) << "Updating entry with text=" << text + << " entryType=" << (*templ).entryType + << " templatePath=" << (*templ).templatePath << endl;*/ + } + else { + (*templ).entryType = SEPARATOR; + } + } +} + +void KNewMenu::fillMenu() +{ + //kdDebug(1203) << "KNewMenu::fillMenu()" << endl; + popupMenu()->clear(); + d->m_menuDev->popupMenu()->clear(); + + KAction *linkURL = 0, *linkApp = 0; // these shall be put at special positions + + int i = 1; // was 2 when there was Folder + QValueList<Entry>::Iterator templ = s_templatesList->begin(); + for ( ; templ != s_templatesList->end(); ++templ, ++i) + { + if ( (*templ).entryType != SEPARATOR ) + { + // There might be a .desktop for that one already, if it's a kdelnk + // This assumes we read .desktop files before .kdelnk files ... + + // In fact, we skip any second item that has the same text as another one. + // Duplicates in a menu look bad in any case. + + bool bSkip = false; + + QValueList<KAction*> actions = d->m_actionCollection->actions(); + QValueListIterator<KAction*> it = actions.begin(); + for( ; it != actions.end() && !bSkip; ++it ) + { + if ( (*it)->text() == (*templ).text ) + { + kdDebug(1203) << "KNewMenu: skipping " << (*templ).filePath << endl; + bSkip = true; + } + } + + if ( !bSkip ) + { + Entry entry = *(s_templatesList->at( i-1 )); + + // The best way to identify the "Create Directory", "Link to Location", "Link to Application" was the template + if ( (*templ).templatePath.endsWith( "emptydir" ) ) + { + KAction * act = new KAction( (*templ).text, (*templ).icon, 0, this, SLOT( slotNewDir() ), + d->m_actionCollection, QCString().sprintf("newmenu%d", i ) ); + act->setGroup( "KNewMenu" ); + act->plug( popupMenu() ); + + KActionSeparator *sep = new KActionSeparator(); + sep->plug( popupMenu() ); + } + else + { + KAction * act = new KAction( (*templ).text, (*templ).icon, 0, this, SLOT( slotNewFile() ), + d->m_actionCollection, QCString().sprintf("newmenu%d", i ) ); + act->setGroup( "KNewMenu" ); + + if ( (*templ).templatePath.endsWith( "URL.desktop" ) ) + { + linkURL = act; + } + else if ( (*templ).templatePath.endsWith( "Program.desktop" ) ) + { + linkApp = act; + } + else if ( KDesktopFile::isDesktopFile( entry.templatePath ) ) + { + KDesktopFile df( entry.templatePath ); + if(df.readType() == "FSDevice") + act->plug( d->m_menuDev->popupMenu() ); + else + act->plug( popupMenu() ); + } + else + { + act->plug( popupMenu() ); + } + } + } + } else { // Separate system from personal templates + Q_ASSERT( (*templ).entryType != 0 ); + + KActionSeparator * act = new KActionSeparator(); + act->plug( popupMenu() ); + } + } + + KActionSeparator * act = new KActionSeparator(); + act->plug( popupMenu() ); + if ( linkURL ) linkURL->plug( popupMenu() ); + if ( linkApp ) linkApp->plug( popupMenu() ); + d->m_menuDev->plug( popupMenu() ); +} + +void KNewMenu::slotFillTemplates() +{ + //kdDebug(1203) << "KNewMenu::slotFillTemplates()" << endl; + // Ensure any changes in the templates dir will call this + if ( ! s_pDirWatch ) + { + s_pDirWatch = new KDirWatch; + QStringList dirs = d->m_actionCollection->instance()->dirs()->resourceDirs("templates"); + for ( QStringList::Iterator it = dirs.begin() ; it != dirs.end() ; ++it ) + { + //kdDebug(1203) << "Templates resource dir: " << *it << endl; + s_pDirWatch->addDir( *it ); + } + connect ( s_pDirWatch, SIGNAL( dirty( const QString & ) ), + this, SLOT ( slotFillTemplates() ) ); + connect ( s_pDirWatch, SIGNAL( created( const QString & ) ), + this, SLOT ( slotFillTemplates() ) ); + connect ( s_pDirWatch, SIGNAL( deleted( const QString & ) ), + this, SLOT ( slotFillTemplates() ) ); + // Ok, this doesn't cope with new dirs in KDEDIRS, but that's another story + } + s_templatesVersion++; + s_filesParsed = false; + + s_templatesList->clear(); + + // Look into "templates" dirs. + QStringList files = d->m_actionCollection->instance()->dirs()->findAllResources("templates"); + KSortableValueList<Entry,QString> slist; + for ( QStringList::Iterator it = files.begin() ; it != files.end() ; ++it ) + { + //kdDebug(1203) << *it << endl; + if ( (*it)[0] != '.' ) + { + Entry e; + e.filePath = *it; + e.entryType = 0; // not parsed yet + // put Directory etc. with special order (see fillMenu()) first in the list (a bit hacky) + if ( (*it).endsWith( "Directory.desktop" ) || + (*it).endsWith( "linkProgram.desktop" ) || + (*it).endsWith( "linkURL.desktop" ) ) + s_templatesList->prepend( e ); + else + { + KSimpleConfig config( *it, true ); + config.setDesktopGroup(); + + // tricky solution to ensure that TextFile is at the beginning + // because this filetype is the most used (according kde-core discussion) + QString key = config.readEntry("Name"); + if ( (*it).endsWith( "TextFile.desktop" ) ) + key = "1_" + key; + else + key = "2_" + key; + + slist.insert( key, e ); + } + } + } + slist.sort(); + for(KSortableValueList<Entry, QString>::ConstIterator it = slist.begin(); it != slist.end(); ++it) + { + s_templatesList->append( (*it).value() ); + } + +} + +void KNewMenu::slotNewDir() +{ + emit activated(); // for KDIconView::slotNewMenuActivated() + + if (popupFiles.isEmpty()) + return; + + KonqOperations::newDir(d->m_parentWidget, popupFiles.first()); +} + +void KNewMenu::slotNewFile() +{ + int id = QString( sender()->name() + 7 ).toInt(); // skip "newmenu" + if (id == 0) + { + // run the command for the templates + KRun::runCommand(QString(sender()->name())); + return; + } + + emit activated(); // for KDIconView::slotNewMenuActivated() + + Entry entry = *(s_templatesList->at( id - 1 )); + //kdDebug(1203) << QString("sFile = %1").arg(sFile) << endl; + + if ( !QFile::exists( entry.templatePath ) ) { + kdWarning(1203) << entry.templatePath << " doesn't exist" << endl; + KMessageBox::sorry( 0L, i18n("<qt>The template file <b>%1</b> does not exist.</qt>").arg(entry.templatePath)); + return; + } + m_isURLDesktopFile = false; + QString name; + if ( KDesktopFile::isDesktopFile( entry.templatePath ) ) + { + KDesktopFile df( entry.templatePath ); + //kdDebug(1203) << df.readType() << endl; + if ( df.readType() == "Link" ) + { + m_isURLDesktopFile = true; + // entry.comment contains i18n("Enter link to location (URL):"). JFYI :) + KURLDesktopFileDlg dlg( i18n("File name:"), entry.comment, d->m_parentWidget ); + // TODO dlg.setCaption( i18n( ... ) ); + if ( dlg.exec() ) + { + name = dlg.fileName(); + m_linkURL = dlg.url(); + if ( name.isEmpty() || m_linkURL.isEmpty() ) + return; + if ( !name.endsWith( ".desktop" ) ) + name += ".desktop"; + } + else + return; + } + else // any other desktop file (Device, App, etc.) + { + KURL::List::Iterator it = popupFiles.begin(); + for ( ; it != popupFiles.end(); ++it ) + { + //kdDebug(1203) << "first arg=" << entry.templatePath << endl; + //kdDebug(1203) << "second arg=" << (*it).url() << endl; + //kdDebug(1203) << "third arg=" << entry.text << endl; + QString text = entry.text; + text.replace( "...", QString::null ); // the ... is fine for the menu item but not for the default filename + + KURL defaultFile( *it ); + defaultFile.addPath( KIO::encodeFileName( text ) ); + if ( defaultFile.isLocalFile() && QFile::exists( defaultFile.path() ) ) + text = KIO::RenameDlg::suggestName( *it, text); + + KURL templateURL; + templateURL.setPath( entry.templatePath ); + (void) new KPropertiesDialog( templateURL, *it, text, d->m_parentWidget ); + } + return; // done, exit. + } + } + else + { + // The template is not a desktop file + // Show the small dialog for getting the destination filename + bool ok; + QString text = entry.text; + text.replace( "...", QString::null ); // the ... is fine for the menu item but not for the default filename + + KURL defaultFile( *(popupFiles.begin()) ); + defaultFile.addPath( KIO::encodeFileName( text ) ); + if ( defaultFile.isLocalFile() && QFile::exists( defaultFile.path() ) ) + text = KIO::RenameDlg::suggestName( *(popupFiles.begin()), text); + + name = KInputDialog::getText( QString::null, entry.comment, + text, &ok, d->m_parentWidget ); + if ( !ok ) + return; + } + + // The template is not a desktop file [or it's a URL one] + // Copy it. + KURL::List::Iterator it = popupFiles.begin(); + + QString src = entry.templatePath; + for ( ; it != popupFiles.end(); ++it ) + { + KURL dest( *it ); + dest.addPath( KIO::encodeFileName(name) ); // Chosen destination file name + d->m_destPath = dest.path(); // will only be used if m_isURLDesktopFile and dest is local + + KURL uSrc; + uSrc.setPath( src ); + //kdDebug(1203) << "KNewMenu : KIO::copyAs( " << uSrc.url() << ", " << dest.url() << ")" << endl; + KIO::CopyJob * job = KIO::copyAs( uSrc, dest ); + job->setDefaultPermissions( true ); + connect( job, SIGNAL( result( KIO::Job * ) ), + SLOT( slotResult( KIO::Job * ) ) ); + if ( m_isURLDesktopFile ) + connect( job, SIGNAL( renamed( KIO::Job *, const KURL&, const KURL& ) ), + SLOT( slotRenamed( KIO::Job *, const KURL&, const KURL& ) ) ); + KURL::List lst; + lst.append( uSrc ); + (void)new KonqCommandRecorder( KonqCommand::COPY, lst, dest, job ); + } +} + +// Special case (filename conflict when creating a link=url file) +// We need to update m_destURL +void KNewMenu::slotRenamed( KIO::Job *, const KURL& from , const KURL& to ) +{ + if ( from.isLocalFile() ) + { + kdDebug() << k_funcinfo << from.prettyURL() << " -> " << to.prettyURL() << " ( m_destPath=" << d->m_destPath << ")" << endl; + Q_ASSERT( from.path() == d->m_destPath ); + d->m_destPath = to.path(); + } +} + +void KNewMenu::slotResult( KIO::Job * job ) +{ + if (job->error()) + job->showErrorDialog(); + else + { + KURL destURL = static_cast<KIO::CopyJob*>(job)->destURL(); + if ( destURL.isLocalFile() ) + { + if ( m_isURLDesktopFile ) + { + // destURL is the original destination for the new file. + // But in case of a renaming (due to a conflict), the real path is in m_destPath + kdDebug(1203) << " destURL=" << destURL.path() << " " << " d->m_destPath=" << d->m_destPath << endl; + KDesktopFile df( d->m_destPath ); + df.writeEntry( "Icon", KProtocolInfo::icon( KURL(m_linkURL).protocol() ) ); + df.writePathEntry( "URL", m_linkURL ); + df.sync(); + } + else + { + // Normal (local) file. Need to "touch" it, kio_file copied the mtime. + (void) ::utime( QFile::encodeName( destURL.path() ), 0 ); + } + } + } +} + +////////// + +KURLDesktopFileDlg::KURLDesktopFileDlg( const QString& textFileName, const QString& textUrl ) + : KDialogBase( Plain, QString::null, Ok|Cancel|User1, Ok, 0L /*parent*/, 0L, true, + true, KStdGuiItem::clear() ) +{ + initDialog( textFileName, QString::null, textUrl, QString::null ); +} + +KURLDesktopFileDlg::KURLDesktopFileDlg( const QString& textFileName, const QString& textUrl, QWidget *parent ) + : KDialogBase( Plain, QString::null, Ok|Cancel|User1, Ok, parent, 0L, true, + true, KStdGuiItem::clear() ) +{ + initDialog( textFileName, QString::null, textUrl, QString::null ); +} + +void KURLDesktopFileDlg::initDialog( const QString& textFileName, const QString& defaultName, const QString& textUrl, const QString& defaultUrl ) +{ + QVBoxLayout * topLayout = new QVBoxLayout( plainPage(), 0, spacingHint() ); + + // First line: filename + QHBox * fileNameBox = new QHBox( plainPage() ); + topLayout->addWidget( fileNameBox ); + + QLabel * label = new QLabel( textFileName, fileNameBox ); + m_leFileName = new KLineEdit( fileNameBox, 0L ); + m_leFileName->setMinimumWidth(m_leFileName->sizeHint().width() * 3); + label->setBuddy(m_leFileName); // please "scheck" style + m_leFileName->setText( defaultName ); + m_leFileName->setSelection(0, m_leFileName->text().length()); // autoselect + connect( m_leFileName, SIGNAL(textChanged(const QString&)), + SLOT(slotNameTextChanged(const QString&)) ); + + // Second line: url + QHBox * urlBox = new QHBox( plainPage() ); + topLayout->addWidget( urlBox ); + label = new QLabel( textUrl, urlBox ); + m_urlRequester = new KURLRequester( defaultUrl, urlBox, "urlRequester" ); + m_urlRequester->setMode( KFile::File | KFile::Directory ); + + m_urlRequester->setMinimumWidth( m_urlRequester->sizeHint().width() * 3 ); + connect( m_urlRequester->lineEdit(), SIGNAL(textChanged(const QString&)), + SLOT(slotURLTextChanged(const QString&)) ); + label->setBuddy(m_urlRequester); // please "scheck" style + + m_urlRequester->setFocus(); + enableButtonOK( !defaultName.isEmpty() && !defaultUrl.isEmpty() ); + connect( this, SIGNAL(user1Clicked()), this, SLOT(slotClear()) ); + m_fileNameEdited = false; +} + +QString KURLDesktopFileDlg::url() const +{ + if ( result() == QDialog::Accepted ) + return m_urlRequester->url(); + else + return QString::null; +} + +QString KURLDesktopFileDlg::fileName() const +{ + if ( result() == QDialog::Accepted ) + return m_leFileName->text(); + else + return QString::null; +} + +void KURLDesktopFileDlg::slotClear() +{ + m_leFileName->setText( QString::null ); + m_urlRequester->clear(); + m_fileNameEdited = false; +} + +void KURLDesktopFileDlg::slotNameTextChanged( const QString& ) +{ + kdDebug() << k_funcinfo << endl; + m_fileNameEdited = true; + enableButtonOK( !m_leFileName->text().isEmpty() && !m_urlRequester->url().isEmpty() ); +} + +void KURLDesktopFileDlg::slotURLTextChanged( const QString& ) +{ + if ( !m_fileNameEdited ) + { + // use URL as default value for the filename + // (we copy only its filename if protocol supports listing, + // but for HTTP we don't want tons of index.html links) + KURL url( m_urlRequester->url() ); + if ( KProtocolInfo::supportsListing( url ) ) + m_leFileName->setText( url.fileName() ); + else + m_leFileName->setText( url.url() ); + m_fileNameEdited = false; // slotNameTextChanged set it to true erroneously + } + enableButtonOK( !m_leFileName->text().isEmpty() && !m_urlRequester->url().isEmpty() ); +} + + +#include "knewmenu.moc" diff --git a/libkonq/knewmenu.h b/libkonq/knewmenu.h new file mode 100644 index 000000000..ecba301e5 --- /dev/null +++ b/libkonq/knewmenu.h @@ -0,0 +1,224 @@ +/* This file is part of the KDE project + Copyright (C) 1998-2000 David Faure <[email protected]> + 2003 Sven Leiber <[email protected]> + + 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. +*/ + +#ifndef __knewmenu_h +#define __knewmenu_h + +#include <qintdict.h> +#include <qstringlist.h> + +#include <kaction.h> +#include <kdialogbase.h> +#include <kurl.h> +#include <libkonq_export.h> + +namespace KIO { class Job; } + +class KDirWatch; +class KLineEdit; +class KURLRequester; +class QPopupMenu; + +/** + * The 'New' submenu, both for the File menu and the RMB popup menu. + * (The same instance can be used by both). + * Fills it with 'Folder' and one item per Template. + * For this you need to connect aboutToShow() of the File menu with slotCheckUpToDate() + * and to call slotCheckUpToDate() before showing the RMB popupmenu. + * + * KNewMenu automatically updates the list of templates if templates are + * added/updated/deleted. + * + * @author David Faure <[email protected]> + * Ideas and code for the new template handling mechanism ('link' desktop files) + * from Christoph Pickart <[email protected]> + */ +class LIBKONQ_EXPORT KNewMenu : public KActionMenu +{ + Q_OBJECT +public: + + /** + * Constructor + */ + KNewMenu( KActionCollection * _collec, const char *name=0L ); + KNewMenu( KActionCollection * _collec, QWidget *parentWidget, const char *name=0L ); + virtual ~KNewMenu(); + + /** + * Set the files the popup is shown for + * Call this before showing up the menu + */ + void setPopupFiles(KURL::List & _files) { + popupFiles = _files; + } + void setPopupFiles(const KURL & _file) { + popupFiles.clear(); + popupFiles.append( _file ); + } + +public slots: + /** + * Checks if updating the list is necessary + * IMPORTANT : Call this in the slot for aboutToShow. + */ + void slotCheckUpToDate( ); + +protected slots: + /** + * Called when New->Directory... is clicked + */ + void slotNewDir(); + + /** + * Called when New->* is clicked + */ + void slotNewFile(); + + /** + * Fills the templates list. + */ + void slotFillTemplates(); + + void slotResult( KIO::Job * ); + // Special case (filename conflict when creating a link=url file) + void slotRenamed( KIO::Job *, const KURL&, const KURL& ); + +private: + + /** + * Fills the menu from the templates list. + */ + void fillMenu(); + + /** + * Opens the desktop files and completes the Entry list + * Input: the entry list. Output: the entry list ;-) + */ + void parseFiles(); + + /** + * Make the main menus on the startup. + */ + void makeMenus(); + + /** + * For entryType + * LINKTOTEMPLATE: a desktop file that points to a file or dir to copy + * TEMPLATE: a real file to copy as is (the KDE-1.x solution) + * SEPARATOR: to put a separator in the menu + * 0 means: not parsed, i.e. we don't know + */ + enum { LINKTOTEMPLATE = 1, TEMPLATE, SEPARATOR }; + + struct Entry { + QString text; + QString filePath; // empty for SEPARATOR + QString templatePath; // same as filePath for TEMPLATE + QString icon; + int entryType; + QString comment; + }; + // NOTE: only filePath is known before we call parseFiles + + /** + * List of all template files. It is important that they are in + * the same order as the 'New' menu. + */ + static QValueList<Entry> * s_templatesList; + + class KNewMenuPrivate; + KNewMenuPrivate* d; + + /** + * Is increased when templatesList has been updated and + * menu needs to be re-filled. Menus have their own version and compare it + * to templatesVersion before showing up + */ + static int s_templatesVersion; + + /** + * Set back to false each time new templates are found, + * and to true on the first call to parseFiles + */ + static bool s_filesParsed; + + int menuItemsVersion; + + /** + * When the user pressed the right mouse button over an URL a popup menu + * is displayed. The URL belonging to this popup menu is stored here. + */ + KURL::List popupFiles; + + /** + * True when a desktop file with Type=URL is being copied + */ + bool m_isURLDesktopFile; + QString m_linkURL; // the url to put in the file + + static KDirWatch * s_pDirWatch; +}; + +/** + * @internal + * Dialog to ask for a filename and a URL, when creating a link to a URL. + * Basically a merge of KLineEditDlg and KURLRequesterDlg ;) + * @author David Faure <[email protected]> + */ +class KURLDesktopFileDlg : public KDialogBase +{ + Q_OBJECT +public: + KURLDesktopFileDlg( const QString& textFileName, const QString& textUrl ); + KURLDesktopFileDlg( const QString& textFileName, const QString& textUrl, QWidget *parent ); + virtual ~KURLDesktopFileDlg() {} + + /** + * @return the filename the user entered (no path) + */ + QString fileName() const; + /** + * @return the URL the user entered + */ + QString url() const; + +protected slots: + void slotClear(); + void slotNameTextChanged( const QString& ); + void slotURLTextChanged( const QString& ); +private: + void initDialog( const QString& textFileName, const QString& defaultName, const QString& textUrl, const QString& defaultUrl ); + + /** + * The line edit widget for the fileName + */ + KLineEdit *m_leFileName; + /** + * The URL requester for the URL :) + */ + KURLRequester *m_urlRequester; + + /** + * True if the filename was manually edited. + */ + bool m_fileNameEdited; +}; + +#endif diff --git a/libkonq/konq_bgnddlg.cc b/libkonq/konq_bgnddlg.cc new file mode 100644 index 000000000..bb871d2fc --- /dev/null +++ b/libkonq/konq_bgnddlg.cc @@ -0,0 +1,211 @@ +/* This file is part of the KDE project + Copyright (C) 1998, 1999 Torben Weis <[email protected]> + Copyright (c) 1999 David Faure <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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 <qbuttongroup.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qradiobutton.h> + +#include <kcolorbutton.h> +#include <kcombobox.h> +#include <kdebug.h> +#include <kimagefilepreview.h> +#include <klocale.h> +//#include <krecentdocument.h> +#include <kstandarddirs.h> +#include <kurlrequester.h> + +#include "konq_bgnddlg.h" + + +KonqBgndDialog::KonqBgndDialog( QWidget* parent, + const QString& pixmapFile, + const QColor& theColor, + const QColor& defaultColor ) + : KDialogBase( parent, "KonqBgndDialog", false, + i18n("Background Settings"), Ok|Cancel, Ok, true ) +{ + QWidget* page = new QWidget( this ); + setMainWidget( page ); + QVBoxLayout* mainLayout = new QVBoxLayout( page, 0, KDialog::spacingHint() ); + + m_buttonGroup = new QButtonGroup( i18n("Background"), page ); + m_buttonGroup->setColumnLayout( 0, Qt::Vertical ); + m_buttonGroup->layout()->setMargin( KDialog::marginHint() ); + m_buttonGroup->layout()->setSpacing( KDialog::spacingHint() ); + QGridLayout* groupLayout = new QGridLayout( m_buttonGroup->layout() ); + groupLayout->setAlignment( Qt::AlignTop ); + mainLayout->addWidget( m_buttonGroup ); + + connect( m_buttonGroup, SIGNAL( clicked(int) ), + this, SLOT( slotBackgroundModeChanged() ) ); + + // color + m_radioColor = new QRadioButton( i18n("Co&lor:"), m_buttonGroup ); + groupLayout->addWidget( m_radioColor, 0, 0 ); + m_buttonColor = new KColorButton( theColor, defaultColor, m_buttonGroup ); + m_buttonColor->setSizePolicy( QSizePolicy::Preferred, + QSizePolicy::Minimum ); + groupLayout->addWidget( m_buttonColor, 0, 1 ); + + connect( m_buttonColor, SIGNAL( changed( const QColor& ) ), + this, SLOT( slotColorChanged() ) ); + + // picture + m_radioPicture = new QRadioButton( i18n("&Picture:"), m_buttonGroup ); + groupLayout->addWidget( m_radioPicture, 1, 0 ); + m_comboPicture = new KURLComboRequester( m_buttonGroup ); + groupLayout->addMultiCellWidget( m_comboPicture, 1, 1, 1, 2 ); + initPictures(); + + connect( m_comboPicture->comboBox(), SIGNAL( activated( int ) ), + this, SLOT( slotPictureChanged() ) ); + connect( m_comboPicture, SIGNAL( urlSelected(const QString &) ), + this, SLOT( slotPictureChanged() ) ); + + QSpacerItem* spacer1 = new QSpacerItem( 0, 0, QSizePolicy::Expanding, + QSizePolicy::Minimum ); + groupLayout->addItem( spacer1, 0, 2 ); + + // preview title + QHBoxLayout* hlay = new QHBoxLayout( mainLayout, KDialog::spacingHint() ); + //mainLayout->addLayout( hlay ); + QLabel* lbl = new QLabel( i18n("Preview"), page ); + hlay->addWidget( lbl ); + QFrame* frame = new QFrame( page ); + frame->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum ); + frame->setFrameShape( QFrame::HLine ); + frame->setFrameShadow( QFrame::Sunken ); + hlay->addWidget( frame ); + + // preview frame + m_preview = new QFrame( page ); + m_preview->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); + m_preview->setMinimumSize( 370, 180 ); + m_preview->setFrameShape( QFrame::Panel ); + m_preview->setFrameShadow( QFrame::Raised ); + mainLayout->addWidget( m_preview ); + + if ( !pixmapFile.isEmpty() ) { + loadPicture( pixmapFile ); + m_buttonColor->setColor( defaultColor ); + m_radioPicture->setChecked( true ); + } + else { + m_buttonColor->setColor( theColor ); + m_comboPicture->comboBox()->setCurrentItem( 0 ); + m_radioColor->setChecked( true ); + } + slotBackgroundModeChanged(); +} + +KonqBgndDialog::~KonqBgndDialog() +{ +} + +QColor KonqBgndDialog::color() const +{ + if ( m_radioColor->isChecked() ) + return m_buttonColor->color(); + + return QColor(); +} + +void KonqBgndDialog::initPictures() +{ + KGlobal::dirs()->addResourceType( "tiles", + KGlobal::dirs()->kde_default("data") + "konqueror/tiles/"); + kdDebug(1203) << KGlobal::dirs()->kde_default("data") + "konqueror/tiles/" << endl; + + QStringList list = KGlobal::dirs()->findAllResources("tiles"); + + if ( list.isEmpty() ) + m_comboPicture->comboBox()->insertItem( i18n("None") ); + else { + QStringList::ConstIterator it; + for ( it = list.begin(); it != list.end(); it++ ) + m_comboPicture->comboBox()->insertItem( + ( (*it).at(0) == '/' ) ? // if absolute path + KURL( *it ).fileName() : // then only fileName + *it ); + } +} + +void KonqBgndDialog::loadPicture( const QString& fileName ) +{ + int i ; + for ( i = 0; i < m_comboPicture->comboBox()->count(); i++ ) { + if ( fileName == m_comboPicture->comboBox()->text( i ) ) { + m_comboPicture->comboBox()->setCurrentItem( i ); + return; + } + } + + if ( !fileName.isEmpty() ) { + m_comboPicture->comboBox()->insertItem( fileName ); + m_comboPicture->comboBox()->setCurrentItem( i ); + } + else + m_comboPicture->comboBox()->setCurrentItem( 0 ); +} + +void KonqBgndDialog::slotPictureChanged() +{ + m_pixmapFile = m_comboPicture->comboBox()->currentText(); + QString file = locate( "tiles", m_pixmapFile ); + if ( file.isEmpty() ) + file = locate("wallpaper", m_pixmapFile); // add fallback for compatibility + if ( file.isEmpty() ) { + kdWarning(1203) << "Couldn't locate wallpaper " << m_pixmapFile << endl; + m_preview->unsetPalette(); + m_pixmap = QPixmap(); + m_pixmapFile = ""; + } + else { + m_pixmap.load( file ); + + if ( m_pixmap.isNull() ) + kdWarning(1203) << "Could not load wallpaper " << file << endl; + } + m_preview->setPaletteBackgroundPixmap( m_pixmap ); +} + +void KonqBgndDialog::slotColorChanged() +{ + m_preview->setPaletteBackgroundColor( m_buttonColor->color() ); +} + +void KonqBgndDialog::slotBackgroundModeChanged() +{ + if ( m_radioColor->isChecked() ) { + m_buttonColor->setEnabled( true ); + m_comboPicture->setEnabled( false ); + m_pixmapFile = ""; + slotColorChanged(); + } + else { // m_comboPicture->isChecked() == true + m_comboPicture->setEnabled( true ); + m_buttonColor->setEnabled( false ); + slotPictureChanged(); + } +} + + +#include "konq_bgnddlg.moc" diff --git a/libkonq/konq_bgnddlg.h b/libkonq/konq_bgnddlg.h new file mode 100644 index 000000000..3a1e3c67d --- /dev/null +++ b/libkonq/konq_bgnddlg.h @@ -0,0 +1,74 @@ +/* This file is part of the KDE project + Copyright (C) 1998, 1999 Torben Weis <[email protected]> + Copyright (c) 1999 David Faure <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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. +*/ + +#ifndef __konq_bgnd_h +#define __konq_bgnd_h + +#include <qstring.h> +#include <qpixmap.h> + +#include <kdialogbase.h> + +class KColorButton; +class KURLRequester; +class QButtonGroup; +class QRadioButton; + +/** + * Dialog for configuring the background + * Currently it defines and shows the pixmaps under the tiles resource + */ +class KonqBgndDialog : public KDialogBase +{ + Q_OBJECT +public: + /** + * Constructor + */ + KonqBgndDialog( QWidget* parent, const QString& pixmapFile, + const QColor& theColor, const QColor& defaultColor ); + ~KonqBgndDialog(); + + QColor color() const; + const QString& pixmapFile() const { return m_pixmapFile; } + +private slots: + void slotBackgroundModeChanged(); + void slotPictureChanged(); + void slotColorChanged(); + +private: + void initPictures(); + void loadPicture( const QString& fileName ); + + QColor m_color; + QPixmap m_pixmap; + QString m_pixmapFile; + QFrame* m_preview; + + QButtonGroup* m_buttonGroup; + QRadioButton* m_radioColor; + QRadioButton* m_radioPicture; + KURLRequester* m_comboPicture; + KColorButton* m_buttonColor; + +}; + +#endif diff --git a/libkonq/konq_defaults.h b/libkonq/konq_defaults.h new file mode 100644 index 000000000..fac4fdb3d --- /dev/null +++ b/libkonq/konq_defaults.h @@ -0,0 +1,48 @@ +/* This file is part of the KDE project + Copyright (C) 1998, 1999 Torben Weis <[email protected]> + Copyright (C) 1999 - 2002 David Faure <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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. +*/ + +// Default values for konqueror/kdesktop settings +// This file is used by konqueror kdesktop, and kcmkonq, +// to share the same defaults + +// appearance tab +#define DEFAULT_UNDERLINELINKS false +#define DEFAULT_WORDWRAPTEXT true // kfm-like, sorry Reggie :-) +#define DEFAULT_TEXTHEIGHT 2 +#define DEFAULT_TEXTWIDTH 0 // 0 = automatic (font depending) +#define DEFAULT_TEXTWIDTH_MULTICOLUMN 600 // maxwidth, as the iconview has dynamic column width +#define DEFAULT_FILESIZEINBYTES false + +#define DEFAULT_RENAMEICONDIRECTLY false + +// transparency of blended mimetype icons in textpreview +#define DEFAULT_TEXTPREVIEW_ICONTRANSPARENCY 70 + +// show hidden files on desktop default +#define DEFAULT_SHOW_HIDDEN_ROOT_ICONS false +#define DEFAULT_VERT_ALIGN true + +// Default terminal for "Open Terminal" in konqueror +#define DEFAULT_TERMINAL "konsole" + +// Confirmations for deletions +#define DEFAULT_CONFIRMTRASH true +#define DEFAULT_CONFIRMDELETE true +#define DEFAULT_CONFIRMSHRED true diff --git a/libkonq/konq_dirpart.cc b/libkonq/konq_dirpart.cc new file mode 100644 index 000000000..0e46ec0c9 --- /dev/null +++ b/libkonq/konq_dirpart.cc @@ -0,0 +1,746 @@ +/* This file is part of the KDE projects + Copyright (C) 2000 David Faure <[email protected]> + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "konq_dirpart.h" +#include "konq_bgnddlg.h" +#include "konq_propsview.h" +#include "konq_settings.h" + +#include <kio/paste.h> +#include <kapplication.h> +#include <kaction.h> +#include <kdatastream.h> +#include <kdebug.h> +#include <kdirlister.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <konq_drag.h> +#include <kparts/browserextension.h> +#include <kurldrag.h> +#include <kuserprofile.h> +#include <kurifilter.h> +#include <kglobalsettings.h> + +#include <qapplication.h> +#include <qclipboard.h> +#include <qfile.h> +#include <qguardedptr.h> +#include <assert.h> +#include <qvaluevector.h> + +class KonqDirPart::KonqDirPartPrivate +{ +public: + KonqDirPartPrivate() : dirLister( 0 ) {} + QStringList mimeFilters; + KToggleAction *aEnormousIcons; + KToggleAction *aSmallMediumIcons; + QValueVector<int> iconSize; + + KDirLister* dirLister; + bool dirSizeDirty; + + void findAvailableIconSizes(void); + int findNearestIconSize(int size); + int nearestIconSizeError(int size); +}; + +void KonqDirPart::KonqDirPartPrivate::findAvailableIconSizes(void) +{ + KIconTheme *root = KGlobal::instance()->iconLoader()->theme(); + iconSize.resize(1); + if (root) { + QValueList<int> avSizes = root->querySizes(KIcon::Desktop); + kdDebug(1203) << "The icon theme handles the sizes:" << avSizes << endl; + qHeapSort(avSizes); + int oldSize = -1; + if (avSizes.count() < 10) { + // Fixed or threshold type icons + QValueListConstIterator<int> i; + for (i = avSizes.begin(); i != avSizes.end(); i++) { + // Skip duplicated values (sanity check) + if (*i != oldSize) iconSize.append(*i); + oldSize = *i; + } + } else { + // Scalable icons. + const int progression[] = {16, 22, 32, 48, 64, 96, 128, 192, 256}; + + QValueListConstIterator<int> j = avSizes.begin(); + for (uint i = 0; i < 9; i++) { + while (j++ != avSizes.end()) { + if (*j >= progression[i]) { + iconSize.append(*j); + kdDebug(1203) << "appending " << *j << " size." << endl; + break; + } + } + } + } + } else { + iconSize.append(KIcon::SizeSmall); // 16 + iconSize.append(KIcon::SizeMedium); // 32 + iconSize.append(KIcon::SizeLarge); // 48 + iconSize.append(KIcon::SizeHuge); // 64 + } + kdDebug(1203) << "Using " << iconSize.count() << " icon sizes." << endl; +} + +int KonqDirPart::KonqDirPartPrivate::findNearestIconSize(int preferred) +{ + int s1 = iconSize[1]; + if (preferred == 0) return KGlobal::iconLoader()->currentSize(KIcon::Desktop); + if (preferred <= s1) return s1; + for (uint i = 2; i <= iconSize.count(); i++) { + if (preferred <= iconSize[i]) { + if (preferred - s1 < iconSize[i] - preferred) return s1; + else return iconSize[i]; + } else { + s1 = iconSize[i]; + } + } + return s1; +} + +int KonqDirPart::KonqDirPartPrivate::nearestIconSizeError(int size) +{ + return QABS(size - findNearestIconSize(size)); +} + +KonqDirPart::KonqDirPart( QObject *parent, const char *name ) + :KParts::ReadOnlyPart( parent, name ), + m_pProps( 0L ), + m_findPart( 0L ) +{ + d = new KonqDirPartPrivate; + resetCount(); + //m_bMultipleItemsSelected = false; + + connect( QApplication::clipboard(), SIGNAL(dataChanged()), this, SLOT(slotClipboardDataChanged()) ); + + actionCollection()->setHighlightingEnabled( true ); + + m_paIncIconSize = new KAction( i18n( "Enlarge Icons" ), "viewmag+", 0, this, SLOT( slotIncIconSize() ), actionCollection(), "incIconSize" ); + m_paDecIconSize = new KAction( i18n( "Shrink Icons" ), "viewmag-", 0, this, SLOT( slotDecIconSize() ), actionCollection(), "decIconSize" ); + + m_paDefaultIcons = new KRadioAction( i18n( "&Default Size" ), 0, actionCollection(), "modedefault" ); + d->aEnormousIcons = new KRadioAction( i18n( "&Huge" ), 0, + actionCollection(), "modeenormous" ); + m_paHugeIcons = new KRadioAction( i18n( "&Very Large" ), 0, actionCollection(), "modehuge" ); + m_paLargeIcons = new KRadioAction( i18n( "&Large" ), 0, actionCollection(), "modelarge" ); + m_paMediumIcons = new KRadioAction( i18n( "&Medium" ), 0, actionCollection(), "modemedium" ); + d->aSmallMediumIcons = new KRadioAction( i18n( "&Small" ), 0, + actionCollection(), "modesmallmedium" ); + m_paSmallIcons = new KRadioAction( i18n( "&Tiny" ), 0, actionCollection(), "modesmall" ); + + m_paDefaultIcons->setExclusiveGroup( "ViewMode" ); + d->aEnormousIcons->setExclusiveGroup( "ViewMode" ); + m_paHugeIcons->setExclusiveGroup( "ViewMode" ); + m_paLargeIcons->setExclusiveGroup( "ViewMode" ); + m_paMediumIcons->setExclusiveGroup( "ViewMode" ); + d->aSmallMediumIcons->setExclusiveGroup( "ViewMode" ); + m_paSmallIcons->setExclusiveGroup( "ViewMode" ); + + connect( m_paDefaultIcons, SIGNAL( toggled( bool ) ), this, SLOT( slotIconSizeToggled( bool ) ) ); + connect( d->aEnormousIcons, SIGNAL( toggled( bool ) ), + this, SLOT( slotIconSizeToggled( bool ) ) ); + connect( m_paHugeIcons, SIGNAL( toggled( bool ) ), this, SLOT( slotIconSizeToggled( bool ) ) ); + connect( m_paLargeIcons, SIGNAL( toggled( bool ) ), this, SLOT( slotIconSizeToggled( bool ) ) ); + connect( m_paMediumIcons, SIGNAL( toggled( bool ) ), this, SLOT( slotIconSizeToggled( bool ) ) ); + connect( d->aSmallMediumIcons, SIGNAL( toggled( bool ) ), + this, SLOT( slotIconSizeToggled( bool ) ) ); + connect( m_paSmallIcons, SIGNAL( toggled( bool ) ), this, SLOT( slotIconSizeToggled( bool ) ) ); + + connect( kapp, SIGNAL(iconChanged(int)), SLOT(slotIconChanged(int)) ); +#if 0 + // Extract 6 icon sizes from the icon theme. + // Use 16,22,32,48,64,128 as default. + // Use these also if the icon theme is scalable. + int i; + d->iconSize[0] = 0; // Default value + d->iconSize[1] = KIcon::SizeSmall; // 16 + d->iconSize[2] = KIcon::SizeSmallMedium; // 22 + d->iconSize[3] = KIcon::SizeMedium; // 32 + d->iconSize[4] = KIcon::SizeLarge; // 48 + d->iconSize[5] = KIcon::SizeHuge; // 64 + d->iconSize[6] = KIcon::SizeEnormous; // 128 + d->iconSize[7] = 192; + d->iconSize[8] = 256; + KIconTheme *root = KGlobal::instance()->iconLoader()->theme(); + if (root) + { + QValueList<int> avSizes = root->querySizes(KIcon::Desktop); + kdDebug(1203) << "the icon theme handles the following sizes:" << avSizes << endl; + if (avSizes.count() < 10) { + // Use the icon sizes supplied by the theme. + // If avSizes contains more than 10 entries, assume a scalable + // icon theme. + QValueList<int>::Iterator it; + for (i=1, it=avSizes.begin(); (it!=avSizes.end()) && (i<7); it++, i++) + { + d->iconSize[i] = *it; + kdDebug(1203) << "m_iIconSize[" << i << "] = " << *it << endl; + } + // Generate missing sizes + for (; i < 7; i++) { + d->iconSize[i] = d->iconSize[i - 1] + d->iconSize[i - 1] / 2 ; + kdDebug(1203) << "m_iIconSize[" << i << "] = " << d->iconSize[i] << endl; + } + } + } +#else + d->iconSize.reserve(10); + d->iconSize.append(0); // Default value + adjustIconSizes(); +#endif + + // Remove in KDE4 ... + // These are here in the event subclasses access them. + m_iIconSize[1] = KIcon::SizeSmall; + m_iIconSize[2] = KIcon::SizeMedium; + m_iIconSize[3] = KIcon::SizeLarge; + m_iIconSize[4] = KIcon::SizeHuge; + // ... up to here + + KAction *a = new KAction( i18n( "Configure Background..." ), "background", 0, this, SLOT( slotBackgroundSettings() ), + actionCollection(), "bgsettings" ); + + a->setToolTip( i18n( "Allows choosing of background settings for this view" ) ); +} + +KonqDirPart::~KonqDirPart() +{ + // Close the find part with us + delete m_findPart; + delete d; + d = 0; +} + +void KonqDirPart::adjustIconSizes() +{ + d->findAvailableIconSizes(); + m_paSmallIcons->setEnabled(d->findNearestIconSize(16) < 20); + d->aSmallMediumIcons->setEnabled(d->nearestIconSizeError(22) < 2); + m_paMediumIcons->setEnabled(d->nearestIconSizeError(32) < 6); + m_paLargeIcons->setEnabled(d->nearestIconSizeError(48) < 8); + m_paHugeIcons->setEnabled(d->nearestIconSizeError(64) < 12); + d->aEnormousIcons->setEnabled(d->findNearestIconSize(128) > 110); + + if (m_pProps) { + int size = m_pProps->iconSize(); + int nearSize = d->findNearestIconSize(size); + + if (size != nearSize) { + m_pProps->setIconSize(nearSize); + } + newIconSize(nearSize); + } +} + +void KonqDirPart::setMimeFilter (const QStringList& mime) +{ + QString u = url().url(); + + if ( u.isEmpty () ) + return; + + if ( mime.isEmpty() ) + d->mimeFilters.clear(); + else + d->mimeFilters = mime; +} + +QStringList KonqDirPart::mimeFilter() const +{ + return d->mimeFilters; +} + +QScrollView * KonqDirPart::scrollWidget() +{ + return static_cast<QScrollView *>(widget()); +} + +void KonqDirPart::slotBackgroundSettings() +{ + QColor bgndColor = m_pProps->bgColor( widget() ); + QColor defaultColor = KGlobalSettings::baseColor(); + // dlg must be created on the heap as widget() can get deleted while dlg.exec(), + // trying to delete dlg as its child then (#124210) - Frank Osterfeld + QGuardedPtr<KonqBgndDialog> dlg = new KonqBgndDialog( widget(), + m_pProps->bgPixmapFile(), + bgndColor, + defaultColor ); + + if ( dlg->exec() == KonqBgndDialog::Accepted ) + { + if ( dlg->color().isValid() ) + { + m_pProps->setBgColor( dlg->color() ); + m_pProps->setBgPixmapFile( "" ); + } + else + { + m_pProps->setBgColor( defaultColor ); + m_pProps->setBgPixmapFile( dlg->pixmapFile() ); + } + m_pProps->applyColors( scrollWidget()->viewport() ); + scrollWidget()->viewport()->repaint(); + } + + delete dlg; +} + +void KonqDirPart::lmbClicked( KFileItem * fileItem ) +{ + KURL url = fileItem->url(); + if ( !fileItem->isReadable() ) + { + // No permissions or local file that doesn't exist - need to find out which + if ( ( !fileItem->isLocalFile() ) || QFile::exists( url.path() ) ) + { + KMessageBox::error( widget(), i18n("<p>You do not have enough permissions to read <b>%1</b></p>").arg(url.prettyURL()) ); + return; + } + KMessageBox::error( widget(), i18n("<p><b>%1</b> does not seem to exist anymore</p>").arg(url.prettyURL()) ); + return; + } + + KParts::URLArgs args; + fileItem->determineMimeType(); + if ( fileItem->isMimeTypeKnown() ) + args.serviceType = fileItem->mimetype(); + args.trustedSource = true; + + if (KonqFMSettings::settings()->alwaysNewWin() && fileItem->isDir()) { + //args.frameName = "_blank"; // open new window + // We tried the other option, passing the path as framename so that + // an existing window for that dir is reused (like MSWindows does when + // the similar option is activated and the sidebar is hidden (!)). + // But this requires some work, including changing the framename + // when navigating, etc. Not very much requested yet, in addition. + KParts::WindowArgs wargs; + KParts::ReadOnlyPart* dummy; + emit m_extension->createNewWindow( url, args, wargs, dummy ); + } + else + { + kdDebug() << "emit m_extension->openURLRequest( " << url.url() << "," << args.serviceType << ")" << endl; + emit m_extension->openURLRequest( url, args ); + } +} + +void KonqDirPart::mmbClicked( KFileItem * fileItem ) +{ + if ( fileItem ) + { + // Optimisation to avoid KRun to call kfmclient that then tells us + // to open a window :-) + KService::Ptr offer = KServiceTypeProfile::preferredService(fileItem->mimetype(), "Application"); + //if (offer) kdDebug(1203) << "KonqDirPart::mmbClicked: got service " << offer->desktopEntryName() << endl; + if ( offer && offer->desktopEntryName().startsWith("kfmclient") ) + { + KParts::URLArgs args; + args.serviceType = fileItem->mimetype(); + emit m_extension->createNewWindow( fileItem->url(), args ); + } + else + fileItem->run(); + } + else + { + m_extension->pasteRequest(); + } +} + +void KonqDirPart::saveState( QDataStream& stream ) +{ + stream << m_nameFilter; +} + +void KonqDirPart::restoreState( QDataStream& stream ) +{ + stream >> m_nameFilter; +} + +void KonqDirPart::saveFindState( QDataStream& stream ) +{ + // assert only doable in KDE4. + //assert( m_findPart ); // test done by caller. + if ( !m_findPart ) + return; + + // When we have a find part, our own URL wasn't saved (see KonqDirPartBrowserExtension) + // So let's do it here + stream << m_url; + + KParts::BrowserExtension* ext = KParts::BrowserExtension::childObject( m_findPart ); + if( !ext ) + return; + + ext->saveState( stream ); +} + +void KonqDirPart::restoreFindState( QDataStream& stream ) +{ + // Restore our own URL + stream >> m_url; + + emit findOpen( this ); + + KParts::BrowserExtension* ext = KParts::BrowserExtension::childObject( m_findPart ); + slotClear(); + + if( !ext ) + return; + + ext->restoreState( stream ); +} + +void KonqDirPart::slotClipboardDataChanged() +{ + // This is very related to KDIconView::slotClipboardDataChanged + + KURL::List lst; + QMimeSource *data = QApplication::clipboard()->data(); + if ( data->provides( "application/x-kde-cutselection" ) && data->provides( "text/uri-list" ) ) + if ( KonqDrag::decodeIsCutSelection( data ) ) + (void) KURLDrag::decode( data, lst ); + + disableIcons( lst ); + + updatePasteAction(); +} + +void KonqDirPart::updatePasteAction() // KDE4: merge into method above +{ + QString actionText = KIO::pasteActionText(); + bool paste = !actionText.isEmpty(); + if ( paste ) + emit m_extension->setActionText( "paste", actionText ); + emit m_extension->enableAction( "paste", paste ); +} + +void KonqDirPart::newItems( const KFileItemList & entries ) +{ + d->dirSizeDirty = true; + if ( m_findPart ) + emitTotalCount(); + + emit itemsAdded( entries ); +} + +void KonqDirPart::deleteItem( KFileItem * fileItem ) +{ + d->dirSizeDirty = true; + emit itemRemoved( fileItem ); +} + +void KonqDirPart::emitTotalCount() +{ + if ( !d->dirLister || d->dirLister->url().isEmpty() ) + return; + if ( d->dirSizeDirty ) { + m_lDirSize = 0; + m_lFileCount = 0; + m_lDirCount = 0; + KFileItemList entries = d->dirLister->items(); + for (KFileItemListIterator it(entries); it.current(); ++it) + { + if ( !it.current()->isDir() ) + { + if (!it.current()->isLink()) // symlinks don't contribute to the size + m_lDirSize += it.current()->size(); + m_lFileCount++; + } + else + m_lDirCount++; + } + d->dirSizeDirty = false; + } + + QString summary = + KIO::itemsSummaryString(m_lFileCount + m_lDirCount, + m_lFileCount, + m_lDirCount, + m_lDirSize, + true); + bool bShowsResult = false; + if (m_findPart) + { + QVariant prop = m_findPart->property( "showsResult" ); + bShowsResult = prop.isValid() && prop.toBool(); + } + //kdDebug(1203) << "KonqDirPart::emitTotalCount bShowsResult=" << bShowsResult << endl; + emit setStatusBarText( bShowsResult ? i18n("Search result: %1").arg(summary) : summary ); +} + +void KonqDirPart::emitCounts( const KFileItemList & lst ) +{ + if ( lst.count() == 1 ) + emit setStatusBarText( ((KFileItemList)lst).first()->getStatusBarInfo() ); + else + { + long long fileSizeSum = 0; + uint fileCount = 0; + uint dirCount = 0; + + for ( KFileItemListIterator it( lst ); it.current(); ++it ) + { + if ( it.current()->isDir() ) + dirCount++; + else + { + if ( !it.current()->isLink() ) // ignore symlinks + fileSizeSum += it.current()->size(); + fileCount++; + } + } + + emit setStatusBarText( KIO::itemsSummaryString( fileCount + dirCount, + fileCount, dirCount, + fileSizeSum, true ) ); + } +} + +void KonqDirPart::emitCounts( const KFileItemList & lst, bool selectionChanged ) +{ + if ( lst.count() == 0 ) + emitTotalCount(); + else + emitCounts( lst ); + + // Yes, the caller could do that too :) + // But this bool could also be used to cache the QString for the last + // selection, as long as selectionChanged is false. + // Not sure it's worth it though. + // MiB: no, I don't think it's worth it. Especially regarding the + // loss of readability of the code. Thus, this will be removed in + // KDE 4.0. + if ( selectionChanged ) + emit m_extension->selectionInfo( lst ); +} + +void KonqDirPart::emitMouseOver( const KFileItem* item ) +{ + emit m_extension->mouseOverInfo( item ); +} + +void KonqDirPart::slotIconSizeToggled( bool toggleOn ) +{ + //kdDebug(1203) << "KonqDirPart::slotIconSizeToggled" << endl; + + // This slot is called when an iconsize action is checked or by calling + // action->setChecked(false) (previously true). So we must filter out + // the 'untoggled' case to prevent odd results here (repaints/loops!) + if ( !toggleOn ) + return; + + if ( m_paDefaultIcons->isChecked() ) + setIconSize(0); + else if ( d->aEnormousIcons->isChecked() ) + setIconSize(d->findNearestIconSize(KIcon::SizeEnormous)); + else if ( m_paHugeIcons->isChecked() ) + setIconSize(d->findNearestIconSize(KIcon::SizeHuge)); + else if ( m_paLargeIcons->isChecked() ) + setIconSize(d->findNearestIconSize(KIcon::SizeLarge)); + else if ( m_paMediumIcons->isChecked() ) + setIconSize(d->findNearestIconSize(KIcon::SizeMedium)); + else if ( d->aSmallMediumIcons->isChecked() ) + setIconSize(d->findNearestIconSize(KIcon::SizeSmallMedium)); + else if ( m_paSmallIcons->isChecked() ) + setIconSize(d->findNearestIconSize(KIcon::SizeSmall)); +} + +void KonqDirPart::slotIncIconSize() +{ + int s = m_pProps->iconSize(); + s = s ? s : KGlobal::iconLoader()->currentSize( KIcon::Desktop ); + uint sizeIndex = 0; + for ( uint idx = 1; idx < d->iconSize.count() ; ++idx ) + if (s == d->iconSize[idx]) { + sizeIndex = idx; + break; + } + if ( sizeIndex > 0 && sizeIndex < d->iconSize.count() - 1 ) + { + setIconSize( d->iconSize[sizeIndex + 1] ); + } +} + +void KonqDirPart::slotDecIconSize() +{ + int s = m_pProps->iconSize(); + s = s ? s : KGlobal::iconLoader()->currentSize( KIcon::Desktop ); + uint sizeIndex = 0; + for ( uint idx = 1; idx < d->iconSize.count() ; ++idx ) + if (s == d->iconSize[idx]) { + sizeIndex = idx; + break; + } + if ( sizeIndex > 1 ) + { + setIconSize( d->iconSize[sizeIndex - 1] ); + } +} + +// Only updates Actions, a GUI update is done in the views by reimplementing this +void KonqDirPart::newIconSize( int size /*0=default, or 16,32,48....*/ ) +{ + int realSize = (size==0) ? KGlobal::iconLoader()->currentSize( KIcon::Desktop ) : size; + m_paDecIconSize->setEnabled(realSize > d->iconSize[1]); + m_paIncIconSize->setEnabled(realSize < d->iconSize.back()); + + m_paDefaultIcons->setChecked(size == 0); + d->aEnormousIcons->setChecked(size == d->findNearestIconSize(KIcon::SizeEnormous)); + m_paHugeIcons->setChecked(size == d->findNearestIconSize(KIcon::SizeHuge)); + m_paLargeIcons->setChecked(size == d->findNearestIconSize(KIcon::SizeLarge)); + m_paMediumIcons->setChecked(size == d->findNearestIconSize(KIcon::SizeMedium)); + d->aSmallMediumIcons->setChecked(size == d->findNearestIconSize(KIcon::SizeSmallMedium)); + m_paSmallIcons->setChecked(size == d->findNearestIconSize(KIcon::SizeSmall)); +} + +// Stores the new icon size and updates the GUI +void KonqDirPart::setIconSize( int size ) +{ + //kdDebug(1203) << "KonqDirPart::setIconSize " << size << " -> updating props and GUI" << endl; + m_pProps->setIconSize( size ); + newIconSize( size ); +} + +bool KonqDirPart::closeURL() +{ + // Tell all the childern objects to clean themselves up for dinner :) + return doCloseURL(); +} + +bool KonqDirPart::openURL(const KURL& url) +{ + if ( m_findPart ) + { + kdDebug(1203) << "KonqDirPart::openURL -> emit findClosed " << this << endl; + delete m_findPart; + m_findPart = 0L; + emit findClosed( this ); + } + + m_url = url; + emit aboutToOpenURL (); + + return doOpenURL(url); +} + +void KonqDirPart::setFindPart( KParts::ReadOnlyPart * part ) +{ + assert(part); + m_findPart = part; + connect( m_findPart, SIGNAL( started() ), + this, SLOT( slotStarted() ) ); + connect( m_findPart, SIGNAL( started() ), + this, SLOT( slotStartAnimationSearching() ) ); + connect( m_findPart, SIGNAL( clear() ), + this, SLOT( slotClear() ) ); + connect( m_findPart, SIGNAL( newItems( const KFileItemList & ) ), + this, SLOT( slotNewItems( const KFileItemList & ) ) ); + connect( m_findPart, SIGNAL( finished() ), // can't name it completed, it conflicts with a KROP signal + this, SLOT( slotCompleted() ) ); + connect( m_findPart, SIGNAL( finished() ), + this, SLOT( slotStopAnimationSearching() ) ); + connect( m_findPart, SIGNAL( canceled() ), + this, SLOT( slotCanceled() ) ); + connect( m_findPart, SIGNAL( canceled() ), + this, SLOT( slotStopAnimationSearching() ) ); + + connect( m_findPart, SIGNAL( findClosed() ), + this, SLOT( slotFindClosed() ) ); + + emit findOpened( this ); + + // set the initial URL in the find part + m_findPart->openURL( url() ); +} + +void KonqDirPart::slotFindClosed() +{ + kdDebug(1203) << "KonqDirPart::slotFindClosed -> emit findClosed " << this << endl; + delete m_findPart; + m_findPart = 0L; + emit findClosed( this ); + // reload where we were before + openURL( url() ); +} + +void KonqDirPart::slotIconChanged( int group ) +{ + if (group != KIcon::Desktop) return; + adjustIconSizes(); +} + +void KonqDirPart::slotStartAnimationSearching() +{ + started(0); +} + +void KonqDirPart::slotStopAnimationSearching() +{ + completed(); +} + +void KonqDirPartBrowserExtension::saveState( QDataStream &stream ) +{ + m_dirPart->saveState( stream ); + bool hasFindPart = m_dirPart->findPart(); + stream << hasFindPart; + assert( ! ( hasFindPart && !strcmp(m_dirPart->className(), "KFindPart") ) ); + if ( !hasFindPart ) + KParts::BrowserExtension::saveState( stream ); + else { + m_dirPart->saveFindState( stream ); + } +} + +void KonqDirPartBrowserExtension::restoreState( QDataStream &stream ) +{ + m_dirPart->restoreState( stream ); + bool hasFindPart; + stream >> hasFindPart; + assert( ! ( hasFindPart && !strcmp(m_dirPart->className(), "KFindPart") ) ); + if ( !hasFindPart ) + // This calls openURL, that's why we don't want to call it in case of a find part + KParts::BrowserExtension::restoreState( stream ); + else { + m_dirPart->restoreFindState( stream ); + } +} + + +void KonqDirPart::resetCount() +{ + m_lDirSize = 0; + m_lFileCount = 0; + m_lDirCount = 0; + d->dirSizeDirty = true; +} + +void KonqDirPart::setDirLister( KDirLister* lister ) +{ + d->dirLister = lister; +} + +#include "konq_dirpart.moc" diff --git a/libkonq/konq_dirpart.h b/libkonq/konq_dirpart.h new file mode 100644 index 000000000..df76e93bd --- /dev/null +++ b/libkonq/konq_dirpart.h @@ -0,0 +1,351 @@ +/* This file is part of the KDE projects + Copyright (C) 2000 David Faure <[email protected]> + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __konqdirpart_h +#define __konqdirpart_h + +#include <qstring.h> +#include <kparts/part.h> +#include <kparts/browserextension.h> +#include <kfileitem.h> +#include <kdatastream.h> +#include <kio/global.h> +#include <libkonq_export.h> + +class KDirLister; +namespace KParts { class BrowserExtension; } +class KonqPropsView; +class QScrollView; +class KAction; +class KToggleAction; +class KonqDirPartBrowserExtension; + +class LIBKONQ_EXPORT KonqDirPart: public KParts::ReadOnlyPart +{ + Q_OBJECT + + friend class KonqDirPartBrowserExtension; + +public: + KonqDirPart( QObject *parent, const char *name ); + + virtual ~KonqDirPart(); + + /** + * The derived part should call this in its constructor + */ + void setBrowserExtension( KonqDirPartBrowserExtension * extension ) + { m_extension = extension; } + + KonqDirPartBrowserExtension * extension() + { return m_extension; } + + /** + * The derived part should call this in its constructor + */ + void setDirLister( KDirLister* lister ); + // TODO KDE4 create the KDirLister here and simplify the parts? + + QScrollView * scrollWidget(); + + virtual void saveState( QDataStream &stream ); + virtual void restoreState( QDataStream &stream ); + + /** Called when LMB'ing an item in a directory view. + * @param fileItem must be set + * @param widget is only set as parent pointer for dialog boxes */ + void lmbClicked( KFileItem * fileItem ); + + /** Called when MMB'ing an item in a directory view. + * @param fileItem if 0 it means we MMB'ed the background. */ + void mmbClicked( KFileItem * fileItem ); + + void setNameFilter( const QString & nameFilter ) { m_nameFilter = nameFilter; } + + QString nameFilter() const { return m_nameFilter; } + + void setFilesToSelect( const QStringList & filesToSelect ) { m_filesToSelect = filesToSelect; } + + /** + * Sets per directory mime-type based filtering. + * + * This method causes only the items matching the mime-type given + * by @p filters to be displayed. You can supply multiple mime-types + * by separating them with a space, eg. "text/plain image/x-png". + * To clear all the filters set for the current url simply call this + * function with a null or empty argument. + * + * NOTE: the filter(s) specified here only apply to the current + * directory as returned by @ref #url(). + * + * @param filter mime-type(s) to filter directory by. + */ + void setMimeFilter (const QStringList& filters); + + /** + * Completely clears the internally stored list of mime filters + * set by call to @ref #setMimeFilter. + */ + QStringList mimeFilter() const; + + + KonqPropsView * props() const { return m_pProps; } + + /** + * "Cut" icons : disable those whose URL is in lst, enable the others + */ + virtual void disableIcons( const KURL::List & lst ) = 0; + + /** + * This class takes care of the counting of items, size etc. in the + * current directory. Call this in slotClear. + */ + void resetCount(); + + /** + * Update the counts for those new items + */ + void newItems( const KFileItemList & entries ); + + /** + * Update the counts with this item being deleted + */ + void deleteItem( KFileItem * fileItem ); + + /** + * Show the counts for the directory in the status bar + */ + void emitTotalCount(); + + // ##### TODO KDE 4: remove! + /** + * Show the counts for the list of items in the status bar. + * If none are provided emitTotalCount() is called to display + * the counts for the whole directory. However, that does not work + * for a treeview. + * + * @deprecated + */ + void emitCounts( const KFileItemList & lst, bool selectionChanged ); + + /** + * Show the counts for the list of items in the status bar. The list + * can be empty. + * + * @param lst the list of fileitems for which to display the counts + * @since 3.4 + */ + void emitCounts( const KFileItemList & lst ); + + void emitMouseOver( const KFileItem * item ); + + /** + * Enables or disables the paste action. This depends both on + * the data in the clipboard and the number of files selected + * (pasting is only possible if not more than one file is selected). + */ + void updatePasteAction(); + + /** + * Change the icon size of the view. + * The view should call it initially. + * The view should also reimplement it, to update the icons. + */ + virtual void newIconSize( int size ); + + /** + * This is called by the actions that change the icon size. + * It stores the new size and calls newIconSize. + */ + void setIconSize( int size ); + + /** + * This is called by konqueror itself, when the "find" functionality is activated + */ + void setFindPart( KParts::ReadOnlyPart * part ); + + KParts::ReadOnlyPart * findPart() const { return m_findPart; } + + virtual const KFileItem * currentItem() = 0; // { return 0L; } + + virtual KFileItemList selectedFileItems() { return KFileItemList(); } + + /** + * Re-implemented for internal reasons. API is unaffected. All inheriting + * classes should re-implement @ref doCloseURL() instead instead of this one. + */ + bool closeURL (); + +signals: + + /** + * Emitted whenever the current URL is about to be changed. + */ + void aboutToOpenURL(); + + /** + * We emit this if we want a find part to be created for us. + * This happens when restoring from history + */ + void findOpen( KonqDirPart * ); + + /** + * We emit this _after_ a find part has been created for us. + * This also happens initially. + */ + void findOpened( KonqDirPart * ); + + /** + * We emit this to ask konq to close the find part + */ + void findClosed( KonqDirPart * ); + + /** + * Emitted as the part is updated with new items. + * Useful for informing plugins of changes in view. + */ + void itemsAdded( const KFileItemList& ); + + /** + * Emitted as the part is updated with these items. + * Useful for informing plugins of changes in view. + */ + void itemRemoved( const KFileItem* ); + + /** + * Emitted with the list of filtered-out items whenever + * a mime-based filter(s) is set. + */ + void itemsFilteredByMime( const KFileItemList& ); + +public slots: + + /** + * Re-implemented for internal reasons. API is unaffected. All inheriting + * classes should re-implement @ref doOpenURL() instead instead of this one. + */ + bool openURL (const KURL&); + + /** + * This is called either by the part's close button, or by the + * dir part itself, if entering a directory. It deletes the find + * part. + */ + void slotFindClosed(); + + /** + * Start the animated "K" during kfindpart's file search + */ + void slotStartAnimationSearching(); + + /** + * Start the animated "K" during kfindpart's file search + */ + void slotStopAnimationSearching(); + + void slotBackgroundSettings(); + + /** + * Called when the clipboard's data changes, to update the 'cut' icons + * Call this when the directory's listing is finished, to draw icons as cut. + */ + void slotClipboardDataChanged(); + + void slotIncIconSize(); + void slotDecIconSize(); + + void slotIconSizeToggled( bool ); + + // slots connected to the directory lister - or to the kfind interface + virtual void slotStarted() = 0; + virtual void slotCanceled() = 0; + virtual void slotCompleted() = 0; + virtual void slotNewItems( const KFileItemList& ) = 0; + virtual void slotDeleteItem( KFileItem * ) = 0; + virtual void slotRefreshItems( const KFileItemList& ) = 0; + virtual void slotClear() = 0; + virtual void slotRedirection( const KURL & ) = 0; + +private slots: + void slotIconChanged(int group); +protected: + /** + * Invoked from openURL to enable childern classes to + * handle open URL requests. + */ + virtual bool doOpenURL( const KURL& ) = 0; + virtual bool doCloseURL () = 0; + +protected: + + QString m_nameFilter; + QStringList m_filesToSelect; + + KonqPropsView * m_pProps; + + KAction *m_paIncIconSize; + KAction *m_paDecIconSize; + KToggleAction *m_paDefaultIcons; + KToggleAction *m_paHugeIcons; + KToggleAction *m_paLargeIcons; + KToggleAction *m_paMediumIcons; + KToggleAction *m_paSmallIcons; + + KParts::ReadOnlyPart * m_findPart; + KonqDirPartBrowserExtension * m_extension; + + // Remove all those in KDE4 + int m_iIconSize[5]; + KIO::filesize_t m_lDirSize; + uint m_lFileCount; + uint m_lDirCount; + +private: + void saveFindState( QDataStream& ); + void restoreFindState( QDataStream& ); + + void adjustIconSizes(); + + class KonqDirPartPrivate; + KonqDirPartPrivate* d; +}; + +class LIBKONQ_EXPORT KonqDirPartBrowserExtension : public KParts::BrowserExtension +{ +public: + KonqDirPartBrowserExtension( KonqDirPart* dirPart ) + : KParts::BrowserExtension( dirPart ) + , m_dirPart( dirPart ) + {} + + /** + * This calls saveState in KonqDirPart, and also takes care of the "find part". + * + * If your KonqDirPart-derived class needs to save and restore state, + * you should probably override KonqDirPart::saveState + * and KonqDirPart::restoreState, not the following methods. + */ + virtual void saveState( QDataStream &stream ); + virtual void restoreState( QDataStream &stream ); + +private: + KonqDirPart* m_dirPart; +}; + +#endif diff --git a/libkonq/konq_drag.cc b/libkonq/konq_drag.cc new file mode 100644 index 000000000..3333061c2 --- /dev/null +++ b/libkonq/konq_drag.cc @@ -0,0 +1,284 @@ +/* This file is part of the KDE projects + Copyright (C) 1998, 1999 Torben Weis <[email protected]> + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "konq_drag.h" +#include <kdebug.h> +#include <kurldrag.h> + +KonqIconDrag::KonqIconDrag( QWidget * dragSource, const char* name ) + : QIconDrag( dragSource, name ), + m_bCutSelection( false ) +{ +} + +const char* KonqIconDrag::format( int i ) const +{ + if ( i == 0 ) + return "application/x-qiconlist"; + else if ( i == 1 ) + return "text/uri-list"; + else if ( i == 2 ) + return "application/x-kde-cutselection"; + else if ( i == 3 ) + return "text/plain"; + else if ( i == 4 ) //These two are imporant because they may end up being format 0, + //which is what KonqDirPart::updatePasteAction() checks + return "text/plain;charset=ISO-8859-1"; + else if ( i == 5 ) //..as well as potentially for interoperability + return "text/plain;charset=UTF-8"; + // warning, don't add anything here without checking KonqIconDrag2 + + else return 0; +} + +QByteArray KonqIconDrag::encodedData( const char* mime ) const +{ + QByteArray a; + QCString mimetype( mime ); + if ( mimetype == "application/x-qiconlist" ) + a = QIconDrag::encodedData( mime ); + else if ( mimetype == "text/uri-list" ) { + QCString s = urls.join( "\r\n" ).latin1(); + if( urls.count() > 0 ) + s.append( "\r\n" ); + a.resize( s.length() + 1 ); // trailing zero + memcpy( a.data(), s.data(), s.length() + 1 ); + } + else if ( mimetype == "application/x-kde-cutselection" ) { + QCString s ( m_bCutSelection ? "1" : "0" ); + a.resize( s.length() + 1 ); // trailing zero + memcpy( a.data(), s.data(), s.length() + 1 ); + } + else if ( mimetype == "text/plain" ) { + if (!urls.isEmpty()) + { + QStringList uris; + for (QStringList::ConstIterator it = urls.begin(); it != urls.end(); ++it) + uris.append(KURLDrag::stringToUrl((*it).latin1()).prettyURL()); + QCString s = uris.join( "\n" ).local8Bit(); + if( uris.count() > 1 ) + s.append( "\n" ); + a.resize( s.length()); // no trailing zero in clipboard text + memcpy( a.data(), s.data(), s.length()); + } + } + else if ( mimetype.lower() == "text/plain;charset=iso-8859-1") + { + if (!urls.isEmpty()) + { + QStringList uris; + + for (QStringList::ConstIterator it = urls.begin(); it != urls.end(); ++it) + uris.append(KURLDrag::stringToUrl((*it).latin1()).url(0, 4)); // 4 for latin1 + + QCString s = uris.join( "\n" ).latin1(); + if( uris.count() > 1 ) + s.append( "\n" ); + a.resize( s.length()); + memcpy( a.data(), s.data(), s.length()); + } + } + else if ( mimetype.lower() == "text/plain;charset=utf-8") + { + if (!urls.isEmpty()) + { + QStringList uris; + for (QStringList::ConstIterator it = urls.begin(); it != urls.end(); ++it) + uris.append(KURLDrag::stringToUrl((*it).latin1()).prettyURL()); + QCString s = uris.join( "\n" ).utf8(); + if( uris.count() > 1 ) + s.append( "\n" ); + a.resize( s.length()); + memcpy( a.data(), s.data(), s.length()); + } + } + return a; +} + +bool KonqIconDrag::canDecode( const QMimeSource* e ) +{ + return e->provides( "application/x-qiconlist" ) || + e->provides( "text/uri-list" ) || + e->provides( "application/x-kde-cutselection" ); +} + +void KonqIconDrag::append( const QIconDragItem &item, const QRect &pr, + const QRect &tr, const QString &url ) +{ + QIconDrag::append( item, pr, tr ); + urls.append( url ); +} + +KonqIconDrag2::KonqIconDrag2( QWidget * dragSource ) + : KonqIconDrag( dragSource ) +{ +} + +void KonqIconDrag2::append( const QIconDragItem &item, const QRect &pr, + const QRect &tr, const QString& url, const KURL &mostLocalURL ) +{ + QString mostLocalURLStr = KURLDrag::urlToString(mostLocalURL); + m_kdeURLs.append( url ); + KonqIconDrag::append( item, pr, tr, mostLocalURLStr ); +} + +const char* KonqIconDrag2::format( int i ) const +{ + if ( i == 6 ) + return "application/x-kde-urilist"; + return KonqIconDrag::format( i ); +} + +QByteArray KonqIconDrag2::encodedData( const char* mime ) const +{ + QCString mimetype( mime ); + if ( mimetype == "application/x-kde-urilist" ) + { + QByteArray a; + int c=0; + for (QStringList::ConstIterator it = m_kdeURLs.begin(); it != m_kdeURLs.end(); ++it) { + QCString url = (*it).utf8(); + int l = url.length(); + a.resize(c+l+2); + memcpy(a.data()+c, url.data(), l); + memcpy(a.data()+c+l,"\r\n",2); + c += l+2; + } + a.resize(c+1); + a[c] = 0; + return a; + } + return KonqIconDrag::encodedData( mime ); +} + +// + +KonqDrag * KonqDrag::newDrag( const KURL::List & urls, bool cut, QWidget * dragSource, const char* name ) +{ + // See KURLDrag::newDrag + QStrList uris; + KURL::List::ConstIterator uit = urls.begin(); + KURL::List::ConstIterator uEnd = urls.end(); + // Get each URL encoded in utf8 - and since we get it in escaped + // form on top of that, .latin1() is fine. + for ( ; uit != uEnd ; ++uit ) + uris.append( KURLDrag::urlToString( *uit ).latin1() ); + return new KonqDrag( uris, cut, dragSource, name ); +} + +// urls must be already checked to have hostname in file URLs +KonqDrag::KonqDrag( const QStrList & urls, bool cut, QWidget * dragSource, const char* name ) + : QUriDrag( urls, dragSource, name ), + m_bCutSelection( cut ), m_urls( urls ) +{} + +// urls must be already checked to have hostname in file URLs +KonqDrag::KonqDrag( const KURL::List & urls, const KURL::List& mostLocalUrls, + bool cut, QWidget * dragSource ) + : QUriDrag( dragSource ), + m_bCutSelection( cut ) +{ + QStrList uris; + KURL::List::ConstIterator uit = urls.begin(); + KURL::List::ConstIterator uEnd = urls.end(); + // Get each URL encoded in utf8 - and since we get it in escaped + // form on top of that, .latin1() is fine. + for ( ; uit != uEnd ; ++uit ) + uris.append( KURLDrag::urlToString( *uit ).latin1() ); + setUris( uris ); // we give the KDE uris to QUriDrag. TODO: do the opposite in KDE4 and add a m_mostLocalUris member. + + uit = mostLocalUrls.begin(); + uEnd = mostLocalUrls.end(); + for ( ; uit != uEnd ; ++uit ) + m_urls.append( KURLDrag::urlToString( *uit ).latin1() ); + // we keep the most-local-uris in m_urls for exporting those as text/plain (for xmms) +} + +const char* KonqDrag::format( int i ) const +{ + if ( i == 0 ) + return "text/uri-list"; + else if ( i == 1 ) + return "application/x-kde-cutselection"; + else if ( i == 2 ) + return "text/plain"; + else if ( i == 3 ) + return "application/x-kde-urilist"; + else return 0; +} + +QByteArray KonqDrag::encodedData( const char* mime ) const +{ + QByteArray a; + QCString mimetype( mime ); + if ( mimetype == "text/uri-list" ) + { + // Code taken from QUriDrag::setUris + int c=0; + for (QStrListIterator it(m_urls); *it; ++it) { + int l = qstrlen(*it); + a.resize(c+l+2); + memcpy(a.data()+c,*it,l); + memcpy(a.data()+c+l,"\r\n",2); + c+=l+2; + } + a.resize(c+1); + a[c] = 0; + } + else if ( mimetype == "application/x-kde-urilist" ) + { + return QUriDrag::encodedData( "text/uri-list" ); + } + else if ( mimetype == "application/x-kde-cutselection" ) + { + QCString s ( m_bCutSelection ? "1" : "0" ); + a.resize( s.length() + 1 ); // trailing zero + memcpy( a.data(), s.data(), s.length() + 1 ); + } + else if ( mimetype == "text/plain" ) + { + QStringList uris; + for (QStrListIterator it(m_urls); *it; ++it) + uris.append(KURLDrag::stringToUrl(*it).prettyURL()); + QCString s = uris.join( "\n" ).local8Bit(); + if( uris.count() > 1 ) + s.append( "\n" ); + a.resize( s.length() + 1 ); // trailing zero + memcpy( a.data(), s.data(), s.length() + 1 ); + } + return a; +} + +// + +// Used for KonqIconDrag too + +bool KonqDrag::decodeIsCutSelection( const QMimeSource *e ) +{ + QByteArray a = e->encodedData( "application/x-kde-cutselection" ); + if ( a.isEmpty() ) + return false; + else + { + kdDebug(1203) << "KonqDrag::decodeIsCutSelection : a=" << QCString(a.data(), a.size() + 1) << endl; + return (a.at(0) == '1'); // true if 1 + } +} + +#include "konq_drag.moc" diff --git a/libkonq/konq_drag.h b/libkonq/konq_drag.h new file mode 100644 index 000000000..dca79676f --- /dev/null +++ b/libkonq/konq_drag.h @@ -0,0 +1,122 @@ +/* This file is part of the KDE project + Copyright (C) 1998, 1999 Torben Weis <[email protected]> + + 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 __konqdrag_h__ +#define __konqdrag_h__ + +#include <qdragobject.h> +#include <qrect.h> +#include <qstring.h> +#include <qiconview.h> + +#include <libkonq_export.h> + +#include <kurl.h> + +/***************************************************************************** + * + * Class KonqIconDrag + * + *****************************************************************************/ + +// Clipboard/dnd data for: Icons + URLS + isCut +class LIBKONQ_EXPORT KonqIconDrag : public QIconDrag +{ + Q_OBJECT + +public: + KonqIconDrag( QWidget * dragSource, const char* name = 0 ); + virtual ~KonqIconDrag() {} + + const char* format( int i ) const; + QByteArray encodedData( const char* mime ) const; + + void append( const QIconDragItem &item, const QRect &pr, + const QRect &tr, const QString &url ); + + void setMoveSelection( bool move ) { m_bCutSelection = move; } + + static bool canDecode( const QMimeSource* e ); + +protected: // KDE4: private. And d pointer... + QStringList urls; + bool m_bCutSelection; +}; + +/** + * Clipboard/dnd data for: Icons + URLs + MostLocal URLs + isCut + * KDE4: merge with KonqIconDrag + * @since 3.5 + */ +class LIBKONQ_EXPORT KonqIconDrag2 : public KonqIconDrag +{ + Q_OBJECT + +public: + KonqIconDrag2( QWidget * dragSource ); + virtual ~KonqIconDrag2() {} + + virtual const char* format( int i ) const; + virtual QByteArray encodedData( const char* mime ) const; + + void append( const QIconDragItem &item, const QRect &pr, + const QRect &tr, const QString &url, const KURL &mostLocalURL ); + +private: + QStringList m_kdeURLs; +}; + +// Clipboard/dnd data for: URLS + isCut +class LIBKONQ_EXPORT KonqDrag : public QUriDrag +{ +public: + // KDE4: remove, use KonqDrag constructor instead + static KonqDrag * newDrag( const KURL::List & urls, + bool move, QWidget * dragSource = 0, const char* name = 0 ); + + /** + * Create a KonqDrag object. + * @param urls a list of URLs, which can use KDE-specific protocols, like system:/ + * @param mostLocalUrls a list of URLs, which should be resolved to most-local urls, i.e. file:/ + * @param cut false for copying, true for "cutting" + * @param dragSource parent object + * @since 3.5 + */ + KonqDrag( const KURL::List & urls, const KURL::List& mostLocalUrls, bool cut, QWidget * dragSource = 0 ); + +protected: + // KDE4: remove + KonqDrag( const QStrList & urls, bool cut, QWidget * dragSource, const char* name ); + +public: + virtual ~KonqDrag() {} + + virtual const char* format( int i ) const; + virtual QByteArray encodedData( const char* mime ) const; + + void setMoveSelection( bool move ) { m_bCutSelection = move; } + + // Returns true if the data was cut (used for KonqIconDrag too) + static bool decodeIsCutSelection( const QMimeSource *e ); + +protected: // KDE4: private. And d pointer... + bool m_bCutSelection; + QStrList m_urls; // this is set to the "most local urls". KDE4: KURL::List +}; + +#endif diff --git a/libkonq/konq_events.cc b/libkonq/konq_events.cc new file mode 100644 index 000000000..f608a8cdc --- /dev/null +++ b/libkonq/konq_events.cc @@ -0,0 +1,8 @@ + +#include <kfileitem.h> + +#include "konq_events.h" + +const char *KonqFileSelectionEvent::s_fileItemSelectionEventName = "Konqueror/FileSelection"; +const char *KonqFileMouseOverEvent::s_fileItemMouseOverEventName = "Konqueror/FileMouseOver"; +const char *KonqConfigEvent::s_configEventName = "Konqueror/Config"; diff --git a/libkonq/konq_events.h b/libkonq/konq_events.h new file mode 100644 index 000000000..b76469717 --- /dev/null +++ b/libkonq/konq_events.h @@ -0,0 +1,70 @@ +#ifndef __konq_events_h__ +#define __konq_events_h__ + +#include <kparts/event.h> +#include <qptrlist.h> +#include <libkonq_export.h> + +namespace KParts +{ + class ReadOnlyPart; +} + +class KConfig; +class KFileItem; +typedef QPtrList<KFileItem> KFileItemList; + +class LIBKONQ_EXPORT KonqFileSelectionEvent : public KParts::Event +{ +public: + KonqFileSelectionEvent( const KFileItemList &selection, KParts::ReadOnlyPart *part ) : KParts::Event( s_fileItemSelectionEventName ), m_selection( selection ), m_part( part ) {} + + KFileItemList selection() const { return m_selection; } + KParts::ReadOnlyPart *part() const { return m_part; } + + static bool test( const QEvent *event ) { return KParts::Event::test( event, s_fileItemSelectionEventName ); } + +private: + static const char *s_fileItemSelectionEventName; + + KFileItemList m_selection; + KParts::ReadOnlyPart *m_part; +}; + +class LIBKONQ_EXPORT KonqFileMouseOverEvent : public KParts::Event +{ +public: + KonqFileMouseOverEvent( const KFileItem *item, KParts::ReadOnlyPart *part ) : KParts::Event( s_fileItemMouseOverEventName ), m_item( item ), m_part( part ) {} + + const KFileItem* item() const { return m_item; } + KParts::ReadOnlyPart *part() const { return m_part; } + + static bool test( const QEvent *event ) { return KParts::Event::test( event, s_fileItemMouseOverEventName ); } + +private: + static const char *s_fileItemMouseOverEventName; + + const KFileItem* m_item; + KParts::ReadOnlyPart *m_part; +}; + +class LIBKONQ_EXPORT KonqConfigEvent : public KParts::Event +{ +public: + KonqConfigEvent( KConfig *config, const QString &prefix, bool save ) : KParts::Event( s_configEventName ), m_config( config ), m_prefix( prefix ), m_save( save ) {} + + KConfig * config() const { return m_config; } + QString prefix() const { return m_prefix; } + bool save() const { return m_save; } + + static bool test( const QEvent *event ) { return KParts::Event::test( event, s_configEventName ); } + +private: + static const char *s_configEventName; + + KConfig *m_config; + QString m_prefix; + bool m_save; +}; + +#endif diff --git a/libkonq/konq_faviconmgr.cc b/libkonq/konq_faviconmgr.cc new file mode 100644 index 000000000..782beca91 --- /dev/null +++ b/libkonq/konq_faviconmgr.cc @@ -0,0 +1,57 @@ +/* This file is part of the KDE project + Copyright (C) 2000 Malte Starostik <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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 <dcopclient.h> +#include <kapplication.h> +#include <kconfig.h> +#include <kglobal.h> +#include <kmimetype.h> + +#include "konq_faviconmgr.moc" + +KonqFavIconMgr::KonqFavIconMgr(QObject *parent, const char *name) + : QObject(parent, name), + DCOPObject("KonqFavIconMgr") +{ + connectDCOPSignal("kded", "favicons", + "iconChanged(bool, QString, QString)", + "notifyChange(bool, QString, QString)", false); +} + +QString KonqFavIconMgr::iconForURL(const QString &url) +{ + return KMimeType::favIconForURL( KURL(url) ); +} + +void KonqFavIconMgr::setIconForURL(const KURL &url, const KURL &iconURL) +{ + QByteArray data; + QDataStream str(data, IO_WriteOnly); + str << url << iconURL; + kapp->dcopClient()->send("kded", "favicons", "setIconForURL(KURL, KURL)", data); +} + +void KonqFavIconMgr::downloadHostIcon(const KURL &url) +{ + QByteArray data; + QDataStream str(data, IO_WriteOnly); + str << url; + kapp->dcopClient()->send("kded", "favicons", "downloadHostIcon(KURL)", data); +} + diff --git a/libkonq/konq_faviconmgr.h b/libkonq/konq_faviconmgr.h new file mode 100644 index 000000000..66175f682 --- /dev/null +++ b/libkonq/konq_faviconmgr.h @@ -0,0 +1,70 @@ +/* This file is part of the KDE project + Copyright (C) 1999 Malte Starostik <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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. +*/ + +#ifndef __konq_faviconmgr_h__ +#define __konq_faviconmgr_h__ + +#include <dcopobject.h> +#include <kurl.h> +#include <libkonq_export.h> + +/** + * Maintains a list of custom icons per URL. This is only a stub + * for the "favicons" KDED Module + */ +class LIBKONQ_EXPORT KonqFavIconMgr : public QObject, public DCOPObject +{ + Q_OBJECT + K_DCOP +public: + /** + * Constructor. + */ + KonqFavIconMgr(QObject *parent = 0, const char *name = 0); + + /** + * Downloads an icon from @p iconURL and associates @p url with it. + */ + static void setIconForURL(const KURL &url, const KURL &iconURL); + + /** + * Downloads /favicon.ico from the host of @p url and associates all + * URLs on that host with it + * (unless a more specific entry for a URL exists) + */ + static void downloadHostIcon(const KURL &url); + + /** + * Looks up an icon for @p url and returns its name if found + * or QString::null otherwise + */ + static QString iconForURL(const QString &url); + +k_dcop: + /** + * an icon changed, updates the combo box + */ + virtual ASYNC notifyChange( bool, QString, QString ) = 0; + +signals: + void changed(); +}; + +#endif + diff --git a/libkonq/konq_filetip.cc b/libkonq/konq_filetip.cc new file mode 100644 index 000000000..c9c661c57 --- /dev/null +++ b/libkonq/konq_filetip.cc @@ -0,0 +1,300 @@ +/* This file is part of the KDE projects + Copyright (C) 1998, 1999 Torben Weis <[email protected]> + Copyright (C) 2000, 2001, 2002 David Faure <[email protected]> + Copyright (C) 2004 Martin Koller <[email protected]> + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <konq_filetip.h> + +#include <kfileitem.h> +#include <kglobalsettings.h> +#include <kstandarddirs.h> +#include <kapplication.h> + +#include <qlabel.h> +#include <qtooltip.h> +#include <qlayout.h> +#include <qpainter.h> +#include <qscrollview.h> +#include <qtimer.h> + +#include <fixx11h.h> +//-------------------------------------------------------------------------------- + +KonqFileTip::KonqFileTip( QScrollView* parent ) + : QFrame( 0, 0, WStyle_Customize | WStyle_NoBorder | WStyle_Tool | WStyle_StaysOnTop | WX11BypassWM ), + m_on( false ), + m_preview( false ), + m_filter( false ), + m_corner( 0 ), + m_num( 0 ), + m_view( parent ), + m_item( 0 ), + m_previewJob( 0 ) +{ + m_iconLabel = new QLabel(this); + m_textLabel = new QLabel(this); + m_textLabel->setAlignment(Qt::AlignAuto | Qt::AlignTop); + + QGridLayout* layout = new QGridLayout(this, 1, 2, 8, 0); + layout->addWidget(m_iconLabel, 0, 0); + layout->addWidget(m_textLabel, 0, 1); + layout->setResizeMode(QLayout::Fixed); + + setPalette( QToolTip::palette() ); + setMargin( 1 ); + setFrameStyle( QFrame::Plain | QFrame::Box ); + + m_timer = new QTimer(this); + + hide(); +} + +KonqFileTip::~KonqFileTip() +{ + if ( m_previewJob ) { + m_previewJob->kill(); + m_previewJob = 0; + } +} + +void KonqFileTip::setPreview(bool on) +{ + m_preview = on; + if(on) + m_iconLabel->show(); + else + m_iconLabel->hide(); +} + +void KonqFileTip::setOptions( bool on, bool preview, int num ) +{ + setPreview(preview); + m_on = on; + m_num = num; +} + +void KonqFileTip::setItem( KFileItem *item, const QRect &rect, const QPixmap *pixmap ) +{ + hideTip(); + + if (!m_on) return; + + if ( m_previewJob ) { + m_previewJob->kill(); + m_previewJob = 0; + } + + m_rect = rect; + m_item = item; + + if ( m_item ) { + if (m_preview) { + if ( pixmap ) + m_iconLabel->setPixmap( *pixmap ); + else + m_iconLabel->setPixmap( QPixmap() ); + } + + // Don't start immediately, because the user could move the mouse over another item + // This avoids a quick sequence of started preview-jobs + m_timer->disconnect( this ); + connect(m_timer, SIGNAL(timeout()), this, SLOT(startDelayed())); + m_timer->start( 300, true ); + } +} + +void KonqFileTip::reposition() +{ + if ( m_rect.isEmpty() || !m_view || !m_view->viewport() ) return; + + QRect rect = m_rect; + QPoint off = m_view->viewport()->mapToGlobal( m_view->contentsToViewport( rect.topRight() ) ); + rect.moveTopRight( off ); + + QPoint pos = rect.center(); + // m_corner: + // 0: upperleft + // 1: upperright + // 2: lowerleft + // 3: lowerright + // 4+: none + m_corner = 0; + // should the tooltip be shown to the left or to the right of the ivi ? + QRect desk = KGlobalSettings::desktopGeometry(rect.center()); + if (rect.center().x() + width() > desk.right()) + { + // to the left + if (pos.x() - width() < 0) { + pos.setX(0); + m_corner = 4; + } else { + pos.setX( pos.x() - width() ); + m_corner = 1; + } + } + // should the tooltip be shown above or below the ivi ? + if (rect.bottom() + height() > desk.bottom()) + { + // above + pos.setY( rect.top() - height() ); + m_corner += 2; + } + else pos.setY( rect.bottom() + 1 ); + + move( pos ); + update(); +} + +void KonqFileTip::gotPreview( const KFileItem* item, const QPixmap& pixmap ) +{ + m_previewJob = 0; + if (item != m_item) return; + + m_iconLabel -> setPixmap(pixmap); +} + +void KonqFileTip::gotPreviewResult() +{ + m_previewJob = 0; +} + +void KonqFileTip::drawContents( QPainter *p ) +{ + static const char * const names[] = { + "arrow_topleft", + "arrow_topright", + "arrow_bottomleft", + "arrow_bottomright" + }; + + if (m_corner >= 4) { // 4 is empty, so don't draw anything + QFrame::drawContents( p ); + return; + } + + if ( m_corners[m_corner].isNull()) + m_corners[m_corner].load( locate( "data", QString::fromLatin1( "konqueror/pics/%1.png" ).arg( names[m_corner] ) ) ); + + QPixmap &pix = m_corners[m_corner]; + + switch ( m_corner ) + { + case 0: + p->drawPixmap( 3, 3, pix ); + break; + case 1: + p->drawPixmap( width() - pix.width() - 3, 3, pix ); + break; + case 2: + p->drawPixmap( 3, height() - pix.height() - 3, pix ); + break; + case 3: + p->drawPixmap( width() - pix.width() - 3, height() - pix.height() - 3, pix ); + break; + } + + QFrame::drawContents( p ); +} + +void KonqFileTip::setFilter( bool enable ) +{ + if ( enable == m_filter ) return; + + if ( enable ) { + kapp->installEventFilter( this ); + QApplication::setGlobalMouseTracking( true ); + } + else { + QApplication::setGlobalMouseTracking( false ); + kapp->removeEventFilter( this ); + } + m_filter = enable; +} + +void KonqFileTip::showTip() +{ + QString text = m_item->getToolTipText(m_num); + + if ( text.isEmpty() ) return; + + m_timer->disconnect( this ); + connect(m_timer, SIGNAL(timeout()), this, SLOT(hideTip())); + m_timer->start( 15000, true ); + + m_textLabel->setText( text ); + + setFilter( true ); + + reposition(); + show(); +} + +void KonqFileTip::hideTip() +{ + m_timer->stop(); + setFilter( false ); + if ( isShown() && m_view && m_view->viewport() && + (m_view->horizontalScrollBar()->isShown() || m_view->verticalScrollBar()->isShown()) ) + m_view->viewport()->update(); + hide(); +} +void KonqFileTip::startDelayed() +{ + if ( m_preview ) { + KFileItemList oneItem; + oneItem.append( m_item ); + + m_previewJob = KIO::filePreview( oneItem, 256, 256, 64, 70, true, true, 0); + connect( m_previewJob, SIGNAL( gotPreview( const KFileItem *, const QPixmap & ) ), + this, SLOT( gotPreview( const KFileItem *, const QPixmap & ) ) ); + connect( m_previewJob, SIGNAL( result( KIO::Job * ) ), + this, SLOT( gotPreviewResult() ) ); + } + + m_timer->disconnect( this ); + connect(m_timer, SIGNAL(timeout()), this, SLOT(showTip())); + m_timer->start( 400, true ); +} + +void KonqFileTip::resizeEvent( QResizeEvent* event ) +{ + QFrame::resizeEvent(event); + reposition(); +} + +bool KonqFileTip::eventFilter( QObject *, QEvent *e ) +{ + switch ( e->type() ) + { + case QEvent::Leave: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::KeyPress: + case QEvent::KeyRelease: + case QEvent::FocusIn: + case QEvent::FocusOut: + case QEvent::Wheel: + hideTip(); + default: break; + } + + return false; +} + +#include "konq_filetip.moc" diff --git a/libkonq/konq_filetip.h b/libkonq/konq_filetip.h new file mode 100644 index 000000000..fc878c074 --- /dev/null +++ b/libkonq/konq_filetip.h @@ -0,0 +1,97 @@ +/* This file is part of the KDE projects + Copyright (C) 1998, 1999 Torben Weis <[email protected]> + Copyright (C) 2000, 2001, 2002 David Faure <[email protected]> + Copyright (C) 2004 Martin Koller <[email protected]> + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KONQ_FILETIP_H +#define KONQ_FILETIP_H + +#include <qframe.h> +#include <qpixmap.h> +#include <kio/previewjob.h> + +#include <libkonq_export.h> + +class KFileItem; +class QLabel; +class QScrollView; +class QTimer; + +//-------------------------------------------------------------------------------- + +class LIBKONQ_EXPORT KonqFileTip : public QFrame +{ + Q_OBJECT + + public: + KonqFileTip( QScrollView *parent ); + ~KonqFileTip(); + + void setPreview(bool on); + + /** + @param on show tooltip at all + @param preview include file preview in tooltip + @param num the number of tooltip texts to get from KFileItem + */ + void setOptions( bool on, bool preview, int num ); + + /** Set the item from which to get the tip information + @param item the item from which to get the tip information + @param rect the rectangle around which the tip will be shown + @param pixmap the pixmap to be shown. If 0, no pixmap is shown + */ + void setItem( KFileItem *item, const QRect &rect = QRect(), + const QPixmap *pixmap = 0 ); + + virtual bool eventFilter( QObject *, QEvent *e ); + + protected: + virtual void drawContents( QPainter *p ); + virtual void resizeEvent( QResizeEvent * ); + + private slots: + void gotPreview( const KFileItem*, const QPixmap& ); + void gotPreviewResult(); + + void startDelayed(); + void showTip(); + void hideTip(); + + private: + void setFilter( bool enable ); + + void reposition(); + + QLabel* m_iconLabel; + QLabel* m_textLabel; + bool m_on : 1; + bool m_preview : 1; // shall the preview icon be shown + bool m_filter : 1; + QPixmap m_corners[4]; + int m_corner; + int m_num; + QScrollView* m_view; + KFileItem* m_item; + KIO::PreviewJob* m_previewJob; + QRect m_rect; + QTimer* m_timer; +}; + +#endif diff --git a/libkonq/konq_historycomm.cc b/libkonq/konq_historycomm.cc new file mode 100644 index 000000000..0875a7945 --- /dev/null +++ b/libkonq/konq_historycomm.cc @@ -0,0 +1,41 @@ +#include "konq_historycomm.h" + +bool KonqHistoryEntry::marshalURLAsStrings; + +// QDataStream operators (read and write a KonqHistoryEntry +// from/into a QDataStream) +QDataStream& operator<< (QDataStream& s, const KonqHistoryEntry& e) { + if (KonqHistoryEntry::marshalURLAsStrings) + s << e.url.url(); + else + s << e.url; + + s << e.typedURL; + s << e.title; + s << e.numberOfTimesVisited; + s << e.firstVisited; + s << e.lastVisited; + + return s; +} + +QDataStream& operator>> (QDataStream& s, KonqHistoryEntry& e) { + if (KonqHistoryEntry::marshalURLAsStrings) + { + QString url; + s >> url; + e.url = url; + } + else + { + s>>e.url; + } + + s >> e.typedURL; + s >> e.title; + s >> e.numberOfTimesVisited; + s >> e.firstVisited; + s >> e.lastVisited; + + return s; +} diff --git a/libkonq/konq_historycomm.h b/libkonq/konq_historycomm.h new file mode 100644 index 000000000..e02e10a61 --- /dev/null +++ b/libkonq/konq_historycomm.h @@ -0,0 +1,77 @@ +/* This file is part of the KDE project + Copyright (C) 2000 Carsten Pfeiffer <[email protected]> + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KONQ_HISTORYCOMM_H +#define KONQ_HISTORYCOMM_H + +#include <qdatetime.h> +#include <qstringlist.h> + +#include <dcopobject.h> +#include <kurl.h> + +class KonqHistoryEntry +{ +public: + //Should URLs be marshaled as strings (for DCOP, V2 history format)? + static bool marshalURLAsStrings; + KonqHistoryEntry() + : numberOfTimesVisited(1) {} + + KURL url; + QString typedURL; + QString title; + Q_UINT32 numberOfTimesVisited; + QDateTime firstVisited; + QDateTime lastVisited; +}; + + +// QDataStream operators (read and write a KonqHistoryEntry +// from/into a QDataStream +QDataStream& operator<< (QDataStream& s, const KonqHistoryEntry& e); +QDataStream& operator>> (QDataStream& s, KonqHistoryEntry& e); + +/////////////////////////////////////////////////////////////////// + + +/** + * DCOP Methods for KonqHistoryManager. Has to be in a separate file, because + * dcopidl2cpp barfs on every second construct ;( + * Implementations of the pure virtual methods are in KonqHistoryManager + */ +class KonqHistoryComm : public DCOPObject +{ + K_DCOP + +protected: + KonqHistoryComm( QCString objId ) : DCOPObject( objId ) {} + +k_dcop: + virtual ASYNC notifyHistoryEntry( KonqHistoryEntry e, QCString saveId) = 0; + virtual ASYNC notifyMaxCount( Q_UINT32 count, QCString saveId ) = 0; + virtual ASYNC notifyMaxAge( Q_UINT32 days, QCString saveId ) = 0; + virtual ASYNC notifyClear( QCString saveId ) = 0; + virtual ASYNC notifyRemove( KURL url, QCString saveId ) = 0; + virtual ASYNC notifyRemove( KURL::List url, QCString saveId ) = 0; + virtual QStringList allURLs() const = 0; + +}; + +#endif // KONQ_HISTORYCOMM_H diff --git a/libkonq/konq_historymgr.cc b/libkonq/konq_historymgr.cc new file mode 100644 index 000000000..72c6be3de --- /dev/null +++ b/libkonq/konq_historymgr.cc @@ -0,0 +1,733 @@ +/* This file is part of the KDE project + Copyright (C) 2000,2001 Carsten Pfeiffer <[email protected]> + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "konq_historymgr.h" + +#include <dcopclient.h> + +#include <kapplication.h> +#include <kdebug.h> +#include <ksavefile.h> +#include <ksimpleconfig.h> +#include <kstandarddirs.h> + +#include <zlib.h> + +#include "konqbookmarkmanager.h" + +const Q_UINT32 KonqHistoryManager::s_historyVersion = 3; + +KonqHistoryManager::KonqHistoryManager( QObject *parent, const char *name ) + : KParts::HistoryProvider( parent, name ), + KonqHistoryComm( "KonqHistoryManager" ) +{ + m_updateTimer = new QTimer( this ); + + // defaults + KConfig *config = KGlobal::config(); + KConfigGroupSaver cs( config, "HistorySettings" ); + m_maxCount = config->readNumEntry( "Maximum of History entries", 500 ); + m_maxCount = QMAX( 1, m_maxCount ); + m_maxAgeDays = config->readNumEntry( "Maximum age of History entries", 90); + + m_history.setAutoDelete( true ); + m_filename = locateLocal( "data", + QString::fromLatin1("konqueror/konq_history" )); + + if ( !kapp->dcopClient()->isAttached() ) + kapp->dcopClient()->attach(); + + + // take care of the completion object + m_pCompletion = new KCompletion; + m_pCompletion->setOrder( KCompletion::Weighted ); + + // and load the history + loadHistory(); + + connect( m_updateTimer, SIGNAL( timeout() ), SLOT( slotEmitUpdated() )); +} + + +KonqHistoryManager::~KonqHistoryManager() +{ + delete m_pCompletion; + clearPending(); +} + +bool KonqHistoryManager::isSenderOfBroadcast() +{ + DCOPClient *dc = callingDcopClient(); + return !dc || (dc->senderId() == dc->appId()); +} + +// loads the entire history +bool KonqHistoryManager::loadHistory() +{ + clearPending(); + m_history.clear(); + m_pCompletion->clear(); + + QFile file( m_filename ); + if ( !file.open( IO_ReadOnly ) ) { + if ( file.exists() ) + kdWarning() << "Can't open " << file.name() << endl; + + // try to load the old completion history + bool ret = loadFallback(); + emit loadingFinished(); + return ret; + } + + QDataStream fileStream( &file ); + QByteArray data; // only used for version == 2 + // we construct the stream object now but fill in the data later. + // thanks to QBA's explicit sharing this works :) + QDataStream crcStream( data, IO_ReadOnly ); + + if ( !fileStream.atEnd() ) { + Q_UINT32 version; + fileStream >> version; + + QDataStream *stream = &fileStream; + + bool crcChecked = false; + bool crcOk = false; + + if ( version == 2 || version == 3) { + Q_UINT32 crc; + crcChecked = true; + fileStream >> crc >> data; + crcOk = crc32( 0, reinterpret_cast<unsigned char *>( data.data() ), data.size() ) == crc; + stream = &crcStream; // pick up the right stream + } + + if ( version == 3 ) + { + //Use KURL marshalling for V3 format. + KonqHistoryEntry::marshalURLAsStrings = false; + } + + if ( version != 0 && version < 3 ) //Versions 1,2 (but not 0) are also valid + { + //Turn on backwards compatibility mode.. + KonqHistoryEntry::marshalURLAsStrings = true; + // it doesn't make sense to save to save maxAge and maxCount in the + // binary file, this would make backups impossible (they would clear + // themselves on startup, because all entries expire). + // [But V1 and V2 formats did it, so we do a dummy read] + Q_UINT32 dummy; + *stream >> dummy; + *stream >> dummy; + + //OK. + version = 3; + } + + if ( s_historyVersion != version || ( crcChecked && !crcOk ) ) { + kdWarning() << "The history version doesn't match, aborting loading" << endl; + file.close(); + emit loadingFinished(); + return false; + } + + + while ( !stream->atEnd() ) { + KonqHistoryEntry *entry = new KonqHistoryEntry; + Q_CHECK_PTR( entry ); + *stream >> *entry; + // kdDebug(1203) << "## loaded entry: " << entry->url << ", Title: " << entry->title << endl; + m_history.append( entry ); + QString urlString2 = entry->url.prettyURL(); + + addToCompletion( urlString2, entry->typedURL, entry->numberOfTimesVisited ); + + // and fill our baseclass. + QString urlString = entry->url.url(); + KParts::HistoryProvider::insert( urlString ); + // DF: also insert the "pretty" version if different + // This helps getting 'visited' links on websites which don't use fully-escaped urls. + + if ( urlString != urlString2 ) + KParts::HistoryProvider::insert( urlString2 ); + } + + kdDebug(1203) << "## loaded: " << m_history.count() << " entries." << endl; + + m_history.sort(); + adjustSize(); + } + + + //This is important - we need to switch to a consistent marshalling format for + //communicating between different konqueror instances. Since during an upgrade + //some "old" copies may still running, we use the old format for the DCOP transfers. + //This doesn't make that much difference performance-wise for single entries anyway. + KonqHistoryEntry::marshalURLAsStrings = true; + + + // Theoretically, we should emit update() here, but as we only ever + // load items on startup up to now, this doesn't make much sense. Same + // thing for the above loadFallback(). + // emit KParts::HistoryProvider::update( some list ); + + + + file.close(); + emit loadingFinished(); + + return true; +} + + +// saves the entire history +bool KonqHistoryManager::saveHistory() +{ + KSaveFile file( m_filename ); + if ( file.status() != 0 ) { + kdWarning() << "Can't open " << file.name() << endl; + return false; + } + + QDataStream *fileStream = file.dataStream(); + *fileStream << s_historyVersion; + + QByteArray data; + QDataStream stream( data, IO_WriteOnly ); + + //We use KURL for marshalling URLs in entries in the V3 + //file format + KonqHistoryEntry::marshalURLAsStrings = false; + QPtrListIterator<KonqHistoryEntry> it( m_history ); + KonqHistoryEntry *entry; + while ( (entry = it.current()) ) { + stream << *entry; + ++it; + } + + //For DCOP, transfer strings instead - wire compat. + KonqHistoryEntry::marshalURLAsStrings = true; + + Q_UINT32 crc = crc32( 0, reinterpret_cast<unsigned char *>( data.data() ), data.size() ); + *fileStream << crc << data; + + file.close(); + + return true; +} + + +void KonqHistoryManager::adjustSize() +{ + KonqHistoryEntry *entry = m_history.getFirst(); + + while ( m_history.count() > m_maxCount || isExpired( entry ) ) { + removeFromCompletion( entry->url.prettyURL(), entry->typedURL ); + + QString urlString = entry->url.url(); + KParts::HistoryProvider::remove( urlString ); + + addToUpdateList( urlString ); + + emit entryRemoved( m_history.getFirst() ); + m_history.removeFirst(); // deletes the entry + + entry = m_history.getFirst(); + } +} + + +void KonqHistoryManager::addPending( const KURL& url, const QString& typedURL, + const QString& title ) +{ + addToHistory( true, url, typedURL, title ); +} + +void KonqHistoryManager::confirmPending( const KURL& url, + const QString& typedURL, + const QString& title ) +{ + addToHistory( false, url, typedURL, title ); +} + + +void KonqHistoryManager::addToHistory( bool pending, const KURL& _url, + const QString& typedURL, + const QString& title ) +{ + kdDebug(1203) << "## addToHistory: " << _url.prettyURL() << " Typed URL: " << typedURL << ", Title: " << title << endl; + + if ( filterOut( _url ) ) // we only want remote URLs + return; + + // http URLs without a path will get redirected immediately to url + '/' + if ( _url.path().isEmpty() && _url.protocol().startsWith("http") ) + return; + + KURL url( _url ); + bool hasPass = url.hasPass(); + url.setPass( QString::null ); // No password in the history, especially not in the completion! + url.setHost( url.host().lower() ); // All host parts lower case + KonqHistoryEntry entry; + QString u = url.prettyURL(); + entry.url = url; + if ( (u != typedURL) && !hasPass ) + entry.typedURL = typedURL; + + // we only keep the title if we are confirming an entry. Otherwise, + // we might get bogus titles from the previous url (actually it's just + // konqueror's window caption). + if ( !pending && u != title ) + entry.title = title; + entry.firstVisited = QDateTime::currentDateTime(); + entry.lastVisited = entry.firstVisited; + + // always remove from pending if available, otherwise the else branch leaks + // if the map already contains an entry for this key. + QMapIterator<QString,KonqHistoryEntry*> it = m_pending.find( u ); + if ( it != m_pending.end() ) { + delete it.data(); + m_pending.remove( it ); + } + + if ( !pending ) { + if ( it != m_pending.end() ) { + // we make a pending entry official, so we just have to update + // and not increment the counter. No need to care about + // firstVisited, as this is not taken into account on update. + entry.numberOfTimesVisited = 0; + } + } + + else { + // We add a copy of the current history entry of the url to the + // pending list, so that we can restore it if the user canceled. + // If there is no entry for the url yet, we just store the url. + KonqHistoryEntry *oldEntry = findEntry( url ); + m_pending.insert( u, oldEntry ? + new KonqHistoryEntry( *oldEntry ) : 0L ); + } + + // notify all konqueror instances about the entry + emitAddToHistory( entry ); +} + +// interface of KParts::HistoryManager +// Usually, we only record the history for non-local URLs (i.e. filterOut() +// returns false). But when using the HistoryProvider interface, we record +// exactly those filtered-out urls. +// Moreover, we don't get any pending/confirming entries, just one insert() +void KonqHistoryManager::insert( const QString& url ) +{ + KURL u ( url ); + if ( !filterOut( u ) || u.protocol() == "about" ) { // remote URL + return; + } + // Local URL -> add to history + KonqHistoryEntry entry; + entry.url = u; + entry.firstVisited = QDateTime::currentDateTime(); + entry.lastVisited = entry.firstVisited; + emitAddToHistory( entry ); +} + +void KonqHistoryManager::emitAddToHistory( const KonqHistoryEntry& entry ) +{ + QByteArray data; + QDataStream stream( data, IO_WriteOnly ); + stream << entry << objId(); + // Protection against very long urls (like data:) + if ( data.size() > 4096 ) + return; + kapp->dcopClient()->send( "konqueror*", "KonqHistoryManager", + "notifyHistoryEntry(KonqHistoryEntry, QCString)", + data ); +} + + +void KonqHistoryManager::removePending( const KURL& url ) +{ + // kdDebug(1203) << "## Removing pending... " << url.prettyURL() << endl; + + if ( url.isLocalFile() ) + return; + + QMapIterator<QString,KonqHistoryEntry*> it = m_pending.find( url.prettyURL() ); + if ( it != m_pending.end() ) { + KonqHistoryEntry *oldEntry = it.data(); // the old entry, may be 0L + emitRemoveFromHistory( url ); // remove the current pending entry + + if ( oldEntry ) // we had an entry before, now use that instead + emitAddToHistory( *oldEntry ); + + delete oldEntry; + m_pending.remove( it ); + } +} + +// clears the pending list and makes sure the entries get deleted. +void KonqHistoryManager::clearPending() +{ + QMapIterator<QString,KonqHistoryEntry*> it = m_pending.begin(); + while ( it != m_pending.end() ) { + delete it.data(); + ++it; + } + m_pending.clear(); +} + +void KonqHistoryManager::emitRemoveFromHistory( const KURL& url ) +{ + QByteArray data; + QDataStream stream( data, IO_WriteOnly ); + stream << url << objId(); + kapp->dcopClient()->send( "konqueror*", "KonqHistoryManager", + "notifyRemove(KURL, QCString)", data ); +} + +void KonqHistoryManager::emitRemoveFromHistory( const KURL::List& urls ) +{ + QByteArray data; + QDataStream stream( data, IO_WriteOnly ); + stream << urls << objId(); + kapp->dcopClient()->send( "konqueror*", "KonqHistoryManager", + "notifyRemove(KURL::List, QCString)", data ); +} + +void KonqHistoryManager::emitClear() +{ + QByteArray data; + QDataStream stream( data, IO_WriteOnly ); + stream << objId(); + kapp->dcopClient()->send( "konqueror*", "KonqHistoryManager", + "notifyClear(QCString)", data ); +} + +void KonqHistoryManager::emitSetMaxCount( Q_UINT32 count ) +{ + QByteArray data; + QDataStream stream( data, IO_WriteOnly ); + stream << count << objId(); + kapp->dcopClient()->send( "konqueror*", "KonqHistoryManager", + "notifyMaxCount(Q_UINT32, QCString)", data ); +} + +void KonqHistoryManager::emitSetMaxAge( Q_UINT32 days ) +{ + QByteArray data; + QDataStream stream( data, IO_WriteOnly ); + stream << days << objId(); + kapp->dcopClient()->send( "konqueror*", "KonqHistoryManager", + "notifyMaxAge(Q_UINT32, QCString)", data ); +} + +/////////////////////////////////////////////////////////////////// +// DCOP called methods + +void KonqHistoryManager::notifyHistoryEntry( KonqHistoryEntry e, + QCString ) +{ + //kdDebug(1203) << "Got new entry from Broadcast: " << e.url.prettyURL() << endl; + + KonqHistoryEntry *entry = findEntry( e.url ); + QString urlString = e.url.url(); + + if ( !entry ) { // create a new history entry + entry = new KonqHistoryEntry; + entry->url = e.url; + entry->firstVisited = e.firstVisited; + entry->numberOfTimesVisited = 0; // will get set to 1 below + m_history.append( entry ); + KParts::HistoryProvider::insert( urlString ); + } + + if ( !e.typedURL.isEmpty() ) + entry->typedURL = e.typedURL; + if ( !e.title.isEmpty() ) + entry->title = e.title; + entry->numberOfTimesVisited += e.numberOfTimesVisited; + entry->lastVisited = e.lastVisited; + + addToCompletion( entry->url.prettyURL(), entry->typedURL ); + + // bool pending = (e.numberOfTimesVisited != 0); + + adjustSize(); + + // note, no need to do the updateBookmarkMetadata for every + // history object, only need to for the broadcast sender as + // the history object itself keeps the data consistant. + bool updated = KonqBookmarkManager::self()->updateAccessMetadata( urlString ); + + if ( isSenderOfBroadcast() ) { + // we are the sender of the broadcast, so we save + saveHistory(); + // note, bk save does not notify, and we don't want to! + if (updated) + KonqBookmarkManager::self()->save(); + } + + addToUpdateList( urlString ); + emit entryAdded( entry ); +} + +void KonqHistoryManager::notifyMaxCount( Q_UINT32 count, QCString ) +{ + m_maxCount = count; + clearPending(); + adjustSize(); + + KConfig *config = KGlobal::config(); + KConfigGroupSaver cs( config, "HistorySettings" ); + config->writeEntry( "Maximum of History entries", m_maxCount ); + + if ( isSenderOfBroadcast() ) { + saveHistory(); + config->sync(); + } +} + +void KonqHistoryManager::notifyMaxAge( Q_UINT32 days, QCString ) +{ + m_maxAgeDays = days; + clearPending(); + adjustSize(); + + KConfig *config = KGlobal::config(); + KConfigGroupSaver cs( config, "HistorySettings" ); + config->writeEntry( "Maximum age of History entries", m_maxAgeDays ); + + if ( isSenderOfBroadcast() ) { + saveHistory(); + config->sync(); + } +} + +void KonqHistoryManager::notifyClear( QCString ) +{ + clearPending(); + m_history.clear(); + m_pCompletion->clear(); + + if ( isSenderOfBroadcast() ) + saveHistory(); + + KParts::HistoryProvider::clear(); // also emits the cleared() signal +} + +void KonqHistoryManager::notifyRemove( KURL url, QCString ) +{ + kdDebug(1203) << "#### Broadcast: remove entry:: " << url.prettyURL() << endl; + + + KonqHistoryEntry *entry = m_history.findEntry( url ); + + if ( entry ) { // entry is now the current item + removeFromCompletion( entry->url.prettyURL(), entry->typedURL ); + + QString urlString = entry->url.url(); + KParts::HistoryProvider::remove( urlString ); + + addToUpdateList( urlString ); + + m_history.take(); // does not delete + emit entryRemoved( entry ); + delete entry; + + if ( isSenderOfBroadcast() ) + saveHistory(); + } +} + +void KonqHistoryManager::notifyRemove( KURL::List urls, QCString ) +{ + kdDebug(1203) << "#### Broadcast: removing list!" << endl; + + bool doSave = false; + KURL::List::Iterator it = urls.begin(); + while ( it != urls.end() ) { + KonqHistoryEntry *entry = m_history.findEntry( *it ); + + if ( entry ) { // entry is now the current item + removeFromCompletion( entry->url.prettyURL(), entry->typedURL ); + + QString urlString = entry->url.url(); + KParts::HistoryProvider::remove( urlString ); + + addToUpdateList( urlString ); + + m_history.take(); // does not delete + emit entryRemoved( entry ); + delete entry; + doSave = true; + } + + ++it; + } + + if (doSave && isSenderOfBroadcast()) + saveHistory(); +} + + +// compatibility fallback, try to load the old completion history +bool KonqHistoryManager::loadFallback() +{ + QString file = locateLocal( "config", QString::fromLatin1("konq_history")); + if ( file.isEmpty() ) + return false; + + KonqHistoryEntry *entry; + KSimpleConfig config( file ); + config.setGroup("History"); + QStringList items = config.readListEntry( "CompletionItems" ); + QStringList::Iterator it = items.begin(); + + while ( it != items.end() ) { + entry = createFallbackEntry( *it ); + if ( entry ) { + m_history.append( entry ); + addToCompletion( entry->url.prettyURL(), QString::null, entry->numberOfTimesVisited ); + + KParts::HistoryProvider::insert( entry->url.url() ); + } + ++it; + } + + m_history.sort(); + adjustSize(); + saveHistory(); + + return true; +} + +// tries to create a small KonqHistoryEntry out of a string, where the string +// looks like "http://www.bla.com/bla.html:23" +// the attached :23 is the weighting from KCompletion +KonqHistoryEntry * KonqHistoryManager::createFallbackEntry(const QString& item) const +{ + // code taken from KCompletion::addItem(), adjusted to use weight = 1 + uint len = item.length(); + uint weight = 1; + + // find out the weighting of this item (appended to the string as ":num") + int index = item.findRev(':'); + if ( index > 0 ) { + bool ok; + weight = item.mid( index + 1 ).toUInt( &ok ); + if ( !ok ) + weight = 1; + + len = index; // only insert until the ':' + } + + + KonqHistoryEntry *entry = 0L; + KURL u( item.left( len )); + if ( u.isValid() ) { + entry = new KonqHistoryEntry; + // that's the only entries we know about... + entry->url = u; + entry->numberOfTimesVisited = weight; + // to make it not expire immediately... + entry->lastVisited = QDateTime::currentDateTime(); + } + + return entry; +} + +KonqHistoryEntry * KonqHistoryManager::findEntry( const KURL& url ) +{ + // small optimization (dict lookup) for items _not_ in our history + if ( !KParts::HistoryProvider::contains( url.url() ) ) + return 0L; + + return m_history.findEntry( url ); +} + +bool KonqHistoryManager::filterOut( const KURL& url ) +{ + return ( url.isLocalFile() || url.host().isEmpty() ); +} + +void KonqHistoryManager::slotEmitUpdated() +{ + emit KParts::HistoryProvider::updated( m_updateURLs ); + m_updateURLs.clear(); +} + +QStringList KonqHistoryManager::allURLs() const +{ + QStringList list; + KonqHistoryIterator it ( m_history ); + for ( ; it.current(); ++it ) + list.append( it.current()->url.url() ); + + return list; +} + +void KonqHistoryManager::addToCompletion( const QString& url, const QString& typedURL, + int numberOfTimesVisited ) +{ + m_pCompletion->addItem( url, numberOfTimesVisited ); + // typed urls have a higher priority + m_pCompletion->addItem( typedURL, numberOfTimesVisited +10 ); +} + +void KonqHistoryManager::removeFromCompletion( const QString& url, const QString& typedURL ) +{ + m_pCompletion->removeItem( url ); + m_pCompletion->removeItem( typedURL ); +} + +////////////////////////////////////////////////////////////////// + + +KonqHistoryEntry * KonqHistoryList::findEntry( const KURL& url ) +{ + // we search backwards, probably faster to find an entry + KonqHistoryEntry *entry = last(); + while ( entry ) { + if ( entry->url == url ) + return entry; + + entry = prev(); + } + + return 0L; +} + +// sort by lastVisited date (oldest go first) +int KonqHistoryList::compareItems( QPtrCollection::Item item1, + QPtrCollection::Item item2 ) +{ + KonqHistoryEntry *entry1 = static_cast<KonqHistoryEntry *>( item1 ); + KonqHistoryEntry *entry2 = static_cast<KonqHistoryEntry *>( item2 ); + + if ( entry1->lastVisited > entry2->lastVisited ) + return 1; + else if ( entry1->lastVisited < entry2->lastVisited ) + return -1; + else + return 0; +} + +using namespace KParts; // for IRIX + +#include "konq_historymgr.moc" diff --git a/libkonq/konq_historymgr.h b/libkonq/konq_historymgr.h new file mode 100644 index 000000000..4ddcb5e83 --- /dev/null +++ b/libkonq/konq_historymgr.h @@ -0,0 +1,385 @@ +/* This file is part of the KDE project + Copyright (C) 2000,2001 Carsten Pfeiffer <[email protected]> + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KONQ_HISTORY_H +#define KONQ_HISTORY_H + +#include <qdatastream.h> +#include <qfile.h> +#include <qptrlist.h> +#include <qobject.h> +#include <qmap.h> +#include <qtimer.h> + +#include <dcopobject.h> + +#include <kcompletion.h> +#include <kurl.h> +#include <kparts/historyprovider.h> + +#include "konq_historycomm.h" + +#include <libkonq_export.h> + +class KCompletion; + + +typedef QPtrList<KonqHistoryEntry> KonqBaseHistoryList; +typedef QPtrListIterator<KonqHistoryEntry> KonqHistoryIterator; + +class LIBKONQ_EXPORT KonqHistoryList : public KonqBaseHistoryList +{ +public: + /** + * Finds an entry by URL. The found item will also be current(). + * If no matching entry is found, 0L is returned and current() will be + * the first item in the list. + */ + KonqHistoryEntry * findEntry( const KURL& url ); + +protected: + /** + * Ensures that the items are sorted by the lastVisited date + */ + virtual int compareItems( QPtrCollection::Item, QPtrCollection::Item ); +}; + + +/////////////////////////////////////////////////////////////////// + + +/** + * This class maintains and manages a history of all URLs visited by one + * Konqueror instance. Additionally it synchronizes the history with other + * Konqueror instances via DCOP to keep one global and persistant history. + * + * It keeps the history in sync with one KCompletion object + */ +class LIBKONQ_EXPORT KonqHistoryManager : public KParts::HistoryProvider, + public KonqHistoryComm +{ + Q_OBJECT + +public: + static KonqHistoryManager *kself() { + return static_cast<KonqHistoryManager*>( KParts::HistoryProvider::self() ); + } + + KonqHistoryManager( QObject *parent, const char *name ); + ~KonqHistoryManager(); + + /** + * Sets a new maximum size of history and truncates the current history + * if necessary. Notifies all other Konqueror instances via DCOP + * to do the same. + * + * The history is saved after receiving the DCOP call. + */ + void emitSetMaxCount( Q_UINT32 count ); + + /** + * Sets a new maximum age of history entries and removes all entries that + * are older than @p days. Notifies all other Konqueror instances via DCOP + * to do the same. + * + * An age of 0 means no expiry based on the age. + * + * The history is saved after receiving the DCOP call. + */ + void emitSetMaxAge( Q_UINT32 days ); + + /** + * Removes the history entry for @p url, if existant. Tells all other + * Konqueror instances via DCOP to do the same. + * + * The history is saved after receiving the DCOP call. + */ + void emitRemoveFromHistory( const KURL& url ); + + /** + * Removes the history entries for the given list of @p urls. Tells all + * other Konqueror instances via DCOP to do the same. + * + * The history is saved after receiving the DCOP call. + */ + void emitRemoveFromHistory( const KURL::List& urls ); + + /** + * @returns the current maximum number of history entries. + */ + Q_UINT32 maxCount() const { return m_maxCount; } + + /** + * @returns the current maximum age (in days) of history entries. + */ + Q_UINT32 maxAge() const { return m_maxAgeDays; } + + /** + * Adds a pending entry to the history. Pending means, that the entry is + * not verified yet, i.e. it is not sure @p url does exist at all. You + * probably don't know the title of the url in that case either. + * Call @ref confirmPending() as soon you know the entry is good and should + * be updated. + * + * If an entry with @p url already exists, + * it will be updated (lastVisited date will become the current time + * and the number of visits will be incremented). + * + * @param url The url of the history entry + * @param typedURL the string that the user typed, which resulted in url + * Doesn't have to be a valid url, e.g. "slashdot.org". + * @param title The title of the URL. If you don't know it (yet), you may + specify it in @ref confirmPending(). + */ + void addPending( const KURL& url, const QString& typedURL = QString::null, + const QString& title = QString::null ); + + /** + * Confirms and updates the entry for @p url. + */ + void confirmPending( const KURL& url, + const QString& typedURL = QString::null, + const QString& title = QString::null ); + + /** + * Removes a pending url from the history, e.g. when the url does not + * exist, or the user aborted loading. + */ + void removePending( const KURL& url ); + + /** + * @returns the KCompletion object. + */ + KCompletion * completionObject() const { return m_pCompletion; } + + /** + * @returns the list of all history entries, sorted by date + * (oldest entries first) + */ + const KonqHistoryList& entries() const { return m_history; } + + // HistoryProvider interfae, let konq handle this + /** + * Reimplemented in such a way that all URLs that would be filtered + * out normally (see @ref filterOut()) will still be added to the history. + * By default, file:/ urls will be filtered out, but if they come thru + * the HistoryProvider interface, they are added to the history. + */ + virtual void insert( const QString& ); + virtual void remove( const QString& ) {} + virtual void clear() {} + + +public slots: + /** + * Loads the history and fills the completion object. + */ + bool loadHistory(); + + /** + * Saves the entire history. + */ + bool saveHistory(); + + /** + * Clears the history and tells all other Konqueror instances via DCOP + * to do the same. + * The history is saved afterwards, if necessary. + */ + void emitClear(); + + +signals: + /** + * Emitted after the entire history was loaded from disk. + */ + void loadingFinished(); + + /** + * Emitted after a new entry was added + */ + void entryAdded( const KonqHistoryEntry *entry ); + + /** + * Emitted after an entry was removed from the history + * Note, that this entry will be deleted immediately after you got + * that signal. + */ + void entryRemoved( const KonqHistoryEntry *entry ); + +protected: + /** + * Resizes the history list to contain less or equal than m_maxCount + * entries. The first (oldest) entries are removed. + */ + void adjustSize(); + + /** + * @returns true if @p entry is older than the given maximum age, + * otherwise false. + */ + inline bool isExpired( KonqHistoryEntry *entry ) { + return (entry && m_maxAgeDays > 0 && entry->lastVisited < + QDate::currentDate().addDays( -m_maxAgeDays )); + } + + /** + * Notifes all running instances about a new HistoryEntry via DCOP + */ + void emitAddToHistory( const KonqHistoryEntry& entry ); + + /** + * Every konqueror instance broadcasts new history entries to the other + * konqueror instances. Those add the entry to their list, but don't + * save the list, because the sender saves the list. + * + * @param e the new history entry + * @param saveId is the DCOPObject::objId() of the sender so that + * only the sender saves the new history. + */ + virtual void notifyHistoryEntry( KonqHistoryEntry e, QCString saveId ); + + /** + * Called when the configuration of the maximum count changed. + * Called via DCOP by some config-module + */ + virtual void notifyMaxCount( Q_UINT32 count, QCString saveId ); + + /** + * Called when the configuration of the maximum age of history-entries + * changed. Called via DCOP by some config-module + */ + virtual void notifyMaxAge( Q_UINT32 days, QCString saveId ); + + /** + * Clears the history completely. Called via DCOP by some config-module + */ + virtual void notifyClear( QCString saveId ); + + /** + * Notifes about a url that has to be removed from the history. + * The instance where saveId == objId() has to save the history. + */ + virtual void notifyRemove( KURL url, QCString saveId ); + + /** + * Notifes about a list of urls that has to be removed from the history. + * The instance where saveId == objId() has to save the history. + */ + virtual void notifyRemove( KURL::List urls, QCString saveId ); + + /** + * @returns a list of all urls in the history. + */ + virtual QStringList allURLs() const; + + /** + * Does the work for @ref addPending() and @ref confirmPending(). + * + * Adds an entry to the history. If an entry with @p url already exists, + * it will be updated (lastVisited date will become the current time + * and the number of visits will be incremented). + * @p pending means, the entry has not been "verified", it's been added + * right after typing the url. + * If @p pending is false, @p url will be removed from the pending urls + * (if available) and NOT be added again in that case. + */ + void addToHistory( bool pending, const KURL& url, + const QString& typedURL = QString::null, + const QString& title = QString::null ); + + + /** + * @returns true if the given @p url should be filtered out and not be + * added to the history. By default, all local urls (url.isLocalFile()) + * will return true, as well as urls with an empty host. + */ + virtual bool filterOut( const KURL& url ); + + void addToUpdateList( const QString& url ) { + m_updateURLs.append( url ); + m_updateTimer->start( 500, true ); + } + + /** + * The list of urls that is going to be emitted in slotEmitUpdated. Add + * urls to it whenever you modify the list of history entries and start + * m_updateTimer. + */ + QStringList m_updateURLs; + +private slots: + /** + * Called by the updateTimer to emit the KParts::HistoryProvider::updated() + * signal so that khtml can repaint the updated links. + */ + void slotEmitUpdated(); + +private: + /** + * Returns whether the DCOP call we are handling was a call from us self + */ + bool isSenderOfBroadcast(); + + void clearPending(); + /** + * a little optimization for KonqHistoryList::findEntry(), + * checking the dict of KParts::HistoryProvider before traversing the list. + * Can't be used everywhere, because it always returns 0L for "pending" + * entries, as those are not added to the dict, currently. + */ + KonqHistoryEntry * findEntry( const KURL& url ); + + /** + * Stuff to create a proper history out of KDE 2.0's konq_history for + * completion. + */ + bool loadFallback(); + KonqHistoryEntry * createFallbackEntry( const QString& ) const; + + void addToCompletion( const QString& url, const QString& typedURL, int numberOfTimesVisited = 1 ); + void removeFromCompletion( const QString& url, const QString& typedURL ); + + QString m_filename; + KonqHistoryList m_history; + + /** + * List of pending entries, which were added to the history, but not yet + * confirmed (i.e. not yet added with pending = false). + * Note: when removing an entry, you have to delete the KonqHistoryEntry + * of the item you remove. + */ + QMap<QString,KonqHistoryEntry*> m_pending; + + Q_UINT32 m_maxCount; // maximum of history entries + Q_UINT32 m_maxAgeDays; // maximum age of a history entry + + KCompletion *m_pCompletion; // the completion object we sync with + + /** + * A timer that will emit the KParts::HistoryProvider::updated() signal + * thru the slotEmitUpdated slot. + */ + QTimer *m_updateTimer; + + static const Q_UINT32 s_historyVersion; +}; + + +#endif // KONQ_HISTORY_H diff --git a/libkonq/konq_iconviewwidget.cc b/libkonq/konq_iconviewwidget.cc new file mode 100644 index 000000000..99d92ebde --- /dev/null +++ b/libkonq/konq_iconviewwidget.cc @@ -0,0 +1,1927 @@ +/* This file is part of the KDE projects + Copyright (C) 1998, 1999 Torben Weis <[email protected]> + Copyright (C) 2000 - 2005 David Faure <[email protected]> + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#include "konq_iconviewwidget.h" +#include "konq_operations.h" +#include "konq_undo.h" +#include "konq_sound.h" +#include "konq_filetip.h" + +#include <qclipboard.h> +#include <qlayout.h> +#include <qtimer.h> +#include <qpainter.h> +#include <qtooltip.h> +#include <qlabel.h> +#include <qmovie.h> +#include <qregexp.h> +#include <qcursor.h> + +#include <kapplication.h> +#include <kdebug.h> +#include <kio/previewjob.h> +#include <kfileivi.h> +#include <konq_settings.h> +#include <konq_drag.h> +#include <kglobalsettings.h> +#include <kpropertiesdialog.h> +#include <kipc.h> +#include <kicontheme.h> +#include <kiconeffect.h> +#include <kurldrag.h> +#include <kstandarddirs.h> +#include <kprotocolinfo.h> +#include <ktrader.h> + +#include <assert.h> +#include <unistd.h> +#include <klocale.h> + + +struct KonqIconViewWidgetPrivate +{ + KonqIconViewWidgetPrivate() { + pActiveItem = 0; + bSoundPreviews = false; + pSoundItem = 0; + bSoundItemClicked = false; + pSoundPlayer = 0; + pSoundTimer = 0; + pPreviewJob = 0; + bAllowSetWallpaper = false; + + doAnimations = true; + m_movie = 0L; + m_movieBlocked = 0; + pFileTip = 0; + pActivateDoubleClick = 0L; + bCaseInsensitive = true; + pPreviewMimeTypes = 0L; + bProgramsURLdrag = false; + } + ~KonqIconViewWidgetPrivate() { + delete pSoundPlayer; + delete pSoundTimer; + delete m_movie; + delete pFileTip; + delete pActivateDoubleClick; + delete pPreviewMimeTypes; + //delete pPreviewJob; done by stopImagePreview + } + KFileIVI *pActiveItem; + // Sound preview + KFileIVI *pSoundItem; + KonqSoundPlayer *pSoundPlayer; + QTimer *pSoundTimer; + bool bSoundPreviews; + bool bSoundItemClicked; + bool bAllowSetWallpaper; + bool bCaseInsensitive; + bool bBoostPreview; + + // Animated icons support + bool doAnimations; + QMovie* m_movie; + int m_movieBlocked; + QString movieFileName; + + KIO::PreviewJob *pPreviewJob; + KonqFileTip* pFileTip; + QStringList previewSettings; + bool renameItem; + bool firstClick; + bool releaseMouseEvent; + QPoint mousePos; + int mouseState; + QTimer *pActivateDoubleClick; + QStringList* pPreviewMimeTypes; + bool bProgramsURLdrag; +}; + +KonqIconViewWidget::KonqIconViewWidget( QWidget * parent, const char * name, WFlags f, bool kdesktop ) + : KIconView( parent, name, f ), + m_rootItem( 0L ), m_size( 0 ) /* default is DesktopIcon size */, + m_bDesktop( kdesktop ), + m_bSetGridX( !kdesktop ) /* No line breaking on the desktop */ +{ + d = new KonqIconViewWidgetPrivate; + connect( this, SIGNAL( dropped( QDropEvent *, const QValueList<QIconDragItem> & ) ), + this, SLOT( slotDropped( QDropEvent*, const QValueList<QIconDragItem> & ) ) ); + + connect( this, SIGNAL( selectionChanged() ), + this, SLOT( slotSelectionChanged() ) ); + + kapp->addKipcEventMask( KIPC::IconChanged ); + connect( kapp, SIGNAL(iconChanged(int)), SLOT(slotIconChanged(int)) ); + connect( this, SIGNAL(onItem(QIconViewItem *)), SLOT(slotOnItem(QIconViewItem *)) ); + connect( this, SIGNAL(onViewport()), SLOT(slotOnViewport()) ); + connect( this, SIGNAL(itemRenamed(QIconViewItem *, const QString &)), SLOT(slotItemRenamed(QIconViewItem *, const QString &)) ); + + m_pSettings = KonqFMSettings::settings(); // already needed in setItemTextPos(), calculateGridX() + d->bBoostPreview = boostPreview(); + + // hardcoded settings + setSelectionMode( QIconView::Extended ); + setItemTextPos( QIconView::Bottom ); + d->releaseMouseEvent = false; + d->pFileTip = new KonqFileTip(this); + d->firstClick = false; + calculateGridX(); + setAutoArrange( true ); + setSorting( true, sortDirection() ); + readAnimatedIconsConfig(); + m_bSortDirsFirst = true; + m_bMousePressed = false; + m_LineupMode = LineupBoth; + // emit our signals + slotSelectionChanged(); + m_iconPositionGroupPrefix = QString::fromLatin1( "IconPosition::" ); + KonqUndoManager::incRef(); +} + +KonqIconViewWidget::~KonqIconViewWidget() +{ + stopImagePreview(); + KonqUndoManager::decRef(); + delete d; +} + +bool KonqIconViewWidget::maySetWallpaper() +{ + return d->bAllowSetWallpaper; +} + +void KonqIconViewWidget::setMaySetWallpaper(bool b) +{ + d->bAllowSetWallpaper = b; +} + +void KonqIconViewWidget::focusOutEvent( QFocusEvent * ev ) +{ + // We can't possibly have the mouse pressed and still lose focus. + // Well, we can, but when we regain focus we should assume the mouse is + // not down anymore or the slotOnItem code will break with highlighting! + m_bMousePressed = false; + + // This will ensure that tooltips don't pop up and the mouseover icon + // effect will go away if the mouse goes out of the view without + // first moving into an empty portion of the view + // Fixes part of #86968, and #85204 + // Matt Newell 2004-09-24 + slotOnViewport(); + + KIconView::focusOutEvent( ev ); +} + +void KonqIconViewWidget::slotItemRenamed(QIconViewItem *item, const QString &name) +{ + kdDebug(1203) << "KonqIconViewWidget::slotItemRenamed" << endl; + KFileIVI *viewItem = static_cast<KFileIVI *>(item); + KFileItem *fileItem = viewItem->item(); + + // The correct behavior is to show the old name until the rename has successfully + // completed. Unfortunately, KIconView forces us to allow the text to be changed + // before we try the rename, so set it back to the pre-rename state. + viewItem->setText( fileItem->text() ); + kdDebug(1203)<<" fileItem->text() ;"<<fileItem->text()<<endl; + // Don't do anything if the user renamed to a blank name. + if( !name.isEmpty() ) + { + // Actually attempt the rename. If it succeeds, KDirLister will update the name. + KURL oldurl( fileItem->url() ); + KURL newurl( oldurl ); + newurl.setPath( newurl.directory(false) + KIO::encodeFileName( name ) ); + kdDebug(1203)<<" newurl :"<<newurl<<endl; + // We use url()+name so that it also works if the name is a relative path (#51176) + KonqOperations::rename( this, oldurl, newurl ); + } +} + +void KonqIconViewWidget::slotIconChanged( int group ) +{ + if (group != KIcon::Desktop) + return; + + int size = m_size; + if ( m_size == 0 ) + m_size = -1; // little trick to force grid change in setIcons + setIcons( size ); // force re-determining all icons + readAnimatedIconsConfig(); +} + +void KonqIconViewWidget::readAnimatedIconsConfig() +{ + KConfigGroup cfgGroup( KGlobal::config(), "DesktopIcons" ); + d->doAnimations = cfgGroup.readBoolEntry( "Animated", true /*default*/ ); +} + +void KonqIconViewWidget::slotOnItem( QIconViewItem *_item ) +{ + KFileIVI* item = static_cast<KFileIVI *>( _item ); + // Reset icon of previous item + if( d->pActiveItem != 0L && d->pActiveItem != item ) + { + if ( d->m_movie && d->pActiveItem->isAnimated() ) + { + d->m_movie->pause(); // we'll see below what we do with it + d->pActiveItem->setAnimated( false ); + d->pActiveItem->refreshIcon( true ); + } + else { + d->pActiveItem->setActive( false ); + } + d->pActiveItem = 0L; + d->pFileTip->setItem( 0L ); + } + + // Stop sound + if (d->pSoundPlayer != 0 && item != d->pSoundItem) + { + d->pSoundPlayer->stop(); + + d->pSoundItem = 0; + if (d->pSoundTimer && d->pSoundTimer->isActive()) + d->pSoundTimer->stop(); + } + + if ( !m_bMousePressed ) + { + if( item != d->pActiveItem ) + { + d->pActiveItem = item; + d->pFileTip->setItem( d->pActiveItem->item(), + item->rect(), + item->pixmap() ); + + if ( d->doAnimations && d->pActiveItem && d->pActiveItem->hasAnimation() ) + { + //kdDebug(1203) << "Playing animation for: " << d->pActiveItem->mouseOverAnimation() << endl; + // Check if cached movie can be used +#if 0 // Qt-mng bug, reusing the movie doesn't work currently. + if ( d->m_movie && d->movieFileName == d->pActiveItem->mouseOverAnimation() ) + { + d->pActiveItem->setAnimated( true ); + if (d->m_movieBlocked) { + kdDebug(1203) << "onitem, but blocked" << endl; + d->m_movie->pause(); + } + else { + kdDebug(1203) << "we go ahead.." << endl; + d->m_movieBlocked++; + QTimer::singleShot(300, this, SLOT(slotReenableAnimation())); + d->m_movie->restart(); + d->m_movie->unpause(); + } + } + else +#endif + { + QMovie movie = KGlobal::iconLoader()->loadMovie( d->pActiveItem->mouseOverAnimation(), KIcon::Desktop, d->pActiveItem->iconSize() ); + if ( !movie.isNull() ) + { + delete d->m_movie; + d->m_movie = new QMovie( movie ); // shallow copy, don't worry + // Fix alpha-channel - currently only if no background pixmap, + // the bg pixmap case requires to uncomment the code at qmovie.cpp:404 + const QPixmap* pm = backgroundPixmap(); + bool hasPixmap = pm && !pm->isNull(); + if ( !hasPixmap ) { + pm = viewport()->backgroundPixmap(); + hasPixmap = pm && !pm->isNull(); + } + if (!hasPixmap && backgroundMode() != NoBackground) + d->m_movie->setBackgroundColor( viewport()->backgroundColor() ); + d->m_movie->connectUpdate( this, SLOT( slotMovieUpdate(const QRect &) ) ); + d->m_movie->connectStatus( this, SLOT( slotMovieStatus(int) ) ); + d->movieFileName = d->pActiveItem->mouseOverAnimation(); + d->pActiveItem->setAnimated( true ); + } + else + { + d->pActiveItem->setAnimated( false ); + if (d->m_movie) + d->m_movie->pause(); + // No movie available, remember it + d->pActiveItem->setMouseOverAnimation( QString::null ); + } + } + } // animations + // Only do the normal "mouseover" effect if no animation is in use + if (d->pActiveItem && !d->pActiveItem->isAnimated()) + { + d->pActiveItem->setActive( true ); + } + } + else // No change in current item + { + // No effect. If we want to underline on hover, we should + // force the IVI to repaint here, though! + d->pActiveItem = 0L; + d->pFileTip->setItem( 0L ); + } + } // bMousePressed + else + { + // All features disabled during mouse clicking, e.g. rectangular + // selection + d->pActiveItem = 0L; + d->pFileTip->setItem( 0L ); + } + + // ## shouldn't this be disabled during rectangular selection too ? + if (d->bSoundPreviews && d->pSoundPlayer && + d->pSoundPlayer->mimeTypes().contains( + item->item()->mimetype()) + && KGlobalSettings::showFilePreview(item->item()->url()) + && topLevelWidget() == kapp->activeWindow()) + { + d->pSoundItem = item; + d->bSoundItemClicked = false; + if (!d->pSoundTimer) + { + d->pSoundTimer = new QTimer(this); + connect(d->pSoundTimer, SIGNAL(timeout()), SLOT(slotStartSoundPreview())); + } + if (d->pSoundTimer->isActive()) + d->pSoundTimer->stop(); + d->pSoundTimer->start(500, true); + } + else + { + if (d->pSoundPlayer) + d->pSoundPlayer->stop(); + d->pSoundItem = 0; + if (d->pSoundTimer && d->pSoundTimer->isActive()) + d->pSoundTimer->stop(); + } +} + +void KonqIconViewWidget::slotOnViewport() +{ + d->pFileTip->setItem( 0L ); + + if (d->pSoundPlayer) + d->pSoundPlayer->stop(); + d->pSoundItem = 0; + if (d->pSoundTimer && d->pSoundTimer->isActive()) + d->pSoundTimer->stop(); + + if (d->pActiveItem == 0L) + return; + + if ( d->doAnimations && d->m_movie && d->pActiveItem->isAnimated() ) + { + d->pActiveItem->setAnimated( false ); +#if 0 + // Aborting before the end of the animation ? + if (d->m_movie->running()) { + d->m_movie->pause(); + d->m_movieBlocked++; + kdDebug(1203) << "on viewport, blocking" << endl; + QTimer::singleShot(300, this, SLOT(slotReenableAnimation())); + } +#endif + d->pActiveItem->refreshIcon( true ); + Q_ASSERT( d->pActiveItem->state() == KIcon::DefaultState ); + //delete d->m_movie; + //d->m_movie = 0L; + // TODO a timer to delete the movie after some time if unused? + } + else + { + d->pActiveItem->setActive( false ); + } + d->pActiveItem = 0L; +} + +void KonqIconViewWidget::slotStartSoundPreview() +{ + if (!d->pSoundItem || d->bSoundItemClicked) + return; + + d->pSoundPlayer->play(d->pSoundItem->item()->url().url()); +} + + +void KonqIconViewWidget::slotPreview(const KFileItem *item, const QPixmap &pix) +{ + // ### slow. Idea: move KonqKfmIconView's m_itemDict into this class + for (QIconViewItem *it = firstItem(); it; it = it->nextItem()) + { + KFileIVI* current = static_cast<KFileIVI *>(it); + if (current->item() == item) + { + if (item->overlays() & KIcon::HiddenOverlay) { + QPixmap p(pix); + + KIconEffect::semiTransparent(p); + current->setThumbnailPixmap(p); + } else { + current->setThumbnailPixmap(pix); + } + break; + } + } +} + +void KonqIconViewWidget::slotPreviewResult() +{ + d->pPreviewJob = 0; + emit imagePreviewFinished(); +} + +void KonqIconViewWidget::slotToolTipPreview(const KFileItem* , const QPixmap &) +{ +// unused - remove for KDE4 +} + +void KonqIconViewWidget::slotToolTipPreviewResult() +{ +// unused - remove for KDE4 +} + +void KonqIconViewWidget::slotMovieUpdate( const QRect& rect ) +{ + //kdDebug(1203) << "KonqIconViewWidget::slotMovieUpdate " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << endl; + Q_ASSERT( d ); + Q_ASSERT( d->m_movie ); + // seems stopAnimation triggers one last update + if ( d->pActiveItem && d->m_movie && d->pActiveItem->isAnimated() ) { + const QPixmap &frame = d->m_movie->framePixmap(); + // This can happen if the icon was scaled to the desired size, so KIconLoader + // will happily return a movie with different dimensions than the icon + int iconSize=d->pActiveItem->iconSize(); + if (iconSize==0) iconSize = KGlobal::iconLoader()->currentSize( KIcon::Desktop ); + if ( frame.width() != iconSize || frame.height() != iconSize ) { + d->pActiveItem->setAnimated( false ); + d->m_movie->pause(); + // No movie available, remember it + d->pActiveItem->setMouseOverAnimation( QString::null ); + d->pActiveItem->setActive( true ); + return; + } + d->pActiveItem->setPixmapDirect( frame, false, false /*no redraw*/ ); + QRect pixRect = d->pActiveItem->pixmapRect(false); + repaintContents( pixRect.x() + rect.x(), pixRect.y() + rect.y(), rect.width(), rect.height(), false ); + } +} + +void KonqIconViewWidget::slotMovieStatus( int status ) +{ + if ( status < 0 ) { + // Error playing the MNG -> forget about it and do normal iconeffect + if ( d->pActiveItem && d->pActiveItem->isAnimated() ) { + d->pActiveItem->setAnimated( false ); + d->pActiveItem->setMouseOverAnimation( QString::null ); + d->pActiveItem->setActive( true ); + } + } +} + +void KonqIconViewWidget::slotReenableAnimation() +{ + if (!--d->m_movieBlocked) { + if ( d->pActiveItem && d->m_movie && d->m_movie->paused()) { + kdDebug(1203) << "reenabled animation" << endl; + d->m_movie->restart(); + d->m_movie->unpause(); + } + } +} + +void KonqIconViewWidget::clear() +{ + d->pFileTip->setItem( 0L ); + stopImagePreview(); // Just in case + KIconView::clear(); + d->pActiveItem = 0L; +} + +void KonqIconViewWidget::takeItem( QIconViewItem *item ) +{ + if ( d->pActiveItem == static_cast<KFileIVI *>(item) ) + { + d->pFileTip->setItem( 0L ); + d->pActiveItem = 0L; + } + + if ( d->pPreviewJob ) + d->pPreviewJob->removeItem( static_cast<KFileIVI *>(item)->item() ); + + KIconView::takeItem( item ); +} + +// Currently unused - remove in KDE 4.0 +void KonqIconViewWidget::setThumbnailPixmap( KFileIVI * item, const QPixmap & pixmap ) +{ + if ( item ) + { + if ( d->pActiveItem == item ) + { + d->pFileTip->setItem( 0L ); + d->pActiveItem = 0L; + } + item->setThumbnailPixmap( pixmap ); + if ( m_bSetGridX && item->width() > gridX() ) + { + setGridX( item->width() ); + if (autoArrange()) + arrangeItemsInGrid(); + } + } +} + +bool KonqIconViewWidget::initConfig( bool bInit ) +{ + bool fontChanged = false; + + // Color settings + QColor normalTextColor = m_pSettings->normalTextColor(); + setItemColor( normalTextColor ); + + if (m_bDesktop) + { + QColor itemTextBg = m_pSettings->itemTextBackground(); + if ( itemTextBg.isValid() ) + setItemTextBackground( itemTextBg ); + else + setItemTextBackground( NoBrush ); + } + + bool on = m_pSettings->showFileTips() && QToolTip::isGloballyEnabled(); + d->pFileTip->setOptions(on, + m_pSettings->showPreviewsInFileTips(), + m_pSettings->numFileTips()); + + // if the user wants our own tooltip, don't show the one from Qts ListView + setShowToolTips(!on); + + // Font settings + QFont font( m_pSettings->standardFont() ); + if (!m_bDesktop) + font.setUnderline( m_pSettings->underlineLink() ); + + if ( font != KonqIconViewWidget::font() ) + { + setFont( font ); + if (!bInit) + { + // QIconView doesn't do it by default... but if the font is made much + // bigger, we really need to give more space between the icons + fontChanged = true; + } + } + + setIconTextHeight( m_pSettings->iconTextHeight() ); + + if ( (itemTextPos() == QIconView::Right) && (maxItemWidth() != gridXValue()) ) + { + int size = m_size; + m_size = -1; // little trick to force grid change in setIcons + setIcons( size ); // force re-determining all icons + } + else if ( d->bBoostPreview != boostPreview() ) // Update icons if settings for preview icon size have changed + setIcons(m_size); + else if (!bInit) + updateContents(); + return fontChanged; +} + +bool KonqIconViewWidget::boostPreview() const +{ + if ( m_bDesktop ) return false; + + KConfigGroup group( KGlobal::config(), "PreviewSettings" ); + return group.readBoolEntry( "BoostSize", false ); +} + +void KonqIconViewWidget::disableSoundPreviews() +{ + d->bSoundPreviews = false; + + if (d->pSoundPlayer) + d->pSoundPlayer->stop(); + d->pSoundItem = 0; + if (d->pSoundTimer && d->pSoundTimer->isActive()) + d->pSoundTimer->stop(); +} + +void KonqIconViewWidget::setIcons( int size, const QStringList& stopImagePreviewFor ) +{ + // size has changed? + bool sizeChanged = (m_size != size); + int oldGridX = gridX(); + m_size = size; + + // boost preview option has changed? + bool boost = boostPreview(); + bool previewSizeChanged = ( d->bBoostPreview != boost ); + d->bBoostPreview = boost; + + if ( sizeChanged || previewSizeChanged ) + { + int realSize = size ? size : KGlobal::iconLoader()->currentSize( KIcon::Desktop ); + // choose spacing depending on font, but min 5 (due to KFileIVI move limit) + setSpacing( ( m_bDesktop || ( realSize > KIcon::SizeSmall ) ) ? + QMAX( 5, QFontMetrics(font()).width('n') ) : 0 ); + } + + if ( sizeChanged || previewSizeChanged || !stopImagePreviewFor.isEmpty() ) + { + calculateGridX(); + } + bool stopAll = !stopImagePreviewFor.isEmpty() && stopImagePreviewFor.first() == "*"; + + // Disable repaints that can be triggered by ivi->setIcon(). Since icons are + // resized in-place, if the icon size is increasing it can happens that the right + // or bottom icons exceed the size of the viewport.. here we prevent the repaint + // event that will be triggered in that case. + bool prevUpdatesState = viewport()->isUpdatesEnabled(); + viewport()->setUpdatesEnabled( false ); + + // Do this even if size didn't change, since this is used by refreshMimeTypes... + for ( QIconViewItem *it = firstItem(); it; it = it->nextItem() ) { + KFileIVI * ivi = static_cast<KFileIVI *>( it ); + // Set a normal icon for files that are not thumbnails, and for files + // that are thumbnails but for which it should be stopped + if ( !ivi->isThumbnail() || + sizeChanged || + previewSizeChanged || + stopAll || + mimeTypeMatch( ivi->item()->mimetype(), stopImagePreviewFor ) ) + { + ivi->setIcon( size, ivi->state(), true, false ); + } + else + ivi->invalidateThumb( ivi->state(), true ); + } + + // Restore viewport update to previous state + viewport()->setUpdatesEnabled( prevUpdatesState ); + + if ( ( sizeChanged || previewSizeChanged || oldGridX != gridX() || + !stopImagePreviewFor.isEmpty() ) && autoArrange() ) + arrangeItemsInGrid( true ); // take new grid into account and repaint + else + viewport()->update(); //Repaint later.. +} + +bool KonqIconViewWidget::mimeTypeMatch( const QString& mimeType, const QStringList& mimeList ) const +{ + // Code duplication from KIO::PreviewJob + KMimeType::Ptr mime = KMimeType::mimeType( mimeType ); + for (QStringList::ConstIterator mt = mimeList.begin(); mt != mimeList.end(); ++mt) + { + if ( mime->is( *mt ) ) + return true; + // Support for *mt == "image/*" + QString tmp( mimeType ); + if ( (*mt).endsWith("*") && tmp.replace(QRegExp("/.*"), "/*") == (*mt) ) + return true; + if ( (*mt) == "text/plain" ) + { + QVariant textProperty = mime->property( "X-KDE-text" ); + if ( textProperty.type() == QVariant::Bool && textProperty.toBool() ) + return true; + } + } + return false; +} + +void KonqIconViewWidget::setItemTextPos( ItemTextPos pos ) +{ + // can't call gridXValue() because this already would need the new itemTextPos() + int sz = m_size ? m_size : KGlobal::iconLoader()->currentSize( KIcon::Desktop ); + + if ( m_bSetGridX ) + if ( pos == QIconView::Bottom ) + setGridX( QMAX( sz + 50, previewIconSize( sz ) + 13 ) ); + else + { + setMaxItemWidth( QMAX( sz, previewIconSize( sz ) ) + m_pSettings->iconTextWidth() ); + setGridX( -1 ); + } + + KIconView::setItemTextPos( pos ); +} + +void KonqIconViewWidget::gridValues( int* x, int* y, int* dx, int* dy, + int* nx, int* ny ) +{ + int previewSize = previewIconSize( m_size ); + int iconSize = m_size ? m_size : KGlobal::iconLoader()->currentSize( KIcon::Desktop ); + + // Grid size + // as KFileIVI limits to move an icon to x >= 5, y >= 5, we define a grid cell as: + // spacing() must be >= 5 (currently set to 5 in setIcons()) + // horizontal: left spacing() + <width> + // vertical : top spacing(), <height>, bottom spacing() + // The doubled space in y-direction gives a better visual separation and makes it clearer + // to which item the text belongs + *dx = spacing() + QMAX( QMAX( iconSize, previewSize ), m_pSettings->iconTextWidth() ); + int textHeight = iconTextHeight() * fontMetrics().height(); + *dy = spacing() + QMAX( iconSize, previewSize ) + 2 + textHeight + spacing(); + + // Icon Area + int w, h; + if ( m_IconRect.isValid() ) { // w and h must be != 0, otherwise we would get a div by zero + *x = m_IconRect.left(); w = m_IconRect.width(); + *y = m_IconRect.top(); h = m_IconRect.height(); + } + else { + *x = 0; w = viewport()->width(); + *y = 0; h = viewport()->height(); + } + + // bug:110775 avoid div by zero (happens e.g. when iconTextHeight or iconTextWidth are very large) + if ( *dx > w ) + *dx = w; + + if ( *dy > h ) + *dy = h; + + *nx = w / *dx; + *ny = h / *dy; + // TODO: Check that items->count() <= nx * ny + + // Let have exactly nx columns and ny rows + if(*nx && *ny) { + *dx = w / *nx; + *dy = h / *ny; + } + kdDebug(1203) << "x=" << *x << " y=" << *y << " spacing=" << spacing() << " iconSize=" << iconSize + << " w=" << w << " h=" << h + << " nx=" << *nx << " ny=" << *ny + << " dx=" << *dx << " dy=" << *dy << endl; +} + +void KonqIconViewWidget::calculateGridX() +{ + if ( m_bSetGridX ) + if ( itemTextPos() == QIconView::Bottom ) + setGridX( gridXValue() ); + else + { + setMaxItemWidth( gridXValue() ); + setGridX( -1 ); + } +} + +int KonqIconViewWidget::gridXValue() const +{ + // this method is only used in konqi as filemanager (not desktop) + int sz = m_size ? m_size : KGlobal::iconLoader()->currentSize( KIcon::Desktop ); + int newGridX; + + if ( itemTextPos() == QIconView::Bottom ) + newGridX = QMAX( sz + 50, previewIconSize( sz ) + 13 ); + else + newGridX = QMAX( sz, previewIconSize( sz ) ) + m_pSettings->iconTextWidth(); + + //kdDebug(1203) << "gridXValue: " << newGridX << " sz=" << sz << endl; + return newGridX; +} + +void KonqIconViewWidget::refreshMimeTypes() +{ + updatePreviewMimeTypes(); + for ( QIconViewItem *it = firstItem(); it; it = it->nextItem() ) + (static_cast<KFileIVI *>( it ))->item()->refreshMimeType(); + setIcons( m_size ); +} + +void KonqIconViewWidget::setURL( const KURL &kurl ) +{ + stopImagePreview(); + m_url = kurl; + + d->pFileTip->setPreview( KGlobalSettings::showFilePreview(m_url) ); + + if ( m_url.isLocalFile() ) + m_dotDirectoryPath = m_url.path(1).append( ".directory" ); + else + m_dotDirectoryPath = QString::null; +} + +void KonqIconViewWidget::startImagePreview( const QStringList &, bool force ) +{ + stopImagePreview(); // just in case + + // Check config + if ( !KGlobalSettings::showFilePreview( url() ) ) { + kdDebug(1203) << "Previews disabled for protocol " << url().protocol() << endl; + emit imagePreviewFinished(); + return; + } + + if ((d->bSoundPreviews = d->previewSettings.contains( "audio/" )) && + !d->pSoundPlayer) + { + KLibFactory *factory = KLibLoader::self()->factory("konq_sound"); + if (factory) + d->pSoundPlayer = static_cast<KonqSoundPlayer *>( + factory->create(this, 0, "KonqSoundPlayer")); + d->bSoundPreviews = (d->pSoundPlayer != 0L); + } + + KFileItemList items; + for ( QIconViewItem *it = firstItem(); it; it = it->nextItem() ) + if ( force || !static_cast<KFileIVI *>( it )->hasValidThumbnail() ) + items.append( static_cast<KFileIVI *>( it )->item() ); + + bool onlyAudio = true; + for ( QStringList::ConstIterator it = d->previewSettings.begin(); it != d->previewSettings.end(); ++it ) { + if ( (*it).startsWith( "audio/" ) ) + d->bSoundPreviews = true; + else + onlyAudio = false; + } + + if ( items.isEmpty() || onlyAudio ) { + emit imagePreviewFinished(); + return; // don't start the preview job if not really necessary + } + + int iconSize = m_size ? m_size : KGlobal::iconLoader()->currentSize( KIcon::Desktop ); + int size; + + d->bBoostPreview = boostPreview(); + size = previewIconSize( iconSize ); + + if ( !d->bBoostPreview ) + iconSize /= 2; + + d->pPreviewJob = KIO::filePreview( items, size, size, iconSize, + m_pSettings->textPreviewIconTransparency(), true /* scale */, + true /* save */, &(d->previewSettings) ); + connect( d->pPreviewJob, SIGNAL( gotPreview( const KFileItem *, const QPixmap & ) ), + this, SLOT( slotPreview( const KFileItem *, const QPixmap & ) ) ); + connect( d->pPreviewJob, SIGNAL( result( KIO::Job * ) ), + this, SLOT( slotPreviewResult() ) ); +} + +void KonqIconViewWidget::stopImagePreview() +{ + if (d->pPreviewJob) + { + d->pPreviewJob->kill(); + d->pPreviewJob = 0; + // Now that previews are updated in-place, calling + // arrangeItemsInGrid() here is not needed anymore + } +} + +bool KonqIconViewWidget::isPreviewRunning() const +{ + return d->pPreviewJob; +} + +KFileItemList KonqIconViewWidget::selectedFileItems() +{ + KFileItemList lstItems; + + QIconViewItem *it = firstItem(); + for (; it; it = it->nextItem() ) + if ( it->isSelected() ) { + KFileItem *fItem = (static_cast<KFileIVI *>(it))->item(); + lstItems.append( fItem ); + } + return lstItems; +} + +void KonqIconViewWidget::slotDropped( QDropEvent *ev, const QValueList<QIconDragItem> & ) +{ + // Drop on background + KURL dirURL = url(); + if ( m_rootItem ) { + bool dummy; + dirURL = m_rootItem->mostLocalURL(dummy); + } + KonqOperations::doDrop( m_rootItem /* may be 0L */, dirURL, ev, this ); +} + +void KonqIconViewWidget::slotAboutToCreate(const QPoint &, const QValueList<KIO::CopyInfo> &) +{ + // Do nothing :-) +} + +void KonqIconViewWidget::drawBackground( QPainter *p, const QRect &r ) +{ + drawBackground(p, r, r.topLeft()); +} + +void KonqIconViewWidget::drawBackground( QPainter *p, const QRect &r , const QPoint &pt) +{ + const QPixmap *pm = backgroundPixmap(); + bool hasPixmap = pm && !pm->isNull(); + if ( !hasPixmap ) { + pm = viewport()->backgroundPixmap(); + hasPixmap = pm && !pm->isNull(); + } + + QRect rtgt(r); + rtgt.moveTopLeft(pt); + if (!hasPixmap && backgroundMode() != NoBackground) { + p->fillRect(rtgt, viewport()->backgroundColor()); + return; + } + + if (hasPixmap) { + int ax = (r.x() + contentsX() + leftMargin()) % pm->width(); + int ay = (r.y() + contentsY() + topMargin()) % pm->height(); + p->drawTiledPixmap(rtgt, *pm, QPoint(ax, ay)); + } +} + +QDragObject * KonqIconViewWidget::dragObject() +{ + if ( !currentItem() ) + return 0; + + return konqDragObject( viewport() ); +} + +KonqIconDrag * KonqIconViewWidget::konqDragObject( QWidget * dragSource ) +{ + //kdDebug(1203) << "KonqIconViewWidget::konqDragObject" << endl; + + KonqIconDrag2 * drag = new KonqIconDrag2( dragSource ); + QIconViewItem *primaryItem = currentItem(); + // Append all items to the drag object + for ( QIconViewItem *it = firstItem(); it; it = it->nextItem() ) { + if ( it->isSelected() ) { + if (!primaryItem) + primaryItem = it; + KFileItem* fileItem = (static_cast<KFileIVI *>(it))->item(); + KURL url = fileItem->url(); + bool dummy; + KURL mostLocalURL = fileItem->mostLocalURL(dummy); + QString itemURL = KURLDrag::urlToString(url); + kdDebug(1203) << "itemURL=" << itemURL << endl; + QIconDragItem id; + id.setData( QCString(itemURL.latin1()) ); + drag->append( id, + QRect( it->pixmapRect(false).topLeft() - m_mousePos, + it->pixmapRect().size() ), + QRect( it->textRect(false).topLeft() - m_mousePos, + it->textRect().size() ), + itemURL, mostLocalURL ); + } + } + + if (primaryItem) + drag->setPixmap( *primaryItem->pixmap(), m_mousePos - primaryItem->pixmapRect(false).topLeft() ); + + return drag; +} + +void KonqIconViewWidget::contentsDragEnterEvent( QDragEnterEvent *e ) +{ + if ( e->provides( "text/uri-list" ) ) + { + QByteArray payload = e->encodedData( "text/uri-list" ); + if ( !payload.size() ) + kdError() << "Empty data !" << endl; + // Cache the URLs, since we need them every time we move over a file + // (see KFileIVI) + bool ok = KURLDrag::decode( e, m_lstDragURLs ); + if( !ok ) + kdError() << "Couldn't decode urls dragged !" << endl; + } + + KURL::List uriList; + if ( KURLDrag::decode(e, uriList) ) + { + if ( uriList.first().protocol() == "programs" ) + { + e->ignore(); + emit dragEntered( false ); + d->bProgramsURLdrag = true; + return; + } + } + + KIconView::contentsDragEnterEvent( e ); + emit dragEntered( true /*accepted*/ ); +} + +void KonqIconViewWidget::contentsDragMoveEvent( QDragMoveEvent *e ) +{ + if ( d->bProgramsURLdrag ) { + emit dragMove( false ); + e->ignore(); + cancelPendingHeldSignal(); + return; + } + + QIconViewItem *item = findItem( e->pos() ); + if ( e->source() != viewport() && + !item && m_rootItem && !m_rootItem->isWritable() ) { + emit dragMove( false ); + e->ignore(); + cancelPendingHeldSignal(); + return; + } + emit dragMove( true ); + KIconView::contentsDragMoveEvent( e ); +} + +void KonqIconViewWidget::contentsDragLeaveEvent( QDragLeaveEvent *e ) +{ + d->bProgramsURLdrag = false; + KIconView::contentsDragLeaveEvent(e); + emit dragLeft(); +} + + +void KonqIconViewWidget::setItemColor( const QColor &c ) +{ + iColor = c; +} + +QColor KonqIconViewWidget::itemColor() const +{ + return iColor; +} + +void KonqIconViewWidget::disableIcons( const KURL::List & lst ) +{ + for ( QIconViewItem *kit = firstItem(); kit; kit = kit->nextItem() ) + { + bool bFound = false; + // Wow. This is ugly. Matching two lists together.... + // Some sorting to optimise this would be a good idea ? + for (KURL::List::ConstIterator it = lst.begin(); !bFound && it != lst.end(); ++it) + { + if ( static_cast<KFileIVI *>( kit )->item()->url() == *it ) + { + bFound = true; + // maybe remove "it" from lst here ? + } + } + static_cast<KFileIVI *>( kit )->setDisabled( bFound ); + } +} + +void KonqIconViewWidget::slotSelectionChanged() +{ + // This code is very related to ListViewBrowserExtension::updateActions + int canCopy = 0; + int canDel = 0; + int canTrash = 0; + bool bInTrash = false; + int iCount = 0; + + for ( QIconViewItem *it = firstItem(); it; it = it->nextItem() ) + { + if ( it->isSelected() ) + { + iCount++; + canCopy++; + + KFileItem *item = ( static_cast<KFileIVI *>( it ) )->item(); + KURL url = item->url(); + QString local_path = item->localPath(); + + if ( url.directory(false) == KGlobalSettings::trashPath() ) + bInTrash = true; + if ( KProtocolInfo::supportsDeleting( url ) ) + canDel++; + if ( !local_path.isEmpty() ) + canTrash++; + } + } + + emit enableAction( "cut", canDel > 0 ); + emit enableAction( "copy", canCopy > 0 ); + emit enableAction( "trash", canDel > 0 && !bInTrash && canTrash==canDel ); + emit enableAction( "del", canDel > 0 ); + emit enableAction( "properties", iCount > 0 && KPropertiesDialog::canDisplay( selectedFileItems() ) ); + emit enableAction( "editMimeType", ( iCount == 1 ) ); + emit enableAction( "rename", ( iCount == 1) && !bInTrash ); +} + +void KonqIconViewWidget::renameCurrentItem() +{ + if ( currentItem() ) + currentItem()->rename(); +} + +void KonqIconViewWidget::renameSelectedItem() +{ + kdDebug(1203) << " -- KonqIconViewWidget::renameSelectedItem() -- " << endl; + QIconViewItem * item = 0L; + QIconViewItem *it = firstItem(); + for (; it; it = it->nextItem() ) + if ( it->isSelected() && !item ) + { + item = it; + break; + } + if (!item) + { + Q_ASSERT(item); + return; + } + item->rename(); +} + +void KonqIconViewWidget::cutSelection() +{ + kdDebug(1203) << " -- KonqIconViewWidget::cutSelection() -- " << endl; + KonqIconDrag * obj = konqDragObject( /* no parent ! */ ); + obj->setMoveSelection( true ); + QApplication::clipboard()->setData( obj ); +} + +void KonqIconViewWidget::copySelection() +{ + kdDebug(1203) << " -- KonqIconViewWidget::copySelection() -- " << endl; + KonqIconDrag * obj = konqDragObject( /* no parent ! */ ); + QApplication::clipboard()->setData( obj ); +} + +void KonqIconViewWidget::pasteSelection() +{ + paste( url() ); +} + +void KonqIconViewWidget::paste( const KURL &url ) +{ + KonqOperations::doPaste( this, url ); +} + +KURL::List KonqIconViewWidget::selectedUrls() +{ + return selectedUrls( UserVisibleUrls ); +} + +KURL::List KonqIconViewWidget::selectedUrls( UrlFlags flags ) const +{ + KURL::List lstURLs; + bool dummy; + for ( QIconViewItem *it = firstItem(); it; it = it->nextItem() ) + if ( it->isSelected() ) { + KFileItem* item = (static_cast<KFileIVI *>( it ))->item(); + lstURLs.append( flags == MostLocalUrls ? item->mostLocalURL( dummy ) : item->url() ); + } + return lstURLs; +} + +QRect KonqIconViewWidget::iconArea() const +{ + return m_IconRect; +} + +void KonqIconViewWidget::setIconArea(const QRect &rect) +{ + m_IconRect = rect; +} + +int KonqIconViewWidget::lineupMode() const +{ + return m_LineupMode; +} + +void KonqIconViewWidget::setLineupMode(int mode) +{ + m_LineupMode = mode; +} + +bool KonqIconViewWidget::sortDirectoriesFirst() const +{ + return m_bSortDirsFirst; +} + +void KonqIconViewWidget::setSortDirectoriesFirst( bool b ) +{ + m_bSortDirsFirst = b; +} + +void KonqIconViewWidget::contentsMouseMoveEvent( QMouseEvent *e ) +{ + if ( (d->pSoundPlayer && d->pSoundPlayer->isPlaying()) || (d->pSoundTimer && d->pSoundTimer->isActive())) + { + // The following call is SO expensive (the ::widgetAt call eats up to 80% + // of the mouse move cpucycles!), so it's mandatory to place that function + // under strict checks, such as d->pSoundPlayer->isPlaying() + if ( QApplication::widgetAt( QCursor::pos() ) != topLevelWidget() ) + { + if (d->pSoundPlayer) + d->pSoundPlayer->stop(); + d->pSoundItem = 0; + if (d->pSoundTimer && d->pSoundTimer->isActive()) + d->pSoundTimer->stop(); + } + } + d->renameItem= false; + KIconView::contentsMouseMoveEvent( e ); +} + +void KonqIconViewWidget::contentsDropEvent( QDropEvent * ev ) +{ + QIconViewItem *i = findItem( ev->pos() ); + + if ( ev->source() != viewport() && + !i && m_rootItem && !m_rootItem->isWritable() ) { + ev->accept( false ); + return; + } + + // Short-circuit QIconView if Ctrl is pressed, so that it's possible + // to drop a file into its own parent widget to copy it. + if ( !i && (ev->action() == QDropEvent::Copy || ev->action() == QDropEvent::Link) + && ev->source() && ev->source() == viewport()) + { + // First we need to call QIconView though, to clear the drag shape + bool bMovable = itemsMovable(); + setItemsMovable(false); // hack ? call it what you want :-) + KIconView::contentsDropEvent( ev ); + setItemsMovable(bMovable); + + QValueList<QIconDragItem> lst; + slotDropped(ev, lst); + } + else + { + KIconView::contentsDropEvent( ev ); + emit dropped(); // What is this for ? (David) KDE4: remove + } + // Don't do this here, it's too early ! + // slotSaveIconPositions(); + // If we want to save after the new file gets listed, though, + // we could reimplement contentsDropEvent in KDIconView and set m_bNeedSave. Bah. + + // This signal is sent last because we need to ensure it is + // taken in account when all the slots triggered by the dropped() signal + // are executed. This way we know that the Drag and Drop is truely finished + emit dragFinished(); +} + +void KonqIconViewWidget::doubleClickTimeout() +{ + d->renameItem= true; + mousePressChangeValue(); + if ( d->releaseMouseEvent ) + { + QMouseEvent e( QEvent::MouseButtonPress,d->mousePos , 1, d->mouseState); + QIconViewItem* item = findItem( e.pos() ); + KURL url; + if ( item ) + { + url= ( static_cast<KFileIVI *>( item ) )->item()->url(); + bool brenameTrash =false; + if ( url.isLocalFile() && (url.directory(false) == KGlobalSettings::trashPath() || url.path(1).startsWith(KGlobalSettings::trashPath()))) + brenameTrash = true; + + if ( url.isLocalFile() && !brenameTrash && d->renameItem && m_pSettings->renameIconDirectly() && e.button() == LeftButton && item->textRect( false ).contains(e.pos())) + { + if( d->pActivateDoubleClick->isActive () ) + d->pActivateDoubleClick->stop(); + item->rename(); + m_bMousePressed = false; + } + } + } + else + { + QMouseEvent e( QEvent::MouseMove,d->mousePos , 1, d->mouseState); + KIconView::contentsMousePressEvent( &e ); + } + if( d->pActivateDoubleClick->isActive() ) + d->pActivateDoubleClick->stop(); + + d->releaseMouseEvent = false; + d->renameItem= false; +} + +void KonqIconViewWidget::wheelEvent(QWheelEvent* e) +{ + // when scrolling with mousewheel, stop possible pending filetip + d->pFileTip->setItem( 0 ); + + if (e->state() == ControlButton) + { + if (e->delta() >= 0) + { + emit incIconSize(); + } + else + { + emit decIconSize(); + } + e->accept(); + return; + } + + KIconView::wheelEvent(e); +} + +void KonqIconViewWidget::leaveEvent( QEvent *e ) +{ + // when leaving the widget, stop possible pending filetip and icon effect + slotOnViewport(); + + KIconView::leaveEvent(e); +} + +void KonqIconViewWidget::mousePressChangeValue() +{ + //kdDebug(1203) << "KonqIconViewWidget::contentsMousePressEvent" << endl; + m_bMousePressed = true; + if (d->pSoundPlayer) + d->pSoundPlayer->stop(); + d->bSoundItemClicked = true; + d->firstClick = false; + + // Once we click on the item, we don't want a tooltip + // Fixes part of #86968 + d->pFileTip->setItem( 0 ); +} + +void KonqIconViewWidget::contentsMousePressEvent( QMouseEvent *e ) +{ + if(d->pActivateDoubleClick && d->pActivateDoubleClick->isActive ()) + d->pActivateDoubleClick->stop(); + QIconViewItem* item = findItem( e->pos() ); + m_mousePos = e->pos(); + KURL url; + if ( item ) + { + url = ( static_cast<KFileIVI *>( item ) )->item()->url(); + bool brenameTrash =false; + if ( url.isLocalFile() && (url.directory(false) == KGlobalSettings::trashPath() || url.path(1).startsWith(KGlobalSettings::trashPath()))) + brenameTrash = true; + if ( !brenameTrash && !KGlobalSettings::singleClick() && m_pSettings->renameIconDirectly() && e->button() == LeftButton && item->textRect( false ).contains(e->pos())&& !d->firstClick && url.isLocalFile() && (!url.protocol().find("device", 0, false)==0)) + { + d->firstClick = true; + d->mousePos = e->pos(); + d->mouseState = e->state(); + if (!d->pActivateDoubleClick) + { + d->pActivateDoubleClick = new QTimer(this); + connect(d->pActivateDoubleClick, SIGNAL(timeout()), this, SLOT(doubleClickTimeout())); + } + if( d->pActivateDoubleClick->isActive () ) + d->pActivateDoubleClick->stop(); + else + d->pActivateDoubleClick->start(QApplication::doubleClickInterval()); + d->releaseMouseEvent = false; + return; + } + else + d->renameItem= false; + } + else + d->renameItem= false; + mousePressChangeValue(); + if(d->pActivateDoubleClick && d->pActivateDoubleClick->isActive()) + d->pActivateDoubleClick->stop(); + KIconView::contentsMousePressEvent( e ); + +} + +void KonqIconViewWidget::contentsMouseReleaseEvent( QMouseEvent *e ) +{ + KIconView::contentsMouseReleaseEvent( e ); + if(d->releaseMouseEvent && d->pActivateDoubleClick && d->pActivateDoubleClick->isActive ()) + d->pActivateDoubleClick->stop(); + slotSelectionChanged(); + d->releaseMouseEvent = true; + m_bMousePressed = false; +} + +void KonqIconViewWidget::slotSaveIconPositions() +{ + // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + // This code is currently not used but left in for compatibility reasons. + // It can be removed in KDE 4.0 + // Saving of desktop icon positions is now done in KDIconView::saveIconPositions() + // in kdebase/kdesktop/kdiconview.cc + // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + + if ( m_dotDirectoryPath.isEmpty() ) + return; + if ( !m_bDesktop ) + return; // Currently not available in Konqueror + kdDebug(1214) << "KonqIconViewWidget::slotSaveIconPositions" << endl; + KSimpleConfig dotDirectory( m_dotDirectoryPath ); + QIconViewItem *it = firstItem(); + if ( !it ) + return; // No more icons. Maybe we're closing and they've been removed already + while ( it ) + { + KFileIVI *ivi = static_cast<KFileIVI *>( it ); + KFileItem *item = ivi->item(); + + dotDirectory.setGroup( QString( m_iconPositionGroupPrefix ).append( item->url().fileName() ) ); + kdDebug(1214) << "KonqIconViewWidget::slotSaveIconPositions " << item->url().fileName() << " " << it->x() << " " << it->y() << endl; + dotDirectory.writeEntry( QString( "X %1" ).arg( width() ), it->x() ); + dotDirectory.writeEntry( QString( "Y %1" ).arg( height() ), it->y() ); + dotDirectory.writeEntry( "Exists", true ); + + it = it->nextItem(); + } + + QStringList groups = dotDirectory.groupList(); + QStringList::ConstIterator gIt = groups.begin(); + QStringList::ConstIterator gEnd = groups.end(); + for (; gIt != gEnd; ++gIt ) + if ( (*gIt).left( m_iconPositionGroupPrefix.length() ) == m_iconPositionGroupPrefix ) + { + dotDirectory.setGroup( *gIt ); + if ( dotDirectory.hasKey( "Exists" ) ) + dotDirectory.deleteEntry( "Exists", false ); + else + { + kdDebug(1214) << "KonqIconViewWidget::slotSaveIconPositions deleting group " << *gIt << endl; + dotDirectory.deleteGroup( *gIt ); + } + } + + dotDirectory.sync(); + + // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + // This code is currently not used but left in for compatibility reasons. + // It can be removed in KDE 4.0 + // Saving of desktop icon positions is now done in KDIconView::saveIconPositions() + // in kdebase/kdesktop/kdiconview.cc + // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING +} + +// Adapted version of QIconView::insertInGrid, that works relative to +// m_IconRect, instead of the entire viewport. + +void KonqIconViewWidget::insertInGrid(QIconViewItem *item) +{ + if (0L == item) + return; + + if (!m_IconRect.isValid()) + { + KIconView::insertInGrid(item); + return; + } + + QRegion r(m_IconRect); + QIconViewItem *i = firstItem(); + int y = -1; + for (; i; i = i->nextItem() ) + { + r = r.subtract(i->rect()); + y = QMAX(y, i->y() + i->height()); + } + + QMemArray<QRect> rects = r.rects(); + QMemArray<QRect>::Iterator it = rects.begin(); + bool foundPlace = FALSE; + for (; it != rects.end(); ++it) + { + QRect rect = *it; + if (rect.width() >= item->width() && rect.height() >= item->height()) + { + int sx = 0, sy = 0; + if (rect.width() >= item->width() + spacing()) + sx = spacing(); + if (rect.height() >= item->height() + spacing()) + sy = spacing(); + item->move(rect.x() + sx, rect.y() + sy); + foundPlace = true; + break; + } + } + + if (!foundPlace) + item->move(m_IconRect.topLeft()); + + //item->dirty = false; + return; +} + + +/* + * The algorithm used for lineing up the icons could be called + * "beating flat the icon field". Imagine the icon field to be some height + * field on a regular grid, with the height being the number of icons in + * each grid element. Now imagine slamming on the field with a shovel or + * some other flat surface. The high peaks will be flattened and spread out + * over their adjacent areas. This is basically what the algorithm tries to + * simulate. + * + * First, the icons are binned to a grid of the desired size. If all bins + * are containing at most one icon, we're done, of course. We just have to + * move all icons to the center of each grid element. + * For each bin which has more than one icon in it, we calculate 4 + * "friction coefficients", one for each cardinal direction. The friction + * coefficient of a direction is the number of icons adjacent in that + * direction. The idea is that this number is somewhat a measure in which + * direction the icons should flow: icons flow in the direction of lowest + * friction coefficient. We move a maximum of one icon per bin and loop over + * all bins. This procedure is repeated some maximum number of times or until + * no icons are moved anymore. + * + * I don't know if this algorithm is good or bad, I don't even know if it will + * work all the time. It seems a correct thing to do, however, and it seems to + * work particularly well. In any case, the number of runs is limited so there + * can be no races. + */ + +void KonqIconViewWidget::lineupIcons() +{ + // even if there are no items yet, calculate the maxItemWidth to have the correct + // item rect when we insert new items + + // Create a grid of (ny x nx) bins. + int x0, y0, dx, dy, nx, ny; + gridValues( &x0, &y0, &dx, &dy, &nx, &ny ); + + int itemWidth = dx - spacing(); + bool newItemWidth = false; + if ( maxItemWidth() != itemWidth ) { + newItemWidth = true; + setMaxItemWidth( itemWidth ); + setFont( font() ); // Force calcRect() + } + + if ( !firstItem() ) { + kdDebug(1203) << "No icons at all ?\n"; + return; + } + + int iconSize = m_size ? m_size : KGlobal::iconLoader()->currentSize( KIcon::Desktop ); + + typedef QValueList<QIconViewItem*> Bin; + Bin*** bins = new Bin**[nx]; + int i; + int j; + for ( i = 0; i < nx ; i++ ) { + bins[i] = new Bin*[ny]; + for ( j = 0; j < ny; j++ ) + bins[i][j] = 0L; + } + + // Insert items into grid + int textHeight = iconTextHeight() * fontMetrics().height(); + + for ( QIconViewItem* item = firstItem(); item; item = item->nextItem() ) { + int x = item->x() + item->width() / 2 - x0; + int y = item->pixmapRect( false ).bottom() - iconSize / 2 + - ( dy - ( iconSize + textHeight ) ) / 2 - y0; + int posX = QMIN( nx-1, QMAX( 0, x / dx ) ); + int posY = QMIN( ny-1, QMAX( 0, y / dy ) ); + + if ( !bins[posX][posY] ) + bins[posX][posY] = new Bin; + bins[posX][posY]->prepend( item ); + } + + // The shuffle code + int n, k; + const int infinity = 10000; + int nmoves = 1; + for ( n = 0; n < 30 && nmoves > 0; n++ ) { + nmoves = 0; + for ( i = 0; i < nx; i++ ) { + for ( j = 0; j < ny; j++ ) { + if ( !bins[i][j] || ( bins[i][j]->count() <= 1 ) ) + continue; + + // Calculate the 4 "friction coefficients". + int tf = 0, bf = 0, lf = 0, rf = 0; + for ( k = j-1; k >= 0 && bins[i][k] && bins[i][k]->count(); k-- ) + tf += bins[i][k]->count(); + if ( k == -1 ) + tf += infinity; + + for ( k = j+1; k < ny && bins[i][k] && bins[i][k]->count(); k++ ) + bf += bins[i][k]->count(); + if ( k == ny ) + bf += infinity; + + for ( k = i-1; k >= 0 && bins[k][j] && bins[k][j]->count(); k-- ) + lf += bins[k][j]->count(); + if ( k == -1 ) + lf += infinity; + + for ( k = i+1; k < nx && bins[k][j] && bins[k][j]->count(); k++ ) + rf += bins[k][j]->count(); + if ( k == nx ) + rf += infinity; + + // If we are stuck between walls, continue + if ( tf >= infinity && bf >= infinity && + lf >= infinity && rf >= infinity ) + continue; + + // Is there a preferred lineup direction? + if ( m_LineupMode == LineupHorizontal ) { + tf += infinity; + bf += infinity; + } + else if ( m_LineupMode == LineupVertical ) { + lf += infinity; + rf += infinity; + } + + // Move one item in the direction of the least friction + QIconViewItem* movedItem; + Bin* items = bins[i][j]; + + int mini = QMIN( QMIN( tf, bf ), QMIN( lf, rf ) ); + if ( tf == mini ) { + // move top item in (i,j) to (i,j-1) + Bin::iterator it = items->begin(); + movedItem = *it; + for ( ++it; it != items->end(); ++it ) { + if ( (*it)->y() < movedItem->y() ) + movedItem = *it; + } + items->remove( movedItem ); + if ( !bins[i][j-1] ) + bins[i][j-1] = new Bin; + bins[i][j-1]->prepend( movedItem ); + } + else if ( bf ==mini ) { + // move bottom item in (i,j) to (i,j+1) + Bin::iterator it = items->begin(); + movedItem = *it; + for ( ++it; it != items->end(); ++it ) { + if ( (*it)->y() > movedItem->y() ) + movedItem = *it; + } + items->remove( movedItem ); + if ( !bins[i][j+1] ) + bins[i][j+1] = new Bin; + bins[i][j+1]->prepend( movedItem ); + } + else if ( lf == mini ) + { + // move left item in (i,j) to (i-1,j) + Bin::iterator it = items->begin(); + movedItem = *it; + for ( ++it; it != items->end(); ++it ) { + if ( (*it)->x() < movedItem->x() ) + movedItem = *it; + } + items->remove( movedItem ); + if ( !bins[i-1][j] ) + bins[i-1][j] = new Bin; + bins[i-1][j]->prepend( movedItem ); + } + else { + // move right item in (i,j) to (i+1,j) + Bin::iterator it = items->begin(); + movedItem = *it; + for ( ++it; it != items->end(); ++it ) { + if ( (*it)->x() > movedItem->x() ) + movedItem = *it; + } + items->remove( movedItem ); + if ( !bins[i+1][j] ) + bins[i+1][j] = new Bin; + bins[i+1][j]->prepend( movedItem ); + } + nmoves++; + } + } + } + + // Perform the actual moving + QRegion repaintRegion; + QValueList<QIconViewItem*> movedItems; + + for ( i = 0; i < nx; i++ ) { + for ( j = 0; j < ny; j++ ) { + Bin* bin = bins[i][j]; + if ( !bin ) + continue; + if ( !bin->isEmpty() ) { + QIconViewItem* item = bin->first(); + int newX = x0 + i*dx + spacing() + + QMAX(0, ( (dx-spacing()) - item->width() ) / 2); // pixmap can be larger as iconsize + // align all icons vertically to their text + int newY = y0 + j*dy + dy - spacing() - ( item->pixmapRect().bottom() + 2 + textHeight ); + if ( item->x() != newX || item->y() != newY ) { + QRect oldRect = item->rect(); + movedItems.prepend( item ); + item->move( newX, newY ); + if ( item->rect() != oldRect ) + repaintRegion = repaintRegion.unite( oldRect ); + } + } + delete bin; + bins[i][j] = 0L; + } + } + + // repaint + if ( newItemWidth ) + updateContents(); + else { + // Repaint only repaintRegion... + QMemArray<QRect> rects = repaintRegion.rects(); + for ( uint l = 0; l < rects.count(); l++ ) { + kdDebug( 1203 ) << "Repainting (" << rects[l].x() << "," + << rects[l].y() << ")\n"; + repaintContents( rects[l], false ); + } + // Repaint icons that were moved + while ( !movedItems.isEmpty() ) { + repaintItem( movedItems.first() ); + movedItems.remove( movedItems.first() ); + } + } + + for ( i = 0; i < nx ; i++ ) { + delete [] bins[i]; + } + delete [] bins; +} + +void KonqIconViewWidget::lineupIcons( QIconView::Arrangement arrangement ) +{ + int x0, y0, dx, dy, nxmax, nymax; + gridValues( &x0, &y0, &dx, &dy, &nxmax, &nymax ); + int textHeight = iconTextHeight() * fontMetrics().height(); + + QRegion repaintRegion; + QValueList<QIconViewItem*> movedItems; + int nx = 0, ny = 0; + + QIconViewItem* item; + for ( item = firstItem(); item; item = item->nextItem() ) { + int newX = x0 + nx*dx + spacing() + + QMAX(0, ( (dx-spacing()) - item->width() ) / 2); // icon can be larger as defined + // align all icons vertically to their text + int newY = y0 + ny*dy + dy - spacing() - ( item->pixmapRect().bottom() + 2 + textHeight ); + if ( item->x() != newX || item->y() != newY ) { + QRect oldRect = item->rect(); + movedItems.prepend( item ); + item->move( newX, newY ); + if ( item->rect() != oldRect ) + repaintRegion = repaintRegion.unite( oldRect ); + } + if ( arrangement == QIconView::LeftToRight ) { + nx++; + if ( nx >= nxmax ) { + ny++; + nx = 0; + } + } + else { + ny++; + if ( ny >= nymax ) { + nx++; + ny = 0; + } + } + } + + // Repaint only repaintRegion... + QMemArray<QRect> rects = repaintRegion.rects(); + for ( uint l = 0; l < rects.count(); l++ ) { + kdDebug( 1203 ) << "Repainting (" << rects[l].x() << "," + << rects[l].y() << ")\n"; + repaintContents( rects[l], false ); + } + // Repaint icons that were moved + while ( !movedItems.isEmpty() ) { + repaintItem( movedItems.first() ); + movedItems.remove( movedItems.first() ); + } +} + +int KonqIconViewWidget::largestPreviewIconSize( int size ) const +{ + int iconSize = size ? size : KGlobal::iconLoader()->currentSize( KIcon::Desktop ); + + if (iconSize < 28) + return 48; + if (iconSize < 40) + return 64; + if (iconSize < 60) + return 96; + if (iconSize < 120) + return 128; + + return 192; +} + +int KonqIconViewWidget::previewIconSize( int size ) const +{ + int iconSize = size ? size : KGlobal::iconLoader()->currentSize( KIcon::Desktop ); + + if (!d->bBoostPreview) + return iconSize; + + return largestPreviewIconSize( iconSize ); +} + +void KonqIconViewWidget::visualActivate(QIconViewItem * item) +{ + // Rect of the QIconViewItem. + QRect irect = item->rect(); + + // Rect of the QIconViewItem's pixmap area. + QRect rect = item->pixmapRect(); + + // Adjust to correct position. If this isn't done, the fact that the + // text may be wider than the pixmap puts us off-centre. + rect.moveBy(irect.x(), irect.y()); + + // Adjust for scrolling (David) + rect.moveBy( -contentsX(), -contentsY() ); + + KIconEffect::visualActivate(viewport(), rect); +} + +void KonqIconViewWidget::backgroundPixmapChange( const QPixmap & ) +{ + viewport()->update(); +} + +void KonqIconViewWidget::setPreviewSettings( const QStringList& settings ) +{ + d->previewSettings = settings; + updatePreviewMimeTypes(); + + int size = m_size; + m_size = -1; // little trick to force grid change in setIcons + setIcons( size ); // force re-determining all icons +} + +const QStringList& KonqIconViewWidget::previewSettings() +{ + return d->previewSettings; +} + +void KonqIconViewWidget::setNewURL( const QString& url ) +{ + KURL u; + if ( url.startsWith( "/" ) ) + u.setPath( url ); + else + u = url; + setURL( u ); +} + +void KonqIconViewWidget::setCaseInsensitiveSort( bool b ) +{ + d->bCaseInsensitive = b; +} + +bool KonqIconViewWidget::caseInsensitiveSort() const +{ + return d->bCaseInsensitive; +} + +bool KonqIconViewWidget::canPreview( KFileItem* item ) +{ + if ( !KGlobalSettings::showFilePreview( url() ) ) + return false; + + if ( d->pPreviewMimeTypes == 0L ) + updatePreviewMimeTypes(); + + return mimeTypeMatch( item->mimetype(), *( d->pPreviewMimeTypes ) ); +} + +void KonqIconViewWidget::updatePreviewMimeTypes() +{ + if ( d->pPreviewMimeTypes == 0L ) + d->pPreviewMimeTypes = new QStringList; + else + d->pPreviewMimeTypes->clear(); + + // Load the list of plugins to determine which mimetypes are supported + KTrader::OfferList plugins = KTrader::self()->query("ThumbCreator"); + KTrader::OfferList::ConstIterator it; + + for ( it = plugins.begin(); it != plugins.end(); ++it ) { + if ( d->previewSettings.contains((*it)->desktopEntryName()) ) { + QStringList mimeTypes = (*it)->property("MimeTypes").toStringList(); + for (QStringList::ConstIterator mt = mimeTypes.begin(); mt != mimeTypes.end(); ++mt) + d->pPreviewMimeTypes->append(*mt); + } + } +} + +#include "konq_iconviewwidget.moc" + +/* vim: set et sw=4 ts=8 softtabstop=4: */ diff --git a/libkonq/konq_iconviewwidget.h b/libkonq/konq_iconviewwidget.h new file mode 100644 index 000000000..6f18cc020 --- /dev/null +++ b/libkonq/konq_iconviewwidget.h @@ -0,0 +1,370 @@ +/* This file is part of the KDE project + Copyright (C) 1998, 1999 Torben Weis <[email protected]> + Copyright (C) 2000, 2001, 2002 David Faure <[email protected]> + + 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_iconviewwidget_h__ +#define __konq_iconviewwidget_h__ + +#include <kiconloader.h> +#include <kiconview.h> +#include <kurl.h> +#include <qguardedptr.h> +#include <kfileitem.h> +#include <kio/jobclasses.h> +#include <libkonq_export.h> + +class KonqFMSettings; +class KFileIVI; +class KonqIconDrag; +namespace KIO { class Job; } + +/** + * A file-aware icon view, implementing drag'n'drop, KDE icon sizes, + * user settings, animated icons... + * Used by kdesktop and konq_iconview. + * + */ +class LIBKONQ_EXPORT KonqIconViewWidget : public KIconView +{ + Q_OBJECT + Q_PROPERTY( bool sortDirectoriesFirst READ sortDirectoriesFirst WRITE setSortDirectoriesFirst ) + Q_PROPERTY( QRect iconArea READ iconArea WRITE setIconArea ) + Q_PROPERTY( int lineupMode READ lineupMode WRITE setLineupMode ) + Q_PROPERTY( QString url READ urlString WRITE setNewURL ) + + friend class KFileIVI; + +public: + + enum LineupMode { LineupHorizontal=1, LineupVertical, LineupBoth }; + + /** + * Constructor + */ + KonqIconViewWidget( QWidget *parent = 0L, const char *name = 0L, WFlags f = 0, bool kdesktop = FALSE ); + virtual ~KonqIconViewWidget(); + + /** + * Read the configuration and apply it. + * Call this in the inherited constructor with bInit=true, + * and in some reparseConfiguration() slot with bInit=false. + * Returns true if the font was changed (which means something has to + * be done so that the icon's texts don't run into each other). + * However Konq and KDesktop handle this differently. + */ + bool initConfig( bool bInit ); + + /** + * Set the area that will be occupied by icons. It is still possible to + * drag icons outside this area; this only applies to automatically placed + * icons. + */ + void setIconArea( const QRect &rect ); + + /** + * Reimplemented to make the slotOnItem highlighting work. + */ + virtual void focusOutEvent( QFocusEvent * /* ev */ ); + + /** + * Returns the icon area. + */ + QRect iconArea() const; + + /** + * Set the lineup mode. This determines in which direction(s) icons are + * moved when lineing them up. + */ + void setLineupMode(int mode); + + /** + * Returns the lineup mode. + */ + int lineupMode() const; + + /** + * Line up the icons to a regular grid. The outline of the grid is + * specified by iconArea. The two length parameters are + * gridX and gridY. + */ + void lineupIcons(); + + /** + * Line up the icons to a regular grid horizontally or vertically. + * + * @param arrangement the arrangement to use (QIconView::LeftToRight + * for a horizontal arrangement and QIconView::TopToBottom + * for vertical) + */ + void lineupIcons( QIconView::Arrangement arrangement ); + + /** + * Sets the icons of all items, and stores the @p size + * This doesn't touch thumbnails, except if @p stopImagePreviewFor is set. + * Takes care of the grid, when changing the size. + * + * @param size size to use for the icons + * @param stopImagePreviewFor set to a list of mimetypes which should be made normal again. + * For instance "text/plain,image/wmf". + * Can be set to "*" for "all mimetypes" and to "image/"+"*" for "all images". + */ + void setIcons( int size, const QStringList& stopImagePreviewFor = QStringList() ); + + /** + * Called on databaseChanged + */ + void refreshMimeTypes(); + + int iconSize() { return m_size; } + + void calculateGridX(); + /** + * The horizontal distance between two icons + * (whether or not a grid has been given to QIconView) + */ + int gridXValue() const; + + /** + * Calculate the geometry of the fixed grid that is used to line up the + * icons, for example when using the lineupIcons() method. + * + * @param x + * @param y + * @param dx Cell width + * @param dy Cell height + * @param nx Number of columns + * @param ny Number of rows + */ + void gridValues( int* x, int* y, int* dx, int* dy, int* nx, int* ny ); + + /** + * Start generating the previews. + * @param ignored this parameter is probably ignored + * @param force if true, all files are looked at. + * Otherwise, only those which are not a thumbnail already. + * + * @todo figure out the parameter meanings again + */ + void startImagePreview( const QStringList &ignored, bool force ); + void stopImagePreview(); + bool isPreviewRunning() const; + // unused + void setThumbnailPixmap( KFileIVI * item, const QPixmap & pixmap ); + void disableSoundPreviews(); + + void setURL ( const KURL & kurl ); + // ### KDE4: make const + const KURL & url() { return m_url; } + QString urlString() const { return m_url.url(); } + void setRootItem ( const KFileItem * item ) { m_rootItem = item; } + + /** + * Get list of selected KFileItems + */ + KFileItemList selectedFileItems(); + + void setItemColor( const QColor &c ); + QColor itemColor() const; + + virtual void cutSelection(); + virtual void copySelection(); + virtual void pasteSelection(); + virtual KURL::List selectedUrls(); // KDE4: remove virtual + add const + enum UrlFlags { UserVisibleUrls = 0, MostLocalUrls = 1 }; + KURL::List selectedUrls( UrlFlags flags ) const; // KDE4: merge with above, default is == UserVisibleUrls + void paste( const KURL &url ); + + bool sortDirectoriesFirst() const; + void setSortDirectoriesFirst( bool b ); + + void setCaseInsensitiveSort( bool b ); + bool caseInsensitiveSort() const; + + /** + * Cache of the dragged URLs over the icon view, used by KFileIVI + */ + const KURL::List & dragURLs() { return m_lstDragURLs; } + + /** + * Reimplemented from QIconView + */ + virtual void clear(); + + /** + * Reimplemented from QIconView + */ + virtual void takeItem( QIconViewItem *item ); + + /** + * Reimplemented from QIconView to take into account iconArea. + */ + virtual void insertInGrid( QIconViewItem *item ); + + /** + * Reimplemented from QIconView to update the gridX + */ + virtual void setItemTextPos( ItemTextPos pos ); + + /** + * Give feedback when item is activated. + */ + virtual void visualActivate(QIconViewItem *); + + bool isDesktop() const { return m_bDesktop; } + + /** + * Provided for KDesktop. + */ + virtual void setWallpaper(const KURL&) { } + + bool maySetWallpaper(); + void setMaySetWallpaper(bool b); + + void disableIcons( const KURL::List & lst ); + + QString iconPositionGroupPrefix() const { return m_iconPositionGroupPrefix; } + QString dotDirectoryPath() const { return m_dotDirectoryPath; } + + void setPreviewSettings(const QStringList& mimeTypes); + const QStringList& previewSettings(); + void setNewURL( const QString& url ); + +public slots: + /** + * Checks the new selection and emits enableAction() signals + */ + virtual void slotSelectionChanged(); + + void slotSaveIconPositions(); + + void renameSelectedItem(); + void renameCurrentItem(); + + void slotToolTipPreview( const KFileItem *, const QPixmap & ); // ### unused - remove for KDE4 + void slotToolTipPreviewResult() ; // ### unused - remove for KDE4 + +signals: + /** + * For cut/copy/paste/move/delete (see kparts/browserextension.h) + */ + void enableAction( const char * name, bool enabled ); + + void dropped(); + void imagePreviewFinished(); + + void incIconSize(); + void decIconSize(); + + /** + * We need to track drag in icon views for the spring loading folders + */ + void dragEntered( bool accepted ); + void dragLeft(); + + void dragMove( bool accepted ); + /** + * Emited after the dropped() event. This way we know when the + * drag'n'drop is really finished. + */ + void dragFinished(); + +protected slots: + virtual void slotDropped( QDropEvent *e, const QValueList<QIconDragItem> & ); + + void slotItemRenamed(QIconViewItem *item, const QString &name); + + void slotIconChanged(int); + void slotOnItem(QIconViewItem *); + void slotOnViewport(); + void slotStartSoundPreview(); + void slotPreview(const KFileItem *, const QPixmap &); + void slotPreviewResult(); + + void slotMovieUpdate( const QRect& rect ); + void slotMovieStatus( int status ); + void slotReenableAnimation(); + + void slotAboutToCreate(const QPoint &pos, const QValueList<KIO::CopyInfo> &files); + void doubleClickTimeout(); + +protected: + virtual QDragObject *dragObject(); + KonqIconDrag *konqDragObject( QWidget * dragSource = 0L ); + bool mimeTypeMatch( const QString& mimeType, const QStringList& mimeList ) const; + + virtual void drawBackground( QPainter *p, const QRect &r ); + /** + * r is the rectangle which you want to paint from the background. + * pt is the upper left point in the painter device where you want to paint + * the rectangle r. + */ + virtual void drawBackground( QPainter *p, const QRect &r, + const QPoint &pt ); + virtual void contentsDragEnterEvent( QDragEnterEvent *e ); + virtual void contentsDragLeaveEvent( QDragLeaveEvent *e ); + virtual void contentsDragMoveEvent( QDragMoveEvent *e ); + virtual void contentsDropEvent( QDropEvent *e ); + virtual void contentsMousePressEvent( QMouseEvent *e ); + virtual void contentsMouseReleaseEvent ( QMouseEvent * e ); + virtual void contentsMouseMoveEvent( QMouseEvent *e ); + virtual void backgroundPixmapChange( const QPixmap & ); + virtual void wheelEvent( QWheelEvent* ); + virtual void leaveEvent( QEvent *e ); + + void readAnimatedIconsConfig(); + void mousePressChangeValue(); + + bool boostPreview() const; + int previewIconSize( int size ) const; + int largestPreviewIconSize( int size ) const; + bool canPreview( KFileItem* item ); + void updatePreviewMimeTypes(); + +private: + KURL m_url; + const KFileItem * m_rootItem; + + KURL::List m_lstDragURLs; + + int m_size; + + /** Konqueror settings */ + KonqFMSettings * m_pSettings; + + bool m_bMousePressed; + QPoint m_mousePos; + + QColor iColor; + + bool m_bSortDirsFirst; + + QString m_iconPositionGroupPrefix; + QString m_dotDirectoryPath; + + int m_LineupMode; + QRect m_IconRect; + + bool m_bDesktop; + bool m_bSetGridX; + +private: + struct KonqIconViewWidgetPrivate *d; + +}; + +#endif diff --git a/libkonq/konq_operations.cc b/libkonq/konq_operations.cc new file mode 100644 index 000000000..5e9e8269b --- /dev/null +++ b/libkonq/konq_operations.cc @@ -0,0 +1,817 @@ +/* This file is part of the KDE project + Copyright (C) 2000 David Faure <[email protected]> + + 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 <qclipboard.h> +#include "konq_operations.h" + +#include <kautomount.h> +#include <kinputdialog.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <knotifyclient.h> +#include <krun.h> +#include <kshell.h> +#include <kshortcut.h> + +#include <kdirnotify_stub.h> + +#include <dcopclient.h> +#include "konq_undo.h" +#include "konq_defaults.h" +#include "konqbookmarkmanager.h" + +// For doDrop +#include <qdir.h>//first +#include <assert.h> +#include <kapplication.h> +#include <kipc.h> +#include <kdebug.h> +#include <kfileitem.h> +#include <kdesktopfile.h> +#include <kurldrag.h> +#include <kglobalsettings.h> +#include <kimageio.h> +#include <kio/job.h> +#include <kio/jobclasses.h> +#include <kio/paste.h> +#include <kio/netaccess.h> +#include <kio/renamedlg.h> +#include <konq_drag.h> +#include <konq_iconviewwidget.h> +#include <kprotocolinfo.h> +#include <kprocess.h> +#include <kstringhandler.h> +#include <qpopupmenu.h> +#include <unistd.h> +#include <X11/Xlib.h> + +KBookmarkManager * KonqBookmarkManager::s_bookmarkManager; + +KonqOperations::KonqOperations( QWidget *parent ) + : QObject( parent, "KonqOperations" ), + m_method( UNKNOWN ), m_info(0L), m_pasteInfo(0L) +{ +} + +KonqOperations::~KonqOperations() +{ + delete m_info; + delete m_pasteInfo; +} + +void KonqOperations::editMimeType( const QString & mimeType ) +{ + QString keditfiletype = QString::fromLatin1("keditfiletype"); + KRun::runCommand( keditfiletype + " " + KProcess::quote(mimeType), + keditfiletype, keditfiletype /*unused*/); +} + +void KonqOperations::del( QWidget * parent, int method, const KURL::List & selectedURLs ) +{ + kdDebug(1203) << "KonqOperations::del " << parent->className() << endl; + if ( selectedURLs.isEmpty() ) + { + kdWarning(1203) << "Empty URL list !" << endl; + return; + } + + KonqOperations * op = new KonqOperations( parent ); + ConfirmationType confirmation = DEFAULT_CONFIRMATION; + op->_del( method, selectedURLs, confirmation ); +} + +void KonqOperations::emptyTrash() +{ + KonqOperations *op = new KonqOperations( 0L ); + op->_del( EMPTYTRASH, KURL("trash:/"), SKIP_CONFIRMATION ); +} + +void KonqOperations::restoreTrashedItems( const KURL::List& urls ) +{ + KonqOperations *op = new KonqOperations( 0L ); + op->_restoreTrashedItems( urls ); +} + +void KonqOperations::mkdir( QWidget *parent, const KURL & url ) +{ + KIO::Job * job = KIO::mkdir( url ); + KonqOperations * op = new KonqOperations( parent ); + op->setOperation( job, MKDIR, KURL::List(), url ); + (void) new KonqCommandRecorder( KonqCommand::MKDIR, KURL(), url, job ); // no support yet, apparently +} + +void KonqOperations::doPaste( QWidget * parent, const KURL & destURL ) +{ + doPaste(parent, destURL, QPoint()); +} + +void KonqOperations::doPaste( QWidget * parent, const KURL & destURL, const QPoint &pos ) +{ + // move or not move ? + bool move = false; + QMimeSource *data = QApplication::clipboard()->data(); + if ( data->provides( "application/x-kde-cutselection" ) ) { + move = KonqDrag::decodeIsCutSelection( data ); + kdDebug(1203) << "move (from clipboard data) = " << move << endl; + } + + KIO::Job *job = KIO::pasteClipboard( destURL, move ); + if ( job ) + { + KonqOperations * op = new KonqOperations( parent ); + KIO::CopyJob * copyJob = static_cast<KIO::CopyJob *>(job); + KIOPasteInfo * pi = new KIOPasteInfo; + pi->mousePos = pos; + op->setPasteInfo( pi ); + op->setOperation( job, move ? MOVE : COPY, copyJob->srcURLs(), copyJob->destURL() ); + (void) new KonqCommandRecorder( move ? KonqCommand::MOVE : KonqCommand::COPY, KURL::List(), destURL, job ); + } +} + +void KonqOperations::copy( QWidget * parent, int method, const KURL::List & selectedURLs, const KURL& destUrl ) +{ + kdDebug(1203) << "KonqOperations::copy() " << parent->className() << endl; + if ((method!=COPY) && (method!=MOVE) && (method!=LINK)) + { + kdWarning(1203) << "Illegal copy method !" << endl; + return; + } + if ( selectedURLs.isEmpty() ) + { + kdWarning(1203) << "Empty URL list !" << endl; + return; + } + + KonqOperations * op = new KonqOperations( parent ); + KIO::Job* job(0); + if (method==LINK) + job= KIO::link( selectedURLs, destUrl); + else if (method==MOVE) + job= KIO::move( selectedURLs, destUrl); + else + job= KIO::copy( selectedURLs, destUrl); + + op->setOperation( job, method, selectedURLs, destUrl ); + + if (method==COPY) + (void) new KonqCommandRecorder( KonqCommand::COPY, selectedURLs, destUrl, job ); + else + (void) new KonqCommandRecorder( method==MOVE?KonqCommand::MOVE:KonqCommand::LINK, selectedURLs, destUrl, job ); +} + +void KonqOperations::_del( int method, const KURL::List & _selectedURLs, ConfirmationType confirmation ) +{ + KURL::List selectedURLs; + for (KURL::List::ConstIterator it = _selectedURLs.begin(); it != _selectedURLs.end(); ++it) + if (KProtocolInfo::supportsDeleting(*it)) + selectedURLs.append(*it); + if (selectedURLs.isEmpty()) { + delete this; + return; + } + + if ( askDeleteConfirmation( selectedURLs, method, confirmation, parentWidget() ) ) + { + //m_srcURLs = selectedURLs; + KIO::Job *job; + m_method = method; + switch( method ) + { + case TRASH: + { + job = KIO::trash( selectedURLs ); + (void) new KonqCommandRecorder( KonqCommand::TRASH, selectedURLs, "trash:/", job ); + break; + } + case EMPTYTRASH: + { + // Same as in ktrash --empty + QByteArray packedArgs; + QDataStream stream( packedArgs, IO_WriteOnly ); + stream << (int)1; + job = KIO::special( "trash:/", packedArgs ); + KNotifyClient::event(0, "Trash: emptied"); + break; + } + case DEL: + job = KIO::del( selectedURLs ); + break; + case SHRED: + job = KIO::del( selectedURLs, true ); + break; + default: + kdWarning() << "Unknown operation: " << method << endl; + delete this; + return; + } + connect( job, SIGNAL( result( KIO::Job * ) ), + SLOT( slotResult( KIO::Job * ) ) ); + } else + delete this; +} + +void KonqOperations::_restoreTrashedItems( const KURL::List& urls ) +{ + m_method = RESTORE; + KonqMultiRestoreJob* job = new KonqMultiRestoreJob( urls, true ); + connect( job, SIGNAL( result( KIO::Job * ) ), + SLOT( slotResult( KIO::Job * ) ) ); +} + +bool KonqOperations::askDeleteConfirmation( const KURL::List & selectedURLs, int method, ConfirmationType confirmation, QWidget* widget ) +{ + if ( confirmation == SKIP_CONFIRMATION ) + return true; + QString keyName; + bool ask = ( confirmation == FORCE_CONFIRMATION ); + if ( !ask ) + { + KConfig config("konquerorrc", true, false); + config.setGroup( "Trash" ); + keyName = ( method == DEL ? "ConfirmDelete" : method == SHRED ? "ConfirmShred" : "ConfirmTrash" ); + bool defaultValue = ( method == DEL ? DEFAULT_CONFIRMDELETE : method == SHRED ? DEFAULT_CONFIRMSHRED : DEFAULT_CONFIRMTRASH ); + ask = config.readBoolEntry( keyName, defaultValue ); + } + if ( ask ) + { + KURL::List::ConstIterator it = selectedURLs.begin(); + QStringList prettyList; + for ( ; it != selectedURLs.end(); ++it ) { + if ( (*it).protocol() == "trash" ) { + QString path = (*it).path(); + // HACK (#98983): remove "0-foo". Note that it works better than + // displaying KFileItem::name(), for files under a subdir. + prettyList.append( path.remove(QRegExp("^/[0-9]*-")) ); + } else + prettyList.append( (*it).pathOrURL() ); + } + + int result; + switch(method) + { + case DEL: + result = KMessageBox::warningContinueCancelList( widget, + i18n( "Do you really want to delete this item?", "Do you really want to delete these %n items?", prettyList.count()), + prettyList, + i18n( "Delete Files" ), + KStdGuiItem::del(), + keyName, KMessageBox::Dangerous); + break; + + case SHRED: + result = KMessageBox::warningContinueCancelList( widget, + i18n( "Do you really want to shred this item?", "Do you really want to shred these %n items?", prettyList.count()), + prettyList, + i18n( "Shred Files" ), + KGuiItem( i18n( "Shred" ), "editshred" ), + keyName, KMessageBox::Dangerous); + break; + + case MOVE: + default: + result = KMessageBox::warningContinueCancelList( widget, + i18n( "Do you really want to move this item to the trash?", "Do you really want to move these %n items to the trash?", prettyList.count()), + prettyList, + i18n( "Move to Trash" ), + KGuiItem( i18n( "Verb", "&Trash" ), "edittrash"), + keyName, KMessageBox::Dangerous); + } + if (!keyName.isEmpty()) + { + // Check kmessagebox setting... erase & copy to konquerorrc. + KConfig *config = kapp->config(); + KConfigGroupSaver saver(config, "Notification Messages"); + if (!config->readBoolEntry(keyName, true)) + { + config->writeEntry(keyName, true); + config->sync(); + KConfig konq_config("konquerorrc", false); + konq_config.setGroup( "Trash" ); + konq_config.writeEntry( keyName, false ); + } + } + return (result == KMessageBox::Continue); + } + return true; +} + +void KonqOperations::doDrop( const KFileItem * destItem, const KURL & dest, QDropEvent * ev, QWidget * parent ) +{ + kdDebug(1203) << "doDrop: dest : " << dest.url() << endl; + KURL::List lst; + QMap<QString, QString> metaData; + if ( KURLDrag::decode( ev, lst, metaData ) ) // Are they urls ? + { + if( lst.count() == 0 ) + { + kdWarning(1203) << "Oooops, no data ...." << endl; + ev->accept(false); + return; + } + kdDebug(1203) << "KonqOperations::doDrop metaData: " << metaData.count() << " entries." << endl; + QMap<QString,QString>::ConstIterator mit; + for( mit = metaData.begin(); mit != metaData.end(); ++mit ) + { + kdDebug(1203) << "metaData: key=" << mit.key() << " value=" << mit.data() << endl; + } + // Check if we dropped something on itself + KURL::List::Iterator it = lst.begin(); + for ( ; it != lst.end() ; it++ ) + { + kdDebug(1203) << "URL : " << (*it).url() << endl; + if ( dest.equals( *it, true /*ignore trailing slashes*/ ) ) + { + // The event source may be the view or an item (icon) + // Note: ev->source() can be 0L! (in case of kdesktop) (Simon) + if ( !ev->source() || ev->source() != parent && ev->source()->parent() != parent ) + KMessageBox::sorry( parent, i18n("You cannot drop a folder on to itself") ); + kdDebug(1203) << "Dropped on itself" << endl; + ev->accept(false); + return; // do nothing instead of displaying kfm's annoying error box + } + } + + // Check the state of the modifiers key at the time of the drop + Window root; + Window child; + int root_x, root_y, win_x, win_y; + uint keybstate; + XQueryPointer( qt_xdisplay(), qt_xrootwin(), &root, &child, + &root_x, &root_y, &win_x, &win_y, &keybstate ); + + QDropEvent::Action action = ev->action(); + // Check for the drop of a bookmark -> we want a Link action + if ( ev->provides("application/x-xbel") ) + { + keybstate |= ControlMask | ShiftMask; + action = QDropEvent::Link; + kdDebug(1203) << "KonqOperations::doDrop Bookmark -> emulating Link" << endl; + } + + KonqOperations * op = new KonqOperations(parent); + op->setDropInfo( new DropInfo( keybstate, lst, metaData, win_x, win_y, action ) ); + + // Ok, now we need destItem. + if ( destItem ) + { + op->asyncDrop( destItem ); // we have it already + } + else + { + // we need to stat to get it. + op->_statURL( dest, op, SLOT( asyncDrop( const KFileItem * ) ) ); + } + // In both cases asyncDrop will delete op when done + + ev->acceptAction(); + } + else + { + //kdDebug(1203) << "Pasting to " << dest.url() << endl; + KonqOperations * op = new KonqOperations(parent); + KIO::CopyJob* job = KIO::pasteMimeSource( ev, dest, + i18n( "File name for dropped contents:" ), + parent ); + if ( job ) // 0 if canceled by user + { + op->setOperation( job, COPY, KURL::List(), job->destURL() ); + (void) new KonqCommandRecorder( KonqCommand::COPY, KURL::List(), dest, job ); + } + ev->acceptAction(); + } +} + +void KonqOperations::asyncDrop( const KFileItem * destItem ) +{ + assert(m_info); // setDropInfo should have been called before asyncDrop + m_destURL = destItem->url(); + + //kdDebug(1203) << "KonqOperations::asyncDrop destItem->mode=" << destItem->mode() << " url=" << m_destURL << endl; + // Check what the destination is + if ( destItem->isDir() ) + { + doFileCopy(); + return; + } + if ( !m_destURL.isLocalFile() ) + { + // We dropped onto a remote URL that is not a directory! + // (e.g. an HTTP link in the sidebar). + // Can't do that, but we can't prevent it before stating the dest.... + kdWarning(1203) << "Cannot drop onto " << m_destURL << endl; + delete this; + return; + } + if ( destItem->mimetype() == "application/x-desktop") + { + // Local .desktop file. What type ? + KDesktopFile desktopFile( m_destURL.path() ); + if ( desktopFile.hasApplicationType() ) + { + QString error; + QStringList stringList; + KURL::List lst = m_info->lst; + KURL::List::Iterator it = lst.begin(); + for ( ; it != lst.end() ; it++ ) + { + stringList.append((*it).url()); + } + if ( KApplication::startServiceByDesktopPath( m_destURL.path(), stringList, &error ) > 0 ) + KMessageBox::error( 0L, error ); + } + else + { + // Device or Link -> adjust dest + if ( desktopFile.hasDeviceType() && desktopFile.hasKey("MountPoint") ) { + QString point = desktopFile.readEntry( "MountPoint" ); + m_destURL.setPath( point ); + QString dev = desktopFile.readDevice(); + QString mp = KIO::findDeviceMountPoint( dev ); + // Is the device already mounted ? + if ( !mp.isNull() ) + doFileCopy(); + else + { + bool ro = desktopFile.readBoolEntry( "ReadOnly", false ); + QString fstype = desktopFile.readEntry( "FSType" ); + KAutoMount* am = new KAutoMount( ro, fstype, dev, point, m_destURL.path(), false ); + connect( am, SIGNAL( finished() ), this, SLOT( doFileCopy() ) ); + } + return; + } + else if ( desktopFile.hasLinkType() && desktopFile.hasKey("URL") ) { + m_destURL = desktopFile.readPathEntry("URL"); + doFileCopy(); + return; + } + // else, well: mimetype, service, servicetype or .directory. Can't really drop anything on those. + } + } + else + { + // Should be a local executable + // (If this fails, there is a bug in KFileItem::acceptsDrops) + kdDebug(1203) << "KonqOperations::doDrop " << m_destURL.path() << "should be an executable" << endl; + Q_ASSERT ( access( QFile::encodeName(m_destURL.path()), X_OK ) == 0 ); + KProcess proc; + proc << m_destURL.path() ; + // Launch executable for each of the files + KURL::List lst = m_info->lst; + KURL::List::Iterator it = lst.begin(); + for ( ; it != lst.end() ; it++ ) + proc << (*it).path(); // assume local files + kdDebug(1203) << "starting " << m_destURL.path() << " with " << lst.count() << " arguments" << endl; + proc.start( KProcess::DontCare ); + } + delete this; +} + +void KonqOperations::doFileCopy() +{ + assert(m_info); // setDropInfo - and asyncDrop - should have been called before asyncDrop + KURL::List lst = m_info->lst; + QDropEvent::Action action = m_info->action; + bool isDesktopFile = false; + bool itemIsOnDesktop = false; + bool allItemsAreFromTrash = true; + KURL::List mlst; // list of items that can be moved + for (KURL::List::ConstIterator it = lst.begin(); it != lst.end(); ++it) + { + bool local = (*it).isLocalFile(); + if ( KProtocolInfo::supportsDeleting( *it ) && (!local || QFileInfo((*it).directory()).isWritable() )) + mlst.append(*it); + if ( local && KDesktopFile::isDesktopFile((*it).path())) + isDesktopFile = true; + if ( local && (*it).path().startsWith(KGlobalSettings::desktopPath())) + itemIsOnDesktop = true; + if ( local || (*it).protocol() != "trash" ) + allItemsAreFromTrash = false; + } + + bool linkOnly = false; + if (isDesktopFile && !kapp->authorize("run_desktop_files") && + (m_destURL.path(1) == KGlobalSettings::desktopPath()) ) + { + linkOnly = true; + } + + if ( !mlst.isEmpty() && m_destURL.protocol() == "trash" ) + { + if ( itemIsOnDesktop && !kapp->authorize("editable_desktop_icons") ) + { + delete this; + return; + } + + m_method = TRASH; + if ( askDeleteConfirmation( mlst, TRASH, DEFAULT_CONFIRMATION, parentWidget() ) ) + action = QDropEvent::Move; + else + { + delete this; + return; + } + } + else if ( allItemsAreFromTrash || m_destURL.protocol() == "trash" ) { + // No point in asking copy/move/link when using dnd from or to the trash. + action = QDropEvent::Move; + } + else if ( (((m_info->keybstate & ControlMask) == 0) && ((m_info->keybstate & ShiftMask) == 0)) || + linkOnly ) + { + // Neither control nor shift are pressed => show popup menu + KonqIconViewWidget *iconView = dynamic_cast<KonqIconViewWidget*>(parent()); + bool bSetWallpaper = false; + if ( iconView && iconView->maySetWallpaper() && lst.count() == 1 ) + { + KURL url = lst.first(); + KMimeType::Ptr mime = KMimeType::findByURL( url ); + if ( ( !KImageIO::type(url.path()).isEmpty() ) || + ( KImageIO::isSupported(mime->name(), KImageIO::Reading) ) || + mime->is( "image/svg+xml" ) ) + { + bSetWallpaper = true; + } + } + + // Check what the source can do + KURL url = lst.first(); // we'll assume it's the same for all URLs (hack) + bool sReading = KProtocolInfo::supportsReading( url ); + bool sDeleting = KProtocolInfo::supportsDeleting( url ); + bool sMoving = KProtocolInfo::supportsMoving( url ); + // Check what the destination can do + bool dWriting = KProtocolInfo::supportsWriting( m_destURL ); + if ( !dWriting ) + { + delete this; + return; + } + + QPopupMenu popup; + if (!mlst.isEmpty() && (sMoving || (sReading && sDeleting)) && !linkOnly ) + popup.insertItem(SmallIconSet("goto"), i18n( "&Move Here" ) + "\t" + KKey::modFlagLabel( KKey::SHIFT ), 2 ); + if ( sReading && !linkOnly) + popup.insertItem(SmallIconSet("editcopy"), i18n( "&Copy Here" ) + "\t" + KKey::modFlagLabel( KKey::CTRL ), 1 ); + popup.insertItem(SmallIconSet("www"), i18n( "&Link Here" ) + "\t" + KKey::modFlagLabel( (KKey::ModFlag)( KKey::CTRL|KKey::SHIFT ) ), 3 ); + if (bSetWallpaper) + popup.insertItem(SmallIconSet("background"), i18n( "Set as &Wallpaper" ), 4 ); + popup.insertSeparator(); + popup.insertItem(SmallIconSet("cancel"), i18n( "C&ancel" ) + "\t" + KKey( Qt::Key_Escape ).toString(), 5); + + int result = popup.exec( m_info->mousePos ); + + switch (result) { + case 1 : action = QDropEvent::Copy; break; + case 2 : action = QDropEvent::Move; break; + case 3 : action = QDropEvent::Link; break; + case 4 : + { + kdDebug(1203) << "setWallpaper iconView=" << iconView << " url=" << lst.first().url() << endl; + if (iconView && iconView->isDesktop() ) iconView->setWallpaper(lst.first()); + delete this; + return; + } + case 5 : + default : delete this; return; + } + } + + KIO::Job * job = 0; + switch ( action ) { + case QDropEvent::Move : + job = KIO::move( lst, m_destURL ); + job->setMetaData( m_info->metaData ); + setOperation( job, m_method == TRASH ? TRASH : MOVE, lst, m_destURL ); + (void) new KonqCommandRecorder( + m_method == TRASH ? KonqCommand::TRASH : KonqCommand::MOVE, + lst, m_destURL, job ); + return; // we still have stuff to do -> don't delete ourselves + case QDropEvent::Copy : + job = KIO::copy( lst, m_destURL ); + job->setMetaData( m_info->metaData ); + setOperation( job, COPY, lst, m_destURL ); + (void) new KonqCommandRecorder( KonqCommand::COPY, lst, m_destURL, job ); + return; + case QDropEvent::Link : + kdDebug(1203) << "KonqOperations::asyncDrop lst.count=" << lst.count() << endl; + job = KIO::link( lst, m_destURL ); + job->setMetaData( m_info->metaData ); + setOperation( job, LINK, lst, m_destURL ); + (void) new KonqCommandRecorder( KonqCommand::LINK, lst, m_destURL, job ); + return; + default : kdError(1203) << "Unknown action " << (int)action << endl; + } + delete this; +} + +void KonqOperations::rename( QWidget * parent, const KURL & oldurl, const KURL& newurl ) +{ + kdDebug(1203) << "KonqOperations::rename oldurl=" << oldurl << " newurl=" << newurl << endl; + if ( oldurl == newurl ) + return; + + KURL::List lst; + lst.append(oldurl); + KIO::Job * job = KIO::moveAs( oldurl, newurl, !oldurl.isLocalFile() ); + KonqOperations * op = new KonqOperations( parent ); + op->setOperation( job, MOVE, lst, newurl ); + (void) new KonqCommandRecorder( KonqCommand::MOVE, lst, newurl, job ); + // if moving the desktop then update config file and emit + if ( oldurl.isLocalFile() && oldurl.path(1) == KGlobalSettings::desktopPath() ) + { + kdDebug(1203) << "That rename was the Desktop path, updating config files" << endl; + KConfig *globalConfig = KGlobal::config(); + KConfigGroupSaver cgs( globalConfig, "Paths" ); + globalConfig->writePathEntry("Desktop" , newurl.path(), true, true ); + globalConfig->sync(); + KIPC::sendMessageAll(KIPC::SettingsChanged, KApplication::SETTINGS_PATHS); + } +} + +void KonqOperations::setOperation( KIO::Job * job, int method, const KURL::List & /*src*/, const KURL & dest ) +{ + m_method = method; + //m_srcURLs = src; + m_destURL = dest; + if ( job ) + { + connect( job, SIGNAL( result( KIO::Job * ) ), + SLOT( slotResult( KIO::Job * ) ) ); + KIO::CopyJob *copyJob = dynamic_cast<KIO::CopyJob*>(job); + KonqIconViewWidget *iconView = dynamic_cast<KonqIconViewWidget*>(parent()); + if (copyJob && iconView) + { + connect(copyJob, SIGNAL(aboutToCreate(KIO::Job *,const QValueList<KIO::CopyInfo> &)), + this, SLOT(slotAboutToCreate(KIO::Job *,const QValueList<KIO::CopyInfo> &))); + connect(this, SIGNAL(aboutToCreate(const QPoint &, const QValueList<KIO::CopyInfo> &)), + iconView, SLOT(slotAboutToCreate(const QPoint &, const QValueList<KIO::CopyInfo> &))); + } + } + else // for link + slotResult( 0L ); +} + +void KonqOperations::slotAboutToCreate(KIO::Job *, const QValueList<KIO::CopyInfo> &files) +{ + emit aboutToCreate( m_info ? m_info->mousePos : m_pasteInfo ? m_pasteInfo->mousePos : QPoint(), files); +} + +void KonqOperations::statURL( const KURL & url, const QObject *receiver, const char *member ) +{ + KonqOperations * op = new KonqOperations( 0L ); + op->_statURL( url, receiver, member ); + op->m_method = STAT; +} + +void KonqOperations::_statURL( const KURL & url, const QObject *receiver, const char *member ) +{ + connect( this, SIGNAL( statFinished( const KFileItem * ) ), receiver, member ); + KIO::StatJob * job = KIO::stat( url /*, false?*/ ); + connect( job, SIGNAL( result( KIO::Job * ) ), + SLOT( slotStatResult( KIO::Job * ) ) ); +} + +void KonqOperations::slotStatResult( KIO::Job * job ) +{ + if ( job->error()) + job->showErrorDialog( (QWidget*)parent() ); + else + { + KIO::StatJob * statJob = static_cast<KIO::StatJob*>(job); + KFileItem * item = new KFileItem( statJob->statResult(), statJob->url() ); + emit statFinished( item ); + delete item; + } + // If we're only here for a stat, we're done. But not if we used _statURL internally + if ( m_method == STAT ) + delete this; +} + +void KonqOperations::slotResult( KIO::Job * job ) +{ + if (job && job->error()) + job->showErrorDialog( (QWidget*)parent() ); + if ( m_method == EMPTYTRASH ) { + // Update konq windows opened on trash:/ + KDirNotify_stub allDirNotify("*", "KDirNotify*"); + allDirNotify.FilesAdded( "trash:/" ); // yeah, files were removed, but we don't know which ones... + } + delete this; +} + +void KonqOperations::rename( QWidget * parent, const KURL & oldurl, const QString & name ) +{ + KURL newurl( oldurl ); + newurl.setPath( oldurl.directory(false, true) + name ); + kdDebug(1203) << "KonqOperations::rename("<<name<<") called. newurl=" << newurl << endl; + rename( parent, oldurl, newurl ); +} + +void KonqOperations::newDir( QWidget * parent, const KURL & baseURL ) +{ + bool ok; + QString name = i18n( "New Folder" ); + if ( baseURL.isLocalFile() && QFileInfo( baseURL.path(+1) + name ).exists() ) + name = KIO::RenameDlg::suggestName( baseURL, i18n( "New Folder" ) ); + + name = KInputDialog::getText ( i18n( "New Folder" ), + i18n( "Enter folder name:" ), name, &ok, parent ); + if ( ok && !name.isEmpty() ) + { + KURL url; + if ((name[0] == '/') || (name[0] == '~')) + { + url.setPath(KShell::tildeExpand(name)); + } + else + { + name = KIO::encodeFileName( name ); + url = baseURL; + url.addPath( name ); + } + KonqOperations::mkdir( 0L, url ); + } +} + +//// + +KonqMultiRestoreJob::KonqMultiRestoreJob( const KURL::List& urls, bool showProgressInfo ) + : KIO::Job( showProgressInfo ), + m_urls( urls ), m_urlsIterator( m_urls.begin() ), + m_progress( 0 ) +{ + QTimer::singleShot(0, this, SLOT(slotStart())); +} + +void KonqMultiRestoreJob::slotStart() +{ + // Well, it's not a total in bytes, so this would look weird + //if ( m_urlsIterator == m_urls.begin() ) // first time: emit total + // emit totalSize( m_urls.count() ); + + if ( m_urlsIterator != m_urls.end() ) + { + const KURL& url = *m_urlsIterator; + + KURL new_url = url; + if ( new_url.protocol()=="system" + && new_url.path().startsWith("/trash") ) + { + QString path = new_url.path(); + path.remove(0, 6); + new_url.setProtocol("trash"); + new_url.setPath(path); + } + + Q_ASSERT( new_url.protocol() == "trash" ); + QByteArray packedArgs; + QDataStream stream( packedArgs, IO_WriteOnly ); + stream << (int)3 << new_url; + KIO::Job* job = KIO::special( new_url, packedArgs ); + addSubjob( job ); + } + else // done! + { + KDirNotify_stub allDirNotify("*", "KDirNotify*"); + allDirNotify.FilesRemoved( m_urls ); + emitResult(); + } +} + +void KonqMultiRestoreJob::slotResult( KIO::Job *job ) +{ + if ( job->error() ) + { + KIO::Job::slotResult( job ); // will set the error and emit result(this) + return; + } + subjobs.remove( job ); + // Move on to next one + ++m_urlsIterator; + ++m_progress; + //emit processedSize( this, m_progress ); + emitPercent( m_progress, m_urls.count() ); + slotStart(); +} + +QWidget* KonqOperations::parentWidget() const +{ + return static_cast<QWidget *>( parent() ); +} + +#include "konq_operations.moc" diff --git a/libkonq/konq_operations.h b/libkonq/konq_operations.h new file mode 100644 index 000000000..82d021c55 --- /dev/null +++ b/libkonq/konq_operations.h @@ -0,0 +1,215 @@ +/* This file is part of the KDE project + Copyright (C) 2000 David Faure <[email protected]> + + 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_operations_h__ +#define __konq_operations_h__ + +#include <kurl.h> +#include <libkonq_export.h> + +#include <qobject.h> +#include <qevent.h> + +namespace KIO { class Job; class CopyInfo; } +class QWidget; +class KFileItem; +class KonqMainWindow; + +/** + * Implements file operations (move,del,trash,shred,paste,copy,move,link...) + * for konqueror and kdesktop whatever the view mode is (icon, tree, ...) + */ +class LIBKONQ_EXPORT KonqOperations : public QObject +{ + Q_OBJECT +protected: + KonqOperations( QWidget * parent ); + virtual ~KonqOperations(); + +public: + /** + * Pop up properties dialog for mimetype @p mimeType. + */ + static void editMimeType( const QString & mimeType ); + + enum { TRASH, DEL, SHRED, COPY, MOVE, LINK, EMPTYTRASH, STAT, MKDIR, RESTORE, UNKNOWN }; + /** + * Delete the @p selectedURLs if possible. + * + * @param parent parent widget (for error dialog box if any) + * @param method should be TRASH, DEL or SHRED + * @param selectedURLs the URLs to be deleted + */ + static void del( QWidget * parent, int method, const KURL::List & selectedURLs ); + + /** + * Copy the @p selectedURLs to the destination @p destURL. + * + * @param parent parent widget (for error dialog box if any) + * @param method should be COPY, MOVE or LINK + * @param selectedURLs the URLs to copy + * @param destURL destination of the copy + * + * @todo document restrictions on the kind of destination + */ + static void copy( QWidget * parent, int method, const KURL::List & selectedURLs, const KURL& destURL ); + /** + * Drop + * @param destItem destination KFileItem for the drop (background or item) + * @param destURL destination URL for the drop. + * @param ev the drop event + * @param parent parent widget (for error dialog box if any) + * + * If destItem is 0L, doDrop will stat the URL to determine it. + */ + static void doDrop( const KFileItem * destItem, const KURL & destURL, QDropEvent * ev, QWidget * parent ); + + /** + * Paste the clipboard contents + */ + static void doPaste( QWidget * parent, const KURL & destURL, const QPoint &pos ); + static void doPaste( QWidget * parent, const KURL & destURL ); + + static void emptyTrash(); + static void restoreTrashedItems( const KURL::List& urls ); + + /** + * Create a directory + */ + static void mkdir( QWidget *parent, const KURL & url ); + + /** + * Get info about a given URL, and when that's done (it's asynchronous!), + * call a given slot with the KFileItem * as argument. + * The KFileItem will be deleted by statURL after calling the slot. Make a copy + * if you need one ! + */ + static void statURL( const KURL & url, const QObject *receiver, const char *member ); + + /** + * Do a renaming. + * @param parent the parent widget, passed to KonqOperations ctor + * @param oldurl the current url of the file to be renamed + * @param name the new name for the file. Shouldn't include '/'. + */ + static void rename( QWidget * parent, const KURL & oldurl, const QString & name ); + + /** + * Do a renaming. + * @param parent the parent widget, passed to KonqOperations ctor + * @param oldurl the current url of the file to be renamed + * @param newurl the new url for the file + * Use this version if the other one wouldn't work :) (e.g. because name could + * be a relative path, including a '/'). + */ + static void rename( QWidget * parent, const KURL & oldurl, const KURL & newurl ); + + /** + * Ask for the name of a new directory and create it. + * @param parent the parent widget + * @param baseURL the directory to create the new directory in + */ + static void newDir( QWidget * parent, const KURL & baseURL ); + + enum ConfirmationType { DEFAULT_CONFIRMATION, SKIP_CONFIRMATION, FORCE_CONFIRMATION }; + /** + * Ask for confirmation before deleting/trashing @p selectedURLs. + * @param selectedURLs the urls about to be deleted + * @param method the type of deletion (DEL for real deletion, anything else for trash) + * @param confirmation default (based on config file), skip (no confirmation) or force (always confirm) + * @param widget parent widget for message boxes + * @return true if confirmed + */ + static bool askDeleteConfirmation( const KURL::List & selectedURLs, int method, ConfirmationType confirmation, QWidget* widget ); + +signals: + void statFinished( const KFileItem * item ); + void aboutToCreate(const QPoint &pos, const QValueList<KIO::CopyInfo> &files); + +protected: + void _del( int method, const KURL::List & selectedURLs, ConfirmationType confirmation ); + void _restoreTrashedItems( const KURL::List& urls ); + void _statURL( const KURL & url, const QObject *receiver, const char *member ); + + // internal, for COPY/MOVE/LINK/MKDIR + void setOperation( KIO::Job * job, int method, const KURL::List & src, const KURL & dest ); + + struct DropInfo + { + DropInfo( uint k, KURL::List & l, const QMap<QString,QString> &m, + int x, int y, QDropEvent::Action a ) : + keybstate(k), lst(l), metaData(m), mousePos(x,y), action(a) {} + uint keybstate; + KURL::List lst; + QMap<QString,QString> metaData; + QPoint mousePos; + QDropEvent::Action action; + }; + // internal, for doDrop + void setDropInfo( DropInfo * info ) { m_info = info; } + + struct KIOPasteInfo // KDE4: remove and use DropInfo instead or a QPoint member + { + QByteArray data; // unused + KURL destURL; // unused + QPoint mousePos; + QString dialogText; // unused + }; + void setPasteInfo( KIOPasteInfo * info ) { m_pasteInfo = info; } + +private: + QWidget* parentWidget() const; + +protected slots: + + void slotAboutToCreate(KIO::Job *job, const QValueList<KIO::CopyInfo> &files); + void slotResult( KIO::Job * job ); + void slotStatResult( KIO::Job * job ); + void asyncDrop( const KFileItem * item ); + void doFileCopy(); + +private: + int m_method; + //KURL::List m_srcURLs; + KURL m_destURL; + // for doDrop + DropInfo * m_info; + KIOPasteInfo * m_pasteInfo; +}; + +#include <kio/job.h> + +/// Restore multiple trashed files +class KonqMultiRestoreJob : public KIO::Job +{ + Q_OBJECT + +public: + KonqMultiRestoreJob( const KURL::List& urls, bool showProgressInfo ); + +protected slots: + virtual void slotStart(); + virtual void slotResult( KIO::Job *job ); + +private: + const KURL::List m_urls; + KURL::List::const_iterator m_urlsIterator; + int m_progress; +}; + +#endif diff --git a/libkonq/konq_pixmapprovider.cc b/libkonq/konq_pixmapprovider.cc new file mode 100644 index 000000000..a666aa6cf --- /dev/null +++ b/libkonq/konq_pixmapprovider.cc @@ -0,0 +1,201 @@ +/* This file is part of the KDE project + Copyright (C) 2000 Carsten Pfeiffer <[email protected]> + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qbitmap.h> + +#include <kapplication.h> +#include <kiconloader.h> +#include <kmimetype.h> +#include <kshell.h> +#include <kprotocolinfo.h> + +#include "konq_pixmapprovider.h" + +KonqPixmapProvider * KonqPixmapProvider::s_self = 0L; + +KonqPixmapProvider * KonqPixmapProvider::self() +{ + if ( !s_self ) + s_self = new KonqPixmapProvider( kapp, "KonqPixmapProvider" ); + + return s_self; +} + +KonqPixmapProvider::KonqPixmapProvider( QObject *parent, const char *name ) + : KPixmapProvider(), + KonqFavIconMgr( parent, name ) +{ +} + +KonqPixmapProvider::~KonqPixmapProvider() +{ + s_self = 0L; +} + +// at first, tries to find the iconname in the cache +// if not available, tries to find the pixmap for the mimetype of url +// if that fails, gets the icon for the protocol +// finally, inserts the url/icon pair into the cache +QString KonqPixmapProvider::iconNameFor( const QString& url ) +{ + QMapIterator<QString,QString> it = iconMap.find( url ); + QString icon; + if ( it != iconMap.end() ) { + icon = it.data(); + if ( !icon.isEmpty() ) + return icon; + } + + if ( url.isEmpty() ) { + // Use the folder icon for the empty URL + icon = KMimeType::mimeType( "inode/directory" )->KServiceType::icon(); + Q_ASSERT( !icon.isEmpty() ); + } + else + { + KURL u; + if ( url.at(0) == '~' ) + u.setPath( KShell::tildeExpand( url ) ); + else if ( url.at(0) == '/' ) + u.setPath( url ); + else + u = url; + + icon = KMimeType::iconForURL( u ); + //Q_ASSERT( !icon.isEmpty() ); + } + + + // cache the icon found for url + iconMap.insert( url, icon ); + + return icon; +} + +QPixmap KonqPixmapProvider::pixmapFor( const QString& url, int size ) +{ + return loadIcon( url, iconNameFor( url ), size ); +} + +void KonqPixmapProvider::load( KConfig *kc, const QString& key ) +{ + iconMap.clear(); + QStringList list; + list = kc->readPathListEntry( key ); + QStringList::Iterator it = list.begin(); + QString url, icon; + while ( it != list.end() ) { + url = (*it); + if ( ++it == list.end() ) + break; + icon = (*it); + iconMap.insert( url, icon ); + + ++it; + } +} + +// only saves the cache for the given list of items to prevent the cache +// from growing forever. +void KonqPixmapProvider::save( KConfig *kc, const QString& key, + const QStringList& items ) +{ + QStringList list; + QStringList::ConstIterator it = items.begin(); + QMapConstIterator<QString,QString> mit; + while ( it != items.end() ) { + mit = iconMap.find( *it ); + if ( mit != iconMap.end() ) { + list.append( mit.key() ); + list.append( mit.data() ); + } + + ++it; + } + kc->writePathEntry( key, list ); +} + +void KonqPixmapProvider::notifyChange( bool isHost, QString hostOrURL, + QString iconName ) +{ + for ( QMapIterator<QString,QString> it = iconMap.begin(); + it != iconMap.end(); + ++it ) + { + KURL url( it.key() ); + if ( url.protocol().startsWith("http") && + ( ( isHost && url.host() == hostOrURL ) || + ( url.host() + url.path() == hostOrURL ) ) ) + { + // For host default-icons still query the favicon manager to get + // the correct icon for pages that have an own one. + QString icon = isHost ? KMimeType::favIconForURL( url ) : iconName; + if ( !icon.isEmpty() ) + *it = icon; + } + } + + emit changed(); +} + +void KonqPixmapProvider::clear() +{ + iconMap.clear(); +} + +QPixmap KonqPixmapProvider::loadIcon( const QString& url, const QString& icon, + int size ) +{ + if ( size <= KIcon::SizeSmall ) + return SmallIcon( icon, size ); + + KURL u; + if ( url.at(0) == '/' ) + u.setPath( url ); + else + u = url; + + QPixmap big; + + // favicon? => blend the favicon in the large + if ( url.startsWith( "http:/" ) && icon.startsWith("favicons/") ) { + QPixmap small = SmallIcon( icon, size ); + big = KGlobal::iconLoader()->loadIcon( KProtocolInfo::icon("http"), + KIcon::Panel, size ); + + int x = big.width() - small.width(); + int y = 0; + + if ( big.mask() ) { + QBitmap mask = *big.mask(); + bitBlt( &mask, x, y, + small.mask() ? const_cast<QBitmap *>(small.mask()) : &small, 0, 0, + small.width(), small.height(), + small.mask() ? OrROP : SetROP ); + big.setMask( mask ); + } + + bitBlt( &big, x, y, &small ); + } + + else // not a favicon.. + big = KGlobal::iconLoader()->loadIcon( icon, KIcon::Panel, size ); + + return big; +} diff --git a/libkonq/konq_pixmapprovider.h b/libkonq/konq_pixmapprovider.h new file mode 100644 index 000000000..b313652f3 --- /dev/null +++ b/libkonq/konq_pixmapprovider.h @@ -0,0 +1,81 @@ +/* This file is part of the KDE project + Copyright (C) 2000 Carsten Pfeiffer <[email protected]> + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KONQ_PIXMAPPROVIDER_H +#define KONQ_PIXMAPPROVIDER_H + +#include <qmap.h> + +#include <kpixmapprovider.h> +#include "konq_faviconmgr.h" + +#include <libkonq_export.h> + +class KConfig; + +class LIBKONQ_EXPORT KonqPixmapProvider : public KonqFavIconMgr, virtual public KPixmapProvider +{ +public: + static KonqPixmapProvider * self(); + + virtual ~KonqPixmapProvider(); + + /** + * Looks up a pixmap for @p url. Uses a cache for the iconname of url. + */ + virtual QPixmap pixmapFor( const QString& url, int size = 0 ); + + /** + * Loads the cache to @p kc from the current KConfig-group from key @p key. + */ + void load( KConfig * kc, const QString& key ); + /** + * Saves the cache to @p kc into the current KConfig-group as key @p key. + * Only those @p items are saved, otherwise the cache would grow forever. + */ + void save( KConfig *, const QString& key, const QStringList& items ); + + /** + * Clears the pixmap cache + */ + void clear(); + + /** + * Looks up an iconname for @p url. Uses a cache for the iconname of url. + * @since 3.4.1 + */ + QString iconNameFor( const QString& url ); + +protected: + KonqPixmapProvider( QObject *parent=0, const char *name=0 ); + + /** + * Overridden from KonqFavIconMgr to update the cache + */ + virtual void notifyChange( bool isHost, QString hostOrURL, QString iconName ); + + QPixmap loadIcon( const QString& url, const QString& icon, int size ); + +private: + QMap<QString,QString> iconMap; + static KonqPixmapProvider * s_self; +}; + + +#endif // KONQ_PIXMAPPROVIDER_H diff --git a/libkonq/konq_popupmenu.cc b/libkonq/konq_popupmenu.cc new file mode 100644 index 000000000..9238122c0 --- /dev/null +++ b/libkonq/konq_popupmenu.cc @@ -0,0 +1,1205 @@ +/* This file is part of the KDE project + Copyright (C) 1998, 1999 David Faure <[email protected]> + Copyright (C) 2001 Holger Freyther <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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 <qdir.h> + +#include <klocale.h> +#include <kapplication.h> +#include <kbookmarkmanager.h> +#include <kdebug.h> +#include <krun.h> +#include <kprotocolinfo.h> +#include <kiconloader.h> +#include <kinputdialog.h> +#include <kglobalsettings.h> +#include <kstandarddirs.h> +#include <kxmlguifactory.h> +#include <kxmlguibuilder.h> +#include <kparts/componentfactory.h> + +#include <assert.h> + +#include <kfileshare.h> +#include <kprocess.h> + +#include "kpropertiesdialog.h" +#include "knewmenu.h" +#include "konq_popupmenu.h" +#include "konq_operations.h" +#include <dcopclient.h> + +/* + Test cases: + iconview file: background + iconview file: file (with and without servicemenus) + iconview file: directory + iconview remote protocol (e.g. ftp: or fish:) + iconview trash:/ + sidebar directory tree + sidebar Devices / Hard Disc + khtml background + khtml link + khtml image (www.kde.org RMB on K logo) + khtmlimage (same as above, then choose View image, then RMB) + selected text in khtml + embedded katepart + kdesktop folder + trash link on desktop + trashed file or directory + application .desktop file + Then the same after uninstalling kdeaddons/konq-plugins (kuick and arkplugin in particular) +*/ + +class KonqPopupMenuGUIBuilder : public KXMLGUIBuilder +{ +public: + KonqPopupMenuGUIBuilder( QPopupMenu *menu ) + : KXMLGUIBuilder( 0 ) + { + m_menu = menu; + } + virtual ~KonqPopupMenuGUIBuilder() + { + } + + virtual QWidget *createContainer( QWidget *parent, int index, + const QDomElement &element, + int &id ) + { + if ( !parent && element.attribute( "name" ) == "popupmenu" ) + return m_menu; + + return KXMLGUIBuilder::createContainer( parent, index, element, id ); + } + + QPopupMenu *m_menu; +}; + +class KonqPopupMenu::KonqPopupMenuPrivate +{ +public: + KonqPopupMenuPrivate() : m_parentWidget( 0 ), + m_itemFlags( KParts::BrowserExtension::DefaultPopupItems ) + { + } + QString m_urlTitle; + QWidget *m_parentWidget; + KParts::BrowserExtension::PopupFlags m_itemFlags; +}; + +KonqPopupMenu::ProtocolInfo::ProtocolInfo() +{ + m_Reading = false; + m_Writing = false; + m_Deleting = false; + m_Moving = false; + m_TrashIncluded = false; +} + +bool KonqPopupMenu::ProtocolInfo::supportsReading() const +{ + return m_Reading; +} + +bool KonqPopupMenu::ProtocolInfo::supportsWriting() const +{ + return m_Writing; +} + +bool KonqPopupMenu::ProtocolInfo::supportsDeleting() const +{ + return m_Deleting; +} + +bool KonqPopupMenu::ProtocolInfo::supportsMoving() const +{ + return m_Moving; +} + +bool KonqPopupMenu::ProtocolInfo::trashIncluded() const +{ + return m_TrashIncluded; +} + +// This helper class stores the .desktop-file actions and the servicemenus +// in order to support X-KDE-Priority and X-KDE-Submenu. +class PopupServices +{ +public: + ServiceList* selectList( const QString& priority, const QString& submenuName ); + + ServiceList builtin; + ServiceList user, userToplevel, userPriority; + QMap<QString, ServiceList> userSubmenus, userToplevelSubmenus, userPrioritySubmenus; +}; + +ServiceList* PopupServices::selectList( const QString& priority, const QString& submenuName ) +{ + // we use the categories .desktop entry to define submenus + // if none is defined, we just pop it in the main menu + if (submenuName.isEmpty()) + { + if (priority == "TopLevel") + { + return &userToplevel; + } + else if (priority == "Important") + { + return &userPriority; + } + } + else if (priority == "TopLevel") + { + return &(userToplevelSubmenus[submenuName]); + } + else if (priority == "Important") + { + return &(userPrioritySubmenus[submenuName]); + } + else + { + return &(userSubmenus[submenuName]); + } + return &user; +} + +////////////////// + +KonqPopupMenu::KonqPopupMenu( KBookmarkManager *mgr, const KFileItemList &items, + KURL viewURL, + KActionCollection & actions, + KNewMenu * newMenu, + bool showProperties ) + : QPopupMenu( 0L, "konq_popupmenu" ), + m_actions( actions ), m_ownActions( static_cast<QWidget *>( 0 ), "KonqPopupMenu::m_ownActions" ), + m_pMenuNew( newMenu ), m_sViewURL(viewURL), m_lstItems(items), m_pManager(mgr) +{ + KonqPopupFlags kpf = ( showProperties ? ShowProperties : IsLink ) | ShowNewWindow; + init(0, kpf, KParts::BrowserExtension::DefaultPopupItems); +} + +KonqPopupMenu::KonqPopupMenu( KBookmarkManager *mgr, const KFileItemList &items, + KURL viewURL, + KActionCollection & actions, + KNewMenu * newMenu, + QWidget * parentWidget, + bool showProperties ) + : QPopupMenu( parentWidget, "konq_popupmenu" ), m_actions( actions ), m_ownActions( static_cast<QWidget *>( 0 ), "KonqPopupMenu::m_ownActions" ), m_pMenuNew( newMenu ), m_sViewURL(viewURL), m_lstItems(items), m_pManager(mgr) +{ + KonqPopupFlags kpf = ( showProperties ? ShowProperties : IsLink ) | ShowNewWindow; + init(parentWidget, kpf, KParts::BrowserExtension::DefaultPopupItems); +} + +KonqPopupMenu::KonqPopupMenu( KBookmarkManager *mgr, const KFileItemList &items, + const KURL& viewURL, + KActionCollection & actions, + KNewMenu * newMenu, + QWidget * parentWidget, + KonqPopupFlags kpf, + KParts::BrowserExtension::PopupFlags flags) + : QPopupMenu( parentWidget, "konq_popupmenu" ), m_actions( actions ), m_ownActions( static_cast<QWidget *>( 0 ), "KonqPopupMenu::m_ownActions" ), m_pMenuNew( newMenu ), m_sViewURL(viewURL), m_lstItems(items), m_pManager(mgr) +{ + init(parentWidget, kpf, flags); +} + +void KonqPopupMenu::init (QWidget * parentWidget, KonqPopupFlags kpf, KParts::BrowserExtension::PopupFlags flags) +{ + d = new KonqPopupMenuPrivate; + d->m_parentWidget = parentWidget; + d->m_itemFlags = flags; + setup(kpf); +} + +int KonqPopupMenu::insertServicesSubmenus(const QMap<QString, ServiceList>& submenus, + QDomElement& menu, + bool isBuiltin) +{ + int count = 0; + QMap<QString, ServiceList>::ConstIterator it; + + for (it = submenus.begin(); it != submenus.end(); ++it) + { + if (it.data().isEmpty()) + { + //avoid empty sub-menus + continue; + } + + QDomElement actionSubmenu = m_doc.createElement( "menu" ); + actionSubmenu.setAttribute( "name", "actions " + it.key() ); + menu.appendChild( actionSubmenu ); + QDomElement subtext = m_doc.createElement( "text" ); + actionSubmenu.appendChild( subtext ); + subtext.appendChild( m_doc.createTextNode( it.key() ) ); + count += insertServices(it.data(), actionSubmenu, isBuiltin); + } + + return count; +} + +int KonqPopupMenu::insertServices(const ServiceList& list, + QDomElement& menu, + bool isBuiltin) +{ + static int id = 1000; + int count = 0; + + ServiceList::const_iterator it = list.begin(); + for( ; it != list.end(); ++it ) + { + if ((*it).isEmpty()) + { + if (!menu.firstChild().isNull() && + menu.lastChild().toElement().tagName().lower() != "separator") + { + QDomElement separator = m_doc.createElement( "separator" ); + menu.appendChild(separator); + } + continue; + } + + if (isBuiltin || (*it).m_display == true) + { + QCString name; + name.setNum( id ); + name.prepend( isBuiltin ? "builtinservice_" : "userservice_" ); + KAction * act = new KAction( QString((*it).m_strName).replace('&',"&&"), 0, + this, SLOT( slotRunService() ), + &m_ownActions, name ); + + if ( !(*it).m_strIcon.isEmpty() ) + { + QPixmap pix = SmallIcon( (*it).m_strIcon ); + act->setIconSet( pix ); + } + + addAction( act, menu ); // Add to toplevel menu + + m_mapPopupServices[ id++ ] = *it; + ++count; + } + } + + return count; +} + +bool KonqPopupMenu::KIOSKAuthorizedAction(KConfig& cfg) +{ + if ( !cfg.hasKey( "X-KDE-AuthorizeAction") ) + { + return true; + } + + QStringList list = cfg.readListEntry("X-KDE-AuthorizeAction"); + if (kapp && !list.isEmpty()) + { + for(QStringList::ConstIterator it = list.begin(); + it != list.end(); + ++it) + { + if (!kapp->authorize((*it).stripWhiteSpace())) + { + return false; + } + } + } + + return true; +} + + +void KonqPopupMenu::setup(KonqPopupFlags kpf) +{ + assert( m_lstItems.count() >= 1 ); + + m_ownActions.setWidget( this ); + + const bool bIsLink = (kpf & IsLink); + bool currentDir = false; + bool sReading = true; + bool sDeleting = ( d->m_itemFlags & KParts::BrowserExtension::NoDeletion ) == 0; + bool sMoving = sDeleting; + bool sWriting = sDeleting && m_lstItems.first()->isWritable(); + m_sMimeType = m_lstItems.first()->mimetype(); + QString mimeGroup = m_sMimeType.left(m_sMimeType.find('/')); + mode_t mode = m_lstItems.first()->mode(); + bool isDirectory = S_ISDIR(mode); + bool bTrashIncluded = false; + bool mediaFiles = false; + bool isReallyLocal = m_lstItems.first()->isLocalFile(); + bool isLocal = isReallyLocal + || m_lstItems.first()->url().protocol()=="media" + || m_lstItems.first()->url().protocol()=="system"; + bool isTrashLink = false; + m_lstPopupURLs.clear(); + int id = 0; + setFont(KGlobalSettings::menuFont()); + m_pluginList.setAutoDelete( true ); + m_ownActions.setHighlightingEnabled( true ); + + attrName = QString::fromLatin1( "name" ); + + prepareXMLGUIStuff(); + m_builder = new KonqPopupMenuGUIBuilder( this ); + m_factory = new KXMLGUIFactory( m_builder ); + + KURL url; + KFileItemListIterator it ( m_lstItems ); + QStringList mimeTypeList; + // Check whether all URLs are correct + for ( ; it.current(); ++it ) + { + url = (*it)->url(); + + // Build the list of URLs + m_lstPopupURLs.append( url ); + + // Determine if common mode among all URLs + if ( mode != (*it)->mode() ) + mode = 0; // modes are different => reset to 0 + + // Determine if common mimetype among all URLs + if ( m_sMimeType != (*it)->mimetype() ) + { + m_sMimeType = QString::null; // mimetypes are different => null + + if ( mimeGroup != (*it)->mimetype().left((*it)->mimetype().find('/'))) + mimeGroup = QString::null; // mimetype groups are different as well! + } + + if ( mimeTypeList.findIndex( (*it)->mimetype() ) == -1 ) + mimeTypeList << (*it)->mimetype(); + + if ( isReallyLocal && !url.isLocalFile() ) + isReallyLocal = false; + if ( isLocal && !url.isLocalFile() && url.protocol() != "media" && url.protocol() != "system" ) + isLocal = false; + + if ( !bTrashIncluded && ( + ( url.protocol() == "trash" && url.path().length() <= 1 ) + || url.url() == "system:/trash" || url.url() == "system:/trash/" ) ) { + bTrashIncluded = true; + isLocal = false; + } + + if ( sReading ) + sReading = KProtocolInfo::supportsReading( url ); + + if ( sWriting ) + sWriting = KProtocolInfo::supportsWriting( url ) && (*it)->isWritable(); + + if ( sDeleting ) + sDeleting = KProtocolInfo::supportsDeleting( url ); + + if ( sMoving ) + sMoving = KProtocolInfo::supportsMoving( url ); + if ( (*it)->mimetype().startsWith("media/") ) + mediaFiles = true; + } + url = m_sViewURL; + url.cleanPath(); + + //check if url is current directory + if ( m_lstItems.count() == 1 ) + { + KURL firstPopupURL( m_lstItems.first()->url() ); + firstPopupURL.cleanPath(); + //kdDebug(1203) << "View path is " << url.url() << endl; + //kdDebug(1203) << "First popup path is " << firstPopupURL.url() << endl; + currentDir = firstPopupURL.equals( url, true /* ignore_trailing */ ); + if ( isLocal && m_sMimeType == "application/x-desktop" ) { + KSimpleConfig cfg( firstPopupURL.path(), true ); + cfg.setDesktopGroup(); + isTrashLink = ( cfg.readEntry("Type") == "Link" && cfg.readEntry("URL") == "trash:/" ); + } + + if ( isTrashLink ) { + sDeleting = false; + } + } + + m_info.m_Reading = sReading; + m_info.m_Writing = sWriting; + m_info.m_Deleting = sDeleting; + m_info.m_Moving = sMoving; + m_info.m_TrashIncluded = bTrashIncluded; + + // isCurrentTrash: popup on trash:/ itself, or on the trash.desktop link + bool isCurrentTrash = ( m_lstItems.count() == 1 && bTrashIncluded ) || isTrashLink; + bool isIntoTrash = ( url.protocol() == "trash" || url.url().startsWith( "system:/trash" ) ) && !isCurrentTrash; // trashed file, not trash:/ itself + //kdDebug() << "isLocal=" << isLocal << " url=" << url << " isCurrentTrash=" << isCurrentTrash << " isIntoTrash=" << isIntoTrash << " bTrashIncluded=" << bTrashIncluded << endl; + bool isSingleMedium = m_lstItems.count() == 1 && mediaFiles; + clear(); + + ////////////////////////////////////////////////////////////////////////// + + KAction * act; + + if (!isCurrentTrash) + addMerge( "konqueror" ); + + bool isKDesktop = QCString( kapp->name() ) == "kdesktop"; + KAction *actNewWindow = 0; + + if (( kpf & ShowProperties ) && isKDesktop && + !kapp->authorize("editable_desktop_icons")) + { + kpf &= ~ShowProperties; // remove flag + } + + // Either 'newview' is in the actions we're given (probably in the tabhandling group) + // or we need to insert it ourselves (e.g. for kdesktop). In the first case, actNewWindow must remain 0. + if ( ((kpf & ShowNewWindow) != 0) && sReading ) + { + QString openStr = isKDesktop ? i18n( "&Open" ) : i18n( "Open in New &Window" ); + actNewWindow = new KAction( openStr, "window_new", 0, this, SLOT( slotPopupNewView() ), &m_ownActions, "newview" ); + } + + if ( actNewWindow && !isKDesktop ) + { + if (isCurrentTrash) + actNewWindow->setToolTip( i18n( "Open the trash in a new window" ) ); + else if (isSingleMedium) + actNewWindow->setToolTip( i18n( "Open the medium in a new window") ); + else + actNewWindow->setToolTip( i18n( "Open the document in a new window" ) ); + } + + if ( S_ISDIR(mode) && sWriting && !isCurrentTrash ) // A dir, and we can create things into it + { + if ( currentDir && m_pMenuNew ) // Current dir -> add the "new" menu + { + // As requested by KNewMenu : + m_pMenuNew->slotCheckUpToDate(); + m_pMenuNew->setPopupFiles( m_lstPopupURLs ); + + addAction( m_pMenuNew ); + + addSeparator(); + } + else + { + if (d->m_itemFlags & KParts::BrowserExtension::ShowCreateDirectory) + { + KAction *actNewDir = new KAction( i18n( "Create &Folder..." ), "folder_new", 0, this, SLOT( slotPopupNewDir() ), &m_ownActions, "newdir" ); + addAction( actNewDir ); + addSeparator(); + } + } + } else if ( isIntoTrash ) { + // Trashed item, offer restoring + act = new KAction( i18n( "&Restore" ), 0, this, SLOT( slotPopupRestoreTrashedItems() ), &m_ownActions, "restore" ); + addAction( act ); + } + + if (d->m_itemFlags & KParts::BrowserExtension::ShowNavigationItems) + { + if (d->m_itemFlags & KParts::BrowserExtension::ShowUp) + addAction( "up" ); + addAction( "back" ); + addAction( "forward" ); + if (d->m_itemFlags & KParts::BrowserExtension::ShowReload) + addAction( "reload" ); + addSeparator(); + } + + // "open in new window" is either provided by us, or by the tabhandling group + if (actNewWindow) + { + addAction( actNewWindow ); + addSeparator(); + } + addGroup( "tabhandling" ); // includes a separator + + if ( !bIsLink ) + { + if ( !currentDir && sReading ) { + if ( sDeleting ) { + addAction( "cut" ); + } + addAction( "copy" ); + } + + if ( S_ISDIR(mode) && sWriting ) { + if ( currentDir ) + addAction( "paste" ); + else + addAction( "pasteto" ); + } + if ( !currentDir ) + { + if ( m_lstItems.count() == 1 && sMoving ) + addAction( "rename" ); + + bool addTrash = false; + bool addDel = false; + + if ( sMoving && !isIntoTrash && !isTrashLink ) + addTrash = true; + + if ( sDeleting ) { + if ( !isLocal ) + addDel = true; + else if (KApplication::keyboardMouseState() & Qt::ShiftButton) { + addTrash = false; + addDel = true; + } + else { + KConfigGroup configGroup( kapp->config(), "KDE" ); + if ( configGroup.readBoolEntry( "ShowDeleteCommand", false ) ) + addDel = true; + } + } + + if ( addTrash ) + addAction( "trash" ); + if ( addDel ) + addAction( "del" ); + } + } + if ( isCurrentTrash ) + { + act = new KAction( i18n( "&Empty Trash Bin" ), "emptytrash", 0, this, SLOT( slotPopupEmptyTrashBin() ), &m_ownActions, "empytrash" ); + KSimpleConfig trashConfig( "trashrc", true ); + trashConfig.setGroup( "Status" ); + act->setEnabled( !trashConfig.readBoolEntry( "Empty", true ) ); + addAction( act ); + } + addGroup( "editactions" ); + + if (d->m_itemFlags & KParts::BrowserExtension::ShowTextSelectionItems) { + addMerge( 0 ); + m_factory->addClient( this ); + return; + } + + if ( !isCurrentTrash && !isIntoTrash && (d->m_itemFlags & KParts::BrowserExtension::ShowBookmark)) + { + addSeparator(); + QString caption; + if (currentDir) + { + bool httpPage = (m_sViewURL.protocol().find("http", 0, false) == 0); + if (httpPage) + caption = i18n("&Bookmark This Page"); + else + caption = i18n("&Bookmark This Location"); + } + else if (S_ISDIR(mode)) + caption = i18n("&Bookmark This Folder"); + else if (bIsLink) + caption = i18n("&Bookmark This Link"); + else + caption = i18n("&Bookmark This File"); + + act = new KAction( caption, "bookmark_add", 0, this, SLOT( slotPopupAddToBookmark() ), &m_ownActions, "bookmark_add" ); + if (m_lstItems.count() > 1) + act->setEnabled(false); + if (kapp->authorizeKAction("bookmarks")) + addAction( act ); + if (bIsLink) + addGroup( "linkactions" ); + } + + ////////////////////////////////////////////////////// + + const bool isSingleLocal = m_lstItems.count() == 1 && isLocal; + PopupServices s; + KURL urlForServiceMenu( m_lstItems.first()->url() ); + if (isLocal && !isReallyLocal) { // media or system + bool dummy; + urlForServiceMenu = m_lstItems.first()->mostLocalURL(dummy); + } + + // 1 - Look for builtin and user-defined services + if ( m_sMimeType == "application/x-desktop" && isSingleLocal ) // .desktop file + { + // get builtin services, like mount/unmount + s.builtin = KDEDesktopMimeType::builtinServices( urlForServiceMenu ); + const QString path = urlForServiceMenu.path(); + KSimpleConfig cfg( path, true ); + cfg.setDesktopGroup(); + const QString priority = cfg.readEntry("X-KDE-Priority"); + const QString submenuName = cfg.readEntry( "X-KDE-Submenu" ); + if ( cfg.readEntry("Type") == "Link" ) { + urlForServiceMenu = cfg.readEntry("URL"); + // TODO: Do we want to make all the actions apply on the target + // of the .desktop file instead of the .desktop file itself? + } + ServiceList* list = s.selectList( priority, submenuName ); + (*list) = KDEDesktopMimeType::userDefinedServices( path, cfg, urlForServiceMenu.isLocalFile() ); + } + + if ( sReading ) + { + + // 2 - Look for "servicesmenus" bindings (konqueror-specific user-defined services) + + // first check the .directory if this is a directory + if (isDirectory && isSingleLocal) + { + QString dotDirectoryFile = urlForServiceMenu.path(1).append(".directory"); + KSimpleConfig cfg( dotDirectoryFile, true ); + cfg.setDesktopGroup(); + + if (KIOSKAuthorizedAction(cfg)) + { + const QString priority = cfg.readEntry("X-KDE-Priority"); + const QString submenuName = cfg.readEntry( "X-KDE-Submenu" ); + ServiceList* list = s.selectList( priority, submenuName ); + (*list) += KDEDesktopMimeType::userDefinedServices( dotDirectoryFile, cfg, true ); + } + } + + // findAllResources() also removes duplicates + const QStringList entries = KGlobal::dirs()->findAllResources("data", + "konqueror/servicemenus/*.desktop", + false /* recursive */, + true /* unique */); + QStringList::ConstIterator eIt = entries.begin(); + const QStringList::ConstIterator eEnd = entries.end(); + for (; eIt != eEnd; ++eIt ) + { + KSimpleConfig cfg( *eIt, true ); + cfg.setDesktopGroup(); + + if (!KIOSKAuthorizedAction(cfg)) + { + continue; + } + + if ( cfg.hasKey( "X-KDE-ShowIfRunning" ) ) + { + const QString app = cfg.readEntry( "X-KDE-ShowIfRunning" ); + if ( !kapp->dcopClient()->isApplicationRegistered( app.utf8() ) ) + continue; + } + if ( cfg.hasKey( "X-KDE-ShowIfDcopCall" ) ) + { + QString dcopcall = cfg.readEntry( "X-KDE-ShowIfDcopCall" ); + const QCString app = dcopcall.section(' ', 0,0).utf8(); + + //if( !kapp->dcopClient()->isApplicationRegistered( app )) + // continue; //app does not exist so cannot send call + + QByteArray dataToSend; + QDataStream dataStream(dataToSend, IO_WriteOnly); + dataStream << m_lstPopupURLs; + + QCString replyType; + QByteArray replyData; + QCString object = dcopcall.section(' ', 1,-2).utf8(); + QString function = dcopcall.section(' ', -1); + if(!function.endsWith("(KURL::List)")) { + kdWarning() << "Desktop file " << *eIt << " contains an invalid X-KDE-ShowIfDcopCall - the function must take the exact parameter (KURL::List) and must be specified." << endl; + continue; //Be safe. + } + + if(!kapp->dcopClient()->call( app, object, + function.utf8(), + dataToSend, replyType, replyData, true, 1000)) + continue; + if(replyType != "bool" || !replyData[0]) + continue; + + } + if ( cfg.hasKey( "X-KDE-Protocol" ) ) + { + const QString protocol = cfg.readEntry( "X-KDE-Protocol" ); + if ( protocol != urlForServiceMenu.protocol() ) + continue; + } + else if ( cfg.hasKey( "X-KDE-Protocols" ) ) + { + QStringList protocols = QStringList::split( "," , cfg.readEntry( "X-KDE-Protocols" ) ); + if ( !protocols.contains( urlForServiceMenu.protocol() ) ) + continue; + } + else if ( urlForServiceMenu.protocol() == "trash" || urlForServiceMenu.url().startsWith( "system:/trash" ) ) + { + // Require servicemenus for the trash to ask for protocol=trash explicitely. + // Trashed files aren't supposed to be available for actions. + // One might want a servicemenu for trash.desktop itself though. + continue; + } + + if ( cfg.hasKey( "X-KDE-Require" ) ) + { + const QStringList capabilities = cfg.readListEntry( "X-KDE-Require" ); + if ( capabilities.contains( "Write" ) && !sWriting ) + continue; + } + if ( (cfg.hasKey( "Actions" ) || cfg.hasKey( "X-KDE-GetActionMenu") ) && cfg.hasKey( "ServiceTypes" ) ) + { + const QStringList types = cfg.readListEntry( "ServiceTypes" ); + const QStringList excludeTypes = cfg.readListEntry( "ExcludeServiceTypes" ); + bool ok = false; + + // check for exact matches or a typeglob'd mimetype if we have a mimetype + for (QStringList::ConstIterator it = types.begin(); + it != types.end() && !ok; + ++it) + { + // first check if we have an all mimetype + bool checkTheMimetypes = false; + if (*it == "all/all" || + *it == "allfiles" /*compat with KDE up to 3.0.3*/) + { + checkTheMimetypes = true; + } + + // next, do we match all files? + if (!ok && + !isDirectory && + *it == "all/allfiles") + { + checkTheMimetypes = true; + } + + // if we have a mimetype, see if we have an exact or a type globbed match + if (!ok && + (!m_sMimeType.isEmpty() && + *it == m_sMimeType) || + (!mimeGroup.isEmpty() && + ((*it).right(1) == "*" && + (*it).left((*it).find('/')) == mimeGroup))) + { + checkTheMimetypes = true; + } + + if (checkTheMimetypes) + { + ok = true; + for (QStringList::ConstIterator itex = excludeTypes.begin(); itex != excludeTypes.end(); ++itex) + { + if( ((*itex).right(1) == "*" && (*itex).left((*itex).find('/')) == mimeGroup) || + ((*itex) == m_sMimeType) ) + { + ok = false; + break; + } + } + } + } + + if ( ok ) + { + const QString priority = cfg.readEntry("X-KDE-Priority"); + const QString submenuName = cfg.readEntry( "X-KDE-Submenu" ); + + ServiceList* list = s.selectList( priority, submenuName ); + (*list) += KDEDesktopMimeType::userDefinedServices( *eIt, cfg, url.isLocalFile(), m_lstPopupURLs ); + } + } + } + + KTrader::OfferList offers; + + if (kapp->authorizeKAction("openwith")) + { + QString constraint = "Type == 'Application' and DesktopEntryName != 'kfmclient' and DesktopEntryName != 'kfmclient_dir' and DesktopEntryName != 'kfmclient_html'"; + QString subConstraint = " and '%1' in ServiceTypes"; + + QStringList::ConstIterator it = mimeTypeList.begin(); + QStringList::ConstIterator end = mimeTypeList.end(); + Q_ASSERT( it != end ); + QString first = *it; + ++it; + while ( it != end ) { + constraint += subConstraint.arg( *it ); + ++it; + } + + offers = KTrader::self()->query( first, constraint ); + } + + //// Ok, we have everything, now insert + + m_mapPopup.clear(); + m_mapPopupServices.clear(); + // "Open With..." for folders is really not very useful, especially for remote folders. + // (media:/something, or trash:/, or ftp://...) + if ( !isDirectory || isLocal ) + { + if ( hasAction() ) + addSeparator(); + + if ( !offers.isEmpty() ) + { + // First block, app and preview offers + id = 1; + + QDomElement menu = m_menuElement; + + if ( offers.count() > 1 ) // submenu 'open with' + { + menu = m_doc.createElement( "menu" ); + menu.setAttribute( "name", "openwith submenu" ); + m_menuElement.appendChild( menu ); + QDomElement text = m_doc.createElement( "text" ); + menu.appendChild( text ); + text.appendChild( m_doc.createTextNode( i18n("&Open With") ) ); + } + + KTrader::OfferList::ConstIterator it = offers.begin(); + for( ; it != offers.end(); it++ ) + { + KService::Ptr service = (*it); + + // Skip OnlyShowIn=Foo and NotShowIn=KDE entries, + // but still offer NoDisplay=true entries, that's the + // whole point of such desktop files. This is why we don't + // use service->noDisplay() here. + const QString onlyShowIn = service->property("OnlyShowIn", QVariant::String).toString(); + if ( !onlyShowIn.isEmpty() ) { + const QStringList aList = QStringList::split(';', onlyShowIn); + if (!aList.contains("KDE")) + continue; + } + const QString notShowIn = service->property("NotShowIn", QVariant::String).toString(); + if ( !notShowIn.isEmpty() ) { + const QStringList aList = QStringList::split(';', notShowIn); + if (aList.contains("KDE")) + continue; + } + + QCString nam; + nam.setNum( id ); + + QString actionName( (*it)->name().replace("&", "&&") ); + if ( menu == m_menuElement ) // no submenu -> prefix single offer + actionName = i18n( "Open with %1" ).arg( actionName ); + + act = new KAction( actionName, (*it)->pixmap( KIcon::Small ), 0, + this, SLOT( slotRunService() ), + &m_ownActions, nam.prepend( "appservice_" ) ); + addAction( act, menu ); + + m_mapPopup[ id++ ] = *it; + } + + QString openWithActionName; + if ( menu != m_menuElement ) // submenu + { + addSeparator( menu ); + openWithActionName = i18n( "&Other..." ); + } + else + { + openWithActionName = i18n( "&Open With..." ); + } + KAction *openWithAct = new KAction( openWithActionName, 0, this, SLOT( slotPopupOpenWith() ), &m_ownActions, "openwith" ); + addAction( openWithAct, menu ); + } + else // no app offers -> Open With... + { + act = new KAction( i18n( "&Open With..." ), 0, this, SLOT( slotPopupOpenWith() ), &m_ownActions, "openwith" ); + addAction( act ); + } + + } + addGroup( "preview" ); + } + + // Second block, builtin + user + QDomElement actionMenu = m_menuElement; + int userItemCount = 0; + if (s.user.count() + s.userSubmenus.count() + + s.userPriority.count() + s.userPrioritySubmenus.count() > 1) + { + // we have more than one item, so let's make a submenu + actionMenu = m_doc.createElement( "menu" ); + actionMenu.setAttribute( "name", "actions submenu" ); + m_menuElement.appendChild( actionMenu ); + QDomElement text = m_doc.createElement( "text" ); + actionMenu.appendChild( text ); + text.appendChild( m_doc.createTextNode( i18n("Ac&tions") ) ); + } + + userItemCount += insertServicesSubmenus(s.userPrioritySubmenus, actionMenu, false); + userItemCount += insertServices(s.userPriority, actionMenu, false); + + // see if we need to put a separator between our priority items and our regular items + if (userItemCount > 0 && + (s.user.count() > 0 || + s.userSubmenus.count() > 0 || + s.builtin.count() > 0) && + actionMenu.lastChild().toElement().tagName().lower() != "separator") + { + QDomElement separator = m_doc.createElement( "separator" ); + actionMenu.appendChild(separator); + } + + userItemCount += insertServicesSubmenus(s.userSubmenus, actionMenu, false); + userItemCount += insertServices(s.user, actionMenu, false); + userItemCount += insertServices(s.builtin, m_menuElement, true); + + userItemCount += insertServicesSubmenus(s.userToplevelSubmenus, m_menuElement, false); + userItemCount += insertServices(s.userToplevel, m_menuElement, false); + + if ( userItemCount > 0 ) + { + addPendingSeparator(); + } + + if ( !isCurrentTrash && !isIntoTrash && !mediaFiles && sReading ) + addPlugins(); // now it's time to add plugins + + if ( KPropertiesDialog::canDisplay( m_lstItems ) && (kpf & ShowProperties) ) + { + act = new KAction( i18n( "&Properties" ), 0, this, SLOT( slotPopupProperties() ), + &m_ownActions, "properties" ); + addAction( act ); + } + + while ( !m_menuElement.lastChild().isNull() && + m_menuElement.lastChild().toElement().tagName().lower() == "separator" ) + m_menuElement.removeChild( m_menuElement.lastChild() ); + + if ( isDirectory && isLocal ) + { + if ( KFileShare::authorization() == KFileShare::Authorized ) + { + addSeparator(); + act = new KAction( i18n("Share"), 0, this, SLOT( slotOpenShareFileDialog() ), + &m_ownActions, "sharefile" ); + addAction( act ); + } + } + + addMerge( 0 ); + //kdDebug() << k_funcinfo << domDocument().toString() << endl; + + m_factory->addClient( this ); +} + +void KonqPopupMenu::slotOpenShareFileDialog() +{ + KPropertiesDialog* dlg = showPropertiesDialog(); + dlg->showFileSharingPage(); +} + +KonqPopupMenu::~KonqPopupMenu() +{ + m_pluginList.clear(); + delete m_factory; + delete m_builder; + delete d; + //kdDebug(1203) << "~KonqPopupMenu leave" << endl; +} + +void KonqPopupMenu::setURLTitle( const QString& urlTitle ) +{ + d->m_urlTitle = urlTitle; +} + +void KonqPopupMenu::slotPopupNewView() +{ + KURL::List::ConstIterator it = m_lstPopupURLs.begin(); + for ( ; it != m_lstPopupURLs.end(); it++ ) + (void) new KRun(*it); +} + +void KonqPopupMenu::slotPopupNewDir() +{ + if (m_lstPopupURLs.empty()) + return; + + KonqOperations::newDir(d->m_parentWidget, m_lstPopupURLs.first()); +} + +void KonqPopupMenu::slotPopupEmptyTrashBin() +{ + KonqOperations::emptyTrash(); +} + +void KonqPopupMenu::slotPopupRestoreTrashedItems() +{ + KonqOperations::restoreTrashedItems( m_lstPopupURLs ); +} + +void KonqPopupMenu::slotPopupOpenWith() +{ + KRun::displayOpenWithDialog( m_lstPopupURLs ); +} + +void KonqPopupMenu::slotPopupAddToBookmark() +{ + KBookmarkGroup root; + if ( m_lstPopupURLs.count() == 1 ) { + KURL url = m_lstPopupURLs.first(); + QString title = d->m_urlTitle.isEmpty() ? url.prettyURL() : d->m_urlTitle; + root = m_pManager->addBookmarkDialog( url.prettyURL(), title ); + } + else + { + root = m_pManager->root(); + KURL::List::ConstIterator it = m_lstPopupURLs.begin(); + for ( ; it != m_lstPopupURLs.end(); it++ ) + root.addBookmark( m_pManager, (*it).prettyURL(), (*it) ); + } + m_pManager->emitChanged( root ); +} + +void KonqPopupMenu::slotRunService() +{ + QCString senderName = sender()->name(); + int id = senderName.mid( senderName.find( '_' ) + 1 ).toInt(); + + // Is it a usual service (application) + QMap<int,KService::Ptr>::Iterator it = m_mapPopup.find( id ); + if ( it != m_mapPopup.end() ) + { + KRun::run( **it, m_lstPopupURLs ); + return; + } + + // Is it a service specific to desktop entry files ? + QMap<int,KDEDesktopMimeType::Service>::Iterator it2 = m_mapPopupServices.find( id ); + if ( it2 != m_mapPopupServices.end() ) + { + KDEDesktopMimeType::executeService( m_lstPopupURLs, it2.data() ); + } + + return; +} + +void KonqPopupMenu::slotPopupMimeType() +{ + KonqOperations::editMimeType( m_sMimeType ); +} + +void KonqPopupMenu::slotPopupProperties() +{ + (void)showPropertiesDialog(); +} + +KPropertiesDialog* KonqPopupMenu::showPropertiesDialog() +{ + // It may be that the kfileitem was created by hand + // (see KonqKfmIconView::slotMouseButtonPressed) + // In that case, we can get more precise info in the properties + // (like permissions) if we stat the URL. + if ( m_lstItems.count() == 1 ) + { + KFileItem * item = m_lstItems.first(); + if (item->entry().count() == 0) // this item wasn't listed by a slave + { + // KPropertiesDialog will use stat to get more info on the file + return new KPropertiesDialog( item->url(), d->m_parentWidget ); + } + } + return new KPropertiesDialog( m_lstItems, d->m_parentWidget ); +} + +KAction *KonqPopupMenu::action( const QDomElement &element ) const +{ + QCString name = element.attribute( attrName ).ascii(); + KAction *res = m_ownActions.action( name ); + + if ( !res ) + res = m_actions.action( name ); + + if ( !res && m_pMenuNew && strcmp( name, m_pMenuNew->name() ) == 0 ) + return m_pMenuNew; + + return res; +} + +KActionCollection *KonqPopupMenu::actionCollection() const +{ + return const_cast<KActionCollection *>( &m_ownActions ); +} + +QString KonqPopupMenu::mimeType() const +{ + return m_sMimeType; +} + +KonqPopupMenu::ProtocolInfo KonqPopupMenu::protocolInfo() const +{ + return m_info; +} + +void KonqPopupMenu::addPlugins() +{ + // search for Konq_PopupMenuPlugins inspired by simons kpropsdlg + //search for a plugin with the right protocol + KTrader::OfferList plugin_offers; + unsigned int pluginCount = 0; + plugin_offers = KTrader::self()->query( m_sMimeType.isNull() ? QString::fromLatin1( "all/all" ) : m_sMimeType, "'KonqPopupMenu/Plugin' in ServiceTypes"); + if ( plugin_offers.isEmpty() ) + return; // no plugins installed do not bother about it + + KTrader::OfferList::ConstIterator iterator = plugin_offers.begin(); + KTrader::OfferList::ConstIterator end = plugin_offers.end(); + + addGroup( "plugins" ); + // travers the offerlist + for(; iterator != end; ++iterator, ++pluginCount ) { + //kdDebug() << (*iterator)->library() << endl; + KonqPopupMenuPlugin *plugin = + KParts::ComponentFactory:: + createInstanceFromLibrary<KonqPopupMenuPlugin>( QFile::encodeName( (*iterator)->library() ), + this, + (*iterator)->name().latin1() ); + if ( !plugin ) + continue; + // This make the kuick plugin insert its stuff above "Properties" + QString pluginClientName = QString::fromLatin1( "Plugin%1" ).arg( pluginCount ); + addMerge( pluginClientName ); + plugin->domDocument().documentElement().setAttribute( "name", pluginClientName ); + m_pluginList.append( plugin ); + insertChildClient( plugin ); + } + + // ## Where is this used? + addMerge( "plugins" ); +} + +KURL KonqPopupMenu::url() const // ### should be viewURL() +{ + return m_sViewURL; +} + +KFileItemList KonqPopupMenu::fileItemList() const +{ + return m_lstItems; +} + +KURL::List KonqPopupMenu::popupURLList() const +{ + return m_lstPopupURLs; +} + +/** + Plugin +*/ + +KonqPopupMenuPlugin::KonqPopupMenuPlugin( KonqPopupMenu *parent, const char *name ) + : QObject( parent, name ) +{ +} + +KonqPopupMenuPlugin::~KonqPopupMenuPlugin() +{ +} + +#include "konq_popupmenu.moc" diff --git a/libkonq/konq_popupmenu.h b/libkonq/konq_popupmenu.h new file mode 100644 index 000000000..44021713f --- /dev/null +++ b/libkonq/konq_popupmenu.h @@ -0,0 +1,221 @@ +/* This file is part of the KDE project + Copyright (C) 1998, 1999 David Faure <[email protected]> + Copyright (C) 2001 Holger Freyther <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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. +*/ + +#ifndef __konqpopupmenu_h +#define __konqpopupmenu_h + +#include <sys/types.h> + +#include <qpopupmenu.h> +#include <qmap.h> +#include <kaction.h> + +#include <qstringlist.h> + +#include <kfileitem.h> +#include <kmimetype.h> // for KDEDesktopMimeType +#include <libkonq_export.h> + +#include <kparts/browserextension.h> + +#include "konq_xmlguiclient.h" + +typedef QValueList<KDEDesktopMimeType::Service> ServiceList; + +class KPropertiesDialog; +class KNewMenu; +class KService; +class KonqPopupMenuPlugin; +class KBookmarkManager; + +// TODO KDE4: change base class to KPopupMenu, see KAction::slotPopupActivated() +/** + * This class implements the popup menu for URLs in konqueror and kdesktop + * It's usage is very simple : on right click, create the KonqPopupMenu instance + * with the correct arguments, then exec() to make it appear, then destroy it. + * + */ +class LIBKONQ_EXPORT KonqPopupMenu : public QPopupMenu, public KonqXMLGUIClient +{ + Q_OBJECT +public: + + /** + * Flags set by the calling application (konqueror/kdesktop), unlike + * KParts::BrowserExtension::PopupFlags, which are set by the calling part + */ + typedef uint KonqPopupFlags; + enum { NoFlags = 0, + ShowProperties = 1, ///< whether to show the "Properties" menu item + IsLink = 2, ///< HTML link. If set, we won't have cut/copy/paste, and we'll say "bookmark this link" + ShowNewWindow = 4 }; + // WARNING: bitfield. Next item is 8 + + /** + * @deprecated lacks parentWidget pointer, and + * uses bool instead of KonqPopupFlags enum, + * might do strange things with the 'new window' action. + */ + KonqPopupMenu( KBookmarkManager* manager, + const KFileItemList &items, + KURL viewURL, + KActionCollection & actions, + KNewMenu * newMenu, + bool showPropertiesAndFileType = true ) KDE_DEPRECATED; + + /** + * @deprecated uses bool instead of KonqPopupFlags enum, + * might do strange things with the 'new window' action. + */ + KonqPopupMenu( KBookmarkManager* manager, + const KFileItemList &items, + KURL viewURL, + KActionCollection & actions, + KNewMenu * newMenu, + QWidget * parentWidget, + bool showPropertiesAndFileType = true ) KDE_DEPRECATED; + + /** + * Constructor + * @param manager the bookmark manager for this bookmark + * @param items the list of file items the popupmenu should be shown for + * @param viewURL the URL shown in the view, to test for RMB click on view background + * @param actions list of actions the caller wants to see in the menu + * @param newMenu "New" menu, shared with the File menu, in konqueror + * @param parentWidget the widget we're showing this popup for. Helps destroying + * the popup if the widget is destroyed before the popup. + * @param kpf flags from the KonqPopupFlags enum, set by the calling application + * @param f flags from the BrowserExtension enum, set by the calling part + * + * The actions to pass in include : + * showmenubar, back, forward, up, cut, copy, paste, pasteto, trash, rename, del + * The others items are automatically inserted. + * + * @since 3.2 + * + * @todo that list is probably not be up-to-date + */ + KonqPopupMenu( KBookmarkManager* manager, + const KFileItemList &items, + const KURL& viewURL, + KActionCollection & actions, + KNewMenu * newMenu, + QWidget * parentWidget, + KonqPopupFlags kpf, + KParts::BrowserExtension::PopupFlags f /*= KParts::BrowserExtension::DefaultPopupItems*/); + + /** + * Don't forget to destroy the object + */ + ~KonqPopupMenu(); + + /** + * Set the title of the URL, when the popupmenu is opened for a single URL. + * This is used if the user chooses to add a bookmark for this URL. + */ + void setURLTitle( const QString& urlTitle ); + + class LIBKONQ_EXPORT ProtocolInfo { + public: + ProtocolInfo(); + bool supportsReading() const; + bool supportsWriting() const; + bool supportsDeleting() const; + bool supportsMoving() const; + bool trashIncluded() const; + private: + friend class KonqPopupMenu; + bool m_Reading:1; + bool m_Writing:1; + bool m_Deleting:1; + bool m_Moving:1; + bool m_TrashIncluded:1; + }; + /** + * Reimplemented for internal purpose + */ + virtual KAction *action( const QDomElement &element ) const; + + + virtual KActionCollection *actionCollection() const; + QString mimeType( ) const; + KURL url( ) const; + KFileItemList fileItemList() const; + KURL::List popupURLList( ) const; + ProtocolInfo protocolInfo() const; + +public slots: // KDE4: why public? + void slotPopupNewDir(); + void slotPopupNewView(); + void slotPopupEmptyTrashBin(); + void slotPopupRestoreTrashedItems(); + void slotPopupOpenWith(); + void slotPopupAddToBookmark(); + void slotRunService(); + void slotPopupMimeType(); + void slotPopupProperties(); + void slotOpenShareFileDialog(); +protected: + KActionCollection &m_actions; + KActionCollection m_ownActions; + +private: + void init (QWidget * parentWidget, KonqPopupFlags kpf, KParts::BrowserExtension::PopupFlags itemFlags); + void setup(KonqPopupFlags kpf); + void addPlugins( ); + int insertServicesSubmenus(const QMap<QString, ServiceList>& list, QDomElement& menu, bool isBuiltin); + int insertServices(const ServiceList& list, QDomElement& menu, bool isBuiltin); + bool KIOSKAuthorizedAction(KConfig& cfg); + KPropertiesDialog* showPropertiesDialog(); + + class KonqPopupMenuPrivate; + KonqPopupMenuPrivate *d; + KNewMenu *m_pMenuNew; + KURL m_sViewURL; + QString m_sMimeType; + KFileItemList m_lstItems; + KURL::List m_lstPopupURLs; + QMap<int,KService::Ptr> m_mapPopup; + QMap<int,KDEDesktopMimeType::Service> m_mapPopupServices; + bool m_bHandleEditOperations; + KXMLGUIFactory *m_factory; + KXMLGUIBuilder *m_builder; + QString attrName; + ProtocolInfo m_info; + QPtrList<KonqPopupMenuPlugin> m_pluginList; + KBookmarkManager* m_pManager; +}; + +class LIBKONQ_EXPORT KonqPopupMenuPlugin : public QObject, public KonqXMLGUIClient { + Q_OBJECT +public: + /** + * Constructor + * If you want to insert a dynamic item or menu to konqpopupmenu + * this class is the right choice. + * Create a KAction and use _popup->addAction(new KAction ); + * If you want to create a submenu use _popup->addGroup( ); + */ + KonqPopupMenuPlugin( KonqPopupMenu *_popup, const char *name ); // this should also be the parent + virtual ~KonqPopupMenuPlugin ( ); +}; + +#endif + diff --git a/libkonq/konq_propsview.cc b/libkonq/konq_propsview.cc new file mode 100644 index 000000000..c2cc3dd3d --- /dev/null +++ b/libkonq/konq_propsview.cc @@ -0,0 +1,584 @@ +/* This file is part of the KDE project + Copyright (C) 1998, 1999 Faure David <[email protected]> + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "konq_propsview.h" +#include "konq_settings.h" + +#include <kdebug.h> +#include <kstandarddirs.h> +#include <kpixmap.h> +#include <qpixmapcache.h> +#include <qiconview.h> +#include <unistd.h> +#include <qfile.h> +#include <iostream> +#include <ktrader.h> +#include <kinstance.h> +#include <assert.h> + +#include <ksimpleconfig.h> + +static QPixmap wallpaperPixmap( const QString & _wallpaper ) +{ + QString key = "wallpapers/"; + key += _wallpaper; + KPixmap pix; + + if ( QPixmapCache::find( key, pix ) ) + return pix; + + QString path = locate("tiles", _wallpaper); + if (path.isEmpty()) + path = locate("wallpaper", _wallpaper); + if (!path.isEmpty()) + { + // This looks really ugly, especially on an 8bit display. + // I'm not sure what it's good for. + // Anyway, if you change it here, keep konq_bgnddlg in sync (David) + // pix.load( path, 0, KPixmap::LowColor ); + pix.load( path ); + if ( pix.isNull() ) + kdWarning(1203) << "Could not load wallpaper " << path << endl; + else + QPixmapCache::insert( key, pix ); + return pix; + } else kdWarning(1203) << "Couldn't locate wallpaper " << _wallpaper << endl; + return QPixmap(); +} + +struct KonqPropsView::Private +{ + QStringList* previewsToShow; + bool previewsEnabled:1; + bool caseInsensitiveSort:1; + bool dirsfirst:1; + bool descending:1; + QString sortcriterion; +}; + +KonqPropsView::KonqPropsView( KInstance * instance, KonqPropsView * defaultProps ) + : m_bSaveViewPropertiesLocally( false ), // will be overridden by setSave... anyway + // if this is the default properties instance, then keep config object for saving + m_dotDirExists( true ), // HACK so that enterDir returns true initially + m_currentConfig( defaultProps ? 0L : instance->config() ), + m_defaultProps( defaultProps ) +{ + + KConfig *config = instance->config(); + KConfigGroupSaver cgs(config, "Settings"); + + d = new Private; + d->previewsToShow = 0; + d->caseInsensitiveSort=config->readBoolEntry( "CaseInsensitiveSort", true ); + + m_iIconSize = config->readNumEntry( "IconSize", 0 ); + m_iItemTextPos = config->readNumEntry( "ItemTextPos", QIconView::Bottom ); + d->sortcriterion = config->readEntry( "SortingCriterion", "sort_nci" ); + d->dirsfirst = config->readBoolEntry( "SortDirsFirst", true ); + d->descending = config->readBoolEntry( "SortDescending", false ); + m_bShowDot = config->readBoolEntry( "ShowDotFiles", false ); + m_bShowDirectoryOverlays = config->readBoolEntry( "ShowDirectoryOverlays", false ); + + m_dontPreview = config->readListEntry( "DontPreview" ); + m_dontPreview.remove("audio/"); //Use the separate setting. + //We default to this off anyway, so it's no harm to remove this + + //The setting for sound previews is stored separately, so we can force + //the default-to-off bias to propagate up. + if (!config->readBoolEntry("EnableSoundPreviews", false)) + { + if (!m_dontPreview.contains("audio/")) + m_dontPreview.append("audio/"); + } + + d->previewsEnabled = config->readBoolEntry( "PreviewsEnabled", true ); + + QColor tc = KonqFMSettings::settings()->normalTextColor(); + m_textColor = config->readColorEntry( "TextColor", &tc ); + m_bgColor = config->readColorEntry( "BgColor" ); // will be set to QColor() if not found + m_bgPixmapFile = config->readPathEntry( "BgImage" ); + //kdDebug(1203) << "KonqPropsView::KonqPropsView from \"config\" : BgImage=" << m_bgPixmapFile << endl; + + // colorsConfig is either the local file (.directory) or the application global file + // (we want the same colors for all types of view) + // The code above reads from the view's config file, for compatibility only. + // So now we read the settings from the app global file, if this is the default props + if (!defaultProps) + { + KConfigGroupSaver cgs2(KGlobal::config(), "Settings"); + m_textColor = KGlobal::config()->readColorEntry( "TextColor", &m_textColor ); + m_bgColor = KGlobal::config()->readColorEntry( "BgColor", &m_bgColor ); + m_bgPixmapFile = KGlobal::config()->readPathEntry( "BgImage", m_bgPixmapFile ); + //kdDebug(1203) << "KonqPropsView::KonqPropsView from KGlobal : BgImage=" << m_bgPixmapFile << endl; + } + + KGlobal::dirs()->addResourceType("tiles", + KGlobal::dirs()->kde_default("data") + "konqueror/tiles/"); +} + +bool KonqPropsView::isCaseInsensitiveSort() const +{ + return d->caseInsensitiveSort; +} + +bool KonqPropsView::isDirsFirst() const +{ + return d->dirsfirst; +} + +bool KonqPropsView::isDescending() const +{ + return d->descending; +} + +KConfigBase * KonqPropsView::currentConfig() +{ + if ( !m_currentConfig ) + { + // 0L ? This has to be a non-default save-locally instance... + assert ( m_bSaveViewPropertiesLocally ); + assert ( !isDefaultProperties() ); + + if (!dotDirectory.isEmpty()) + m_currentConfig = new KSimpleConfig( dotDirectory ); + // the "else" is when we want to save locally but this is a remote URL -> no save + } + return m_currentConfig; +} + +KConfigBase * KonqPropsView::currentColorConfig() +{ + // Saving locally ? + if ( m_bSaveViewPropertiesLocally && !isDefaultProperties() ) + return currentConfig(); // Will create it if necessary + else + // Save color settings in app's file, not in view's file + return KGlobal::config(); +} + +KonqPropsView::~KonqPropsView() +{ + delete d->previewsToShow; + delete d; + d=0; +} + +bool KonqPropsView::enterDir( const KURL & dir ) +{ + //kdDebug(1203) << "enterDir " << dir.prettyURL() << endl; + // Can't do that with default properties + assert( !isDefaultProperties() ); + + // Check for .directory + KURL u ( dir ); + u.addPath(".directory"); + bool dotDirExists = u.isLocalFile() && QFile::exists( u.path() ); + dotDirectory = u.isLocalFile() ? u.path() : QString::null; + + // Revert to default setting first - unless there is no .directory + // in the previous dir nor in this one (then we can keep the current settings) + if (dotDirExists || m_dotDirExists) + { + m_iIconSize = m_defaultProps->iconSize(); + m_iItemTextPos = m_defaultProps->itemTextPos(); + d->sortcriterion = m_defaultProps->sortCriterion(); + d->dirsfirst = m_defaultProps->isDirsFirst(); + d->descending = m_defaultProps->isDescending(); + m_bShowDot = m_defaultProps->isShowingDotFiles(); + d->caseInsensitiveSort=m_defaultProps->isCaseInsensitiveSort(); + m_dontPreview = m_defaultProps->m_dontPreview; + m_textColor = m_defaultProps->m_textColor; + m_bgColor = m_defaultProps->m_bgColor; + m_bgPixmapFile = m_defaultProps->bgPixmapFile(); + } + + if (dotDirExists) + { + //kdDebug(1203) << "Found .directory file" << endl; + KSimpleConfig * config = new KSimpleConfig( dotDirectory, true ); + config->setGroup("URL properties"); + + m_iIconSize = config->readNumEntry( "IconSize", m_iIconSize ); + m_iItemTextPos = config->readNumEntry( "ItemTextPos", m_iItemTextPos ); + d->sortcriterion = config->readEntry( "SortingCriterion" , d->sortcriterion ); + d->dirsfirst = config->readBoolEntry( "SortDirsFirst", d->dirsfirst ); + d->descending = config->readBoolEntry( "SortDescending", d->descending ); + m_bShowDot = config->readBoolEntry( "ShowDotFiles", m_bShowDot ); + d->caseInsensitiveSort=config->readBoolEntry("CaseInsensitiveSort",d->caseInsensitiveSort); + m_bShowDirectoryOverlays = config->readBoolEntry( "ShowDirectoryOverlays", m_bShowDirectoryOverlays ); + if (config->hasKey( "DontPreview" )) + { + m_dontPreview = config->readListEntry( "DontPreview" ); + + //If the .directory file says something about sound previews, + //obey it, otherwise propagate the setting up from the defaults + //All this really should be split into a per-thumbnail setting, + //but that's too invasive at this point + if (config->hasKey("EnableSoundPreviews")) + { + + if (!config->readBoolEntry("EnableSoundPreviews", false)) + if (!m_dontPreview.contains("audio/")) + m_dontPreview.append("audio/"); + } + else + { + if (m_defaultProps->m_dontPreview.contains("audio/")) + if (!m_dontPreview.contains("audio/")) + m_dontPreview.append("audio/"); + } + } + + + + m_textColor = config->readColorEntry( "TextColor", &m_textColor ); + m_bgColor = config->readColorEntry( "BgColor", &m_bgColor ); + m_bgPixmapFile = config->readPathEntry( "BgImage", m_bgPixmapFile ); + //kdDebug(1203) << "KonqPropsView::enterDir m_bgPixmapFile=" << m_bgPixmapFile << endl; + d->previewsEnabled = config->readBoolEntry( "PreviewsEnabled", d->previewsEnabled ); + delete config; + } + //if there is or was a .directory then the settings probably have changed + bool configChanged=(m_dotDirExists|| dotDirExists); + m_dotDirExists = dotDirExists; + m_currentConfig = 0L; // new dir, not current config for saving yet + //kdDebug(1203) << "KonqPropsView::enterDir returning " << configChanged << endl; + return configChanged; +} + +void KonqPropsView::setSaveViewPropertiesLocally( bool value ) +{ + assert( !isDefaultProperties() ); + //kdDebug(1203) << "KonqPropsView::setSaveViewPropertiesLocally " << value << endl; + + if ( m_bSaveViewPropertiesLocally ) + delete m_currentConfig; // points to a KSimpleConfig + + m_bSaveViewPropertiesLocally = value; + m_currentConfig = 0L; // mark as dirty +} + +void KonqPropsView::setIconSize( int size ) +{ + m_iIconSize = size; + if ( m_defaultProps && !m_bSaveViewPropertiesLocally ) + m_defaultProps->setIconSize( size ); + else if (currentConfig()) + { + KConfigGroupSaver cgs(currentConfig(), currentGroup()); + currentConfig()->writeEntry( "IconSize", m_iIconSize ); + currentConfig()->sync(); + } +} + +void KonqPropsView::setItemTextPos( int pos ) +{ + m_iItemTextPos = pos; + if ( m_defaultProps && !m_bSaveViewPropertiesLocally ) + m_defaultProps->setItemTextPos( pos ); + else if (currentConfig()) + { + KConfigGroupSaver cgs(currentConfig(), currentGroup()); + currentConfig()->writeEntry( "ItemTextPos", m_iItemTextPos ); + currentConfig()->sync(); + } +} + +void KonqPropsView::setSortCriterion( const QString &criterion ) +{ + d->sortcriterion = criterion; + if ( m_defaultProps && !m_bSaveViewPropertiesLocally ) + m_defaultProps->setSortCriterion( criterion ); + else if (currentConfig()) + { + KConfigGroupSaver cgs(currentConfig(), currentGroup()); + currentConfig()->writeEntry( "SortingCriterion", d->sortcriterion ); + currentConfig()->sync(); + } +} + +void KonqPropsView::setDirsFirst( bool first) +{ + d->dirsfirst = first; + if ( m_defaultProps && !m_bSaveViewPropertiesLocally ) + m_defaultProps->setDirsFirst( first ); + else if (currentConfig()) + { + KConfigGroupSaver cgs(currentConfig(), currentGroup()); + currentConfig()->writeEntry( "SortDirsFirst", d->dirsfirst ); + currentConfig()->sync(); + } +} + +void KonqPropsView::setDescending( bool descend) +{ + d->descending = descend; + if ( m_defaultProps && !m_bSaveViewPropertiesLocally ) + m_defaultProps->setDescending( descend ); + else if (currentConfig()) + { + KConfigGroupSaver cgs(currentConfig(), currentGroup()); + currentConfig()->writeEntry( "SortDescending", d->descending ); + currentConfig()->sync(); + } +} + +void KonqPropsView::setShowingDotFiles( bool show ) +{ + kdDebug(1203) << "KonqPropsView::setShowingDotFiles " << show << endl; + m_bShowDot = show; + if ( m_defaultProps && !m_bSaveViewPropertiesLocally ) + { + kdDebug(1203) << "Saving in default properties" << endl; + m_defaultProps->setShowingDotFiles( show ); + } + else if (currentConfig()) + { + kdDebug(1203) << "Saving in current config" << endl; + KConfigGroupSaver cgs(currentConfig(), currentGroup()); + currentConfig()->writeEntry( "ShowDotFiles", m_bShowDot ); + currentConfig()->sync(); + } +} + +void KonqPropsView::setCaseInsensitiveSort( bool on ) +{ + kdDebug(1203) << "KonqPropsView::setCaseInsensitiveSort " << on << endl; + d->caseInsensitiveSort = on; + if ( m_defaultProps && !m_bSaveViewPropertiesLocally ) + { + kdDebug(1203) << "Saving in default properties" << endl; + m_defaultProps->setCaseInsensitiveSort( on ); + } + else if (currentConfig()) + { + kdDebug(1203) << "Saving in current config" << endl; + KConfigGroupSaver cgs(currentConfig(), currentGroup()); + currentConfig()->writeEntry( "CaseInsensitiveSort", d->caseInsensitiveSort ); + currentConfig()->sync(); + } +} + +void KonqPropsView::setShowingDirectoryOverlays( bool show ) +{ + kdDebug(1203) << "KonqPropsView::setShowingDirectoryOverlays " << show << endl; + m_bShowDirectoryOverlays = show; + if ( m_defaultProps && !m_bSaveViewPropertiesLocally ) + { + kdDebug(1203) << "Saving in default properties" << endl; + m_defaultProps->setShowingDirectoryOverlays( show ); + } + else if (currentConfig()) + { + kdDebug(1203) << "Saving in current config" << endl; + KConfigGroupSaver cgs(currentConfig(), currentGroup()); + currentConfig()->writeEntry( "ShowDirectoryOverlays", m_bShowDirectoryOverlays ); + currentConfig()->sync(); + } +} + +void KonqPropsView::setShowingPreview( const QString &preview, bool show ) +{ + if ( m_dontPreview.contains( preview ) != show ) + return; + else if ( show ) + m_dontPreview.remove( preview ); + else + m_dontPreview.append( preview ); + if ( m_defaultProps && !m_bSaveViewPropertiesLocally ) + m_defaultProps->setShowingPreview( preview, show ); + else if (currentConfig()) + { + KConfigGroupSaver cgs(currentConfig(), currentGroup()); + + //Audio is special-cased, as we use a binary setting + //for it to get it to follow the defaults right. + bool audioEnabled = !m_dontPreview.contains("audio/"); + + //Don't write it out into the DontPreview line + if (!audioEnabled) + m_dontPreview.remove("audio/"); + currentConfig()->writeEntry( "DontPreview", m_dontPreview ); + currentConfig()->writeEntry( "EnableSoundPreviews", audioEnabled ); + currentConfig()->sync(); + if (!audioEnabled) + m_dontPreview.append("audio/"); + + } + + delete d->previewsToShow; + d->previewsToShow = 0; +} + +void KonqPropsView::setShowingPreview( bool show ) +{ + d->previewsEnabled = show; + + if ( m_defaultProps && !m_bSaveViewPropertiesLocally ) + { + kdDebug(1203) << "Saving in default properties" << endl; + m_defaultProps-> setShowingPreview( show ); + } + else if (currentConfig()) + { + kdDebug(1203) << "Saving in current config" << endl; + KConfigGroupSaver cgs(currentConfig(), currentGroup()); + currentConfig()->writeEntry( "PreviewsEnabled", d->previewsEnabled ); + currentConfig()->sync(); + } + + delete d->previewsToShow; + d->previewsToShow = 0; +} + +bool KonqPropsView::isShowingPreview() +{ + return d->previewsEnabled; +} + +void KonqPropsView::setBgColor( const QColor & color ) +{ + m_bgColor = color; + if ( m_defaultProps && !m_bSaveViewPropertiesLocally ) + { + m_defaultProps->setBgColor( color ); + } + else + { + KConfigBase * colorConfig = currentColorConfig(); + if (colorConfig) // 0L when saving locally but remote URL + { + KConfigGroupSaver cgs(colorConfig, currentGroup()); + colorConfig->writeEntry( "BgColor", m_bgColor ); + colorConfig->sync(); + } + } +} + +const QColor & KonqPropsView::bgColor( QWidget * widget ) const +{ + if ( !m_bgColor.isValid() ) + return widget->colorGroup().base(); + else + return m_bgColor; +} + +void KonqPropsView::setTextColor( const QColor & color ) +{ + m_textColor = color; + if ( m_defaultProps && !m_bSaveViewPropertiesLocally ) + { + m_defaultProps->setTextColor( color ); + } + else + { + KConfigBase * colorConfig = currentColorConfig(); + if (colorConfig) // 0L when saving locally but remote URL + { + KConfigGroupSaver cgs(colorConfig, currentGroup()); + colorConfig->writeEntry( "TextColor", m_textColor ); + colorConfig->sync(); + } + } +} + +const QColor & KonqPropsView::textColor( QWidget * widget ) const +{ + if ( !m_textColor.isValid() ) + return widget->colorGroup().text(); + else + return m_textColor; +} + +void KonqPropsView::setBgPixmapFile( const QString & file ) +{ + m_bgPixmapFile = file; + + if ( m_defaultProps && !m_bSaveViewPropertiesLocally ) + { + m_defaultProps->setBgPixmapFile( file ); + } + else + { + KConfigBase * colorConfig = currentColorConfig(); + if (colorConfig) // 0L when saving locally but remote URL + { + KConfigGroupSaver cgs(colorConfig, currentGroup()); + colorConfig->writePathEntry( "BgImage", file ); + colorConfig->sync(); + } + } +} + +QPixmap KonqPropsView::loadPixmap() const +{ + //kdDebug(1203) << "KonqPropsView::loadPixmap " << m_bgPixmapFile << endl; + QPixmap bgPixmap; + if ( !m_bgPixmapFile.isEmpty() ) + bgPixmap = wallpaperPixmap( m_bgPixmapFile ); + return bgPixmap; +} + +void KonqPropsView::applyColors(QWidget * widget) const +{ + if ( m_bgPixmapFile.isEmpty() ) + widget->setPaletteBackgroundColor( bgColor( widget ) ); + else + { + QPixmap pix = loadPixmap(); + // don't set an null pixmap, as this leads to + // undefined results with regards to the background of widgets + // that have the iconview as a parent and on the iconview itself + // e.g. the rename textedit widget when renaming a QIconViewItem + // Qt-issue: N64698 + if ( ! pix.isNull() ) + widget->setBackgroundPixmap( pix ); + // setPaletteBackgroundPixmap leads to flicker on window activation(!) + } + + if ( m_textColor.isValid() ) + widget->setPaletteForegroundColor( m_textColor ); +} + +const QStringList& KonqPropsView::previewSettings() +{ + if ( ! d->previewsToShow ) + { + d->previewsToShow = new QStringList; + + if (d->previewsEnabled) { + KTrader::OfferList plugins = KTrader::self()->query( "ThumbCreator" ); + for ( KTrader::OfferList::ConstIterator it = plugins.begin(); it != plugins.end(); ++it ) + { + QString name = (*it)->desktopEntryName(); + if ( ! m_dontPreview.contains(name) ) + d->previewsToShow->append( name ); + } + if ( ! m_dontPreview.contains( "audio/" ) ) + d->previewsToShow->append( "audio/" ); + } + } + + return *(d->previewsToShow); +} + +const QString& KonqPropsView::sortCriterion() const { + return d->sortcriterion; +} + diff --git a/libkonq/konq_propsview.h b/libkonq/konq_propsview.h new file mode 100644 index 000000000..679ce8a0e --- /dev/null +++ b/libkonq/konq_propsview.h @@ -0,0 +1,185 @@ +/* This file is part of the KDE project + Copyright (C) 1997 David Faure <[email protected]> + + 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_viewprops_h__ +#define __konq_viewprops_h__ + +#include <qpixmap.h> +#include <qstringlist.h> + +#include <kurl.h> +#include <libkonq_export.h> + +class KInstance; +class KConfigBase; +class KConfig; + +/** + * The class KonqPropsView holds the properties for a Konqueror View + * + * Separating them from the view class allows to store the default + * values (the one read from \<kinstance\>rc) in one instance of this class + * and to have another instance of this class in each view, storing the + * current values of the view. + * + * The local values can be read from a desktop entry, if any (.directory, + * bookmark, ...). [ .directory is implemented, bookmark isn't ]. + */ +class LIBKONQ_EXPORT KonqPropsView +{ +public: + + /** + * Constructs a KonqPropsView instance from an instance config file. + * defaultProps is a "parent" object. If non null, then this instance + * is the one used by a view, and its value can differ from the default ones. + * The instance parameter should be the same for both... + */ + KonqPropsView( KInstance * instance, KonqPropsView * defaultProps /*= 0L*/ ); + + /** Destructor */ + virtual ~KonqPropsView(); + + /** + * Is this the instance representing default properties ? + */ + bool isDefaultProperties() const { + // No parent -> we are the default properties + return m_defaultProps == 0L; + } + + /** + * Called when entering a directory + * Checks for a .directory, read it. + * Don't do this on the default properties instance + * Returns TRUE if the settings for the new directories are + * different from the settings in the old directory. + */ + bool enterDir( const KURL & dir ); + + /** + * Turn on/off saving properties locally + * Don't do this on the default properties instance + */ + void setSaveViewPropertiesLocally( bool value ); + + /// + + void setIconSize( int size ); // in pixel, 0 for default + int iconSize() const { return m_iIconSize; } + + void setItemTextPos( int pos ); // QIconView::Bottom or QIconView::Right, currently + int itemTextPos() const { return m_iItemTextPos; } + + void setSortCriterion( const QString &criterion ); + const QString& sortCriterion() const; + + void setDirsFirst ( bool first ); + bool isDirsFirst() const; + + void setDescending (bool descending); + bool isDescending() const; + + void setShowingDotFiles( bool show ); + bool isShowingDotFiles() const { return m_bShowDot; } + + void setCaseInsensitiveSort( bool show ); + bool isCaseInsensitiveSort() const; + + void setShowingDirectoryOverlays( bool show ); + bool isShowingDirectoryOverlays() const { return m_bShowDirectoryOverlays; } + + void setShowingPreview( const QString &preview, bool show ); + void setShowingPreview( bool show ); + bool isShowingPreview( const QString &preview ) const { return ! m_dontPreview.contains(preview); } + bool isShowingPreview(); + const QStringList &previewSettings(); + + void setBgColor( const QColor & color ); + const QColor& bgColor(QWidget * widget) const; + void setTextColor( const QColor & color ); + const QColor& textColor(QWidget * widget) const; + void setBgPixmapFile( const QString & file ); + const QString& bgPixmapFile() const { return m_bgPixmapFile; } + + // Applies bgcolor, textcolor, pixmap to the @p widget + void applyColors( QWidget * widget ) const; + +protected: + + QPixmap loadPixmap() const; + + // Current config object for _saving_ + KConfigBase * currentConfig(); + + // Current config object for _saving_ settings related to colors + KConfigBase * currentColorConfig(); + + QString currentGroup() const { + return isDefaultProperties() ? + QString::fromLatin1("Settings") : QString::fromLatin1("URL properties"); + } + +private: + // The actual properties + + int m_iIconSize; + int m_iItemTextPos; + bool m_bShowDot; + bool m_bShowDirectoryOverlays; + QStringList m_dontPreview; + QColor m_textColor; + QColor m_bgColor; + QString m_bgPixmapFile; + + // Path to .directory file, whether it exists or not + QString dotDirectory; + + bool m_bSaveViewPropertiesLocally; + + // True if we found a .directory file to read + bool m_dotDirExists; + + // Points to the current .directory file if we are in + // save-view-properties-locally mode, otherwise to the global config + // It is set to 0L to mark it as "needs to be constructed". + // This is to be used for SAVING only. + // Can be a KConfig or a KSimpleConfig + KConfigBase * m_currentConfig; + + // If this is not a "default properties" instance (but one used by a view) + // then m_defaultProps points to the "default properties" instance + // Otherwise it's 0L. + KonqPropsView * m_defaultProps; + + /** + * Private data for KonqPropsView + * Implementation in konq_propsview.cc + */ + struct Private; + + Private *d; + +private: + KonqPropsView( const KonqPropsView & ); + KonqPropsView(); +}; + + +#endif diff --git a/libkonq/konq_settings.cc b/libkonq/konq_settings.cc new file mode 100644 index 000000000..07866b08b --- /dev/null +++ b/libkonq/konq_settings.cc @@ -0,0 +1,184 @@ +/* This file is part of the KDE project + Copyright (C) 1999 David Faure <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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 "konq_settings.h" +#include "konq_defaults.h" +#include "kglobalsettings.h" +#include <kglobal.h> +#include <kservicetype.h> +#include <kdesktopfile.h> +#include <kdebug.h> +#include <assert.h> +#include <qfontmetrics.h> + +struct KonqFMSettingsPrivate +{ + KonqFMSettingsPrivate() { + showPreviewsInFileTips = true; + m_renameIconDirectly = false; + } + + bool showPreviewsInFileTips; + bool m_renameIconDirectly; + bool localeAwareCompareIsCaseSensitive; + int m_iconTextWidth; +}; + +//static +KonqFMSettings * KonqFMSettings::s_pSettings = 0L; + +//static +KonqFMSettings * KonqFMSettings::settings() +{ + if (!s_pSettings) + { + KConfig *config = KGlobal::config(); + KConfigGroupSaver cgs(config, "FMSettings"); + s_pSettings = new KonqFMSettings(config); + } + return s_pSettings; +} + +//static +void KonqFMSettings::reparseConfiguration() +{ + if (s_pSettings) + { + KConfig *config = KGlobal::config(); + KConfigGroupSaver cgs(config, "FMSettings"); + s_pSettings->init( config ); + } +} + +KonqFMSettings::KonqFMSettings( KConfig * config ) +{ + d = new KonqFMSettingsPrivate; + init( config ); +} + +KonqFMSettings::~KonqFMSettings() +{ + delete d; +} + +void KonqFMSettings::init( KConfig * config ) +{ + // Fonts and colors + m_standardFont = config->readFontEntry( "StandardFont" ); + + m_normalTextColor = KGlobalSettings::textColor(); + m_normalTextColor = config->readColorEntry( "NormalTextColor", &m_normalTextColor ); + m_highlightedTextColor = KGlobalSettings::highlightedTextColor(); + m_highlightedTextColor = config->readColorEntry( "HighlightedTextColor", &m_highlightedTextColor ); + m_itemTextBackground = config->readColorEntry( "ItemTextBackground" ); + + d->m_iconTextWidth = config->readNumEntry( "TextWidth", DEFAULT_TEXTWIDTH ); + if ( d->m_iconTextWidth == DEFAULT_TEXTWIDTH ) + d->m_iconTextWidth = QFontMetrics(m_standardFont).width("0000000000"); + + m_iconTextHeight = config->readNumEntry( "TextHeight", 0 ); + if ( m_iconTextHeight == 0 ) { + if ( config->readBoolEntry( "WordWrapText", true ) ) + m_iconTextHeight = DEFAULT_TEXTHEIGHT; + else + m_iconTextHeight = 1; + } + m_bWordWrapText = ( m_iconTextHeight > 1 ); + + m_underlineLink = config->readBoolEntry( "UnderlineLinks", DEFAULT_UNDERLINELINKS ); + d->m_renameIconDirectly = config->readBoolEntry( "RenameIconDirectly", DEFAULT_RENAMEICONDIRECTLY ); + m_fileSizeInBytes = config->readBoolEntry( "DisplayFileSizeInBytes", DEFAULT_FILESIZEINBYTES ); + m_iconTransparency = config->readNumEntry( "TextpreviewIconOpacity", DEFAULT_TEXTPREVIEW_ICONTRANSPARENCY ); + if ( m_iconTransparency < 0 || m_iconTransparency > 255 ) + m_iconTransparency = DEFAULT_TEXTPREVIEW_ICONTRANSPARENCY; + + // Behaviour + m_alwaysNewWin = config->readBoolEntry( "AlwaysNewWin", FALSE ); + + m_homeURL = config->readPathEntry("HomeURL", "~"); + + m_showFileTips = config->readBoolEntry("ShowFileTips", true); + d->showPreviewsInFileTips = config->readBoolEntry("ShowPreviewsInFileTips", true); + m_numFileTips = config->readNumEntry("FileTipsItems", 6); + + m_embedMap = config->entryMap( "EmbedSettings" ); + + /// true if QString::localeAwareCompare is case sensitive (it usually isn't, when LC_COLLATE is set) + d->localeAwareCompareIsCaseSensitive = QString( "a" ).localeAwareCompare( "B" ) > 0; // see #40131 +} + +bool KonqFMSettings::shouldEmbed( const QString & serviceType ) const +{ + // First check in user's settings whether to embed or not + // 1 - in the mimetype file itself + KServiceType::Ptr serviceTypePtr = KServiceType::serviceType( serviceType ); + bool hasLocalProtocolRedirect = false; + if ( serviceTypePtr ) + { + hasLocalProtocolRedirect = !serviceTypePtr->property( "X-KDE-LocalProtocol" ).toString().isEmpty(); + QVariant autoEmbedProp = serviceTypePtr->property( "X-KDE-AutoEmbed" ); + if ( autoEmbedProp.isValid() ) + { + bool autoEmbed = autoEmbedProp.toBool(); + kdDebug(1203) << "X-KDE-AutoEmbed set to " << (autoEmbed ? "true" : "false") << endl; + return autoEmbed; + } else + kdDebug(1203) << "No X-KDE-AutoEmbed, looking for group" << endl; + } + // 2 - in the configuration for the group if nothing was found in the mimetype + QString serviceTypeGroup = serviceType.left(serviceType.find("/")); + kdDebug(1203) << "KonqFMSettings::shouldEmbed : serviceTypeGroup=" << serviceTypeGroup << endl; + if ( serviceTypeGroup == "inode" || serviceTypeGroup == "Browser" || serviceTypeGroup == "Konqueror" ) + return true; //always embed mimetype inode/*, Browser/* and Konqueror/* + QMap<QString, QString>::ConstIterator it = m_embedMap.find( QString::fromLatin1("embed-")+serviceTypeGroup ); + if ( it != m_embedMap.end() ) { + kdDebug(1203) << "KonqFMSettings::shouldEmbed: " << it.data() << endl; + return it.data() == QString::fromLatin1("true"); + } + // 3 - if no config found, use default. + // Note: if you change those defaults, also change kcontrol/filetypes/typeslistitem.cpp ! + // Embedding is false by default except for image/* and for zip, tar etc. + if ( serviceTypeGroup == "image" || hasLocalProtocolRedirect ) + return true; + return false; +} + +bool KonqFMSettings::showPreviewsInFileTips() const +{ + return d->showPreviewsInFileTips; +} + +bool KonqFMSettings::renameIconDirectly() const +{ + return d->m_renameIconDirectly; +} + +int KonqFMSettings::caseSensitiveCompare( const QString& a, const QString& b ) const +{ + if ( d->localeAwareCompareIsCaseSensitive ) { + return a.localeAwareCompare( b ); + } + else // can't use localeAwareCompare, have to fallback to normal QString compare + return a.compare( b ); +} + +int KonqFMSettings::iconTextWidth() const +{ + return d->m_iconTextWidth; +} diff --git a/libkonq/konq_settings.h b/libkonq/konq_settings.h new file mode 100644 index 000000000..8f3d981fc --- /dev/null +++ b/libkonq/konq_settings.h @@ -0,0 +1,141 @@ +/* This file is part of the KDE project + Copyright (C) 1999 David Faure <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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. +*/ + +#ifndef __konq_settings_h__ +#define __konq_settings_h__ + +class KConfig; +#include <qcolor.h> +#include <qstring.h> +#include <qfont.h> +#include <qmap.h> + +#include <libkonq_export.h> + +/** + * The class KonqFMSettings holds the general settings for the + * icon/tree views in konqueror/kdesktop. + * There is no 'local' (per-URL) instance of it. + * All those settings can only be changed in kcmkonq. + * + * Using this class from konqueror and from kdesktop return + * different settings, since the config file is different. + * konquerorrc, group "FMSettings", and kdesktoprc, group "FMSettings" + * The kcontrol modules handles both files, depending where + * it's called from. + */ + +class LIBKONQ_EXPORT KonqFMSettings +{ +protected: + /** + * @internal + * Constructs a KonqFMSettings instance from a config file. + */ + KonqFMSettings( KConfig * config ); + + /** Destructor. Don't delete any instance by yourself. */ + virtual ~KonqFMSettings(); + +public: + + /** + * The static instance of KonqFMSettings + */ + static KonqFMSettings * settings(); + + /** + * Reparse the configuration to update the already-created instances + * + * Warning : you need to call KGlobal::config()->reparseConfiguration() + * first (This is not done here so that the caller can avoid too much + * reparsing if having several classes from the same config file) + */ + static void reparseConfiguration(); + + // Use settings (and mimetype definition files) + // to find whether to embed a certain service type or not + // Only makes sense in konqueror. + bool shouldEmbed( const QString & serviceType ) const; + + // Behaviour settings + bool wordWrapText() const { return m_bWordWrapText; } + int iconTextHeight() const { return m_iconTextHeight; } + int iconTextWidth() const; + bool underlineLink() const { return m_underlineLink; } + bool fileSizeInBytes() const { return m_fileSizeInBytes; } + bool alwaysNewWin() const { return m_alwaysNewWin; } + const QString & homeURL() const { return m_homeURL; } + + bool showFileTips() const {return m_showFileTips; } + bool showPreviewsInFileTips() const; + int numFileTips() const {return m_numFileTips; } + bool renameIconDirectly() const; + + // Font settings + const QFont& standardFont() const { return m_standardFont; } + + // Color settings + const QColor& normalTextColor() const { return m_normalTextColor; } + const QColor& highlightedTextColor() const { return m_highlightedTextColor; } + const QColor& itemTextBackground() const { return m_itemTextBackground; } + + int textPreviewIconTransparency() const { return m_iconTransparency; } + + int caseSensitiveCompare( const QString& a, const QString& b ) const; + +private: + + static KonqFMSettings * s_pSettings; + + bool m_underlineLink; + bool m_fileSizeInBytes; + bool m_alwaysNewWin; + bool m_bTreeFollow; + + QMap<QString, QString> m_embedMap; + + QFont m_standardFont; + + QColor m_normalTextColor; + QColor m_highlightedTextColor; + QColor m_itemTextBackground; + + bool m_bWordWrapText; + int m_iconTextHeight; + + QString m_homeURL; + bool m_showFileTips; + int m_numFileTips; + + // used for the textpreview + int m_iconTransparency; + + /** Called by constructor and reparseConfiguration */ + void init( KConfig * config ); + + struct KonqFMSettingsPrivate * d; + + // There is no default constructor. Use the provided ones. + KonqFMSettings(); + // No copy constructor either. What for ? + KonqFMSettings( const KonqFMSettings &); +}; + +#endif diff --git a/libkonq/konq_sound.cc b/libkonq/konq_sound.cc new file mode 100644 index 000000000..c609df553 --- /dev/null +++ b/libkonq/konq_sound.cc @@ -0,0 +1,137 @@ +/* This file is part of the KDE Project + Copyright (c) 2001 Malte Starostik <[email protected]> + + 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 <kartsdispatcher.h> +#include <kdebug.h> +#include <kplayobjectfactory.h> +#include <soundserver.h> + +#include "konq_sound.h" + +using namespace std; + +class KonqSoundPlayerImpl : public KonqSoundPlayer +{ +public: + KonqSoundPlayerImpl(); + virtual ~KonqSoundPlayerImpl(); + + virtual const QStringList &mimeTypes(); + virtual void play(const QString &fileName); + virtual void stop(); + virtual bool isPlaying(); + +private: + QStringList m_mimeTypes; + + KArtsDispatcher m_dispatcher; + Arts::SoundServerV2 m_soundServer; + KDE::PlayObjectFactory *m_factory; + KDE::PlayObject *m_player; +}; + +KonqSoundPlayerImpl::KonqSoundPlayerImpl() + : m_player(0) +{ + m_soundServer = Arts::Reference("global:Arts_SoundServerV2"); + m_factory = new KDE::PlayObjectFactory(m_soundServer); +} + +KonqSoundPlayerImpl::~KonqSoundPlayerImpl() +{ + delete m_player; + delete m_factory; +} + +const QStringList &KonqSoundPlayerImpl::mimeTypes() +{ + if (m_mimeTypes.isEmpty()) + { + Arts::TraderQuery query; + vector<Arts::TraderOffer> *offers = query.query(); + + for (vector<Arts::TraderOffer>::iterator it = offers->begin(); + it != offers->end(); ++it) + { + vector<string> *prop = (*it).getProperty("MimeType"); + for (vector<string>::iterator mt = prop->begin(); + mt != prop->end(); ++mt) + if ((*mt).length()) // && (*mt).find("video/") == string::npos) + m_mimeTypes << (*mt).c_str(); + delete prop; + } + delete offers; + } + return m_mimeTypes; +} + +void KonqSoundPlayerImpl::play(const QString &fileName) +{ + if (m_soundServer.isNull()) + return; + + delete m_player; + if ((m_player = m_factory->createPlayObject(fileName, true))) + { + if (m_player->isNull()) + stop(); + else + m_player->play(); + } +} + +void KonqSoundPlayerImpl::stop() +{ + delete m_player; + m_player = 0; +} + +bool KonqSoundPlayerImpl::isPlaying() +{ + return m_player ? (m_player->state() == Arts::posPlaying) : false; +} + +class KonqSoundFactory : public KLibFactory +{ +public: + KonqSoundFactory(QObject *parent = 0, const char *name = 0) + : KLibFactory(parent, name) {}; + virtual ~KonqSoundFactory() {}; + +protected: + virtual QObject *createObject(QObject * = 0, const char * = 0, + const char *className = "QObject", const QStringList &args = QStringList()); +}; + +QObject *KonqSoundFactory::createObject(QObject *, const char *, + const char *className, const QStringList &) +{ + if (qstrcmp(className, "KonqSoundPlayer") == 0) + return new KonqSoundPlayerImpl(); + return 0; +} + +extern "C" +{ + KDE_EXPORT KLibFactory *init_konq_sound() + { + return new KonqSoundFactory(); + } +} + +// vim: ts=4 sw=4 noet diff --git a/libkonq/konq_sound.h b/libkonq/konq_sound.h new file mode 100644 index 000000000..78fcc968b --- /dev/null +++ b/libkonq/konq_sound.h @@ -0,0 +1,35 @@ +/* This file is part of the KDE Project + Copyright (c) 2001 Malte Starostik <[email protected]> + + 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. +*/ + +#ifndef __konq_sound_h__ +#define __konq_sound_h__ + +#include <klibloader.h> + +class KonqSoundPlayer : public QObject +{ +public: + virtual const QStringList &mimeTypes() = 0; + virtual void play(const QString &fileName) = 0; + virtual void stop() = 0; + virtual bool isPlaying() = 0; +}; + +#endif + +// vim: ts=4 sw=4 noet diff --git a/libkonq/konq_undo.cc b/libkonq/konq_undo.cc new file mode 100644 index 000000000..7f8d1a4c6 --- /dev/null +++ b/libkonq/konq_undo.cc @@ -0,0 +1,667 @@ +/* This file is part of the KDE project + Copyright (C) 2000 Simon Hausmann <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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 "konq_undo.h" + +#undef Always + +#include <kio/uiserver_stub.h> +#include "konq_operations.h" + +#include <assert.h> + +#include <dcopclient.h> +#include <dcopref.h> + +#include <kapplication.h> +#include <kdatastream.h> +#include <kdebug.h> +#include <klocale.h> +#include <kglobalsettings.h> +#include <kconfig.h> +#include <kipc.h> + +#include <kio/job.h> +#include <kdirnotify_stub.h> + +inline const char *dcopTypeName( const KonqCommand & ) { return "KonqCommand"; } +inline const char *dcopTypeName( const KonqCommand::Stack & ) { return "KonqCommand::Stack"; } + +/** + * checklist: + * copy dir -> overwrite -> works + * move dir -> overwrite -> works + * copy dir -> rename -> works + * move dir -> rename -> works + * + * copy dir -> works + * move dir -> works + * + * copy files -> works + * move files -> works (TODO: optimize (change FileCopyJob to use the renamed arg for copyingDone) + * + * copy files -> overwrite -> works + * move files -> overwrite -> works + * + * copy files -> rename -> works + * move files -> rename -> works + */ + +class KonqUndoJob : public KIO::Job +{ +public: + KonqUndoJob() : KIO::Job( true ) { KonqUndoManager::incRef(); }; + virtual ~KonqUndoJob() { KonqUndoManager::decRef(); } + + virtual void kill( bool q) { KonqUndoManager::self()->stopUndo( true ); KIO::Job::kill( q ); } +}; + +class KonqCommandRecorder::KonqCommandRecorderPrivate +{ +public: + KonqCommandRecorderPrivate() + { + } + ~KonqCommandRecorderPrivate() + { + } + + KonqCommand m_cmd; +}; + +KonqCommandRecorder::KonqCommandRecorder( KonqCommand::Type op, const KURL::List &src, const KURL &dst, KIO::Job *job ) + : QObject( job, "konqcmdrecorder" ) +{ + d = new KonqCommandRecorderPrivate; + d->m_cmd.m_type = op; + d->m_cmd.m_valid = true; + d->m_cmd.m_src = src; + d->m_cmd.m_dst = dst; + connect( job, SIGNAL( result( KIO::Job * ) ), + this, SLOT( slotResult( KIO::Job * ) ) ); + + if ( op != KonqCommand::MKDIR ) { + connect( job, SIGNAL( copyingDone( KIO::Job *, const KURL &, const KURL &, bool, bool ) ), + this, SLOT( slotCopyingDone( KIO::Job *, const KURL &, const KURL &, bool, bool ) ) ); + connect( job, SIGNAL( copyingLinkDone( KIO::Job *, const KURL &, const QString &, const KURL & ) ), + this, SLOT( slotCopyingLinkDone( KIO::Job *, const KURL &, const QString &, const KURL & ) ) ); + } + + KonqUndoManager::incRef(); +} + +KonqCommandRecorder::~KonqCommandRecorder() +{ + KonqUndoManager::decRef(); + delete d; +} + +void KonqCommandRecorder::slotResult( KIO::Job *job ) +{ + if ( job->error() ) + return; + + KonqUndoManager::self()->addCommand( d->m_cmd ); +} + +void KonqCommandRecorder::slotCopyingDone( KIO::Job *job, const KURL &from, const KURL &to, bool directory, bool renamed ) +{ + KonqBasicOperation op; + op.m_valid = true; + op.m_directory = directory; + op.m_renamed = renamed; + op.m_src = from; + op.m_dst = to; + op.m_link = false; + + if ( d->m_cmd.m_type == KonqCommand::TRASH ) + { + Q_ASSERT( from.isLocalFile() ); + Q_ASSERT( to.protocol() == "trash" ); + QMap<QString, QString> metaData = job->metaData(); + QMap<QString, QString>::ConstIterator it = metaData.find( "trashURL-" + from.path() ); + if ( it != metaData.end() ) { + // Update URL + op.m_dst = it.data(); + } + } + + d->m_cmd.m_opStack.prepend( op ); +} + +void KonqCommandRecorder::slotCopyingLinkDone( KIO::Job *, const KURL &from, const QString &target, const KURL &to ) +{ + KonqBasicOperation op; + op.m_valid = true; + op.m_directory = false; + op.m_renamed = false; + op.m_src = from; + op.m_target = target; + op.m_dst = to; + op.m_link = true; + d->m_cmd.m_opStack.prepend( op ); +} + +KonqUndoManager *KonqUndoManager::s_self = 0; +unsigned long KonqUndoManager::s_refCnt = 0; + +class KonqUndoManager::KonqUndoManagerPrivate +{ +public: + KonqUndoManagerPrivate() + { + m_uiserver = new UIServer_stub( "kio_uiserver", "UIServer" ); + m_undoJob = 0; + } + ~KonqUndoManagerPrivate() + { + delete m_uiserver; + } + + bool m_syncronized; + + KonqCommand::Stack m_commands; + + KonqCommand m_current; + KIO::Job *m_currentJob; + UndoState m_undoState; + QValueStack<KURL> m_dirStack; + QValueStack<KURL> m_dirCleanupStack; + QValueStack<KURL> m_fileCleanupStack; + QValueList<KURL> m_dirsToUpdate; + + bool m_lock; + + UIServer_stub *m_uiserver; + int m_uiserverJobId; + + KonqUndoJob *m_undoJob; +}; + +KonqUndoManager::KonqUndoManager() +: DCOPObject( "KonqUndoManager" ) +{ + if ( !kapp->dcopClient()->isAttached() ) + kapp->dcopClient()->attach(); + + d = new KonqUndoManagerPrivate; + d->m_syncronized = initializeFromKDesky(); + d->m_lock = false; + d->m_currentJob = 0; +} + +KonqUndoManager::~KonqUndoManager() +{ + delete d; +} + +void KonqUndoManager::incRef() +{ + s_refCnt++; +} + +void KonqUndoManager::decRef() +{ + s_refCnt--; + if ( s_refCnt == 0 && s_self ) + { + delete s_self; + s_self = 0; + } +} + +KonqUndoManager *KonqUndoManager::self() +{ + if ( !s_self ) + { + if ( s_refCnt == 0 ) + s_refCnt++; // someone forgot to call incRef + s_self = new KonqUndoManager; + } + return s_self; +} + +void KonqUndoManager::addCommand( const KonqCommand &cmd ) +{ + broadcastPush( cmd ); +} + +bool KonqUndoManager::undoAvailable() const +{ + return ( d->m_commands.count() > 0 ) && !d->m_lock; +} + +QString KonqUndoManager::undoText() const +{ + if ( d->m_commands.count() == 0 ) + return i18n( "Und&o" ); + + KonqCommand::Type t = d->m_commands.top().m_type; + if ( t == KonqCommand::COPY ) + return i18n( "Und&o: Copy" ); + else if ( t == KonqCommand::LINK ) + return i18n( "Und&o: Link" ); + else if ( t == KonqCommand::MOVE ) + return i18n( "Und&o: Move" ); + else if ( t == KonqCommand::TRASH ) + return i18n( "Und&o: Trash" ); + else if ( t == KonqCommand::MKDIR ) + return i18n( "Und&o: Create Folder" ); + else + assert( false ); + /* NOTREACHED */ + return QString::null; +} + +void KonqUndoManager::undo() +{ + KonqCommand cmd = d->m_commands.top(); + assert( cmd.m_valid ); + + d->m_current = cmd; + + QValueList<KonqBasicOperation>& opStack = d->m_current.m_opStack; + + // Let's first ask for confirmation if we need to delete any file (#99898) + KURL::List fileCleanupStack; + QValueList<KonqBasicOperation>::Iterator it = opStack.begin(); + for ( ; it != opStack.end() ; ++it ) { + if ( !(*it).m_directory && !(*it).m_link && d->m_current.m_type == KonqCommand::COPY ) { + fileCleanupStack.append( (*it).m_dst ); + } + } + if ( !fileCleanupStack.isEmpty() ) { + // Because undo can happen with an accidental Ctrl-Z, we want to always confirm. + if ( !KonqOperations::askDeleteConfirmation( fileCleanupStack, KonqOperations::DEL, + KonqOperations::FORCE_CONFIRMATION, + 0 /* TODO parent */ ) ) + return; + } + + d->m_dirCleanupStack.clear(); + d->m_dirStack.clear(); + d->m_dirsToUpdate.clear(); + + d->m_undoState = MOVINGFILES; + + broadcastPop(); + broadcastLock(); + + it = opStack.begin(); + QValueList<KonqBasicOperation>::Iterator end = opStack.end(); + while ( it != end ) + { + if ( (*it).m_directory && !(*it).m_renamed ) + { + d->m_dirStack.push( (*it).m_src ); + d->m_dirCleanupStack.prepend( (*it).m_dst ); + it = d->m_current.m_opStack.remove( it ); + d->m_undoState = MAKINGDIRS; + kdDebug(1203) << "KonqUndoManager::undo MAKINGDIRS" << endl; + } + else if ( (*it).m_link ) + { + if ( !d->m_fileCleanupStack.contains( (*it).m_dst ) ) + d->m_fileCleanupStack.prepend( (*it).m_dst ); + + if ( d->m_current.m_type != KonqCommand::MOVE ) + it = d->m_current.m_opStack.remove( it ); + else + ++it; + } + else + ++it; + } + + /* this shouldn't be necessary at all: + * 1) the source list may contain files, we don't want to + * create those as... directories + * 2) all directories that need creation should already be in the + * directory stack + if ( d->m_undoState == MAKINGDIRS ) + { + KURL::List::ConstIterator it = d->m_current.m_src.begin(); + KURL::List::ConstIterator end = d->m_current.m_src.end(); + for (; it != end; ++it ) + if ( !d->m_dirStack.contains( *it) ) + d->m_dirStack.push( *it ); + } + */ + + if ( d->m_current.m_type != KonqCommand::MOVE ) + d->m_dirStack.clear(); + + d->m_undoJob = new KonqUndoJob; + d->m_uiserverJobId = d->m_undoJob->progressId(); + undoStep(); +} + +void KonqUndoManager::stopUndo( bool step ) +{ + d->m_current.m_opStack.clear(); + d->m_dirCleanupStack.clear(); + d->m_fileCleanupStack.clear(); + d->m_undoState = REMOVINGDIRS; + d->m_undoJob = 0; + + if ( d->m_currentJob ) + d->m_currentJob->kill( true ); + + d->m_currentJob = 0; + + if ( step ) + undoStep(); +} + +void KonqUndoManager::slotResult( KIO::Job *job ) +{ + d->m_uiserver->jobFinished( d->m_uiserverJobId ); + if ( job->error() ) + { + job->showErrorDialog( 0L ); + d->m_currentJob = 0; + stopUndo( false ); + if ( d->m_undoJob ) + { + delete d->m_undoJob; + d->m_undoJob = 0; + } + } + + undoStep(); +} + + +void KonqUndoManager::addDirToUpdate( const KURL& url ) +{ + if ( d->m_dirsToUpdate.find( url ) == d->m_dirsToUpdate.end() ) + d->m_dirsToUpdate.prepend( url ); +} + +void KonqUndoManager::undoStep() +{ + d->m_currentJob = 0; + + if ( d->m_undoState == MAKINGDIRS ) + undoMakingDirectories(); + + if ( d->m_undoState == MOVINGFILES ) + undoMovingFiles(); + + if ( d->m_undoState == REMOVINGFILES ) + undoRemovingFiles(); + + if ( d->m_undoState == REMOVINGDIRS ) + undoRemovingDirectories(); + + if ( d->m_currentJob ) + connect( d->m_currentJob, SIGNAL( result( KIO::Job * ) ), + this, SLOT( slotResult( KIO::Job * ) ) ); +} + +void KonqUndoManager::undoMakingDirectories() +{ + if ( !d->m_dirStack.isEmpty() ) { + KURL dir = d->m_dirStack.pop(); + kdDebug(1203) << "KonqUndoManager::undoStep creatingDir " << dir.prettyURL() << endl; + d->m_currentJob = KIO::mkdir( dir ); + d->m_uiserver->creatingDir( d->m_uiserverJobId, dir ); + } + else + d->m_undoState = MOVINGFILES; +} + +void KonqUndoManager::undoMovingFiles() +{ + if ( !d->m_current.m_opStack.isEmpty() ) + { + KonqBasicOperation op = d->m_current.m_opStack.pop(); + + assert( op.m_valid ); + if ( op.m_directory ) + { + if ( op.m_renamed ) + { + kdDebug(1203) << "KonqUndoManager::undoStep rename " << op.m_dst.prettyURL() << " " << op.m_src.prettyURL() << endl; + d->m_currentJob = KIO::rename( op.m_dst, op.m_src, false ); + d->m_uiserver->moving( d->m_uiserverJobId, op.m_dst, op.m_src ); + } + else + assert( 0 ); // this should not happen! + } + else if ( op.m_link ) + { + kdDebug(1203) << "KonqUndoManager::undoStep symlink " << op.m_target << " " << op.m_src.prettyURL() << endl; + d->m_currentJob = KIO::symlink( op.m_target, op.m_src, true, false ); + } + else if ( d->m_current.m_type == KonqCommand::COPY ) + { + kdDebug(1203) << "KonqUndoManager::undoStep file_delete " << op.m_dst.prettyURL() << endl; + d->m_currentJob = KIO::file_delete( op.m_dst ); + d->m_uiserver->deleting( d->m_uiserverJobId, op.m_dst ); + } + else if ( d->m_current.m_type == KonqCommand::MOVE + || d->m_current.m_type == KonqCommand::TRASH ) + { + kdDebug(1203) << "KonqUndoManager::undoStep file_move " << op.m_dst.prettyURL() << " " << op.m_src.prettyURL() << endl; + d->m_currentJob = KIO::file_move( op.m_dst, op.m_src, -1, true ); + d->m_uiserver->moving( d->m_uiserverJobId, op.m_dst, op.m_src ); + } + + // The above KIO jobs are lowlevel, they don't trigger KDirNotify notification + // So we need to do it ourselves (but schedule it to the end of the undo, to compress them) + KURL url( op.m_dst ); + url.setPath( url.directory() ); + addDirToUpdate( url ); + + url = op.m_src; + url.setPath( url.directory() ); + addDirToUpdate( url ); + } + else + d->m_undoState = REMOVINGFILES; +} + +void KonqUndoManager::undoRemovingFiles() +{ + kdDebug(1203) << "KonqUndoManager::undoStep REMOVINGFILES" << endl; + if ( !d->m_fileCleanupStack.isEmpty() ) + { + KURL file = d->m_fileCleanupStack.pop(); + kdDebug(1203) << "KonqUndoManager::undoStep file_delete " << file.prettyURL() << endl; + d->m_currentJob = KIO::file_delete( file ); + d->m_uiserver->deleting( d->m_uiserverJobId, file ); + + KURL url( file ); + url.setPath( url.directory() ); + addDirToUpdate( url ); + } + else + { + d->m_undoState = REMOVINGDIRS; + + if ( d->m_dirCleanupStack.isEmpty() && d->m_current.m_type == KonqCommand::MKDIR ) + d->m_dirCleanupStack << d->m_current.m_dst; + } +} + +void KonqUndoManager::undoRemovingDirectories() +{ + if ( !d->m_dirCleanupStack.isEmpty() ) + { + KURL dir = d->m_dirCleanupStack.pop(); + kdDebug(1203) << "KonqUndoManager::undoStep rmdir " << dir.prettyURL() << endl; + d->m_currentJob = KIO::rmdir( dir ); + d->m_uiserver->deleting( d->m_uiserverJobId, dir ); + addDirToUpdate( dir ); + } + else + { + d->m_current.m_valid = false; + d->m_currentJob = 0; + if ( d->m_undoJob ) + { + kdDebug(1203) << "KonqUndoManager::undoStep deleting undojob" << endl; + d->m_uiserver->jobFinished( d->m_uiserverJobId ); + delete d->m_undoJob; + d->m_undoJob = 0; + } + KDirNotify_stub allDirNotify( "*", "KDirNotify*" ); + QValueList<KURL>::ConstIterator it = d->m_dirsToUpdate.begin(); + for( ; it != d->m_dirsToUpdate.end(); ++it ) { + kdDebug() << "Notifying FilesAdded for " << *it << endl; + allDirNotify.FilesAdded( *it ); + } + broadcastUnlock(); + } +} + +void KonqUndoManager::push( const KonqCommand &cmd ) +{ + d->m_commands.push( cmd ); + emit undoAvailable( true ); + emit undoTextChanged( undoText() ); +} + +void KonqUndoManager::pop() +{ + d->m_commands.pop(); + emit undoAvailable( undoAvailable() ); + emit undoTextChanged( undoText() ); +} + +void KonqUndoManager::lock() +{ +// assert( !d->m_lock ); + d->m_lock = true; + emit undoAvailable( undoAvailable() ); +} + +void KonqUndoManager::unlock() +{ +// assert( d->m_lock ); + d->m_lock = false; + emit undoAvailable( undoAvailable() ); +} + +KonqCommand::Stack KonqUndoManager::get() const +{ + return d->m_commands; +} + +void KonqUndoManager::broadcastPush( const KonqCommand &cmd ) +{ + if ( !d->m_syncronized ) + { + push( cmd ); + return; + } + + DCOPRef( "kdesktop", "KonqUndoManager" ).send( "push", cmd ); + DCOPRef( "konqueror*", "KonqUndoManager" ).send( "push", cmd ); +} + +void KonqUndoManager::broadcastPop() +{ + if ( !d->m_syncronized ) + { + pop(); + return; + } + DCOPRef( "kdesktop", "KonqUndoManager" ).send( "pop" ); + DCOPRef( "konqueror*", "KonqUndoManager" ).send( "pop" ); +} + +void KonqUndoManager::broadcastLock() +{ +// assert( !d->m_lock ); + + if ( !d->m_syncronized ) + { + lock(); + return; + } + DCOPRef( "kdesktop", "KonqUndoManager" ).send( "lock" ); + DCOPRef( "konqueror*", "KonqUndoManager" ).send( "lock" ); +} + +void KonqUndoManager::broadcastUnlock() +{ +// assert( d->m_lock ); + + if ( !d->m_syncronized ) + { + unlock(); + return; + } + DCOPRef( "kdesktop", "KonqUndoManager" ).send( "unlock" ); + DCOPRef( "konqueror*", "KonqUndoManager" ).send( "unlock" ); +} + +bool KonqUndoManager::initializeFromKDesky() +{ + // ### workaround for dcop problem and upcoming 2.1 release: + // in case of huge io operations the amount of data sent over + // dcop (containing undo information broadcasted for global undo + // to all konqueror instances) can easily exceed the 64kb limit + // of dcop. In order not to run into trouble we disable global + // undo for now! (Simon) + // ### FIXME: post 2.1 + return false; + + DCOPClient *client = kapp->dcopClient(); + + if ( client->appId() == "kdesktop" ) // we are master :) + return true; + + if ( !client->isApplicationRegistered( "kdesktop" ) ) + return false; + + d->m_commands = DCOPRef( "kdesktop", "KonqUndoManager" ).call( "get" ); + return true; +} + +QDataStream &operator<<( QDataStream &stream, const KonqBasicOperation &op ) +{ + stream << op.m_valid << op.m_directory << op.m_renamed << op.m_link + << op.m_src << op.m_dst << op.m_target; + return stream; +} +QDataStream &operator>>( QDataStream &stream, KonqBasicOperation &op ) +{ + stream >> op.m_valid >> op.m_directory >> op.m_renamed >> op.m_link + >> op.m_src >> op.m_dst >> op.m_target; + return stream; +} + +QDataStream &operator<<( QDataStream &stream, const KonqCommand &cmd ) +{ + stream << cmd.m_valid << (Q_INT8)cmd.m_type << cmd.m_opStack << cmd.m_src << cmd.m_dst; + return stream; +} + +QDataStream &operator>>( QDataStream &stream, KonqCommand &cmd ) +{ + Q_INT8 type; + stream >> cmd.m_valid >> type >> cmd.m_opStack >> cmd.m_src >> cmd.m_dst; + cmd.m_type = static_cast<KonqCommand::Type>( type ); + return stream; +} + +#include "konq_undo.moc" diff --git a/libkonq/konq_undo.h b/libkonq/konq_undo.h new file mode 100644 index 000000000..fae2b5e61 --- /dev/null +++ b/libkonq/konq_undo.h @@ -0,0 +1,162 @@ +/* This file is part of the KDE project + Copyright (C) 2000 Simon Hausmann <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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. +*/ +#ifndef __konq_undo_h__ +#define __konq_undo_h__ + +#include <qobject.h> +#include <qstring.h> +#include <qvaluestack.h> + +#include <dcopobject.h> + +#include <kurl.h> +#include <libkonq_export.h> + +namespace KIO +{ + class Job; +} + +class KonqUndoJob; + +struct KonqBasicOperation +{ + typedef QValueStack<KonqBasicOperation> Stack; + + KonqBasicOperation() + { m_valid = false; } + + bool m_valid; + bool m_directory; + bool m_renamed; + bool m_link; + KURL m_src; + KURL m_dst; + QString m_target; +}; + +struct KonqCommand +{ + typedef QValueStack<KonqCommand> Stack; + + enum Type { COPY, MOVE, LINK, MKDIR, TRASH }; + + KonqCommand() + { m_valid = false; } + + bool m_valid; + + Type m_type; + KonqBasicOperation::Stack m_opStack; + KURL::List m_src; + KURL m_dst; +}; + +class KonqCommandRecorder : public QObject +{ + Q_OBJECT +public: + KonqCommandRecorder( KonqCommand::Type op, const KURL::List &src, const KURL &dst, KIO::Job *job ); + virtual ~KonqCommandRecorder(); + +private slots: + void slotResult( KIO::Job *job ); + + void slotCopyingDone( KIO::Job *, const KURL &from, const KURL &to, bool directory, bool renamed ); + void slotCopyingLinkDone( KIO::Job *, const KURL &from, const QString &target, const KURL &to ); + +private: + class KonqCommandRecorderPrivate; + KonqCommandRecorderPrivate *d; +}; + +class LIBKONQ_EXPORT KonqUndoManager : public QObject, public DCOPObject +{ + Q_OBJECT + K_DCOP + friend class KonqUndoJob; +public: + enum UndoState { MAKINGDIRS, MOVINGFILES, REMOVINGDIRS, REMOVINGFILES }; + + KonqUndoManager(); + virtual ~KonqUndoManager(); + + static void incRef(); + static void decRef(); + static KonqUndoManager *self(); + + void addCommand( const KonqCommand &cmd ); + + bool undoAvailable() const; + QString undoText() const; + +public slots: + void undo(); + +signals: + void undoAvailable( bool avail ); + void undoTextChanged( const QString &text ); + +protected: + /** + * @internal + */ + void stopUndo( bool step ); + +private: +k_dcop: + virtual ASYNC push( const KonqCommand &cmd ); + virtual ASYNC pop(); + virtual ASYNC lock(); + virtual ASYNC unlock(); + + virtual KonqCommand::Stack get() const; + +private slots: + void slotResult( KIO::Job *job ); + +private: + void undoStep(); + + void undoMakingDirectories(); + void undoMovingFiles(); + void undoRemovingFiles(); + void undoRemovingDirectories(); + + void broadcastPush( const KonqCommand &cmd ); + void broadcastPop(); + void broadcastLock(); + void broadcastUnlock(); + + void addDirToUpdate( const KURL& url ); + bool initializeFromKDesky(); + + class KonqUndoManagerPrivate; + KonqUndoManagerPrivate *d; + static KonqUndoManager *s_self; + static unsigned long s_refCnt; +}; + +QDataStream &operator<<( QDataStream &stream, const KonqBasicOperation &op ); +QDataStream &operator>>( QDataStream &stream, KonqBasicOperation &op ); + +QDataStream &operator<<( QDataStream &stream, const KonqCommand &cmd ); +QDataStream &operator>>( QDataStream &stream, KonqCommand &cmd ); + +#endif diff --git a/libkonq/konq_xmlguiclient.cc b/libkonq/konq_xmlguiclient.cc new file mode 100644 index 000000000..7737acd7c --- /dev/null +++ b/libkonq/konq_xmlguiclient.cc @@ -0,0 +1,157 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Holger Freyther <[email protected]> + Copyright (c) 1998, 1999 David Faure <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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 "kapplication.h" + +#include "konq_xmlguiclient.h" +#include <kdebug.h> + +class KonqXMLGUIClient::Private +{ +public: + Private() : attrName( "name" ), separatorPending( false ), hasAction( false ) {} + QString attrName; + bool separatorPending; + bool hasAction; +}; + +KonqXMLGUIClient::KonqXMLGUIClient( ) : KXMLGUIClient( ) +{ + d = new Private; + prepareXMLGUIStuff( ); +} + +KonqXMLGUIClient::KonqXMLGUIClient( KXMLGUIClient *parent ) : KXMLGUIClient(parent ) +{ + d = new Private; + prepareXMLGUIStuff( ); +} + +void KonqXMLGUIClient::prepareXMLGUIStuff() +{ + m_doc = QDomDocument( "kpartgui" ); + + QDomElement root = m_doc.createElement( "kpartgui" ); + m_doc.appendChild( root ); + root.setAttribute( d->attrName, "popupmenu" ); + + m_menuElement = m_doc.createElement( "Menu" ); + root.appendChild( m_menuElement ); + m_menuElement.setAttribute( d->attrName, "popupmenu" ); + + /*m_builder = new KonqPopupMenuGUIBuilder( this ); + m_factory = new KXMLGUIFactory( m_builder ); */ +} + +QDomElement KonqXMLGUIClient::DomElement() const +{ + return m_menuElement; +} + +QDomDocument KonqXMLGUIClient::domDocument() const +{ + return m_doc; +} + +void KonqXMLGUIClient::addAction( KAction *act, const QDomElement &menu ) +{ + addAction( act->name(), menu ); +} + +void KonqXMLGUIClient::addAction( const char *name, const QDomElement &menu ) +{ + static const QString& tagAction = KGlobal::staticQString( "action" ); + + if (!kapp->authorizeKAction(name)) + return; + + handlePendingSeparator(); + QDomElement parent = menu; + if ( parent.isNull() ) { + parent = m_menuElement; + } + + QDomElement e = m_doc.createElement( tagAction ); + parent.appendChild( e ); + e.setAttribute( d->attrName, name ); + d->hasAction = true; +} + +void KonqXMLGUIClient::addSeparator( const QDomElement &menu ) +{ + static const QString& tagSeparator = KGlobal::staticQString( "separator" ); + + QDomElement parent = menu; + if ( parent.isNull() ) { + parent = m_menuElement; + } + + parent.appendChild( m_doc.createElement( tagSeparator ) ); + + d->separatorPending = false; +} + +//void KonqXMLGUIClient::addWeakSeparator() +//{ +// static const QString& tagWeakSeparator = KGlobal::staticQString( "weakSeparator" ); +// m_menuElement.appendChild( m_doc.createElement( tagWeakSeparator ) ); +//} + +void KonqXMLGUIClient::addMerge( const QString &name ) +{ + // can't call handlePendingSeparator. Merge could be empty + // (testcase: RMB in embedded katepart) + QDomElement merge = m_doc.createElement( "merge" ); + m_menuElement.appendChild( merge ); + if ( !name.isEmpty() ) + merge.setAttribute( d->attrName, name ); +} + +void KonqXMLGUIClient::addGroup( const QString &grp ) +{ + handlePendingSeparator(); + QDomElement group = m_doc.createElement( "definegroup" ); + m_menuElement.appendChild( group ); + group.setAttribute( d->attrName, grp ); +} + +KonqXMLGUIClient::~KonqXMLGUIClient() +{ + delete d; +} + +void KonqXMLGUIClient::handlePendingSeparator() +{ + if ( d->separatorPending ) { + addSeparator(); + } +} + +void KonqXMLGUIClient::addPendingSeparator() +{ + d->separatorPending = true; +} + +bool KonqXMLGUIClient::hasAction() const +{ + return d->hasAction; +} + + diff --git a/libkonq/konq_xmlguiclient.h b/libkonq/konq_xmlguiclient.h new file mode 100644 index 000000000..175d991c7 --- /dev/null +++ b/libkonq/konq_xmlguiclient.h @@ -0,0 +1,70 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Holger Freyther <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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. +*/ + +#ifndef __konqxmlguiclient_h +#define __konqxmlguiclient_h + +#include <sys/types.h> + +#include <kaction.h> +#include <kxmlguiclient.h> +#include <qstringlist.h> +#include <libkonq_export.h> + +/** + * This class implements common methods to manipulate the DOMDocument of KXMLGUIClient + * + */ +class LIBKONQ_EXPORT KonqXMLGUIClient : public KXMLGUIClient +{ +public: + KonqXMLGUIClient( ); + KonqXMLGUIClient( KXMLGUIClient *parent ); + virtual ~KonqXMLGUIClient( ); + /** + * Reimplemented for internal purpose + */ + QDomDocument domDocument( ) const; + + QDomElement DomElement( ) const; // KDE4: s/D/d/ + +protected: + void addAction( KAction *action, const QDomElement &menu = QDomElement() ); + void addAction( const char *name, const QDomElement &menu = QDomElement() ); + void addSeparator( const QDomElement &menu = QDomElement() ); + /// only add a separator if an action is added afterwards + void addPendingSeparator(); + void addGroup( const QString &grp ); + void addMerge( const QString &name ); + + // @return true if addAction was called at least once + bool hasAction() const; + void prepareXMLGUIStuff(); + +// KDE4: make private + QDomElement m_menuElement; + QDomDocument m_doc; + +private: + void handlePendingSeparator(); + class Private; + Private *d; +}; +#endif + diff --git a/libkonq/konqbookmarkmanager.h b/libkonq/konqbookmarkmanager.h new file mode 100644 index 000000000..5fb45f3e6 --- /dev/null +++ b/libkonq/konqbookmarkmanager.h @@ -0,0 +1,24 @@ +#ifndef KONQBOOKMARKMANAGER_H +#define KONQBOOKMARKMANAGER_H + +#include <kbookmarkmanager.h> +#include <kstandarddirs.h> +#include <libkonq_export.h> + +class LIBKONQ_EXPORT KonqBookmarkManager +{ +public: + static KBookmarkManager * self() { + if ( !s_bookmarkManager ) + { + QString bookmarksFile = locateLocal("data", QString::fromLatin1("konqueror/bookmarks.xml")); + s_bookmarkManager = KBookmarkManager::managerForFile( bookmarksFile ); + } + return s_bookmarkManager; + } + +private: + static KBookmarkManager *s_bookmarkManager; +}; + +#endif diff --git a/libkonq/konqpopupmenuplugin.desktop b/libkonq/konqpopupmenuplugin.desktop new file mode 100644 index 000000000..984fe6949 --- /dev/null +++ b/libkonq/konqpopupmenuplugin.desktop @@ -0,0 +1,76 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=KonqPopupMenu/Plugin +Comment=Plugin for the Konqueror Popup Menu +Comment[af]=Inplak vir die Konqueror Opspring Kieslys +Comment[az]=Konqueror üçün PopupMenu əlavəsi +Comment[be]=Утулка для выплыўнага меню Konqueror +Comment[bg]=Приставка за контекстното меню на браузъра +Comment[bn]=কনকরার পপ-আপ মেনুর জন্য প্লাগ-ইন +Comment[bs]=Dodatak za Konqueror pop-up meni +Comment[ca]=Connector per al menú emergent del Konqueror +Comment[cs]=Modul pro kontextovou nabídku Konqueroru +Comment[csb]=Plugins do menu (òtmëkô knąpą mëszë) Konquerora +Comment[cy]=Ategyn i Naidlen Konqueror +Comment[da]=Plugin for Konqueror popop-menu +Comment[de]=Plugin für das Popup-Menü von Konqueror +Comment[el]=Πρόσθετο για το αναδυόμενο μενού του Konqueror +Comment[eo]=Kromaĵo por la ŝprucmenuo de Konkeranto +Comment[es]=Complemento para el menú emergente de Konqueror +Comment[et]=Konquerori hüpikmenüü plugin +Comment[eu]=Konquerorren laster-menuetarako plugina +Comment[fa]=وصله برای گزینگان بالاپر Konqueror +Comment[fi]=Sovelma Konquerorin ponnahdusvalikolle +Comment[fr]=Module pour le menu contextuel de Konqueror +Comment[fy]=Plugin foar Konqueror's fluesmenu +Comment[gl]=Plugin para o Menu Emerxente de Konqueror +Comment[he]=תוסף לתפריט המוקפץ של Konqueror +Comment[hi]=कॉन्करर पॉपअप मेन्यू के लिए प्लगइन +Comment[hr]=Dodatak za Konqueror pop-up izbornik +Comment[hu]=Bővítőmodul a Konqueror felbukkanó menühöz +Comment[is]=Íhlutur fyrir stillingarforritið +Comment[it]=Plugin per il menu a comparsa di Konqueror +Comment[ja]=Konqueror ポップアップメニューのプラグイン +Comment[ka]=Konqueror-ის ჩამოშლადი მენიუს პლაგინი +Comment[kk]=Konqueror баптау мәзір модулі +Comment[km]=កម្មវិធីជំនួយ សម្រាប់ម៉ឺនុយលេចឡើង Konqueror +Comment[ko]=Konqueror 팝업 메뉴 플러그인 +Comment[lo]=ປັກອິນສຳລັບເມນປອບອັບ Konqueror +Comment[lt]=Priedas pasirodančiam Konqueror meniu +Comment[lv]=Iekarotāja Uzlēcošās Izvēlnes spraudnis +Comment[mk]=Приклучок за контекстното мени на Konqueror +Comment[mn]=Конкюрорын тагтан-цэсний Плугин +Comment[ms]=Plugin untuk Menu Popuo Konqueror +Comment[mt]=Plagin għall-menu kuntestwali ta' Konqueror +Comment[nb]=Programtillegg for sprettoppmenyen i Konqueror +Comment[nds]=Plugin för dat Konqueror-Opdukmenü +Comment[ne]=कन्क्वेरर पपअप मेनुका लागि प्लगइन +Comment[nl]=Plugin voor Konqueror's contextmenu +Comment[nn]=Tillegg til sprettoppmenyen i Konqueror +Comment[nso]=Plugin ya Menu wa Thlarogo ya Konqueror +Comment[pa]=ਕੋਨਕਿਉਰੋਰ ਪਾਪਅੱਪ ਮੇਨੂ ਲਈ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka do menu (otwieranego przyciskiem myszy) Konquerora +Comment[pt]='Plugin' para o Menu do Konqueror +Comment[pt_BR]=Plug-in do Konqueror para Menus Flutuantes +Comment[ro]=Modul pentru meniuri popup Konqueror +Comment[ru]=Модуль для диалога настроек +Comment[rw]=Gucomeka bijyanye n'Ibikubiyemo Byirambura bya Konqueror +Comment[se]=Lassemoduvla Konquerora báhccanfállui +Comment[sk]=Modul pre kontextové menu Konquerora +Comment[sl]=Vstavek za Konquerorjev pojavni menu +Comment[sr]=Прикључак за Konqueror-ов искачући мени +Comment[sr@Latn]=Priključak za Konqueror-ov iskačući meni +Comment[sv]=Insticksprogram för Konquerors popupmeny +Comment[ta]=கான்கொரர் தோன்றும் பட்டிக்கான செருகல்கள் +Comment[tg]=Мутассалкунандаи меню ҷиҳандаи Konqueror +Comment[th]=ปลั้กอินสำหรับเมนูป้อบอัพของคอนเควอร์เรอร์ +Comment[tr]=Konqueror Seyyar Menüsü İçin Eklenti +Comment[tt]=Konqueror'da Yözüçe Saylaq buldıruçı Östämä +Comment[uk]=Втулок вигулькних меню Konqueror +Comment[ven]=Plugin ya menu wau sokou bvelela wa Konqueror +Comment[vi]=Trình bổ sung cho Thực đơn Nhảy ra của Konqueror +Comment[wa]=Tchôke-divins po l' aspitant menu di Konqueror +Comment[xh]=Iplagi efakiweyo ye Konqueror ye Popup Menu +Comment[zh_CN]=Konqueror 弹出菜单插件 +Comment[zh_TW]=Konqueror 對話選單的外掛程式 +Comment[zu]=I-Plugin yemenu egxumayo ye-Konqueror diff --git a/libkonq/libkonq_export.h b/libkonq/libkonq_export.h new file mode 100644 index 000000000..88192c0c4 --- /dev/null +++ b/libkonq/libkonq_export.h @@ -0,0 +1,39 @@ +/* + This file is part of the KDE project + Copyright (C) 2004 Jaroslaw Staniek <[email protected]> + (C) 2004 Dirk Mueller <[email protected]> + + 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. +*/ + +#ifndef LIBKONQ_EXPORT_H +#define LIBKONQ_EXPORT_H + +/* needed for KDE_EXPORT macros */ +#include <kdemacros.h> + +/* needed, because e.g. Q_OS_UNIX is so frequently used */ +#include <qglobal.h> + +#ifdef Q_WS_WIN + +#else /* Q_OS_UNIX */ + +/* export statements for unix */ +#define LIBKONQ_EXPORT KDE_EXPORT + +#endif + +#endif diff --git a/libkonq/pics/Makefile.am b/libkonq/pics/Makefile.am new file mode 100644 index 000000000..8731dcc29 --- /dev/null +++ b/libkonq/pics/Makefile.am @@ -0,0 +1,2 @@ +libkonq_pics_datadir = $(kde_datadir)/konqueror/pics +libkonq_pics_data_DATA = thumbnailfont_7x4.png arrow_topleft.png arrow_topright.png arrow_bottomleft.png arrow_bottomright.png diff --git a/libkonq/pics/arrow_bottomleft.png b/libkonq/pics/arrow_bottomleft.png Binary files differnew file mode 100644 index 000000000..90ef9754c --- /dev/null +++ b/libkonq/pics/arrow_bottomleft.png diff --git a/libkonq/pics/arrow_bottomright.png b/libkonq/pics/arrow_bottomright.png Binary files differnew file mode 100644 index 000000000..92f7c9c58 --- /dev/null +++ b/libkonq/pics/arrow_bottomright.png diff --git a/libkonq/pics/arrow_topleft.png b/libkonq/pics/arrow_topleft.png Binary files differnew file mode 100644 index 000000000..b27b6df5c --- /dev/null +++ b/libkonq/pics/arrow_topleft.png diff --git a/libkonq/pics/arrow_topright.png b/libkonq/pics/arrow_topright.png Binary files differnew file mode 100644 index 000000000..624b2b213 --- /dev/null +++ b/libkonq/pics/arrow_topright.png diff --git a/libkonq/pics/thumbnailfont_7x4.png b/libkonq/pics/thumbnailfont_7x4.png Binary files differnew file mode 100644 index 000000000..a442e3cab --- /dev/null +++ b/libkonq/pics/thumbnailfont_7x4.png diff --git a/libkonq/tests/Makefile.am b/libkonq/tests/Makefile.am new file mode 100644 index 000000000..eeffe6bda --- /dev/null +++ b/libkonq/tests/Makefile.am @@ -0,0 +1,6 @@ +INCLUDES = -I$(top_srcdir)/libkonq $(all_includes) +AM_LDFLAGS = $(all_libraries) +check_PROGRAMS = konqdragtest +konqdragtest_SOURCES = konqdragtest.cpp +konqdragtest_LDADD = ../libkonq.la +TESTS = konqdragtest diff --git a/libkonq/tests/konqdragtest.cpp b/libkonq/tests/konqdragtest.cpp new file mode 100644 index 000000000..ad03b21fc --- /dev/null +++ b/libkonq/tests/konqdragtest.cpp @@ -0,0 +1,62 @@ +/* This file is part of the KDE libraries + Copyright (c) 2005 David Faure <[email protected]> + + 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 <kurl.h> +#include <kdebug.h> +#include <kurldrag.h> +#include <assert.h> +#include <qiconview.h> +#include <kapplication.h> +#include <kcmdlineargs.h> + +#include <konq_drag.h> + +// for future qttestlib porting :) +#define VERIFY assert +#define COMPARE( a, b ) assert( (a) == (b) ) + +void testKonqIconDrag2() +{ + // Those URLs don't have to exist. + KURL mediaURL = "media:/hda1/tmp/Mat%C3%A9riel"; + KURL localURL = "file:///tmp/Mat%C3%A9riel"; + KonqIconDrag2 iconDrag( 0 ); + QIconDragItem item; + iconDrag.append( item, QRect( 1, 2, 3, 4 ), QRect( 5, 6, 7, 8 ), + mediaURL.url(), localURL.url() ); + + + VERIFY( KURLDrag::canDecode( &iconDrag ) ); + KURL::List lst; + KURLDrag::decode( &iconDrag, lst ); + VERIFY( !lst.isEmpty() ); + COMPARE( lst.count(), 1 ); + kdDebug() << "lst[0]=" << lst << endl; + kdDebug() << "mediaURL=" << mediaURL << endl; + COMPARE( lst[0].url(), mediaURL.url() ); +} + +int main(int argc, char *argv[]) +{ + KApplication::disableAutoDcopRegistration(); + KCmdLineArgs::init( argc, argv, "kurltest", 0, 0, 0, 0 ); + KApplication app; // GUI needed for QPixmaps + + testKonqIconDrag2(); + return 0; +} |