diff --git a/KDE4PORTING.html b/KDE4PORTING.html index 91a5f422cb..e4ed2ebf5f 100644 --- a/KDE4PORTING.html +++ b/KDE4PORTING.html @@ -1,2941 +1,2959 @@ Guide to Porting Applications to KDE 4.0

Porting Applications to KDE 4.0

Note

All modules should build from /trunk/KDE/kdelibs; there is no snapshot branch any longer. Major changes happen on Mondays (in any timezone) so expect rapid changes during that time. Things should be generally stable otherwise, but this is the development area, so there are no guarantees.

Deprecated classed that have been renamed to e.g. K3Foo are in kde3support.

This document contains the changes you have to apply to programs written for KDE 3.x when you want to port them to KDE 4.0.

Table of Contents

Getting started

-As a start you should have a look at doc/html/porting4.html in the Qt package, +

As a start you should have a look at doc/html/porting4.html in the Qt package, or this page online.

To start with the porting, you can run "qt3to4 -strict list_of_files" followed by kdesdk/scripts/qt4/adapt-to-qt4-api.pl and kdesdk/scripts/qt4/adapt-to-kde4-api.pl

Return to the Table of Contents

Global Changes

Return to the Table of Contents

Changes in kab

Return to the Table of Contents

Changes in kdefx

NOTE: kdefx is still being discussed. The following text represents "the current plan" to the best of my knowledge, and is being provided to inform the developement community. Future-tense text is not set in stone. -- mwoehlke

kdefx is slated for removal if possible. If the list of replacements doesn't help, and you are not the only user of a particular function, please raise the issue on k-c-d. Include what function you are using and who else is using it. If you are the only user, you should copy the required code to your application.

KPixmap

Removed. Use QPixmap instead.

Return to the Table of Contents

Changes in kdecore

KAboutData

I18N_NOOPs in general replaced with KLocalizedString, produced by ki18n()/ki18nc() calls, or just KLocalizedString() if wanted empty. See also the I18N section below.

KAccel

Removed. Replaced by the new KAction/QAction framework. See KActionCollection::setAssociatedWidget() if you need the old KAccel widget-binding behavior.

KAccelAction

KAccelBase

Removed. Replaced by the new KAction/QAction framework.

KApplication

KAudioPlayer

Removed, use now Phonon::AudioPlayer

KCatalogue

Renamed to KCatalog. Method name parts with "catalogue" have been renamed to use "catalog" instead.

KCharsets

KCodecs

KConfigBackend

KSimpleConfig

This class was removed. It was a very thin wrapper around KConfig and should be replaced.

KConfig

KConfigBase

KConfigGroupSaver

The class KConfigGroupSaver has been removed, instead use KConfigGroup. The difference is a KConfigGroupSaver object directly affects the config object it is acting on, while a KConfigGroup object only ensures the entries read or written through it are in the correct group.

Instead of:
 KConfig *config = KGlobal::config();
 KConfigGroupSaver saver(config, "group name");
   ...
 config->readXXX("entry name");
 
should be rewritten as:
 KConfigGroup cg = KGlobal::config()->group("group name");
   ...
 cg.readXXX("entry name");
 

KCmdLineArgs

KCmdLineOptions

Now a proper class, instead of statically initialized struct. Use add() method for adding options (option syntax same as before); add() returns object reference, so calls can be chained to avoid repeating variable name all the way.

KComponentData (was KInstance)

KDesktopFile

KExtendedSocket

This class has been removed. See below on KNetwork::K*Socket.

KFilterDev

KGlobal

KIcon

Deprecated and renamed to K3Icon; replacement is KIcon in kdeui.

Note: at the moment you have to keep using K3Icon as a namespace for states, groups, and contexts though.

KInstance

Renamed to KComponentData

KIPC

Removed. Use KGlobalSettings::self()->emitChange() instead of KIPC::sendMessageAll().

KKey

Removed. Use plain ints as the Qt key code instead.

KKeyNative

Removed. Use plain ints as the Qt key code instead.

KKeySequence

Removed. Use QKeySequence instead. Note that QKeySequence expects multiple shortcuts to be separated by ", " (i.e. a comma then a space) in converting from text.

KLibLoader

KLibFactory

KGenericFactory

KLocale

See also the I18N section below.

KMacroExpander

KMimeSourceFactory

KMimeSourceFactory was automatically instanciated by KApplication which allowed the use of "icon:name" in Qt richtext. Q3MimeSourceFactory being in Qt3Support, and this feature being very rarely used, K3MimeSourceFactory isn't instanciated automatically anymore, you need to call K3MimeSourceFactory::install() in your main if you still depend on this feature.

KNetwork::KIpAddress

Removed. Use QHostAddress instead.

KNetwork::KResolver

Removed. If you need to resolve a hostname, use QHostInfo from QtNetwork.

KNetwork::KSocketAddress and KNetwork::KInetSocketAddress

Removed. Use QHostAddress and an integer (a quint16_t) to store the port number.

KNetwork::KUnixSocketAddress

Removed. Simply use a QString to represent the path or identifier. See the documentation on KLocalSocket.

KNetwork::K*Socket

KNotifyClient

Replaced by KNotification. The API is quite similar, but the config file need to be updated. See KNotification documentation

KProcess

Deprecated and renamed to K3Process, header is k3process.h. Use QProcess instead.

KProcCtrl

Deprecated and renamed to K3ProcessController, header is k3processcontroller.h.

KProcIO

Deprecated and renamed to K3ProcIO, header is k3procio.h.

KRegExp

Removed. Use QRegExp instead.

KRFCDate

This class has been deprecated by KDateTime. It has been renamed K3RFCDate and moved to the libkde3support. The following method changes make porting easy:

KSaveFile

KShortcutDialog

Removed, see KKeySequenceWidget/KShortcutWidget.

KShortcutList and derivatives

Removed - no longer needed. Saving and loading of shortcuts is now performed in KActionCollection, KGlobalAccel, and KStandardShortcut.

KSortableValueList

KStandardDirs

KStandardShortcut (was KStdAccel)

KStaticDeleter

Either use K3StaticDeleter or port to the K_GLOBAL_STATIC macro.

KStdAccel

Renamed to KStandardShortcut

KStringHandler

KTempDir

KTempFile

Deprecated and renamed to K3TempFile. Use KTemporaryFile instead.

When porting old KTempFile calls to KTemporaryFile, you need to remember that KTempFile by default will not automatically remove the fileonce the object is destroyed, but KTemporaryFile will! You also need to call open() in order to actually create the file.

KURL

KURLDrag

This class has been moved to kde3support. Use KUrl::populateMimeData() and KUrl::List::populateMimeData() in the drag/copy side, and the static methods KUrl::List::canDecode() and KUrl::List::fromMimeData() in the drop/paste side.

KVMAllocator

Removed, as it was unused.

KWin

This class has been merged with KWinModule into class KWindowSystem (in kdeui).

KWinModule

This class has been merged with KWinModule into class KWindowSystem (in kdeui). Don't create instances, use static functions and KWindowSystem::self() for connecting to signals.

KWin::WindowInfo

This class has been renamed KWindowInfo and moved to its own header: KWindowInfo or kwindowinfo.h. This class is not to be confused with KWindowInfo in KDE3 which was a small utility class to display message in the window icon and title. KWindowInfo represents the windowing system information for a give window, such as its geometry, desktop positioning, type, name, etc.

locate

NETRootInfo

Return to the Table of Contents

Changes in kdeui

KAboutContainer

Deprecated and renamed to K3AboutContainer. Use KAboutApplicationDialog or KAboutKdeDialog directly.

KAboutContributor

Deprecated and renamed to K3AboutContributor. Use KAboutApplicationDialog or KAboutKdeDialog directly.

KAboutDialog

Deprecated and renamed to K3AboutDialog. Use KAboutApplicationDialog or KAboutKdeDialog directly.

KAboutWidget

Deprecated and renamed to K3AboutWidget. Use KAboutApplicationDialog or KAboutKdeDialog directly.

KAction

Making KAction a subclass of QAction (actually QWidgetAction) brings KDE more into line with the Qt way of creating user interfaces, improves accessibility, and removes code duplication.

QAction in Qt4 is a true first-class citizen - all QWidgets now have a list of associated actions, and QToolBar, QMenu etc. all use this list directly to show the graphical items (widgets, entries in the menu, etc). This has replaced all usage of integers as "id"s. There is also a new QEvent (QActionEvent), and the corresponding virtual protected function in QWidget, actionEvent().

KActionCollection

KActionSelector

KActionSeparator

Used to be renamed to KSeparatorAction. KSeparatorAction/KActionSeparator is dead now. Just create a QAction object and call setSeparator(true); on it.

Note: you need to create multiple separator actions if you want to insert multiple separators into a widget, unlike with KDE3.

-

KActiveLabel

Deprecated and renamed to K3ActiveLabel. Use QLabel instead.

KAnimatedButton (was KAnimWidget)

KAnimWidget

Renamed to KAnimatedButton

KAuthIcon

Removed since it was not used.

KBugReport

KButtonBox

Deprecated and renamed to K3ButtonBox. Use KDialogButtonBox instead. Add the role parameter to the addButton function. Or check if it can't be achieved using KDialog.

KCharSelectTable

Removed from public API. Use KCharSelect.

KCharSelect

KColorCells

KColorCollection (was KPalette)

KColorDrag

Deprecated and renamed to K3ColorDrag. Use QMimeData directly instead or KColorMimeData, which most closely resembles KColorDrag

KColorScheme

New class that provides access to system-wide, user configurable color roles. Like QPalette, the return type is now QBrush instead of QColor. As of 4.0 the brushes are solid colors, but this may change in the future.

Warning: KDE4 is trying really hard to abandon the notion that the active palette == the inactive palette (because this is really helpful for the oxygen windeco which does not use "traditional" windeco colors, and because for various reasons kwin can't be entirely responsible for the effect). When porting to KDE4, you will need to take this into consideration. The new class KStatefulBrush has been added to help with this, as well as the adjustForeground and adjustBackground methods of KColorScheme.

There are lots of bugs as a result of this; some in applications, and at least a few in Qt. Please report application bugs to the appropriate maintainer or list, and general (i.e. occurring in many/all applications) bugs to k-c-d.

See the KColorScheme documentation for further information.

KColorTable (was KPaletteTable)

KComboBox

KCommand

Deprecated and renamed to K3Command, header is k3command.h.

KCommandHistory

Deprecated and renamed to K3CommandHistory, header is k3command.h.

KCompletion

KConfigDialog

KContextMenuManager

KCursor

KDatePicker

KDateTable

KDateWidget

KDialog

KDialogBase

Deprecated. Use KDialog or KPageDialog instead.

KDockWindow

This class is obsolete and is provided for compatibility only. Use KSystemTrayIcon instead.

KDualColorButton

Removed since it was not used.

KEdit

KEditListBox

KEditToolbar

KEditToolbarWidget

KFind

KFontChooser

KFontDialog

KGlobalAccel

KGlobalSettings

KGradientSelector

KGuiItem

KHistoryCombo

Renamed to KHistoryComboBox.

KHSSelector

KIcon

KIconLoader

KInputDialog

KIntNumInput, KDoubleNumInput, KDoubleSpinBox

KJanusWidget

Deprecated. Use KPageWidget instead.

KKeyButton

Removed. Use either KKeySequenceWidget or KShortcutWidget. Use KShortcutWidget only if you really want two possible shortcuts. Both don't pop up a modal dialog.

KKeyChooser

Renamed to KShortcutsEditor.

KKeyDialog

Renamed to KShortcutsDialog.

KLed

KLineEdit

KLineEditDlg

Was deprecated, has been removed. Use KInputDialog instead.

KListAction

Removed; as per comments, did not add anything to KSelectAction.

KListBox

Deprecated and renamed to K3ListBox. Use KListWidget (based on the QListWidget class).

KListView

Deprecated and renamed to K3ListView.
There is no direct KDE replacement for this class yet.
Either use KListWidget for list-based widgets or QTreeWidget for tree-based widgets. Note that only QTreeWidget supports headers.

KListViewSearchLine

Deprecated and renamed to K3ListViewSearchLine.
You should port from K3ListView to QTreeWidget and then use KTreeWidgetSearchLine instead.

KMacroCommand

Deprecated and renamed to to K3MacroCommand, header is k3command.h.

KMainWindow

KMainWindowInterface

KMenu (was KPopupMenu)

KMessageBox

KNamedCommand

Deprecated and renamed to K3NamedCommand, header is k3command.h.

KPalette

KPaletteTable

KPassivePopup

KPasswordEdit

Deprecated and renamed to K3PasswordEdit. Use KLineEdit, with KLineEdit::setPasswordMode(true).

KPasswordDialog

Deprecated and renamed to K3PasswordDialog.

 KPasswordDialog dlg(parent);
 dlg.setPrompt( prompt );
 dlg.setWindowTitle( caption );
 if( dlg.exec() != QDialog::Accepted )
     return;
 use( dlg.password() );
 
-

KPixmapIO

Removed. Use QPixmap.toImage() and QPixmap::fromImage().

KPixmapRegionSelecorDialog

KPopupMenu

Renamed to KMenu.

KPopupTitle

Removed; QMenu doesn't accept widgets, and QAction allows specification of font, icon etc... and gradients / background pixmaps weren't even working in KDE3. You can use KMenu::addTitle() instead, which creates QAction.

KProgress

Removed. Use QProgressBar instead.

KProgressDialog

KPushButton

KRadioAction

Removed. Use KAction (or KToggleAction if you need different text/icons for the selected state) instead, in combination with an exclusive QActionGroup

KRecentFilesAction

KRootPixmap

Removed since it was not used.

KSelectAction

KSeparator

KSessionManager

Moved from kdecore, renamed from KSessionManaged, and moved to it's own header: ksessionmanager.h or KSessionManager

KSharedPixmap

Removed since is was not used.

KShortcut

KShortcutsEditor (was KKeyChooser)

See also KShortcutsDialog.

KShortcutsDialog (was KKeyDialog)

KStatusBar

KStdAction

Renamed to KStandardAction, header is kstandardaction.h.

KStdGuiItem

Renamed to KStandardGuiItem, header is kstandardguiitem.h.

KSyntaxHighlighter

KSystemTray

Renamed to KSystemTrayIcon for consistency with its actual purpose and naming in Qt (QSystemTrayIcon).

KTabBar

KTabCtl

Removed. Use QTabWidget.

KTextBrowser

KTextEdit

KTimeWidget

Removed. Use QTimeEdit.

KTimezoneWidget

Renamed to KTimeZoneWidget.

KToggleAction

KToggleFullScreenAction

KToolBar

KToolBarButton

Removed. Done by QToolButton now.

KToolBarLabelAction

KToolBarRadioGroup

Removed. Done by QToolButton now.

KToolBarSeparator

Removed. Use QAction::setSeparator().

KUndoRedoAction

Deprecated and renamed to K3UndoRedoAction, header is k3command.h.

KURLLabel

KValueSelector

Renamed to KColorValueSelector, moved to kcolorvalueselector.h

KWidgetAction

Removed. Use KAction or QWidgetAction and utilise its widget-setting capabilities.

KWindowListMenu

Moved to libkworkspace in kdebase

KWizard

Deprecated and renamed to K3Wizard. Use KAssistantDialog.

Return to the Table of Contents

Changes in kio

KAbstractFileWidget

KArchive/KTar/KZip/KAr

KBookmark classes

KDEDesktopMimeType class

KDirLister class

KDiskFreeSp class

KDESasl class

KDirSelectDialog

KFileDialog,KDirSelectDialog,KUrlRequesterDlg,KCustomMenuEditor,KUrlBarItemDialog

KFileDialog, KFileView, KDirOperator

KFileDialog

KFileIconView, KFileDetailView, KFileTreeView, KFileTreeViewItem

KFileItemList

KFileMetaInfo

KFileMetaInfo has a new API and a new implementation. The change moves the emphasis away from mimetypes and groups of metadata to individual properties of files. These properties are still called KFileMetaInfoItem. The no longer belong to a group; they belong to the KFileMetaInfo object. The class KFileMetaInfoGroup is only there to group the items. Each KFileMetaInfoItem now has a member called PredicateProperties. This term comes from RDF. It determines the type of the item. This type encompasses the key (a URI), a name in the current locale, a description in the current locale, a type and a QValidator.

KFileOpenWithHandler

KFilePlugin

This class has been replaced by Strigi analyzers and KFileWritePlugin. The Strigi analyzer should be installed in the directory lib/strigi to be picked up. Introspection provided by the factories that are defined in the plugin helps in determining when a particular analyzer will be used.

Strigi analyzer have the advantage that they are faster then KFilePlugins. In addition, having one mechanism for extracting metadata from files saves code and more importantly ensures consistency between the display of metadata and the ablility to search in it. Another advantage is that the way the metadata are described matches well with the methods employed in the semantic desktop, in particular Nepomuk.

How to convert a KFilePlugin?

If your KFilePlugin only reads from files, it is easiest. Then you have to write only a Strigi analyzer. Depending on the way you read the data from the file it is best to write either a StreamThroughAnalyzer or a StreamEndAnalyzer. How to make the decision is explained in the link above. When your analyzer is ready you have to decide where to put it. You can put it in the same place as the KFilePlugin was or, if there are little or no dependencies in your analyzer, you can put it in kdesupport/strigi/src/streamindexer. The latter option has the advantage that you do not have to add the code for loading a plugin.

When you have write functionality in your plugin, you must implement KFileWritePlugin and place the code for writing the file in there. The details of how to do this are not entirely clear at the moment as is explained in the entry for KFileWritePlugin.

KFileWritePlugin

The write functionality of the class KFilePlugin is now handled by KFileWritePlugin. A global instance of this class that is attached to a KFileMetaInfoItem can be used to write back changes that were made to a files' metadata.

The mechanism by which these global instances are tied to KFileMetaDataItems is not yet finished. The implementation of will go into kio/kio/kfilemetainfo.cpp.

KIconButton

KIconDialog

KImageIO classes

KIO:: global methods

KIO::Job

KIO::UDSEntry

KIO::RenameDialog

KIO::RenameDialogPlugin

KIO::PasswordDialog

KIO::PreviewJob

KIO::SkipDialog

KIO::Observer

KIO::SlaveBase

KMimeMagic

KMimeType

KMimeTypeResolver

KOpenWithDlg

KPropertiesDialog

KPropertiesDialogPlugin

KProtocolInfo

KRun

KURLBar

KURLComboBox

KURLCompletion

KURLPixmapProvider

KURLRequester

KService

KServiceType

KServiceTypeProfile

KTrader

PasswordDialog

UIServer

Return to the Table of Contents

Changes in kparts

BrowserExtension

BrowserHostExtension

BrowserRun

ComponentFactory

KParts::URLArgs

Return to the Table of Contents

Changes in kded

Return to the Table of Contents

Changes in kspell

KSpell

Return to the Table of Contents

Changes in kdnssd

DomainBrowser

ServiceBase

ServiceBrowser

RemoteService

PublicService

Query

Return to the Table of Contents

API-cleanups in KHTML

Return to the Table of Contents

Changes in kfile

KUrlRequesterDialog

KDirOperator

KFileWidget

Return to the Table of Contents

Changes in KControl

Return to the Table of Contents

Panel Applets and Extensions

Return to the Table of Contents

libkmid

KScreensaver

libkscreensaver

Return to the Table of Contents

I18N

Messages.sh files

Instead of the "messages" target of the Makefile.am files, KDE4 uses now Bash scripts with Messages.sh file names. The Perl script branches/work/l10n-kde4/scripts/conversion/Makefile2Messages.pl can be used as an half-automatic conversion. (Please verify the result!)

rc.cpp file

The rc.cpp is now always generated. It is mandatory to have it as parameter of the $XGETTEXT if you are using the class KAboutData and if you are not explicitly setting KAboutData::setTranslator.

Gettext 0.16.1

Scripty uses xgettext 0.16.1 for extracting messages out of the source code. This is a huge step from the former xgettext 0.10.35-kde used in KDE3.

Important changes:

Translator Comments

Translator comments are a feature that already existed in KDE3, but which was seldom used.

 // i18n: this is a test sentence
 i18n("The quick brown fox jumps over the lazy dog");
 

i18n calls

The basic call, a message without arguments, context or plural, remains as is:

 i18n("Just plain info");
 

If there are arguments, arg methods are no longer used to substitute the placeholders. Instead, arguments are added to the call:

 i18n("%1 has scored %2", playerName, score);
 

Arguments can be of several integer types, double, QString, QChar (and perhaps more in the future).

Context call now has a different name, i18nc, and takes arguments in the same way:

 i18nc("Player name - score", "%1 - %2", playerName, score);
 

Plural call is renamed to i18np and does away with %n placeholder, all being numbered instead:

 i18np("One image in album %2", "%1 images in album %2", n, albumName);
 

The plural form is decided by first integer-valued argument.

There is one new call variant, the context-plural call:

 i18ncp("Personal file", "One file", "%1 files", n);
 

i18n* calls are realized as templates, and so will take at most some finite number of arguments.

Notes on placeholder substitution:

Sometimes you might need old-style syntax for argument substitution. For example, when there are many arguments, or, more importantly, when it is convenient to defer substitution of arguments. This is possible using new ki18n call, with subs methods for argument substitution and toString method for finalization:

 KLocalizedString ks = ki18n("The Foo-machine reported: %1");
 case (fooErrCode) {
     ERR_OXI: ks.subs(i18n("Out of oxidizer")); break;
     ERR_HYD: ks.subs(i18n("Out of hydrazine")); break;
     ERR_PIL: ks.subs(i18n("Out of pilots")); break;
     default: ks.subs(i18n("Unknown catastrophe"));
 }
 QString scream = ks.toString();
 

Note that ki18n returns object of type KLocalizedString, hence the toString method for conversion to plain QString. The ki18n call should be used rarely; the previous example can as well be rewritten to:

 QString problem;
 case (fooErrCode) {
     ERR_OXI: problem = i18n("Out of oxidizer"); break;
     ...
 }
 QString scream = i18n("The Foo-machine reported: %1", problem);
 

Another case when you might want to use ki18n is if you want to format arguments. subs methods can take formatting arguments, similar to those of arg methods in QString. You should never use methods other than subs to format numbers in localized messages:

 i18n("Rounds: %1", myNumberFormat(n, 8)); // bad, number not localized
 ki18n("Rounds: %1").subs(n, 8).toString(); // good, number is localized
 

There is a context, plural and context-plural variant of ki18n as well:

 ki18nc("No function", "None").toString();
 ki18np("One file found", "%1 files found").subs(n).toString();
 ki18ncp("Personal file", "One file", "%1 files").subs(n).toString();
 

If you need translation using locale (KLocale object) other than the default, you can use overloaded toString method which takes pointer to locale object (this replaces KLocale::translate(...) from KDE3):

 KLocale *myLocale;
 ...
 ki18n("Welcome").toString(myLocale);
 

There is a script for semi-automatic conversion of KDE 3 code, i18nk34conv.pl in branches/work/kde4-l10n/scripts/conversion/. It will convert most of the calls automatically, and mark the places that need manual review. There is also the script i18ncheckarg.pl in branches/work/kde4-l10n/scripts, which will check whether placeholders and arguments match. Use it to catch and correct deferred substitutions (like in the examples above). Both these scripts you can just run without any arguments in top directory of your sources (but you can also specify paths or filenames as arguments). Do not run conversion script twice on same sources!

Return to the Table of Contents

Changes in KSSL

KSSLCertDialog

KSSLInfoDialog

Return to the Table of Contents

Changes in mime types names

KDE 4 switched to the XDG mime types, thus the names for some mime types have changed.

The following list contains the renamings from the KDE mime types to the XDG ones.

Notes:

- +
Old KDE mime type XDG mime type
application/chm application/x-chm
application/fits image/x-fits
application/java application/x-java
application/mathml+xml text/mathml
application/msexcel application/vnd.ms-excel (alias)
application/mspowerpoint application/vnd.ms-powerpoint (alias)
application/ms-tnef application/vnd.ms-tnef
application/pgp application/pgp-encrypted (alias)
application/vnd.ms-asf video/x-ms-asf
application/vnd.ms-word application/msword (alias)
application/vnd.palm application/x-palm-database (alias)
application/vnd.stardivision.writer-global application/vnd.stardivision.writer
application/vnd.sun.xml.base application/vnd.oasis.opendocument.database
application/vnd.sun.xml.writer.master application/vnd.sun.xml.writer.global
application/wordperfect application/vnd.wordperfect (alias)
application/x-7z application/x-7z-compressed
application/x-afm application/x-font-afm
application/x-applixgraphics image/x-applix-graphics
application/x-applixspread application/x-applix-spreadsheet
application/x-applixword application/x-applix-word
application/x-bz2dvi application/x-bzdvi
application/x-bzip2 application/x-bzip (alias)
application/x-dbase application/x-dbf (alias)
application/x-font-ghostscript application/x-font-type1
application/x-font-ttc application/x-font-ttf
application/x-gettext text/x-gettext-translation (*.po)
text/x-gettext-translation-template (*.pot)
application/x-hancomword application/x-hwp
application/x-iso application/x-cd-image
application/x-jar application/x-java-archive (alias)
application/x-javascript application/javascript (alias)
application/x-msaccess application/vnd.ms-access (alias)
application/x-msdos-program application/x-ms-dos-executable
application/x-msmetafile image/x-wmf
application/x-ogg application/ogg
application/x-perl-module application/x-perl
application/x-python text/x-python
application/x-rar-compressed application/x-rar
application/x-tbz application/x-bzip-compressed-tar
application/x-tgz application/x-compressed-tar
application/x-troff text/troff (alias)
application/x-zip application/zip
application/x-zip-compressed application/zip (alias)
application/xml-dtd text/x-dtd
audio/mpegurl audio/x-mpegurl (alias)
audio/x-midi audio/midi (alias)
audio/x-mp3 audio/mpeg (alias)
audio/x-oggflac audio/x-flac+ogg
audio/x-pn-realaudio audio/vnd.rn-realaudio (alias)
audio/x-speex audio/x-speex+ogg
audio/x-vorbis audio/x-vorbis+ogg
audio/vorbis audio/x-vorbis+ogg
image/fits image/x-fits
image/jp2 image/jpeg2000
image/jpg image/jpeg
image/pjpeg image/jpeg (alias)
image/svg-xml image/svg+xml
image/svg+xml image/svg+xml (*.svg)
image/svg+xml-compressed (*.svgz)
image/x-bmp image/bmp
image/x-djvu image/vnd.djvu (alias)
image/x-portable-greymap image/x-portable-graymap
image/x-raw image/x-dcraw (general mime type)

Specific mime types (all subclasses of image/x-dcraw):
image/x-panasonic-raw (*.raw)
image/x-kodak-dcr (*.dcr)
image/x-adobe-dng (*.dng)
image/x-canon-crw (*.crw *.cr2)
image/x-nikon-nef (*.nef)
image/x-minolta-mrw (*.mrw)
image/x-kodak-k25 (*.k25)
image/x-kodak-kdc (*.kdc)
image/x-olympus-orf (*.orf)
image/x-pentax-pef (*.pef)
image/x-fuji-raf (*.raf)
image/x-sony-sr2 (*.sr2)
image/x-sony-srf (*.srf)
image/x-sigma-x3f (*.x3f)
other extensions that were part of image/x-raw: image/x-kde-raw
image/x-targa image/x-tga
image/x-vnd.adobe.photoshop image/x-psd
image/x-xbm image/x-xbitmap
image/x-xcf-gimp image/x-xcf
image/x-xpm image/x-xpixmap
text/docbook application/docbook+xml
text/javascript application/javascript (alias)
text/rss application/rss+xml (alias)
text/rtf application/rtf
text/x-csv text/csv
text/x-diff text/x-patch
text/x-latex text/x-tex
text/xml application/xml (alias)
text/x-mswinurl application/x-mswinurl
text/x-vcalendar text/calendar (alias)
text/x-vcard text/directory (alias)
text/x-xslt application/xslt+xml
video/avi video/x-msvideo
video/x-ogm video/x-ogm+ogg
video/x-theora video/x-theora+ogg

Return to the Table of Contents

Changes in KNewStuff

General

KNewStuff is replaced with KNewStuff2. The API for applications has changed completely, as the library is a rewrite and not an update. Do not inherit from any class, use only KNS::Engine. All application-specific properties especially about what to do with downloaded files are configured in *.knsrc files; have a look at knewstuff/doc/porting.txt and knewstuff/doc/tutorial.txt.

Upload

Download

Return to the Table of Contents

DCOP

DCOP has been replaced with D-Bus.
Help with porting from DCOP to D-Bus can be found at http://techbase.kde.org/Development/Tutorials/Porting_to_D-Bus

Return to the Table of Contents

diff --git a/kfile/k3filetreeview.cpp b/kfile/k3filetreeview.cpp index 632685690a..b20e1125ed 100644 --- a/kfile/k3filetreeview.cpp +++ b/kfile/k3filetreeview.cpp @@ -1,679 +1,679 @@ /* This file is part of the KDEproject Copyright (C) 2000 David Faure 2000 Carsten Pfeiffer 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 "k3filetreeview.h" #include "kfiletreebranch.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include K3FileTreeView::K3FileTreeView( QWidget *parent ) : K3ListView( parent ), m_wantOpenFolderPixmaps( true ) { setDragEnabled(true); setSelectionModeExt( K3ListView::Single ); m_animationTimer = new QTimer( this ); connect( m_animationTimer, SIGNAL( timeout() ), this, SLOT( slotAnimation() ) ); m_currentBeforeDropItem = 0; m_dropItem = 0; m_autoOpenTimer = new QTimer( this ); connect( m_autoOpenTimer, SIGNAL( timeout() ), this, SLOT( slotAutoOpenFolder() ) ); /* The executed-Slot only opens a path, while the expanded-Slot populates it */ connect( this, SIGNAL( executed( Q3ListViewItem * ) ), this, SLOT( slotExecuted( Q3ListViewItem * ) ) ); connect( this, SIGNAL( expanded ( Q3ListViewItem *) ), this, SLOT( slotExpanded( Q3ListViewItem *) )); connect( this, SIGNAL( collapsed( Q3ListViewItem *) ), this, SLOT( slotCollapsed( Q3ListViewItem* ))); /* connections from the konqtree widget */ connect( this, SIGNAL( selectionChanged() ), this, SLOT( slotSelectionChanged() ) ); connect( this, SIGNAL( onItem( Q3ListViewItem * )), this, SLOT( slotOnItem( Q3ListViewItem * ) ) ); connect( this, SIGNAL(itemRenamed(Q3ListViewItem*, const QString &, int)), this, SLOT(slotItemRenamed(Q3ListViewItem*, const QString &, int))); m_bDrag = false; m_branches.setAutoDelete( true ); m_openFolderPixmap = DesktopIcon( "folder-open",K3Icon::SizeSmall,K3Icon::ActiveState ); } K3FileTreeView::~K3FileTreeView() { // we must make sure that the KFileTreeViewItems are deleted _before_ the // branches are deleted. Otherwise, the KFileItems would be destroyed // and the KFileTreeViewItems had dangling pointers to them. hide(); clear(); m_branches.clear(); // finally delete the branches and KFileItems } bool K3FileTreeView::isValidItem( Q3ListViewItem *item) { if (!item) return false; Q3PtrList lst; Q3ListViewItemIterator it( this ); while ( it.current() ) { if ( it.current() == item ) return true; ++it; } return false; } void K3FileTreeView::contentsDragEnterEvent( QDragEnterEvent *ev ) { if ( ! acceptDrag( ev ) ) { ev->ignore(); return; } ev->acceptProposedAction(); m_currentBeforeDropItem = selectedItem(); Q3ListViewItem *item = itemAt( contentsToViewport( ev->pos() ) ); if( item ) { m_dropItem = item; m_autoOpenTimer->start( KFileView::autoOpenDelay() ); } else { m_dropItem = 0; } } void K3FileTreeView::contentsDragMoveEvent( QDragMoveEvent *e ) { if( ! acceptDrag( e ) ) { e->ignore(); return; } e->acceptProposedAction(); Q3ListViewItem *afterme; Q3ListViewItem *parent; findDrop( e->pos(), parent, afterme ); // "afterme" is 0 when aiming at a directory itself Q3ListViewItem *item = afterme ? afterme : parent; if( item && item->isSelectable() ) { setSelected( item, true ); if( item != m_dropItem ) { m_autoOpenTimer->stop(); m_dropItem = item; m_autoOpenTimer->start( KFileView::autoOpenDelay() ); } } else { m_autoOpenTimer->stop(); m_dropItem = 0; } } void K3FileTreeView::contentsDragLeaveEvent( QDragLeaveEvent * ) { // Restore the current item to what it was before the dragging (#17070) if ( isValidItem(m_currentBeforeDropItem) ) { setSelected( m_currentBeforeDropItem, true ); ensureItemVisible( m_currentBeforeDropItem ); } else if ( isValidItem(m_dropItem) ) setSelected( m_dropItem, false ); // no item selected m_currentBeforeDropItem = 0; m_dropItem = 0; } void K3FileTreeView::contentsDropEvent( QDropEvent *e ) { m_autoOpenTimer->stop(); m_dropItem = 0; kDebug(250) << "contentsDropEvent !"; if( ! acceptDrag( e ) ) { e->ignore(); return; } e->acceptProposedAction(); Q3ListViewItem *afterme; Q3ListViewItem *parent; findDrop(e->pos(), parent, afterme); //kDebug(250) << " parent=" << (parent?parent->text(0):QString()) // << " afterme=" << (afterme?afterme->text(0):QString()) << endl; if (e->source() == viewport() && itemsMovable()) movableDropEvent(parent, afterme); else { emit dropped(e, afterme); emit dropped(this, e, afterme); emit dropped(e, parent, afterme); emit dropped(this, e, parent, afterme); KUrl::List urls = KUrl::List::fromMimeData( e->mimeData() ); if ( urls.isEmpty() ) return; emit dropped( this, e, urls ); KUrl parentURL; if( parent ) parentURL = static_cast(parent)->url(); else // can happen when dropping above the root item // Should we choose the first branch in such a case ?? return; emit dropped( urls, parentURL ); emit dropped( this , e, urls, parentURL ); } } bool K3FileTreeView::acceptDrag(QDropEvent* e ) const { bool ancestOK= acceptDrops(); // kDebug(250) << "Do accept drops: " << ancestOK; ancestOK = ancestOK && itemsMovable(); // kDebug(250) << "acceptDrag: " << ancestOK; // kDebug(250) << "canDecode: " << KUrl::List::canDecode(e->mimeData()); // kDebug(250) << "action: " << e->action(); /* K3ListView::acceptDrag(e); */ /* this is what K3ListView does: * acceptDrops() && itemsMovable() && (e->source()==viewport()); * ask acceptDrops and itemsMovable, but not the third */ return ancestOK && KUrl::List::canDecode( e->mimeData() ) && // Why this test? All DnDs are one of those AFAIK (DF) ( e->dropAction() == Qt::CopyAction || e->dropAction() == Qt::MoveAction || e->dropAction() == Qt::LinkAction ); } Q3DragObject * K3FileTreeView::dragObject() { KUrl::List urls; const QList fileList = selectedItems(); for (int i = 0; i < fileList.size(); ++i) { urls.append( static_cast(fileList.at(i))->url() ); } QPoint hotspot; QPixmap pixmap; if( urls.count() > 1 ){ pixmap = DesktopIcon( "kmultiple", 16 ); } if( pixmap.isNull() ) - pixmap = currentKFileTreeViewItem()->fileItem()->pixmap( 16 ); + pixmap = currentKFileTreeViewItem()->fileItem().pixmap( 16 ); hotspot.setX( pixmap.width() / 2 ); hotspot.setY( pixmap.height() / 2 ); #if 0 // there is no more kurldrag, this should use urls.setInMimeData( mimeData ) instead Q3DragObject* dragObject = new KUrlDrag( urls, this ); if( dragObject ) dragObject->setPixmap( pixmap, hotspot ); return dragObject; #endif return 0; } void K3FileTreeView::slotCollapsed( Q3ListViewItem *item ) { K3FileTreeViewItem *kftvi = static_cast(item); kDebug(250) << "hit slotCollapsed"; if( kftvi && kftvi->isDir()) { item->setPixmap( 0, itemIcon(kftvi)); } } void K3FileTreeView::slotExpanded( Q3ListViewItem *item ) { kDebug(250) << "slotExpanded here !"; if( ! item ) return; K3FileTreeViewItem *it = static_cast(item); KFileTreeBranch *branch = it->branch(); /* Start the animation for the branch object */ if( it->isDir() && branch && item->childCount() == 0 ) { /* check here if the branch really needs to be populated again */ kDebug(250 ) << "starting to open " << it->url().prettyUrl(); startAnimation( it ); bool branchAnswer = branch->populate( it->url(), it ); kDebug(250) << "Branches answer: " << branchAnswer; if( ! branchAnswer ) { kDebug(250) << "ERR: Could not populate!"; stopAnimation( it ); } } /* set a pixmap 'open folder' */ if( it->isDir() && isOpen( item ) ) { kDebug(250)<< "Setting open Pixmap"; item->setPixmap( 0, itemIcon( it )); // 0, m_openFolderPixmap ); } } void K3FileTreeView::slotExecuted( Q3ListViewItem *item ) { if ( !item ) return; /* This opens the dir and causes the Expanded-slot to be called, * which strolls through the children. */ if( static_cast(item)->isDir()) { item->setOpen( !item->isOpen() ); } } void K3FileTreeView::slotAutoOpenFolder() { m_autoOpenTimer->stop(); if ( !isValidItem(m_dropItem) || m_dropItem->isOpen() ) return; m_dropItem->setOpen( true ); m_dropItem->repaint(); } void K3FileTreeView::slotSelectionChanged() { if ( !m_dropItem ) // don't do this while the dragmove thing { } } KFileTreeBranch* K3FileTreeView::addBranch( const KUrl &path, const QString& name, bool showHidden ) { const QPixmap& folderPix = KIconLoader::global()->loadMimeTypeIcon( KMimeType::mimeType("inode/directory")->iconName(), K3Icon::Desktop, K3Icon::SizeSmall ); return addBranch( path, name, folderPix, showHidden); } KFileTreeBranch* K3FileTreeView::addBranch( const KUrl &path, const QString& name, const QPixmap& pix, bool showHidden ) { kDebug(250) << "adding another root " << path.prettyUrl(); /* Open a new branch */ KFileTreeBranch *newBranch = new KFileTreeBranch( this, path, name, pix, showHidden ); return addBranch(newBranch); } KFileTreeBranch *K3FileTreeView::addBranch(KFileTreeBranch *newBranch) { connect( newBranch, SIGNAL(populateFinished( K3FileTreeViewItem* )), this, SLOT( slotPopulateFinished( K3FileTreeViewItem* ))); connect( newBranch, SIGNAL( newTreeViewItems( KFileTreeBranch*, const K3FileTreeViewItemList& )), this, SLOT( slotNewTreeViewItems( KFileTreeBranch*, const K3FileTreeViewItemList& ))); m_branches.append( newBranch ); return( newBranch ); } KFileTreeBranch *K3FileTreeView::branch( const QString& searchName ) { KFileTreeBranch *branch = 0; Q3PtrListIterator it( m_branches ); while ( (branch = it.current()) != 0 ) { ++it; QString bname = branch->name(); kDebug(250) << "This is the branches name: " << bname; if( bname == searchName ) { kDebug(250) << "Found branch " << bname << " and return ptr"; return( branch ); } } return ( 0L ); } KFileTreeBranchList& K3FileTreeView::branches() { return( m_branches ); } bool K3FileTreeView::removeBranch( KFileTreeBranch *branch ) { if(m_branches.contains(branch)) { delete (branch->root()); m_branches.remove( branch ); return true; } else { return false; } } void K3FileTreeView::setDirOnlyMode( KFileTreeBranch* branch, bool bom ) { if( branch ) { branch->setDirOnlyMode( bom ); } } void K3FileTreeView::slotPopulateFinished( K3FileTreeViewItem *it ) { if( it && it->isDir()) stopAnimation( it ); } void K3FileTreeView::slotNewTreeViewItems( KFileTreeBranch* branch, const K3FileTreeViewItemList& itemList ) { if( ! branch ) return; kDebug(250) << "hitting slotNewTreeViewItems"; /* Sometimes it happens that new items should become selected, i.e. if the user * creates a new dir, he probably wants it to be selected. This can not be done * right after creating the directory or file, because it takes some time until * the item appears here in the treeview. Thus, the creation code sets the member * m_neUrlToSelect to the required url. If this url appears here, the item becomes * selected and the member nextUrlToSelect will be cleared. */ if( ! m_nextUrlToSelect.isEmpty() ) { K3FileTreeViewItemListIterator it( itemList ); bool end = false; for( ; !end && it.current(); ++it ) { KUrl url = (*it)->url(); if( m_nextUrlToSelect.equals(url, KUrl::CompareWithoutTrailingSlash )) // ignore trailing / on dirs { setCurrentItem( static_cast(*it) ); m_nextUrlToSelect = KUrl(); end = true; } } } } QPixmap K3FileTreeView::itemIcon( K3FileTreeViewItem *item, int gap ) const { QPixmap pix; kDebug(250) << "Setting icon for column " << gap; if( item ) { /* Check if it is a branch root */ KFileTreeBranch *brnch = item->branch(); if( item == brnch->root() ) { pix = brnch->pixmap(); if( m_wantOpenFolderPixmaps && brnch->root()->isOpen() ) { pix = brnch->openPixmap(); } } else { // TODO: different modes, user Pixmaps ? - pix = item->fileItem()->pixmap( K3Icon::SizeSmall ); // , K3Icon::DefaultState); + pix = item->fileItem().pixmap( K3Icon::SizeSmall ); // , K3Icon::DefaultState); /* Only if it is a dir and the user wants open dir pixmap and it is open, * change the fileitem's pixmap to the open folder pixmap. */ if( item->isDir() && m_wantOpenFolderPixmaps ) { if( isOpen( static_cast(item))) pix = m_openFolderPixmap; } } } return pix; } void K3FileTreeView::slotAnimation() { MapCurrentOpeningFolders::Iterator it = m_mapCurrentOpeningFolders.begin(); MapCurrentOpeningFolders::Iterator end = m_mapCurrentOpeningFolders.end(); for (; it != end;) { K3FileTreeViewItem *item = it.key(); if (!isValidItem(item)) { ++it; m_mapCurrentOpeningFolders.remove(item); continue; } uint & iconNumber = it.value().iconNumber; QString icon = QString::fromLatin1( it.value().iconBaseName ).append( QString::number( iconNumber ) ); // kDebug(250) << "Loading icon " << icon; item->setPixmap( 0, DesktopIcon( icon,K3Icon::SizeSmall,K3Icon::ActiveState )); // KFileTreeViewFactory::instance() ) ); iconNumber++; if ( iconNumber > it.value().iconCount ) iconNumber = 1; ++it; } } void K3FileTreeView::startAnimation( K3FileTreeViewItem * item, const char * iconBaseName, uint iconCount ) { /* TODO: allow specific icons */ if( ! item ) { kDebug(250) << " startAnimation Got called without valid item !"; return; } m_mapCurrentOpeningFolders.insert( item, AnimationInfo( iconBaseName, iconCount, itemIcon(item, 0) ) ); if ( !m_animationTimer->isActive() ) m_animationTimer->start( 50 ); } void K3FileTreeView::stopAnimation( K3FileTreeViewItem * item ) { if( ! item ) return; kDebug(250) << "Stoping Animation !"; MapCurrentOpeningFolders::Iterator it = m_mapCurrentOpeningFolders.find(item); if ( it != m_mapCurrentOpeningFolders.end() ) { if( item->isDir() && isOpen( item) ) { kDebug(250) << "Setting folder open pixmap !"; item->setPixmap( 0, itemIcon( item )); } else { item->setPixmap( 0, it.value().originalPixmap ); } m_mapCurrentOpeningFolders.remove( item ); } else { if( item ) kDebug(250)<< "StopAnimation - could not find item " << item->url().prettyUrl(); else kDebug(250)<< "StopAnimation - item is zero !"; } if (m_mapCurrentOpeningFolders.isEmpty()) m_animationTimer->stop(); } K3FileTreeViewItem * K3FileTreeView::currentKFileTreeViewItem() const { return static_cast( selectedItem() ); } KUrl K3FileTreeView::currentUrl() const { K3FileTreeViewItem *item = currentKFileTreeViewItem(); if ( item ) return currentKFileTreeViewItem()->url(); else return KUrl(); } void K3FileTreeView::slotOnItem( Q3ListViewItem *item ) { K3FileTreeViewItem *i = static_cast( item ); if( i ) { const KUrl url = i->url(); if ( url.isLocalFile() ) emit onItem( url.path() ); else emit onItem( url.prettyUrl() ); } } void K3FileTreeView::slotItemRenamed(Q3ListViewItem* item, const QString &name, int col) { (void) item; kDebug(250) << "Do not bother: " << name << col; } K3FileTreeViewItem *K3FileTreeView::findItem( const QString& branchName, const QString& relUrl ) { KFileTreeBranch *br = branch( branchName ); return( findItem( br, relUrl )); } K3FileTreeViewItem *K3FileTreeView::findItem( KFileTreeBranch* brnch, const QString& relUrl ) { K3FileTreeViewItem *ret = 0; if( brnch ) { KUrl url = brnch->rootUrl(); if( ! relUrl.isEmpty() && QDir::isRelativePath(relUrl) ) { QString partUrl( relUrl ); if( partUrl.endsWith('/')) partUrl.truncate( relUrl.length()-1 ); url.addPath( partUrl ); kDebug(250) << "assembled complete dir string " << url.prettyUrl(); - KFileItem *fi = brnch->findByUrl( url ); - if( fi ) + KFileItem fi = brnch->findByUrl( url ); + if( !fi.isNull() ) { ret = const_cast( - static_cast(fi->extraData(brnch))); + static_cast(fi.extraData(brnch))); kDebug(250) << "Found item !" <root(); } } return( ret ); } bool K3FileTreeView::showFolderOpenPixmap() const { return m_wantOpenFolderPixmaps; } void K3FileTreeView::setShowFolderOpenPixmap( bool showIt ) { m_wantOpenFolderPixmaps = showIt; } void K3FileTreeView::slotSetNextUrlToSelect( const KUrl &url ) { m_nextUrlToSelect = url; } /////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////// #include "k3filetreeview.moc" diff --git a/kfile/k3filetreeviewitem.cpp b/kfile/k3filetreeviewitem.cpp index 310714707b..c5c4bc912b 100644 --- a/kfile/k3filetreeviewitem.cpp +++ b/kfile/k3filetreeviewitem.cpp @@ -1,83 +1,86 @@ /* This file is part of the KDEproject Copyright (C) 2000 David Faure 2000 Carsten Pfeiffer 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 "k3filetreeviewitem.h" #include #include #include /* --- K3FileTreeViewItem --- */ /* */ K3FileTreeViewItem::K3FileTreeViewItem( K3FileTreeViewItem *parent, - KFileItem* item, + const KFileItem &item, KFileTreeBranch *brnch ) : K3ListViewItem( parent ), m_kfileitem( item ), m_branch( brnch ), m_wasListed(false) { - setPixmap(0, item->pixmap( K3Icon::SizeSmall )); - setText( 0, item->text()); + setPixmap(0, item.pixmap( K3Icon::SizeSmall )); + setText( 0, item.text()); + m_kfileitem.setExtraData( m_branch, this ); } K3FileTreeViewItem::K3FileTreeViewItem( K3FileTreeView* parent, - KFileItem* item, + const KFileItem &item, KFileTreeBranch *brnch ) :K3ListViewItem( (Q3ListView*)parent ), m_kfileitem(item ), m_branch( brnch ), m_wasListed(false) { - setPixmap(0, item->pixmap( K3Icon::SizeSmall )); - setText( 0, item->text()); + setPixmap(0, item.pixmap( K3Icon::SizeSmall )); + setText( 0, item.text()); + + m_kfileitem.setExtraData( m_branch, this ); } K3FileTreeViewItem::~K3FileTreeViewItem() { - if ( m_kfileitem ) - m_kfileitem->removeExtraData( m_branch ); + if ( !m_kfileitem.isNull() ) + m_kfileitem.removeExtraData( m_branch ); } bool K3FileTreeViewItem::alreadyListed() const { return m_wasListed; } void K3FileTreeViewItem::setListed( bool wasListed ) { m_wasListed = wasListed; } KUrl K3FileTreeViewItem::url() const { - return m_kfileitem ? m_kfileitem->url() : KUrl(); + return !m_kfileitem.isNull() ? m_kfileitem.url() : KUrl(); } QString K3FileTreeViewItem::path() const { - return m_kfileitem ? m_kfileitem->url().path() : QString(); + return !m_kfileitem.isNull() ? m_kfileitem.url().path() : QString(); } bool K3FileTreeViewItem::isDir() const { - return m_kfileitem ? m_kfileitem->isDir() : false; + return !m_kfileitem.isNull() ? m_kfileitem.isDir() : false; } diff --git a/kfile/k3filetreeviewitem.h b/kfile/k3filetreeviewitem.h index 4fc930d555..9ec8f0e383 100644 --- a/kfile/k3filetreeviewitem.h +++ b/kfile/k3filetreeviewitem.h @@ -1,107 +1,107 @@ /* This file is part of the KDE project Copyright (C) 2000 David Faure 2000 Carsten Pfeiffer 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 kfile_tree_view_item_h #define kfile_tree_view_item_h #include "kfile_export.h" #include #include #include #include #include #include class KUrl; class K3FileTreeView; class KFileTreeBranch; /** * An item for a K3FileTreeView that knows about its own KFileItem. */ class KFILE_EXPORT K3FileTreeViewItem : public K3ListViewItem { public: - K3FileTreeViewItem( K3FileTreeViewItem*, KFileItem*, KFileTreeBranch * ); - K3FileTreeViewItem( K3FileTreeView*, KFileItem*, KFileTreeBranch * ); + K3FileTreeViewItem( K3FileTreeViewItem*, const KFileItem&, KFileTreeBranch * ); + K3FileTreeViewItem( K3FileTreeView*, const KFileItem&, KFileTreeBranch * ); ~K3FileTreeViewItem(); /** * @return the KFileTreeBranch the item is sorted in. */ KFileTreeBranch* branch() const { return m_branch; } /** * @return the KFileItem the viewitem is representing. */ - KFileItem *fileItem() const { return m_kfileitem; } + KFileItem fileItem() const { return m_kfileitem; } /** * @return the path of the item. */ QString path() const; /** * @return the items KUrl */ KUrl url() const; /** * @return if the item represents a directory */ bool isDir() const; /** * @return if this directory was already seen by a KDirLister. */ bool alreadyListed() const; /** * set the flag if the directory was already listed. */ void setListed( bool wasListed ); protected: private: - KFileItem *m_kfileitem; + KFileItem m_kfileitem; KFileTreeBranch *m_branch; bool m_wasListed; class K3FileTreeViewItemPrivate; K3FileTreeViewItemPrivate *d; }; /** * List of KFileTreeViewItems */ typedef Q3PtrList K3FileTreeViewItemList; /** * Iterator for KFileTreeViewItemList */ typedef Q3PtrListIterator K3FileTreeViewItemListIterator; #endif diff --git a/kfile/kdiroperator.cpp b/kfile/kdiroperator.cpp index 9c5ecf7eff..43778dd9a7 100644 --- a/kfile/kdiroperator.cpp +++ b/kfile/kdiroperator.cpp @@ -1,2092 +1,2086 @@ /* This file is part of the KDE libraries Copyright (C) 1999,2000 Stephan Kulow 1999,2000,2001,2002,2003 Carsten Pfeiffer 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 "kdiroperator.h" #include "kdirmodel.h" #include "kdirsortfilterproxymodel.h" #include "kfileview.h" #include "kfileitem.h" #include "kfilemetapreview.h" #include "kpreviewwidgetbase.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -template class QHash; +template class QHash; /** * Default icon view for KDirOperator using * custom view options. */ class DirOperatorIconView : public QListView { public: DirOperatorIconView(QWidget *parent = 0); virtual ~DirOperatorIconView(); protected: virtual QStyleOptionViewItem viewOptions() const; virtual void mousePressEvent(QMouseEvent *event); private: QStyleOptionViewItem m_viewOptions; }; DirOperatorIconView::DirOperatorIconView(QWidget *parent) : QListView(parent) { setViewMode(QListView::IconMode); setFlow(QListView::TopToBottom); setResizeMode(QListView::Adjust); setSpacing(KDialog::spacingHint()); setMovement(QListView::Static); m_viewOptions = QListView::viewOptions(); m_viewOptions.showDecorationSelected = true; m_viewOptions.decorationPosition = QStyleOptionViewItem::Left; m_viewOptions.displayAlignment = Qt::AlignLeft | Qt::AlignVCenter; m_viewOptions.decorationSize = QSize(K3Icon::SizeSmall, K3Icon::SizeSmall); QFontMetrics metrics(m_viewOptions.font); const int fontHeight = metrics.height(); setGridSize(QSize(fontHeight * 10, fontHeight + 4)); } DirOperatorIconView::~DirOperatorIconView() { } QStyleOptionViewItem DirOperatorIconView::viewOptions() const { return m_viewOptions; } void DirOperatorIconView::mousePressEvent(QMouseEvent *event) { if (!indexAt(event->pos()).isValid()) { const Qt::KeyboardModifiers modifier = QApplication::keyboardModifiers(); if (!(modifier & Qt::ShiftModifier) && !(modifier & Qt::ControlModifier)) { clearSelection(); } } QListView::mousePressEvent(event); } /** * Default detail view for KDirOperator using * custom resizing options and columns. */ class DirOperatorDetailView : public QTreeView { public: DirOperatorDetailView(QWidget *parent = 0); virtual ~DirOperatorDetailView(); protected: virtual bool event(QEvent *event); virtual void resizeEvent(QResizeEvent* event); }; DirOperatorDetailView::DirOperatorDetailView(QWidget *parent) : QTreeView(parent) { setRootIsDecorated(false); setSortingEnabled(true); setUniformRowHeights(true); setSelectionBehavior(QAbstractItemView::SelectRows); } DirOperatorDetailView::~DirOperatorDetailView() { } bool DirOperatorDetailView::event(QEvent *event) { if (event->type() == QEvent::Polish) { // Assure that by respecting the available width that: // - the 'Name' column is stretched as large as possible // - the remaining columns are as small as possible QHeaderView *headerView = header(); headerView->setStretchLastSection(false); headerView->setResizeMode(QHeaderView::ResizeToContents); headerView->setResizeMode(0, QHeaderView::Stretch); // hide columns hideColumn(KDirModel::Permissions); hideColumn(KDirModel::Owner); hideColumn(KDirModel::Group); } return QTreeView::event(event); } void DirOperatorDetailView::resizeEvent(QResizeEvent* event) { QTreeView::resizeEvent(event); // assure that the width of the name-column does not get too small const int minWidth = 120; QHeaderView* headerView = header(); bool useFixedWidth = (headerView->sectionSize(KDirModel::Name) <= minWidth) && (headerView->resizeMode(0) != QHeaderView::Fixed); if (useFixedWidth) { // the current width of the name-column is too small, hence // use a fixed size headerView->setResizeMode(QHeaderView::Fixed); headerView->setResizeMode(0, QHeaderView::Fixed); headerView->resizeSection(KDirModel::Name, minWidth); } else if (headerView->resizeMode(0) != QHeaderView::Stretch) { // check whether there is enough available viewport width // to automatically resize the columns const int availableWidth = viewport()->width(); int headerWidth = 0; const int count = headerView->count(); for (int i = 0; i < count; ++i) { headerWidth += headerView->sectionSize(i); } if (headerWidth < availableWidth) { headerView->setResizeMode(QHeaderView::ResizeToContents); headerView->setResizeMode(0, QHeaderView::Stretch); } } } class KDirOperator::KDirOperatorPrivate { public: KDirOperatorPrivate(); ~KDirOperatorPrivate(); QStack backStack; ///< Contains all URLs you can reach with the back button. QStack forwardStack; ///< Contains all URLs you can reach with the forward button. KDirLister *dirLister; KUrl currUrl; KCompletion completion; KCompletion dirCompletion; bool completeListDirty; QDir::SortFlags sorting; QSplitter *splitter; QAbstractItemView *itemView; KDirModel *dirModel; KDirSortFilterProxyModel *proxyModel; KFileItemList pendingMimeTypes; // the enum KFile::FileView as an int int viewKind; int defaultView; KFile::Modes mode; QProgressBar *progressBar; KPreviewWidgetBase *preview; QTimer *previewTimer; KUrl previewUrl; int previewWidth; bool leftButtonPressed; bool dirHighlighting; bool onlyDoubleClickSelectsFiles; QString lastURL; // used for highlighting a directory on cdUp QTimer *progressDelayTimer; int dropOptions; KActionMenu *actionMenu; KActionCollection *actionCollection; KConfigGroup *configGroup; }; KDirOperator::KDirOperatorPrivate::KDirOperatorPrivate() : dirLister(0), splitter(0), itemView(0), dirModel(0), proxyModel(0), progressBar(0), preview(0), previewTimer(0), previewUrl(), previewWidth(0), leftButtonPressed(false), dirHighlighting(false), onlyDoubleClickSelectsFiles(false), progressDelayTimer(0), dropOptions(0), actionMenu(0), actionCollection(0), configGroup(0) { } KDirOperator::KDirOperatorPrivate::~KDirOperatorPrivate() { delete itemView; // TODO: // if (configGroup) { // itemView->writeConfig(configGroup); // } qDeleteAll(backStack); qDeleteAll(forwardStack); delete preview; delete dirLister; delete configGroup; delete progressDelayTimer; } KDirOperator::KDirOperator(const KUrl& _url, QWidget *parent) : QWidget(parent), d(new KDirOperatorPrivate) { d->splitter = new QSplitter(this); d->splitter->setChildrenCollapsible(false); connect(d->splitter, SIGNAL(splitterMoved(int, int)), this, SLOT(slotSplitterMoved(int, int))); d->preview = 0; d->previewTimer = new QTimer(this); d->previewTimer->setSingleShot(true); connect(d->previewTimer, SIGNAL(timeout()), this, SLOT(showPreview())); d->mode = KFile::File; d->viewKind = KFile::Simple; d->sorting = QDir::Name | QDir::DirsFirst; if (_url.isEmpty()) { // no dir specified -> current dir QString strPath = QDir::currentPath(); strPath.append(QChar('/')); d->currUrl = KUrl(); d->currUrl.setProtocol(QLatin1String("file")); d->currUrl.setPath(strPath); } else { d->currUrl = _url; if (d->currUrl.protocol().isEmpty()) d->currUrl.setProtocol(QLatin1String("file")); d->currUrl.addPath("/"); // make sure we have a trailing slash! } setDirLister(new KDirLister()); connect(&d->completion, SIGNAL(match(const QString&)), SLOT(slotCompletionMatch(const QString&))); d->progressBar = new QProgressBar(this); d->progressBar->setObjectName("d->progressBar"); d->progressBar->adjustSize(); d->progressBar->move(2, height() - d->progressBar->height() - 2); d->progressDelayTimer = new QTimer(this); d->progressDelayTimer->setObjectName(QLatin1String("d->progressBar delay timer")); connect(d->progressDelayTimer, SIGNAL(timeout()), SLOT(slotShowProgress())); d->completeListDirty = false; // action stuff setupActions(); setupMenu(); setFocusPolicy(Qt::WheelFocus); } KDirOperator::~KDirOperator() { resetCursor(); delete d; } void KDirOperator::setSorting(QDir::SortFlags spec) { d->sorting = spec; triggerSorting(); updateSortActions(); } QDir::SortFlags KDirOperator::sorting() const { return d->sorting; } bool KDirOperator::isRoot() const { return url().path() == QString(QLatin1Char('/')); } KDirLister *KDirOperator::dirLister() const { return d->dirLister; } void KDirOperator::resetCursor() { QApplication::restoreOverrideCursor(); d->progressBar->hide(); } void KDirOperator::sortByName() { d->actionCollection->action("by name")->setChecked(true); } void KDirOperator::sortBySize() { d->actionCollection->action("by size")->setChecked(true); } void KDirOperator::sortByDate() { d->actionCollection->action("by date")->setChecked(true); } void KDirOperator::sortByType() { d->actionCollection->action("by type")->setChecked(true); } void KDirOperator::sortReversed() { QAction* action = d->actionCollection->action("descending"); action->setChecked(!action->isChecked()); } void KDirOperator::toggleDirsFirst() { // TODO: not offered yet } void KDirOperator::toggleIgnoreCase() { if (d->proxyModel != 0) { Qt::CaseSensitivity cs = d->proxyModel->sortCaseSensitivity(); cs = (cs == Qt::CaseSensitive) ? Qt::CaseInsensitive : Qt::CaseSensitive; d->proxyModel->setSortCaseSensitivity(cs); } } void KDirOperator::updateSelectionDependentActions() { const bool hasSelection = (d->itemView != 0) && d->itemView->selectionModel()->hasSelection(); d->actionCollection->action("trash")->setEnabled(hasSelection); d->actionCollection->action("delete")->setEnabled(hasSelection); d->actionCollection->action("properties")->setEnabled(hasSelection); } void KDirOperator::setPreviewWidget(KPreviewWidgetBase *w) { const bool showPreview = (w != 0); if (showPreview) { d->viewKind = (d->viewKind | KFile::PreviewContents); } else { d->viewKind = (d->viewKind & ~KFile::PreviewContents); } delete d->preview; d->preview = w; KToggleAction *previewAction = static_cast(d->actionCollection->action("preview")); previewAction->setEnabled(showPreview); previewAction->setChecked(showPreview); setView(static_cast(d->viewKind)); } QList KDirOperator::selectedItems() const { QList itemList; if (d->itemView == 0) { return itemList; } const QItemSelection selection = d->proxyModel->mapSelectionToSource(d->itemView->selectionModel()->selection()); const QModelIndexList indexList = selection.indexes(); foreach(QModelIndex index, indexList) { KFileItem item = d->dirModel->itemForIndex(index); if (!item.isNull()) { itemList.append(item); } } return itemList; } -bool KDirOperator::isSelected(const KFileItem *item) const +bool KDirOperator::isSelected(const KFileItem &item) const { - if ((item == 0) || (d->itemView == 0)) { + if ((item.isNull()) || (d->itemView == 0)) { return false; } - const QModelIndex dirIndex = d->dirModel->indexForItem(*item); + const QModelIndex dirIndex = d->dirModel->indexForItem(item); const QModelIndex proxyIndex = d->proxyModel->mapFromSource(dirIndex); return d->itemView->selectionModel()->isSelected(proxyIndex); } int KDirOperator::numDirs() const { return (d->dirLister == 0) ? 0 : d->dirLister->directories().count(); } int KDirOperator::numFiles() const { return (d->dirLister == 0) ? 0 : d->dirLister->items().count() - numDirs(); } KCompletion * KDirOperator::completionObject() const { return const_cast(&d->completion); } KCompletion *KDirOperator::dirCompletionObject() const { return const_cast(&d->dirCompletion); } KActionCollection * KDirOperator::actionCollection() const { return d->actionCollection; } void KDirOperator::slotDetailedView() { KFile::FileView view = static_cast((d->viewKind & ~KFile::Simple) | KFile::Detail); setView(view); } void KDirOperator::slotSimpleView() { KFile::FileView view = static_cast((d->viewKind & ~KFile::Detail) | KFile::Simple); setView(view); } void KDirOperator::slotToggleHidden(bool show) { d->dirLister->setShowingDotFiles(show); updateDir(); assureVisibleSelection(); } void KDirOperator::togglePreview(bool on) { if (on) { d->viewKind = d->viewKind | KFile::PreviewContents; if (d->preview == 0) { d->preview = new KFileMetaPreview(this); d->actionCollection->action("preview")->setChecked(true); d->splitter->addWidget(d->preview); } d->preview->show(); QMetaObject::invokeMethod(this, "assureVisibleSelection", Qt::QueuedConnection); if (d->itemView != 0) { const QModelIndex index = d->itemView->selectionModel()->currentIndex(); if (index.isValid()) { triggerPreview(index); } } } else if (d->preview != 0) { d->viewKind = d->viewKind & ~KFile::PreviewContents; d->preview->hide(); d->previewTimer->stop(); } } void KDirOperator::slotSortByName() { d->sorting = QDir::Name; d->actionCollection->action("by name")->setChecked(true); triggerSorting(); } void KDirOperator::slotSortBySize() { d->sorting = (d->sorting & ~QDir::SortByMask) | QDir::Size; d->actionCollection->action("by size")->setChecked(true); triggerSorting(); } void KDirOperator::slotSortByDate() { d->sorting = (d->sorting & ~QDir::SortByMask) | QDir::Time; d->actionCollection->action("by date")->setChecked(true); triggerSorting(); } void KDirOperator::slotSortByType() { d->sorting = (d->sorting & ~QDir::SortByMask) | QDir::Type; d->actionCollection->action("by type")->setChecked(true); triggerSorting(); } void KDirOperator::slotSortReversed() { if (d->sorting & QDir::Reversed) { d->sorting = d->sorting & ~QDir::Reversed; d->actionCollection->action("descending")->setChecked(false); } else { d->sorting = d->sorting | QDir::Reversed; d->actionCollection->action("descending")->setChecked(true); } triggerSorting(); } void KDirOperator::slotToggleDirsFirst() { // TODO: port to Qt4's QAbstractItemView /*if ( !d->fileView ) return; QDir::SortFlags sorting = d->fileView->sorting(); if ( !KFile::isSortDirsFirst( sorting ) ) d->fileView->setSorting( sorting | QDir::DirsFirst ); else d->fileView->setSorting( sorting & ~QDir::DirsFirst ); d->sorting = d->fileView->sorting();*/ } void KDirOperator::slotToggleIgnoreCase() { // TODO: port to Qt4's QAbstractItemView /*if ( !d->fileView ) return; QDir::SortFlags sorting = d->fileView->sorting(); if ( !KFile::isSortCaseInsensitive( sorting ) ) d->fileView->setSorting( sorting | QDir::IgnoreCase ); else d->fileView->setSorting( sorting & ~QDir::IgnoreCase ); d->sorting = d->fileView->sorting();*/ } void KDirOperator::mkdir() { bool ok; QString where = url().pathOrUrl(); QString name = i18n("New Folder"); #ifdef Q_WS_WIN if (url().isLocalFile() && QFileInfo(url().toLocalFile() + name).exists()) #else if (url().isLocalFile() && QFileInfo(url().path(KUrl::AddTrailingSlash) + name).exists()) #endif name = KIO::RenameDialog::suggestName(url(), name); QString folder = KInputDialog::getText(i18n("New Folder"), i18n("Create new folder in:\n%1" , where), name, &ok, this); if (ok) mkdir(KIO::encodeFileName(folder), true); } bool KDirOperator::mkdir(const QString& directory, bool enterDirectory) { // Creates "directory", relative to the current directory (d->currUrl). // The given path may contain any number directories, existant or not. // They will all be created, if possible. bool writeOk = false; bool exists = false; KUrl url(d->currUrl); QStringList dirs = directory.split(QDir::separator(), QString::SkipEmptyParts); QStringList::ConstIterator it = dirs.begin(); for (; it != dirs.end(); ++it) { url.addPath(*it); exists = KIO::NetAccess::exists(url, KIO::NetAccess::DestinationSide, 0); writeOk = !exists && KIO::NetAccess::mkdir(url, topLevelWidget()); } if (exists) { // url was already existant KMessageBox::sorry(d->itemView, i18n("A file or folder named %1 already exists.", url.pathOrUrl())); enterDirectory = false; } else if (!writeOk) { KMessageBox::sorry(d->itemView, i18n("You do not have permission to " "create that folder.")); } else if (enterDirectory) { setUrl(url, true); } return writeOk; } KIO::DeleteJob * KDirOperator::del(const QList& items, QWidget *parent, bool ask, bool showProgress) { if (items.isEmpty()) { KMessageBox::information(parent, i18n("You did not select a file to delete."), i18n("Nothing to Delete")); return 0L; } if (parent == 0) { parent = this; } KUrl::List urls; QStringList files; foreach (const KFileItem item, items) { const KUrl url = item.url(); urls.append(url); files.append(url.pathOrUrl()); } bool doIt = !ask; if (ask) { int ret; if (items.count() == 1) { ret = KMessageBox::warningContinueCancel(parent, i18n("Do you really want to delete\n '%1'?" , files.first()), i18n("Delete File"), KStandardGuiItem::del(), KStandardGuiItem::cancel(), "AskForDelete"); } else ret = KMessageBox::warningContinueCancelList(parent, i18np("Do you really want to delete this item?", "Do you really want to delete these %1 items?", items.count()), files, i18n("Delete Files"), KStandardGuiItem::del(), KStandardGuiItem::cancel(), "AskForDelete"); doIt = (ret == KMessageBox::Continue); } if (doIt) { KIO::DeleteJob *job = KIO::del(urls, false, showProgress); job->ui()->setWindow(topLevelWidget()); job->ui()->setAutoErrorHandlingEnabled(true); return job; } return 0L; } void KDirOperator::deleteSelected() { const QList list = selectedItems(); if (!list.isEmpty()) { del(list, this); } } KIO::CopyJob * KDirOperator::trash(const QList& items, QWidget *parent, bool ask, bool showProgress) { if (items.isEmpty()) { KMessageBox::information(parent, i18n("You did not select a file to trash."), i18n("Nothing to Trash")); return 0L; } KUrl::List urls; QStringList files; foreach (const KFileItem item, items) { const KUrl url = item.url(); urls.append(url); files.append(url.pathOrUrl()); } bool doIt = !ask; if (ask) { int ret; if (items.count() == 1) { ret = KMessageBox::warningContinueCancel(parent, i18n("Do you really want to trash\n '%1'?" , files.first()), i18n("Trash File"), KGuiItem(i18nc("to trash", "&Trash"), "edit-trash"), KStandardGuiItem::cancel(), "AskForTrash"); } else ret = KMessageBox::warningContinueCancelList(parent, i18np("translators: not called for n == 1", "Do you really want to trash these %1 items?", items.count()), files, i18n("Trash Files"), KGuiItem(i18nc("to trash", "&Trash"), "edit-trash"), KStandardGuiItem::cancel(), "AskForTrash"); doIt = (ret == KMessageBox::Continue); } if (doIt) { KIO::CopyJob *job = KIO::trash(urls, showProgress); job->ui()->setWindow(topLevelWidget()); job->ui()->setAutoErrorHandlingEnabled(true); return job; } return 0L; } void KDirOperator::trashSelected() { if (d->itemView == 0) { return; } if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { deleteSelected(); return; } const QList list = selectedItems(); if (!list.isEmpty()) { trash(list, this); } } void KDirOperator::close() { resetCursor(); d->pendingMimeTypes.clear(); d->completion.clear(); d->dirCompletion.clear(); d->completeListDirty = true; d->dirLister->stop(); } void KDirOperator::checkPath(const QString &, bool /*takeFiles*/) // SLOT { #if 0 // copy the argument in a temporary string QString text = _txt; // it's unlikely to happen, that at the beginning are spaces, but // for the end, it happens quite often, I guess. text = text.trimmed(); // if the argument is no URL (the check is quite fragil) and it's // no absolute path, we add the current directory to get a correct url if (text.find(':') < 0 && text[0] != '/') text.insert(0, d->currUrl); // in case we have a selection defined and someone patched the file- // name, we check, if the end of the new name is changed. if (!selection.isNull()) { int position = text.lastIndexOf('/'); ASSERT(position >= 0); // we already inserted the current d->dirLister in case QString filename = text.mid(position + 1, text.length()); if (filename != selection) selection = QString(); } KUrl u(text); // I have to take care of entered URLs bool filenameEntered = false; if (u.isLocalFile()) { // the empty path is kind of a hack KFileItem i("", u.toLocalFile()); if (i.isDir()) setUrl(text, true); else { if (takeFiles) if (acceptOnlyExisting && !i.isFile()) warning("you entered an invalid URL"); else filenameEntered = true; } } else setUrl(text, true); if (filenameEntered) { filename_ = u.url(); emit fileSelected(filename_); QApplication::restoreOverrideCursor(); accept(); } #endif kDebug(kfile_area) << "TODO KDirOperator::checkPath()"; } void KDirOperator::setUrl(const KUrl& _newurl, bool clearforward) { KUrl newurl; if (!_newurl.isValid()) newurl.setPath(QDir::homePath()); else newurl = _newurl; #ifdef Q_WS_WIN QString pathstr = newurl.toLocalFile(); #else QString pathstr = newurl.path(KUrl::AddTrailingSlash); #endif newurl.setPath(pathstr); // already set if (newurl.equals(d->currUrl, KUrl::CompareWithoutTrailingSlash)) return; if (!isReadable(newurl)) { // maybe newurl is a file? check its parent directory newurl.cd(QLatin1String("..")); if (!isReadable(newurl)) { resetCursor(); KMessageBox::error(d->itemView, i18n("The specified folder does not exist " "or was not readable.")); return; } } if (clearforward) { // autodelete should remove this one d->backStack.push(new KUrl(d->currUrl)); qDeleteAll(d->forwardStack); d->forwardStack.clear(); } d->lastURL = d->currUrl.url(KUrl::RemoveTrailingSlash); d->currUrl = newurl; pathChanged(); emit urlEntered(newurl); // enable/disable actions QAction* forwardAction = d->actionCollection->action("forward"); forwardAction->setEnabled(!d->forwardStack.isEmpty()); QAction* backAction = d->actionCollection->action("back"); backAction->setEnabled(!d->backStack.isEmpty()); QAction* upAction = d->actionCollection->action("up"); upAction->setEnabled(!isRoot()); openUrl(newurl); } void KDirOperator::updateDir() { d->dirLister->emitChanges(); } void KDirOperator::rereadDir() { pathChanged(); openUrl(d->currUrl, false, true); } bool KDirOperator::openUrl(const KUrl& url, bool keep, bool reload) { bool result = d->dirLister->openUrl(url, keep, reload); if (!result) // in that case, neither completed() nor canceled() will be emitted by KDL slotCanceled(); return result; } int KDirOperator::sortColumn() const { int column = KDirModel::Name; if (KFile::isSortByDate(d->sorting)) { column = KDirModel::ModifiedTime; } else if (KFile::isSortBySize(d->sorting)) { column = KDirModel::Size; } else if (KFile::isSortByType(d->sorting)) { column = KDirModel::Type; } else { Q_ASSERT(KFile::isSortByName(d->sorting)); } return column; } Qt::SortOrder KDirOperator::sortOrder() const { return (d->sorting & QDir::Reversed) ? Qt::DescendingOrder : Qt::AscendingOrder; } void KDirOperator::triggerSorting() { d->proxyModel->sort(sortColumn(), sortOrder()); // TODO: The headers from QTreeView don't take care about a sorting // change of the proxy model hence they must be updated the manually. // This is done here by a qobject_cast, but it would be nicer to: // - provide a signal 'sortingChanged()' // - connect DirOperatorDetailView() with this signal and update the // header internally QTreeView* treeView = qobject_cast(d->itemView); if (treeView != 0) { QHeaderView* headerView = treeView->header(); headerView->setSortIndicator(sortColumn(), sortOrder()); } assureVisibleSelection(); } // Protected void KDirOperator::pathChanged() { if (d->itemView == 0) return; d->pendingMimeTypes.clear(); //d->fileView->clear(); TODO d->completion.clear(); d->dirCompletion.clear(); // it may be, that we weren't ready at this time QApplication::restoreOverrideCursor(); // when KIO::Job emits finished, the slot will restore the cursor QApplication::setOverrideCursor(Qt::WaitCursor); if (!isReadable(d->currUrl)) { KMessageBox::error(d->itemView, i18n("The specified folder does not exist " "or was not readable.")); if (d->backStack.isEmpty()) home(); else back(); } } void KDirOperator::slotRedirected(const KUrl& newURL) { d->currUrl = newURL; d->pendingMimeTypes.clear(); d->completion.clear(); d->dirCompletion.clear(); d->completeListDirty = true; emit urlEntered(newURL); } // Code pinched from kfm then hacked void KDirOperator::back() { if (d->backStack.isEmpty()) return; d->forwardStack.push(new KUrl(d->currUrl)); KUrl *s = d->backStack.pop(); setUrl(*s, false); delete s; } // Code pinched from kfm then hacked void KDirOperator::forward() { if (d->forwardStack.isEmpty()) return; d->backStack.push(new KUrl(d->currUrl)); KUrl *s = d->forwardStack.pop(); setUrl(*s, false); delete s; } KUrl KDirOperator::url() const { return d->currUrl; } void KDirOperator::cdUp() { KUrl tmp(d->currUrl); tmp.cd(QLatin1String("..")); setUrl(tmp, true); } void KDirOperator::home() { KUrl u; u.setPath(QDir::homePath()); setUrl(u, true); } void KDirOperator::clearFilter() { d->dirLister->setNameFilter(QString()); d->dirLister->clearMimeFilter(); checkPreviewSupport(); } void KDirOperator::setNameFilter(const QString& filter) { d->dirLister->setNameFilter(filter); checkPreviewSupport(); } QString KDirOperator::nameFilter() const { return d->dirLister->nameFilter(); } void KDirOperator::setMimeFilter(const QStringList& mimetypes) { d->dirLister->setMimeFilter(mimetypes); checkPreviewSupport(); } QStringList KDirOperator::mimeFilter() const { return d->dirLister->mimeFilters(); } bool KDirOperator::checkPreviewSupport() { KToggleAction *previewAction = static_cast(d->actionCollection->action("preview")); bool hasPreviewSupport = false; KConfigGroup cg(KGlobal::config(), ConfigGroup); if (cg.readEntry("Show Default Preview", true)) hasPreviewSupport = checkPreviewInternal(); previewAction->setEnabled(hasPreviewSupport); return hasPreviewSupport; } void KDirOperator::activatedMenu(const KFileItem &item, const QPoint &pos) { Q_UNUSED(item); setupMenu(); updateSelectionDependentActions(); d->actionMenu->menu()->exec(pos); } bool KDirOperator::checkPreviewInternal() const { QStringList supported = KIO::PreviewJob::supportedMimeTypes(); // no preview support for directories? if (dirOnlyMode() && supported.indexOf("inode/directory") == -1) return false; QStringList mimeTypes = d->dirLister->mimeFilters(); QStringList nameFilter = d->dirLister->nameFilter().split(" ", QString::SkipEmptyParts); if (mimeTypes.isEmpty() && nameFilter.isEmpty() && !supported.isEmpty()) return true; else { QRegExp r; r.setPatternSyntax(QRegExp::Wildcard); // the "mimetype" can be "image/*" if (!mimeTypes.isEmpty()) { QStringList::Iterator it = supported.begin(); for (; it != supported.end(); ++it) { r.setPattern(*it); QStringList result = mimeTypes.filter(r); if (!result.isEmpty()) { // matches! -> we want previews return true; } } } if (!nameFilter.isEmpty()) { // find the mimetypes of all the filter-patterns QStringList::const_iterator it1 = nameFilter.begin(); for (; it1 != nameFilter.end(); ++it1) { if ((*it1) == "*") { return true; } KMimeType::Ptr mt = KMimeType::findByPath(*it1, 0, true /*fast mode, no file contents exist*/); if (!mt) continue; QString mime = mt->name(); // the "mimetypes" we get from the PreviewJob can be "image/*" // so we need to check in wildcard mode QStringList::Iterator it2 = supported.begin(); for (; it2 != supported.end(); ++it2) { r.setPattern(*it2); if (r.indexIn(mime) != -1) { return true; } } } } } return false; } QAbstractItemView* KDirOperator::createView(QWidget* parent, KFile::FileView viewKind) { QAbstractItemView *itemView = 0; if (KFile::isDetailView(viewKind)) { DirOperatorDetailView *detailView = new DirOperatorDetailView(parent); connect(detailView->header(), SIGNAL(sortIndicatorChanged (int, Qt::SortOrder)), this, SLOT(synchronizeSortingState(int, Qt::SortOrder))); itemView = detailView; } else { itemView = new DirOperatorIconView(parent); } return itemView; } void KDirOperator::setAcceptDrops(bool b) { // TODO: //if (d->fileView) // d->fileView->widget()->setAcceptDrops(b); QWidget::setAcceptDrops(b); } void KDirOperator::setDropOptions(int options) { d->dropOptions = options; // TODO: //if (d->fileView) // d->fileView->setDropOptions(options); } void KDirOperator::setView(KFile::FileView viewKind) { bool preview = (KFile::isPreviewInfo(viewKind) || KFile::isPreviewContents(viewKind)); if (viewKind == KFile::Default) { if (KFile::isDetailView((KFile::FileView)d->defaultView)) { viewKind = KFile::Detail; } else { viewKind = KFile::Simple; } const KFile::FileView defaultViewKind = static_cast(d->defaultView); preview = (KFile::isPreviewInfo(defaultViewKind) || KFile::isPreviewContents(defaultViewKind)) && d->actionCollection->action("preview")->isEnabled(); } d->viewKind = static_cast(viewKind); viewKind = static_cast(d->viewKind); QAbstractItemView *newView = createView(this, viewKind); setView(newView); togglePreview(preview); } QAbstractItemView * KDirOperator::view() const { return d->itemView; } KFile::Modes KDirOperator::mode() const { return d->mode; } void KDirOperator::setMode(KFile::Modes mode) { if (d->mode == mode) return; d->mode = mode; d->dirLister->setDirOnlyMode(dirOnlyMode()); // reset the view with the different mode setView(static_cast(d->viewKind)); } void KDirOperator::setView(QAbstractItemView *view) { if (view == d->itemView) { return; } // TODO: do a real timer and restart it after that d->pendingMimeTypes.clear(); const bool listDir = (d->itemView == 0); if (d->mode & KFile::Files) { view->setSelectionMode(QAbstractItemView::ExtendedSelection); } else { view->setSelectionMode(QAbstractItemView::SingleSelection); } QItemSelectionModel *selectionModel = 0; if ((d->itemView != 0) && d->itemView->selectionModel()->hasSelection()) { // remember the selection of the current item view and apply this selection // to the new view later const QItemSelection selection = d->itemView->selectionModel()->selection(); selectionModel = new QItemSelectionModel(d->proxyModel, this); selectionModel->select(selection, QItemSelectionModel::Select); } delete d->itemView; d->itemView = view; d->itemView->setModel(d->proxyModel); KFileItemDelegate *delegate = new KFileItemDelegate(d->itemView); d->itemView->setItemDelegate(delegate); d->itemView->viewport()->setAttribute(Qt::WA_Hover); d->itemView->setContextMenuPolicy(Qt::CustomContextMenu); d->itemView->setMouseTracking(true); //d->itemView->setDropOptions(d->dropOptions); connect(d->itemView, SIGNAL(pressed(const QModelIndex&)), this, SLOT(slotPressed(const QModelIndex&))); connect(d->itemView, SIGNAL(clicked(const QModelIndex&)), this, SLOT(slotClicked(const QModelIndex&))); connect(d->itemView, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(slotDoubleClicked(const QModelIndex&))); connect(d->itemView, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(openContextMenu(const QPoint&))); connect(d->itemView, SIGNAL(entered(const QModelIndex&)), this, SLOT(triggerPreview(const QModelIndex&))); connect(d->itemView, SIGNAL(viewportEntered()), this, SLOT(cancelPreview())); // assure that the sorting state d->sorting matches with the current action const bool descending = d->actionCollection->action("descending")->isChecked(); if (!descending && d->sorting & QDir::Reversed) { d->sorting = d->sorting & ~QDir::Reversed; } else if (descending && !(d->sorting & QDir::Reversed)) { d->sorting = d->sorting | QDir::Reversed; } triggerSorting(); updateViewActions(); d->splitter->insertWidget(0, d->itemView); d->splitter->resize(size()); d->itemView->show(); if (listDir) { QApplication::setOverrideCursor(Qt::WaitCursor); openUrl(d->currUrl); } if (selectionModel != 0) { d->itemView->setSelectionModel(selectionModel); QMetaObject::invokeMethod(this, "assureVisibleSelection", Qt::QueuedConnection); } emit viewChanged(view); } void KDirOperator::setDirLister(KDirLister *lister) { if (lister == d->dirLister) // sanity check return; delete d->dirModel; d->dirModel = 0; delete d->proxyModel; d->proxyModel = 0; delete d->dirLister; d->dirLister = lister; d->dirModel = new KDirModel(); d->dirModel->setDirLister(d->dirLister); d->dirModel->setDropsAllowed(KDirModel::DropOnDirectory); d->proxyModel = new KDirSortFilterProxyModel(this); d->proxyModel->setSourceModel(d->dirModel); d->dirLister->setAutoUpdate(true); d->dirLister->setDelayedMimeTypes(true); QWidget* mainWidget = topLevelWidget(); d->dirLister->setMainWindow(mainWidget); kDebug(kfile_area) << "mainWidget=" << mainWidget; connect(d->dirLister, SIGNAL(percent(int)), SLOT(slotProgress(int))); connect(d->dirLister, SIGNAL(started(const KUrl&)), SLOT(slotStarted())); connect(d->dirLister, SIGNAL(completed()), SLOT(slotIOFinished())); connect(d->dirLister, SIGNAL(canceled()), SLOT(slotCanceled())); connect(d->dirLister, SIGNAL(redirection(const KUrl&)), SLOT(slotRedirected(const KUrl&))); } -void KDirOperator::selectDir(const KFileItem *item) +void KDirOperator::selectDir(const KFileItem &item) { - setUrl(item->url(), true); + setUrl(item.url(), true); } -void KDirOperator::selectFile(const KFileItem *item) +void KDirOperator::selectFile(const KFileItem &item) { QApplication::restoreOverrideCursor(); emit fileSelected(item); } -void KDirOperator::highlightFile(const KFileItem *item) +void KDirOperator::highlightFile(const KFileItem &item) { - Q_ASSERT(item != 0); + Q_ASSERT(!item.isNull()); if (d->preview != 0) { - d->preview->showPreview(item->url()); + d->preview->showPreview(item.url()); } emit fileHighlighted(item); } void KDirOperator::setCurrentItem(const QString& filename) { if (d->itemView == 0) { return; } - const KFileItem *item = 0; + KFileItem item; if ( !filename.isNull() ) { item = d->dirLister->findByName(filename); } QItemSelectionModel *selModel = d->itemView->selectionModel(); selModel->clear(); - if (item != 0) { - const QModelIndex dirIndex = d->dirModel->indexForItem(*item); + if (!item.isNull()) { + const QModelIndex dirIndex = d->dirModel->indexForItem(item); const QModelIndex proxyIndex = d->proxyModel->mapFromSource(dirIndex); selModel->setCurrentIndex(proxyIndex, QItemSelectionModel::Select); selModel->select(proxyIndex, QItemSelectionModel::Select); assureVisibleSelection(); } } QString KDirOperator::makeCompletion(const QString& string) { if (string.isEmpty()) { d->itemView->selectionModel()->clear(); return QString(); } prepareCompletionObjects(); return d->completion.makeCompletion(string); } QString KDirOperator::makeDirCompletion(const QString& string) { if (string.isEmpty()) { d->itemView->selectionModel()->clear(); return QString(); } prepareCompletionObjects(); return d->dirCompletion.makeCompletion(string); } void KDirOperator::prepareCompletionObjects() { if (d->itemView == 0) { return; } if (d->completeListDirty) { // create the list of all possible completions const KFileItemList itemList = d->dirLister->items(); - foreach (const KFileItem *item, itemList) { - d->completion.addItem(item->name()); - if (item->isDir()) { - d->dirCompletion.addItem(item->name()); + foreach (const KFileItem item, itemList) { + d->completion.addItem(item.name()); + if (item.isDir()) { + d->dirCompletion.addItem(item.name()); } } d->completeListDirty = false; } } void KDirOperator::slotCompletionMatch(const QString& match) { setCurrentItem(match); emit completion(match); } void KDirOperator::setupActions() { d->actionCollection = new KActionCollection(this); d->actionCollection->setObjectName("KDirOperator::actionCollection"); d->actionCollection->setAssociatedWidget(topLevelWidget()); d->actionMenu = new KActionMenu(i18n("Menu"), this); d->actionCollection->addAction("popupMenu", d->actionMenu); QAction* upAction = d->actionCollection->addAction(KStandardAction::Up, "up", this, SLOT(cdUp())); upAction->setText(i18n("Parent Folder")); d->actionCollection->addAction(KStandardAction::Back, "back", this, SLOT(back())); d->actionCollection->addAction(KStandardAction::Forward, "forward", this, SLOT(forward())); QAction* homeAction = d->actionCollection->addAction(KStandardAction::Home, "home", this, SLOT(home())); homeAction->setText(i18n("Home Folder")); QAction* reloadAction = d->actionCollection->addAction(KStandardAction::Redisplay, "reload", this, SLOT(rereadDir())); reloadAction->setText(i18n("Reload")); reloadAction->setShortcuts(KStandardShortcut::shortcut(KStandardShortcut::Reload)); QAction* mkdirAction = new KAction(i18n("New Folder..."), this); d->actionCollection->addAction("mkdir", mkdirAction); mkdirAction->setIcon(KIcon(QLatin1String("folder-new"))); connect(mkdirAction, SIGNAL(triggered(bool)), this, SLOT(mkdir())); KAction* trash = new KAction(i18n("Move to Trash"), this); d->actionCollection->addAction("trash", trash); trash->setIcon(KIcon("edit-trash")); trash->setShortcuts(KShortcut(Qt::Key_Delete)); connect(trash, SIGNAL(triggered(bool)), SLOT(trashSelected())); KAction* action = new KAction(i18n("Delete"), this); d->actionCollection->addAction("delete", action); action->setIcon(KIcon("edit-delete")); action->setShortcuts(KShortcut(Qt::SHIFT + Qt::Key_Delete)); connect(action, SIGNAL(triggered(bool)), this, SLOT(deleteSelected())); // the sort menu actions KActionMenu *sortMenu = new KActionMenu(i18n("Sorting"), this); d->actionCollection->addAction("sorting menu", sortMenu); KToggleAction *byNameAction = new KToggleAction(i18n("By Name"), this); d->actionCollection->addAction("by name", byNameAction); connect(byNameAction, SIGNAL(triggered(bool)), this, SLOT(slotSortByName())); KToggleAction *bySizeAction = new KToggleAction(i18n("By Size"), this); d->actionCollection->addAction("by size", bySizeAction); connect(bySizeAction, SIGNAL(triggered(bool)), this, SLOT(slotSortBySize())); KToggleAction *byDateAction = new KToggleAction(i18n("By Date"), this); d->actionCollection->addAction("by date", byDateAction); connect(byDateAction, SIGNAL(triggered(bool)), this, SLOT(slotSortByDate())); KToggleAction *byTypeAction = new KToggleAction(i18n("By Type"), this); d->actionCollection->addAction("by type", byTypeAction); connect(byTypeAction, SIGNAL(triggered(bool)), this, SLOT(slotSortByType())); KToggleAction *descendingAction = new KToggleAction(i18n("Descending"), this); d->actionCollection->addAction("descending", descendingAction); connect(descendingAction, SIGNAL(triggered(bool)), this, SLOT(slotSortReversed())); QActionGroup* sortGroup = new QActionGroup(this); byNameAction->setActionGroup(sortGroup); bySizeAction->setActionGroup(sortGroup); byDateAction->setActionGroup(sortGroup); byTypeAction->setActionGroup(sortGroup); KToggleAction *shortAction = new KToggleAction(i18n("Short View"), this); d->actionCollection->addAction("short view", shortAction); shortAction->setIcon(KIcon(QLatin1String("fileview-multicolumn"))); connect(shortAction, SIGNAL(activated()), SLOT(slotSimpleView())); KToggleAction *detailedAction = new KToggleAction(i18n("Detailed View"), this); d->actionCollection->addAction("detailed view", detailedAction); detailedAction->setIcon(KIcon(QLatin1String("fileview-detailed"))); connect(detailedAction, SIGNAL(activated()), SLOT(slotDetailedView())); QActionGroup* viewGroup = new QActionGroup(this); shortAction->setActionGroup(viewGroup); detailedAction->setActionGroup(viewGroup); KToggleAction *showHiddenAction = new KToggleAction(i18n("Show Hidden Files"), this); d->actionCollection->addAction("show hidden", showHiddenAction); connect(showHiddenAction, SIGNAL(toggled(bool)), SLOT(slotToggleHidden(bool))); KToggleAction *previewAction = new KToggleAction(i18n("Show Preview"), this); d->actionCollection->addAction("preview", previewAction); previewAction->setIcon(KIcon("thumbnail-show")); connect(previewAction, SIGNAL(toggled(bool)), SLOT(togglePreview(bool))); action = new KAction(i18n("Properties"), this); d->actionCollection->addAction("properties", action); action->setShortcut(KShortcut(Qt::ALT + Qt::Key_Return)); connect(action, SIGNAL(triggered(bool)), this, SLOT(slotProperties())); // the view menu actions KActionMenu* viewMenu = new KActionMenu(i18n("&View"), this); d->actionCollection->addAction("view menu", viewMenu); viewMenu->addAction(shortAction); viewMenu->addAction(detailedAction); // TODO: QAbstractItemView does not offer an action collection. Provide // an interface to add a custom action collection. } void KDirOperator::setupMenu() { setupMenu(SortActions | ViewActions | FileActions); } void KDirOperator::setupMenu(int whichActions) { // first fill the submenus (sort and view) KActionMenu *sortMenu = static_cast(d->actionCollection->action("sorting menu")); sortMenu->menu()->clear(); sortMenu->addAction(d->actionCollection->action("by name")); sortMenu->addAction(d->actionCollection->action("by size")); sortMenu->addAction(d->actionCollection->action("by date")); sortMenu->addAction(d->actionCollection->action("by type")); sortMenu->addSeparator(); sortMenu->addAction(d->actionCollection->action("descending")); // now plug everything into the popupmenu d->actionMenu->menu()->clear(); if (whichActions & NavActions) { d->actionMenu->addAction(d->actionCollection->action("up")); d->actionMenu->addAction(d->actionCollection->action("back")); d->actionMenu->addAction(d->actionCollection->action("forward")); d->actionMenu->addAction(d->actionCollection->action("home")); d->actionMenu->addSeparator(); } if (whichActions & FileActions) { d->actionMenu->addAction(d->actionCollection->action("mkdir")); if (d->currUrl.isLocalFile() && !(QApplication::keyboardModifiers() & Qt::ShiftModifier)) { d->actionMenu->addAction(d->actionCollection->action("trash")); } KConfigGroup cg(KGlobal::config(), QLatin1String("KDE")); const bool del = !d->currUrl.isLocalFile() || (QApplication::keyboardModifiers() & Qt::ShiftModifier) || cg.readEntry("ShowDeleteCommand", false); if (del) { d->actionMenu->addAction(d->actionCollection->action("delete")); } d->actionMenu->addSeparator(); } if (whichActions & SortActions) { d->actionMenu->addAction(sortMenu); if (!(whichActions & ViewActions)) { d->actionMenu->addSeparator(); } } if (whichActions & ViewActions) { d->actionMenu->addAction(d->actionCollection->action("view menu")); d->actionMenu->addSeparator(); } if (whichActions & FileActions) { d->actionMenu->addAction(d->actionCollection->action("properties")); } } void KDirOperator::updateSortActions() { if (KFile::isSortByName(d->sorting)) { d->actionCollection->action("by name")->setChecked(true); } else if (KFile::isSortByDate(d->sorting)) { d->actionCollection->action("by date")->setChecked(true); } else if (KFile::isSortBySize(d->sorting)) { d->actionCollection->action("by size")->setChecked(true); } else if (KFile::isSortByType(d->sorting)) { d->actionCollection->action("by type")->setChecked(true); } d->actionCollection->action("descending")->setChecked(d->sorting & QDir::Reversed); } void KDirOperator::updateViewActions() { KFile::FileView fv = static_cast(d->viewKind); //QAction *separateDirs = d->actionCollection->action("separate dirs"); //separateDirs->setChecked(KFile::isSeparateDirs(fv) && // separateDirs->isEnabled()); d->actionCollection->action("short view")->setChecked(KFile::isSimpleView(fv)); d->actionCollection->action("detailed view")->setChecked(KFile::isDetailView(fv)); } void KDirOperator::readConfig(const KConfigGroup& configGroup) { d->defaultView = 0; QDir::SortFlags sorting = QDir::Name; QString viewStyle = configGroup.readEntry("View Style", "Simple"); if (viewStyle == QLatin1String("Detail")) { d->defaultView |= KFile::Detail; } else { d->defaultView |= KFile::Simple; } //if (configGroup.readEntry(QLatin1String("Separate Directories"), // DefaultMixDirsAndFiles)) { // d->defaultView |= KFile::SeparateDirs; //} if (configGroup.readEntry(QLatin1String("Show Preview"), false)) { d->defaultView |= KFile::PreviewContents; } d->previewWidth = configGroup.readEntry(QLatin1String("Preview Width"), 100); if (configGroup.readEntry(QLatin1String("Sort directories first"), DefaultDirsFirst)) { sorting |= QDir::DirsFirst; } QString name = QLatin1String("Name"); QString sortBy = configGroup.readEntry(QLatin1String("Sort by"), name); if (sortBy == name) { sorting |= QDir::Name; } else if (sortBy == QLatin1String("Size")) { sorting |= QDir::Size; } else if (sortBy == QLatin1String("Date")) { sorting |= QDir::Time; } else if (sortBy == QLatin1String("Type")) { sorting |= QDir::Type; } d->sorting = sorting; setSorting(d->sorting); if (configGroup.readEntry(QLatin1String("Show hidden files"), DefaultShowHidden)) { d->actionCollection->action("show hidden")->setChecked(true); d->dirLister->setShowingDotFiles(true); } const bool descending = configGroup.readEntry(QLatin1String("Sort reversed"), DefaultSortReversed); d->actionCollection->action("descending")->setChecked(descending); if (descending) { d->sorting = d->sorting | QDir::Reversed; } } void KDirOperator::writeConfig(KConfigGroup& configGroup) { QString sortBy = QLatin1String("Name"); if (KFile::isSortBySize(d->sorting)) { sortBy = QLatin1String("Size"); } else if (KFile::isSortByDate(d->sorting)) { sortBy = QLatin1String("Date"); } else if (KFile::isSortByType(d->sorting)) { sortBy = QLatin1String("Type"); } configGroup.writeEntry(QLatin1String("Sort by"), sortBy); configGroup.writeEntry(QLatin1String("Sort reversed"), d->actionCollection->action("descending")->isChecked()); // don't save the preview when an application specific preview is in use. bool appSpecificPreview = false; if (d->preview) { KFileMetaPreview *tmp = dynamic_cast(d->preview); appSpecificPreview = (tmp == 0); } if (!appSpecificPreview) { KToggleAction *previewAction = static_cast(d->actionCollection->action("preview")); if (previewAction->isEnabled()) { bool hasPreview = previewAction->isChecked(); configGroup.writeEntry(QLatin1String("Show Preview"), hasPreview); if (hasPreview) { // remember the width of the preview widget QList sizes = d->splitter->sizes(); Q_ASSERT(sizes.count() == 2); configGroup.writeEntry(QLatin1String("Preview Width"), sizes[1]); } } } configGroup.writeEntry(QLatin1String("Show hidden files"), d->actionCollection->action("show hidden")->isChecked()); KFile::FileView fv = static_cast(d->viewKind); QString style; if (KFile::isDetailView(fv)) style = QLatin1String("Detail"); else if (KFile::isSimpleView(fv)) style = QLatin1String("Simple"); configGroup.writeEntry(QLatin1String("View Style"), style); } void KDirOperator::resizeEvent(QResizeEvent *) { // resize the splitter and assure that the width of // the preview widget is restored QList sizes = d->splitter->sizes(); const bool hasPreview = (sizes.count() == 2); const bool restorePreviewWidth = hasPreview && (d->previewWidth != sizes[1]); d->splitter->resize(size()); sizes = d->splitter->sizes(); if (restorePreviewWidth) { const int availableWidth = sizes[0] + sizes[1]; sizes[0] = availableWidth - d->previewWidth; sizes[1] = d->previewWidth; d->splitter->setSizes(sizes); } if (hasPreview) { d->previewWidth = sizes[1]; } if (d->progressBar->parent() == this) { // might be reparented into a statusbar d->progressBar->move(2, height() - d->progressBar->height() - 2); } } void KDirOperator::setOnlyDoubleClickSelectsFiles(bool enable) { d->onlyDoubleClickSelectsFiles = enable; // TODO: port to Qt4's QAbstractItemModel //if (d->itemView != 0) { // d->itemView->setOnlyDoubleClickSelectsFiles(enable); //} } bool KDirOperator::onlyDoubleClickSelectsFiles() const { return d->onlyDoubleClickSelectsFiles; } void KDirOperator::slotStarted() { d->progressBar->setValue(0); // delay showing the progressbar for one second d->progressDelayTimer->setSingleShot(true); d->progressDelayTimer->start(1000); } void KDirOperator::slotShowProgress() { d->progressBar->raise(); d->progressBar->show(); QApplication::flush(); } void KDirOperator::slotProgress(int percent) { d->progressBar->setValue(percent); // we have to redraw this as fast as possible if (d->progressBar->isVisible()) QApplication::flush(); } void KDirOperator::slotIOFinished() { d->progressDelayTimer->stop(); slotProgress(100); d->progressBar->hide(); emit finishedLoading(); resetCursor(); } void KDirOperator::slotCanceled() { emit finishedLoading(); resetCursor(); } QProgressBar * KDirOperator::progressBar() const { return d->progressBar; } void KDirOperator::clearHistory() { qDeleteAll(d->backStack); d->backStack.clear(); d->actionCollection->action("back")->setEnabled(false); qDeleteAll(d->forwardStack); d->forwardStack.clear(); d->actionCollection->action("forward")->setEnabled(false); } void KDirOperator::slotViewActionAdded(KAction *action) { KActionMenu* viewMenu = static_cast(d->actionCollection->action("view menu")); viewMenu->addAction(action); } void KDirOperator::slotViewActionRemoved(KAction *action) { KActionMenu* viewMenu = static_cast(d->actionCollection->action("view menu")); viewMenu->removeAction(action); } void KDirOperator::setEnableDirHighlighting(bool enable) { d->dirHighlighting = enable; } bool KDirOperator::dirHighlighting() const { return d->dirHighlighting; } bool KDirOperator::dirOnlyMode() const { return dirOnlyMode(d->mode); } bool KDirOperator::dirOnlyMode(uint mode) { return ((mode & KFile::Directory) && (mode & (KFile::File | KFile::Files)) == 0); } void KDirOperator::slotProperties() { if (d->itemView == 0) { return; } const QList list = selectedItems(); if (!list.isEmpty()) { - // TODO: KPropertiesDialog still uses pointer-based KFileItemList - KFileItemList itemPtrList; - foreach (KFileItem item, list) { - itemPtrList << &item; - } - - KPropertiesDialog dialog(itemPtrList, this); + KPropertiesDialog dialog(list, this); dialog.exec(); } } void KDirOperator::slotPressed(const QModelIndex& index) { Q_UNUSED(index); // Remember whether the left mouse button has been pressed, to prevent // that a right-click on an item opens an item (see slotClicked(), // slotDoubleClicked() and openContextMenu()). d->leftButtonPressed = (QApplication::mouseButtons() & Qt::LeftButton); } void KDirOperator::slotClicked(const QModelIndex& index) { if (!d->leftButtonPressed) { return; } const QModelIndex dirIndex = d->proxyModel->mapToSource(index); KFileItem item = d->dirModel->itemForIndex(dirIndex); if (item.isDir()) { if (KGlobalSettings::singleClick()) { - selectDir(&item); + selectDir(item); } else { - highlightFile(&item); + highlightFile(item); } } else { - highlightFile(&item); + highlightFile(item); } } void KDirOperator::slotDoubleClicked(const QModelIndex& index) { if (!d->leftButtonPressed) { return; } const QModelIndex dirIndex = d->proxyModel->mapToSource(index); KFileItem item = d->dirModel->itemForIndex(dirIndex); if (item.isDir()) { - selectDir(&item); + selectDir(item); } else { - selectFile(&item); + selectFile(item); } } void KDirOperator::openContextMenu(const QPoint& pos) { d->leftButtonPressed = false; const QModelIndex proxyIndex = d->itemView->indexAt(pos); const QModelIndex dirIndex = d->proxyModel->mapToSource(proxyIndex); KFileItem item = d->dirModel->itemForIndex(dirIndex); activatedMenu(item, QCursor::pos()); } void KDirOperator::triggerPreview(const QModelIndex& index) { if ((d->preview != 0) && index.isValid() && (index.column() == KDirModel::Name)) { const QModelIndex dirIndex = d->proxyModel->mapToSource(index); const KFileItem item = d->dirModel->itemForIndex(dirIndex); if (!item.isDir()) { d->previewUrl = item.url(); d->previewTimer->start(300); } } } void KDirOperator::cancelPreview() { d->previewTimer->stop(); } void KDirOperator::showPreview() { if (d->preview != 0) { d->preview->showPreview(d->previewUrl); } } void KDirOperator::slotSplitterMoved(int pos, int index) { Q_UNUSED(pos); Q_UNUSED(index); const QList sizes = d->splitter->sizes(); if (sizes.count() == 2) { // remember the width of the preview widget (see KDirOperator::resizeEvent()) d->previewWidth = sizes[1]; } } void KDirOperator::assureVisibleSelection() { if (d->itemView == 0) { return; } QItemSelectionModel* selModel = d->itemView->selectionModel(); if (selModel->hasSelection()) { const QModelIndexList list = selModel->selection().indexes(); const QModelIndex index = list.first(); d->itemView->scrollTo(index); triggerPreview(index); } } void KDirOperator::synchronizeSortingState(int logicalIndex, Qt::SortOrder order) { switch (logicalIndex) { case KDirModel::Name: sortByName(); break; case KDirModel::Size: sortBySize(); break; case KDirModel::ModifiedTime: sortByDate(); break; case KDirModel::Type: sortByType(); break; } const bool descending = d->actionCollection->action("descending")->isChecked(); const bool reverseSorting = ((order == Qt::Ascending) && descending) || ((order == Qt::Descending) && !descending); if (reverseSorting) { sortReversed(); } d->proxyModel->sort(sortColumn(), sortOrder()); QMetaObject::invokeMethod(this, "assureVisibleSelection", Qt::QueuedConnection); } void KDirOperator::setViewConfig(KConfigGroup& configGroup) { delete d->configGroup; d->configGroup = new KConfigGroup(configGroup); } KConfigGroup* KDirOperator::viewConfigGroup() const { return d->configGroup; } void KDirOperator::setShowHiddenFiles(bool s) { d->actionCollection->action("show hidden")->setChecked(s); } bool KDirOperator::showHiddenFiles() const { return d->actionCollection->action("show hidden")->isChecked(); } // ### temporary code #include bool KDirOperator::isReadable(const KUrl& url) { if (!url.isLocalFile()) return true; // what else can we say? KDE_struct_stat buf; #ifdef Q_WS_WIN QString ts = url.toLocalFile(); #else QString ts = url.path(KUrl::AddTrailingSlash); #endif bool readable = (KDE_stat(QFile::encodeName(ts), &buf) == 0); if (readable) { // further checks DIR *test; test = opendir(QFile::encodeName(ts)); // we do it just to test here readable = (test != 0); if (test) closedir(test); } return readable; } #include "kdiroperator.moc" diff --git a/kfile/kdiroperator.h b/kfile/kdiroperator.h index a3ba296c6a..48c4f12a26 100644 --- a/kfile/kdiroperator.h +++ b/kfile/kdiroperator.h @@ -1,864 +1,863 @@ // -*- c++ -*- /* This file is part of the KDE libraries Copyright (C) 1999 Stephan Kulow 2000,2001 Carsten Pfeiffer 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 KDIROPERATOR_H_ #define KDIROPERATOR_H_ #include #include #include #include #include #include #include #include class QAbstractItemView; class QModelIndex; class QProgressBar; class KAction; class KActionMenu; class KDirLister; class KDirModel; class KDirSortFilterProxyModel; class KPreviewWidgetBase; class KToggleAction; namespace KIO { class CopyJob; class DeleteJob; } /** * This widget works as a network transparent filebrowser. You specify a URL * to display and this url will be loaded via KDirLister. The user can * browse through directories, highlight and select files, delete or rename * files. * * It supports different views, e.g. a detailed view (see KFileDetailView), * a simple icon view (see KFileIconView), a combination of two views, * separating directories and files ( KCombiView). * * Additionally, a preview view is available (see KFilePreview), which can * show either a simple or detailed view and additionally a preview widget * (see setPreviewWidget()). KImageFilePreview is one implementation * of a preview widget, that displays previews for all supported filetypes * utilizing KIO::PreviewJob. * * Currently, those classes don't support Drag&Drop out of the box -- there * you have to use your own view-classes. You can use some DnD-aware views * from Bj�n Sahlstr� until they will be integrated * into this library. See http://devel-home.kde.org/~pfeiffer/DnD-classes.tar.gz * * This widget is the one used in the KFileDialog. * * Basic usage is like this: * \code * KDirOperator *op = new KDirOperator( KUrl( "file:/home/gis" ), this ); * // some signals you might be interested in * connect(op, SIGNAL(urlEntered(const KUrl&)), * SLOT(urlEntered(const KUrl&))); - * connect(op, SIGNAL(fileHighlighted(const KFileItem *)), - * SLOT(fileHighlighted(const KFileItem *))); - * connect(op, SIGNAL(fileSelected(const KFileItem *)), - * SLOT(fileSelected(const KFileItem *))); + * connect(op, SIGNAL(fileHighlighted(const KFileItem &)), + * SLOT(fileHighlighted(const KFileItem &))); + * connect(op, SIGNAL(fileSelected(const KFileItem &)), + * SLOT(fileSelected(const KFileItem &))); * connect(op, SIGNAL(finishedLoading()), * SLOT(slotLoadingFinished())); * * KConfigGroup grp(KGlobal::config(),"Your KDiroperator ConfigGroup" ); * op->readConfig( &grp); * op->setView(KFile::Default); * \endcode * * This will create a childwidget of 'this' showing the directory contents * of /home/gis in the default-view. The view is determined by the readConfig() * call, which will read the KDirOperator settings, the user left your program * with (and which you saved with op->writeConfig()). * * @short A widget for displaying files and browsing directories. * @author Stephan Kulow , Carsten Pfeiffer */ class KFILE_EXPORT KDirOperator : public QWidget { Q_OBJECT public: /** * The various action types. These values can be or'd together */ enum ActionType { SortActions = 1, ViewActions = 2, NavActions = 4, FileActions = 8, AllActions = 15 }; /** * Constructs the KDirOperator with no initial view. As the views are * configurable, call readConfig() to load the user's configuration * and then setView to explicitly set a view. * * This constructor doesn't start loading the url, setView will do it. */ explicit KDirOperator(const KUrl& urlName = KUrl(), QWidget *parent = 0); /** * Destroys the KDirOperator. */ virtual ~KDirOperator(); /** * Enables/disables showing hidden files. */ virtual void setShowHiddenFiles(bool s); /** * @returns true when hidden files are shown or false otherwise. */ bool showHiddenFiles() const; /** * Stops loading immediately. You don't need to call this, usually. */ void close(); /** * Sets a filter like "*.cpp *.h *.o". Only files matching that filter * will be shown. * * @see KDirLister::setNameFilter * @see nameFilter */ void setNameFilter(const QString& filter); /** * @returns the current namefilter. * @see setNameFilter */ QString nameFilter() const; /** * Sets a list of mimetypes as filter. Only files of those mimetypes * will be shown. * * Example: * \code * QStringList filter; * filter << "text/html" << "image/png" << "inode/directory"; * dirOperator->setMimefilter( filter ); * \endcode * * Node: Without the mimetype inode/directory, only files would be shown. * Call updateDir() to apply it. * * @see KDirLister::setMimeFilter * @see mimeFilter */ void setMimeFilter(const QStringList& mimetypes); /** * @returns the current mime filter. */ QStringList mimeFilter() const; /** * Clears both the namefilter and mimetype filter, so that all files and * directories will be shown. Call updateDir() to apply it. * * @see setMimeFilter * @see setNameFilter */ void clearFilter(); /** * @returns the current url */ KUrl url() const; /** * Sets a new url to list. * @param clearforward specifies whether the "forward" history should be cleared. * @param url the URL to set */ virtual void setUrl(const KUrl& url, bool clearforward); /** * Clears the current selection and attempts to set @p filename * the current file. filename is just the name, no path or url. */ void setCurrentItem(const QString& filename); /** * Sets a new KFileView to be used for showing and browsing files. * Note: this will read the current url() to fill the view. * * @see KFileView * @see KFileIconView * @see KFileDetailView * @see KCombiView * @see view */ virtual void setView(QAbstractItemView *view); /** * @returns the currently used view. * @see setView */ QAbstractItemView* view() const; /** * Sets one of the predefined fileviews. * @see KFile::FileView */ virtual void setView(KFile::FileView viewKind); /** * Sets the way to sort files and directories. */ void setSorting(QDir::SortFlags); /** * @returns the current way of sorting files and directories */ QDir::SortFlags sorting() const; /** * @returns true if we are displaying the root directory of the current url */ bool isRoot() const; /** * @returns the object listing the directory */ KDirLister* dirLister() const; /** * @returns the progress widget, that is shown during directory listing. * You can for example reparent() it to put it into a statusbar. */ QProgressBar* progressBar() const; /** * Sets the listing/selection mode for the views, an OR'ed combination of * @li File * @li Directory * @li Files * @li ExistingOnly * @li LocalOnly * * You cannot mix File and Files of course, as the former means * single-selection mode, the latter multi-selection. */ virtual void setMode(KFile::Modes m); /** * @returns the listing/selection mode. */ KFile::Modes mode() const; /** * Sets a preview-widget to be shown next to the file-view. * The ownership of @p w is transferred to KDirOperator, so don't * delete it yourself! */ virtual void setPreviewWidget(KPreviewWidgetBase *w); /** * @returns a list of all currently selected items. If there is no view, * or there are no selected items, an empty list is returned. */ QList selectedItems() const; /** * @returns true if @p item is currently selected, or false otherwise. */ - // ### KDE5: change 'const KFileItem *item' to 'const KFileItem& item' - bool isSelected(const KFileItem *item) const; + bool isSelected(const KFileItem &item) const; /** * @returns the number of directories in the currently listed url. * Returns 0 if there is no view. */ int numDirs() const; /** * @returns the number of files in the currently listed url. * Returns 0 if there is no view. */ int numFiles() const; /** * @returns a KCompletion object, containing all filenames and * directories of the current directory/URL. * You can use it to insert it into a KLineEdit or KComboBox * Note: it will only contain files, after prepareCompletionObjects() * has been called. It will be implicitly called from makeCompletion() * or makeDirCompletion() */ KCompletion* completionObject() const; /** * @returns a KCompletion object, containing only all directories of the * current directory/URL. * You can use it to insert it into a KLineEdit or KComboBox * Note: it will only contain directories, after * prepareCompletionObjects() has been called. It will be implicitly * called from makeCompletion() or makeDirCompletion() */ KCompletion* dirCompletionObject() const; /** * an accessor to a collection of all available Actions. The actions * are static, they will be there all the time (no need to connect to * the signals KActionCollection::inserted() or removed(). * * There are the following actions: * * @li popupMenu : an ActionMenu presenting a popupmenu with all actions * @li up : changes to the parent directory * @li back : goes back to the previous directory * @li forward : goes forward in the history * @li home : changes to the user's home directory * @li reload : reloads the current directory * @li mkdir : opens a dialog box to create a directory * @li delete : deletes the selected files/directories * @li sorting menu : an ActionMenu containing all sort-options * @li by name : sorts by name * @li by size : sorts by size * @li by date : sorts by date * @li by type : sorts by type * @li descending : reverses the sort order * @li view menu : an ActionMenu containing all actions concerning the view * @li short view : shows a simple fileview * @li detailed view : shows a detailed fileview (dates, permissions ,...) * @li show hidden : shows hidden files * @li preview : shows a preview next to the fileview * @li properties : shows a KPropertiesDialog for the selected files * * The short and detailed view are in an exclusive group. The sort-by * actions are in an exclusive group as well. Also the "separate dirs", * "preview" and "single" actions are in an exclusive group. * * You can e.g. use * \code * actionCollection()->action( "up" )->plug( someToolBar ); * \endcode * to add a button into a toolbar, which makes the dirOperator change to * its parent directory. * * @returns all available Actions */ KActionCollection* actionCollection() const; /** * Sets the config object and the to be used group in KDirOperator. This * will be used to store the view's configuration via * KFileView::writeConfig() (and for KFileView::readConfig()). * If you don't set this, the views cannot save and restore their * configuration. * * Usually you call this right after KDirOperator creation so that the view * instantiation can make use of it already. * * Note that KDirOperator does NOT take ownership of that object (typically * it's KGlobal::config() anyway. * * You must not delete the KConfig or KConfigGroup object (and master config object) before * either deleting the KDirOperator or calling setViewConfig(0); or something like that * * @see viewConfig * @see viewConfigGroup */ virtual void setViewConfig(KConfigGroup& configGroup); /* * @returns the group set by setViewConfig configuration. */ KConfigGroup* viewConfigGroup() const; /** * Reads the default settings for a view, i.e. the default KFile::FileView. * Also reads the sorting and whether hidden files should be shown. * Note: the default view will not be set - you have to call * \code * setView( KFile::Default ) * \endcode * to apply it. * * @see setView * @see setViewConfig * @see writeConfig */ virtual void readConfig(const KConfigGroup& configGroup); /** * Saves the current settings like sorting, simple or detailed view. * * @see readConfig * @see setViewConfig */ virtual void writeConfig(KConfigGroup& configGroup); /** * This is a KFileDialog specific hack: we want to select directories with * single click, but not files. But as a generic class, we have to be able * to select files on single click as well. * * This gives us the opportunity to do both. * * The default is false, set it to true if you don't want files selected * with single click. */ void setOnlyDoubleClickSelectsFiles(bool enable); /** * @returns whether files (not directories) should only be select()ed by * double-clicks. * @see setOnlyDoubleClickSelectsFiles */ bool onlyDoubleClickSelectsFiles() const; /** * Creates the given directory/url. If it is a relative path, * it will be completed with the current directory. * If enterDirectory is true, the directory will be entered after a * successful operation. If unsuccessful, a messagebox will be presented * to the user. * @returns true if the directory could be created. */ virtual bool mkdir(const QString& directory, bool enterDirectory = true); /** * Starts and returns a KIO::DeleteJob to delete the given @p items. * * @param items the list of items to be deleted * @param parent the parent widget used for the confirmation dialog * @param ask specifies whether a confirmation dialog should be shown * @param showProgress passed to the DeleteJob to show a progress dialog */ virtual KIO::DeleteJob* del(const QList& items, QWidget *parent = 0, bool ask = true, bool showProgress = true); /** * Clears the forward and backward history. */ void clearHistory(); /** * When going up in the directory hierarchy, KDirOperator can highlight * the directory that was just left. * * I.e. when you go from /home/gis/src to /home/gis, the item "src" will * be made the current item. * * Default is off. */ virtual void setEnableDirHighlighting(bool enable); /** * @returns whether the last directory will be made the current item * when going up in the directory hierarchy. * * Default is false. */ bool dirHighlighting() const; /** * @returns true if we are in directory-only mode, that is, no files are * shown. */ bool dirOnlyMode() const; static bool dirOnlyMode(uint mode); /** * Sets up the action menu. * @param whichActions is an value of OR'd ActionTypes that controls which actions to show in the action menu */ void setupMenu(int whichActions); /** * Reimplemented - allow dropping of files if @p b is true * @param b true if the widget should allow dropping of files */ virtual void setAcceptDrops(bool b); /** * Sets the options for dropping files. * @see KFileView::DropOptions */ virtual void setDropOptions(int options); /** * Starts and returns a KIO::CopyJob to trash the given @p items. * * @param items the list of items to be trashed * @param parent the parent widget used for the confirmation dialog * @param ask specifies whether a confirmation dialog should be shown * @param showProgress passed to the CopyJob to show a progress dialog */ virtual KIO::CopyJob* trash(const QList& items, QWidget *parent, bool ask = true, bool showProgress = true); protected: /** * A view factory for creating predefined fileviews. Called internally by setView, * but you can also call it directly. Reimplement this if you depend on self defined fileviews. * @param parent is the QWidget to be set as parent * @param viewKind is the predefined view to be set, note: this can be several ones OR:ed together * @returns the created KFileView * @see KFile::FileView * @see setView */ virtual QAbstractItemView* createView(QWidget *parent, KFile::FileView viewKind); /** * Sets a custom KDirLister to list directories. */ virtual void setDirLister(KDirLister *lister); virtual void resizeEvent(QResizeEvent *event); /** * Sets up all the actions. Called from the constructor, you usually * better not call this. */ void setupActions(); /** * Updates the sorting-related actions to comply with the current sorting * @see sorting */ void updateSortActions(); /** * Updates the view-related actions to comply with the current * KFile::FileView */ void updateViewActions(); /** * Sets up the context-menu with all the necessary actions. Called from the * constructor, you usually don't need to call this. */ void setupMenu(); /** * Synchronizes the completion objects with the entries of the * currently listed url. * * Automatically called from makeCompletion() and * makeDirCompletion() */ void prepareCompletionObjects(); /** * Checks if there support from KIO::PreviewJob for the currently * shown files, taking mimeFilter() and nameFilter() into account * Enables/disables the preview-action accordingly. */ bool checkPreviewSupport(); /** * Called upon right-click to activate the popupmenu. */ virtual void activatedMenu(const KFileItem &item, const QPoint &pos); public Q_SLOTS: /** * Goes one step back in the history and opens that url. */ virtual void back(); /** * Goes one step forward in the history and opens that url. */ virtual void forward(); /** * Enters the home directory. */ virtual void home(); /** * Goes one directory up from the current url. */ virtual void cdUp(); /** * to update the view after changing the settings */ void updateDir(); /** * Re-reads the current url. */ virtual void rereadDir(); /** * Opens a dialog to create a new directory. */ virtual void mkdir(); /** * Deletes the currently selected files/directories. */ virtual void deleteSelected(); /** * Enables/disables actions that are selection dependent. Call this e.g. * when you are about to show a popup menu using some of KDirOperators * actions. */ void updateSelectionDependentActions(); /** * Tries to complete the given string (only completes files). */ QString makeCompletion(const QString&); /** * Tries to complete the given string (only completes directores). */ QString makeDirCompletion(const QString&); /** * Trashes the currently selected files/directories. * * This function used to take activation reason and keyboard modifiers, * in order to call deleteSelected() if the user wanted to delete. * Instead, call deleteSelected(). * * FIXME KAction Port: link deleteSelected() up correctly */ virtual void trashSelected(); protected Q_SLOTS: /** * Restores the normal cursor after showing the busy-cursor. Also hides * the progressbar. */ void resetCursor(); /** * Called after setUrl() to load the directory, update the history, * etc. */ void pathChanged(); /** * Enters the directory specified by the given @p item. */ - virtual void selectDir(const KFileItem *item); + virtual void selectDir(const KFileItem &item); /** * Emits fileSelected( item ) */ - void selectFile(const KFileItem *item); + void selectFile(const KFileItem &item); /** * Emits fileHighlighted(item) */ - void highlightFile(const KFileItem *item); + void highlightFile(const KFileItem &item); /** * Changes sorting to sort by name */ void sortByName(); /** * Changes sorting to sort by size */ void sortBySize(); /** * Changes sorting to sort by date */ void sortByDate(); /** * Changes sorting to sort by date */ void sortByType(); /** * Changes sorting to reverse sorting */ void sortReversed(); /** * Toggles showing directories first / having them sorted like files. */ void toggleDirsFirst(); /** * Toggles case sensitive / case insensitive sorting */ void toggleIgnoreCase(); /** * Tries to make the given @p match as current item in the view and emits * completion( match ) */ void slotCompletionMatch(const QString &match); Q_SIGNALS: void urlEntered(const KUrl&); void updateInformation(int files, int dirs); void completion(const QString&); void finishedLoading(); /** * Emitted whenever the current fileview is changed, either by an explicit * call to setView() or by the user selecting a different view thru * the GUI. */ void viewChanged(QAbstractItemView *newView); /** * Emitted when a file is highlighted or generally the selection changes in * multiselection mode. In the latter case, @p item is 0L. You can access * the selected items with selectedItems(). */ - void fileHighlighted(const KFileItem *item); - void dirActivated(const KFileItem *item); - void fileSelected(const KFileItem *item); + void fileHighlighted(const KFileItem &item); + void dirActivated(const KFileItem &item); + void fileSelected(const KFileItem &item); /** * Emitted when files are dropped. Dropping files is disabled by * default. You need to enable it with setAcceptDrops() * @param item the item on which the drop occurred or 0. * @param event the drop event itself. * @param urls the urls that where dropped. */ - void dropped(const KFileItem *item, QDropEvent *event, const KUrl::List &urls); + void dropped(const KFileItem &item, QDropEvent *event, const KUrl::List &urls); private: /** * Checks whether we preview support is available for the current * mimetype/namefilter */ bool checkPreviewInternal() const; /** * takes action on the new location. If it's a directory, change * into it, if it's a file, correct the name, etc. */ void checkPath(const QString &txt, bool takeFiles = false); bool openUrl(const KUrl &url, bool keep = false, bool reload = false); /** * Returns the column used by KDirModel dependent from d->sortOrder. */ int sortColumn() const; /** * Returns the sort order dependent from d->sortOrder. */ Qt::SortOrder sortOrder() const; /** * Sorts the model depending from d->sorting and emits * the signal sortingChanged(). It is assured that the * selected item stays visible. */ void triggerSorting(); private Q_SLOTS: /** * @internal */ void slotDetailedView(); void slotSimpleView(); void slotToggleHidden(bool); void togglePreview(bool on); void slotSortByName(); void slotSortBySize(); void slotSortByDate(); void slotSortByType(); void slotSortReversed(); void slotToggleDirsFirst(); void slotToggleIgnoreCase(); void slotStarted(); void slotProgress(int); void slotShowProgress(); void slotIOFinished(); void slotCanceled(); void slotRedirected(const KUrl&); void slotViewActionAdded(KAction *); void slotViewActionRemoved(KAction *); void slotProperties(); void slotPressed(const QModelIndex &index); void slotClicked(const QModelIndex &index); void slotDoubleClicked(const QModelIndex &index); void openContextMenu(const QPoint &pos); /** * Triggers a delayed preview of the item \a index. * @see KDirOperator::cancelPreview() * @see KDirOperator::showPreview() */ void triggerPreview(const QModelIndex& index); /** * Cancels a pending preview (see also KDirOperator::triggerPreview()). */ void cancelPreview(); /** * Tells the preview widget to show the preview for the last * highlighted/selected URL. */ void showPreview(); void slotSplitterMoved(int pos, int index); /** * Assures that the selection remains visible for * the current view. This method should get invoked whenever * the view or the layout has been changed. */ void assureVisibleSelection(); /** * Synchronizes the sorting actions with the current * state of the sorting. */ void synchronizeSortingState(int logicalIndex, Qt::SortOrder order); private: static bool isReadable(const KUrl &url); private: class KDirOperatorPrivate; KDirOperatorPrivate* const d; }; #endif diff --git a/kfile/kfiletreebranch.cpp b/kfile/kfiletreebranch.cpp index 783b89f663..ed51d4124d 100644 --- a/kfile/kfiletreebranch.cpp +++ b/kfile/kfiletreebranch.cpp @@ -1,570 +1,570 @@ /* This file is part of the KDEproject Copyright (C) 2000 David Faure 2000 Carsten Pfeiffer 2002 Klaas Freitag 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 "kfiletreebranch.h" #include #include #include #include #include #include #include /* --- K3FileTreeViewToplevelItem --- */ KFileTreeBranch::KFileTreeBranch( K3FileTreeView *parent, const KUrl& url, const QString& name, const QPixmap& pix, bool showHidden, K3FileTreeViewItem *branchRoot ) : KDirLister(), m_root( branchRoot ), m_startURL( url ), m_name ( name ), m_rootIcon( pix ), m_openRootIcon( pix ), m_recurseChildren(true), m_showExtensions(true) { kDebug( 250) << "Creating branch for url " << url.prettyUrl(); /* if non exists, create one */ if( ! branchRoot ) { m_root = new K3FileTreeViewItem( parent, - new KFileItem( url, "inode/directory", + KFileItem( url, "inode/directory", S_IFDIR ), this ); } m_root->setExpandable( true ); m_root->setPixmap( 0, pix ); m_root->setText( 0, name ); setShowingDotFiles( showHidden ); - connect( this, SIGNAL( refreshItems(const KFileItemList&)), - this, SLOT ( slotRefreshItems( const KFileItemList& ))); + connect( this, SIGNAL( refreshItems(const QList >&)), + this, SLOT ( slotRefreshItems( const QList >& ))); connect( this, SIGNAL( newItems(const KFileItemList&)), this, SLOT ( addItems( const KFileItemList& ))); connect( this, SIGNAL( completed(const KUrl& )), this, SLOT(slCompleted(const KUrl&))); connect( this, SIGNAL( started( const KUrl& )), this, SLOT( slotListerStarted( const KUrl& ))); - connect( this, SIGNAL( deleteItem( KFileItem* )), - this, SLOT( slotDeleteItem( KFileItem* ))); + connect( this, SIGNAL( deleteItem( const KFileItem& )), + this, SLOT( slotDeleteItem( const KFileItem& ))); connect( this, SIGNAL( canceled(const KUrl&) ), this, SLOT( slotCanceled(const KUrl&) )); connect( this, SIGNAL( clear()), this, SLOT( slotDirlisterClear())); connect( this, SIGNAL( clear(const KUrl&)), this, SLOT( slotDirlisterClearUrl(const KUrl&))); connect( this, SIGNAL( redirection( const KUrl& , const KUrl& ) ), this, SLOT( slotRedirect( const KUrl&, const KUrl& ))); m_openChildrenURLs.append( url ); } KUrl KFileTreeBranch::rootUrl() const { return( m_startURL ); } void KFileTreeBranch::setRoot( K3FileTreeViewItem *r ) { m_root = r; } K3FileTreeViewItem *KFileTreeBranch::root( ) { return( m_root ); } QString KFileTreeBranch::name() const { return( m_name ); } void KFileTreeBranch::setName( const QString n ) { m_name = n; } QPixmap KFileTreeBranch::pixmap() const { return m_rootIcon; } QPixmap KFileTreeBranch::openPixmap() const { return m_openRootIcon; } void KFileTreeBranch::setOpen( bool setopen ) { if ( root() ) { root()->setOpen( setopen ); } } void KFileTreeBranch::setOpenPixmap( const QPixmap& pix ) { m_openRootIcon = pix; if( root()->isOpen()) { root()->setPixmap( 0, pix ); } } void KFileTreeBranch::slotListerStarted( const KUrl &url ) { /* set the parent correct if it is zero. */ kDebug( 250) << "Starting to list " << url.prettyUrl(); } -K3FileTreeViewItem* KFileTreeBranch::treeItemForFileItem(KFileItem *it) +K3FileTreeViewItem* KFileTreeBranch::treeItemForFileItem(const KFileItem &it) { return const_cast( - static_cast(it->extraData(this))); + static_cast(it.extraData(this))); } -K3FileTreeViewItem *KFileTreeBranch::parentKFTVItem( KFileItem *item ) +K3FileTreeViewItem *KFileTreeBranch::parentKFTVItem( const KFileItem &item ) { K3FileTreeViewItem *parent = 0; - if( ! item ) return 0; + if( item.isNull() ) return 0; /* If it is a directory, check, if it exists in the dict. If not, go one up * and check again. */ - KUrl url = item->url(); + KUrl url = item.url(); // kDebug(250) << "Item's url is " << url.prettyUrl(); KUrl dirUrl( url ); dirUrl.setFileName( QString() ); // kDebug(250) << "Directory url is " << dirUrl.prettyUrl(); parent = findTVIByUrl( dirUrl ); // kDebug(250) << "Returning as parent item <" << parent << ">"; return( parent ); } -void KFileTreeBranch::slotRefreshItems( const KFileItemList& list ) +void KFileTreeBranch::slotRefreshItems( const QList > &list ) { kDebug(250) << "Refreshing " << list.count() << " items !"; - KFileItemList::const_iterator kit = list.begin(); - const KFileItemList::const_iterator kend = list.end(); - for ( ; kit != kend; ++kit ) + for ( int i = 0; i < list.count(); ++i ) { - K3FileTreeViewItem *item = findTVIByUrl((*kit)->url()); + const KFileItem fileItem = list[ i ].second; + + K3FileTreeViewItem *item = findTVIByUrl(fileItem.url()); if (item) { - item->setPixmap(0, item->fileItem()->pixmap( K3Icon::SizeSmall )); - item->setText( 0, item->fileItem()->text()); + item->setPixmap(0, item->fileItem().pixmap( K3Icon::SizeSmall )); + item->setText( 0, item->fileItem().text()); } } } void KFileTreeBranch::addItems( const KFileItemList& list ) { kDebug(250) << "Adding " << list.count() << " items !"; K3FileTreeViewItemList treeViewItList; K3FileTreeViewItem *parentItem = 0; KFileItemList::const_iterator kit = list.begin(); const KFileItemList::const_iterator kend = list.end(); for ( ; kit != kend; ++kit ) { - KFileItem* currItem = *kit; + KFileItem currItem = *kit; parentItem = parentKFTVItem( currItem ); /* Only create a new K3FileTreeViewItem if it does not yet exist */ K3FileTreeViewItem *newKFTVI = treeItemForFileItem(currItem); if( ! newKFTVI ) { newKFTVI = createTreeViewItem( parentItem, currItem ); if (!newKFTVI) { // TODO: Don't fail if parentItem == 0 continue; } - currItem->setExtraData( this, newKFTVI ); + currItem.setExtraData( this, newKFTVI ); /* Cut off the file extension in case it is not a directory */ - if( !m_showExtensions && !currItem->isDir() ) /* Need to cut the extension */ + if( !m_showExtensions && !currItem.isDir() ) /* Need to cut the extension */ { - QString name = currItem->text(); + QString name = currItem.text(); int mPoint = name.lastIndexOf( '.' ); if( mPoint > 0 ) name = name.left( mPoint ); newKFTVI->setText( 0, name ); } } /* Now try to find out if there are children for dirs in the treeview */ /* This stats a directory on the local file system and checks the */ /* hardlink entry in the stat-buf. This works only for local directories. */ - if( dirOnlyMode() && !m_recurseChildren && currItem->isLocalFile( ) && currItem->isDir() ) + if( dirOnlyMode() && !m_recurseChildren && currItem.isLocalFile( ) && currItem.isDir() ) { - KUrl url = currItem->url(); + KUrl url = currItem.url(); QString filename = url.directory( KUrl::ObeyTrailingSlash ) + url.fileName(); /* do the stat trick of Carsten. The problem is, that the hardlink * count only contains directory links. Thus, this method only seem * to work in dir-only mode */ kDebug(250) << "Doing stat on " << filename; KDE_struct_stat statBuf; if( KDE_stat( QFile::encodeName( filename ), &statBuf ) == 0 ) { int hardLinks = statBuf.st_nlink; /* Count of dirs */ kDebug(250) << "stat succeeded, hardlinks: " << hardLinks; // If the link count is > 2, the directory likely has subdirs. If it's < 2 // it's something weird like a mounted SMB share. In that case we don't know // if there are subdirs, thus show it as expandable. if( hardLinks != 2 ) { newKFTVI->setExpandable(true); } else { newKFTVI->setExpandable(false); } if( hardLinks >= 2 ) // "Normal" directory with subdirs { kDebug(250) << "Emitting for " << url.prettyUrl(); emit( directoryChildCount( newKFTVI, hardLinks-2)); // parentItem, hardLinks-1 )); } } else { kDebug(250) << "stat of " << filename << " failed !"; } } treeViewItList.append( newKFTVI ); } emit newTreeViewItems( this, treeViewItList ); } K3FileTreeViewItem* KFileTreeBranch::createTreeViewItem( K3FileTreeViewItem *parent, - KFileItem *fileItem ) + const KFileItem &fileItem ) { K3FileTreeViewItem *tvi = 0; - if( parent && fileItem ) + if( parent && !fileItem.isNull() ) { tvi = new K3FileTreeViewItem( parent, fileItem, this ); } else { kDebug(250) << "createTreeViewItem: Have no parent"; } return( tvi ); } void KFileTreeBranch::setChildRecurse( bool t ) { m_recurseChildren = t; if( t == false ) m_openChildrenURLs.clear(); } bool KFileTreeBranch::childRecurse() { return m_recurseChildren; } void KFileTreeBranch::setShowExtensions( bool visible ) { m_showExtensions = visible; } bool KFileTreeBranch::showExtensions( ) const { return( m_showExtensions ); } /* * The signal that tells that a directory was deleted may arrive before the signal * for its children arrive. Thus, we must walk through the children of a dir and * remove them before removing the dir itself. */ -void KFileTreeBranch::slotDeleteItem( KFileItem *it ) +void KFileTreeBranch::slotDeleteItem( const KFileItem &it ) { - if( !it ) return; - kDebug(250) << "Slot Delete Item hitted for " << it->url().prettyUrl(); + if( it.isNull() ) return; + kDebug(250) << "Slot Delete Item hitted for " << it.url().prettyUrl(); K3FileTreeViewItem *kfti = treeItemForFileItem(it); if( kfti ) { kDebug( 250 ) << "Child count: " << kfti->childCount(); if( kfti->childCount() > 0 ) { K3FileTreeViewItem *child = static_cast(kfti->firstChild()); while( child ) { kDebug(250) << "Calling child to be deleted !"; K3FileTreeViewItem *nextChild = static_cast(child->nextSibling()); slotDeleteItem( child->fileItem()); child = nextChild; } } kDebug(250) << "Found corresponding K3FileTreeViewItem"; - if( m_lastFoundURL.equals(it->url(), KUrl::CompareWithoutTrailingSlash )) + if( m_lastFoundURL.equals(it.url(), KUrl::CompareWithoutTrailingSlash )) { m_lastFoundURL = KUrl(); m_lastFoundItem = 0L; } delete( kfti ); } else { kDebug(250) << "Error: kfiletreeviewitem: "<< kfti; } } void KFileTreeBranch::slotCanceled( const KUrl& url ) { // ### anything else to do? // remove the url from the childrento-recurse-list m_openChildrenURLs.removeAll( url); // stop animations etc. K3FileTreeViewItem *item = findTVIByUrl(url); if (!item) return; // Uh oh... emit populateFinished(item); } void KFileTreeBranch::slotDirlisterClear() { kDebug(250)<< "*** Clear all !"; /* this slots needs to clear all listed items, but NOT the root item */ if( m_root ) deleteChildrenOf( m_root ); } void KFileTreeBranch::slotDirlisterClearUrl( const KUrl& url ) { kDebug(250)<< "*** Clear for URL !" << url.prettyUrl(); - KFileItem *item = findByUrl( url ); - if( item ) + const KFileItem item = findByUrl( url ); + if( !item.isNull() ) { K3FileTreeViewItem *ftvi = treeItemForFileItem(item); deleteChildrenOf( ftvi ); } } void KFileTreeBranch::deleteChildrenOf( Q3ListViewItem *parent ) { // for some strange reason, slotDirlisterClearURL() sometimes calls us // with a 0L parent. if ( !parent ) return; while ( parent->firstChild() ) delete parent->firstChild(); } void KFileTreeBranch::slotRedirect( const KUrl& oldUrl, const KUrl&newUrl ) { if( oldUrl.equals( m_startURL, KUrl::CompareWithoutTrailingSlash )) { m_startURL = newUrl; } } K3FileTreeViewItem* KFileTreeBranch::findTVIByUrl( const KUrl& url ) { K3FileTreeViewItem *resultItem = 0; if( m_startURL.equals(url, KUrl::CompareWithoutTrailingSlash) ) { kDebug(250) << "findByURL: Returning root as a parent !"; resultItem = m_root; } else if( m_lastFoundURL.equals( url, KUrl::CompareWithoutTrailingSlash )) { kDebug(250) << "findByURL: Returning from lastFoundURL!"; resultItem = m_lastFoundItem; } else { kDebug(250) << "findByURL: searching by dirlister: " << url.url(); - KFileItem *it = findByUrl( url ); + const KFileItem it = findByUrl( url ); - if( it ) + if( !it.isNull() ) { resultItem = treeItemForFileItem(it); m_lastFoundItem = resultItem; m_lastFoundURL = url; } } return( resultItem ); } void KFileTreeBranch::slCompleted( const KUrl& url ) { kDebug(250) << "SlotCompleted hit for " << url.prettyUrl(); K3FileTreeViewItem *currParent = findTVIByUrl( url ); if( ! currParent ) return; kDebug(250) << "current parent " << currParent << " is already listed: " << currParent->alreadyListed() << endl; emit( populateFinished(currParent)); emit( directoryChildCount(currParent, currParent->childCount())); /* This is a walk through the children of the last populated directory. * Here we start the dirlister on every child of the dir and wait for its * finish. When it has finished, we go to the next child. * This must be done for non local file systems in dirOnly- and Full-Mode * and for local file systems only in full mode, because the stat trick * (see addItem-Method) does only work for dirs, not for files in the directory. */ /* Set bit that the parent dir was listed completely */ currParent->setListed(true); kDebug(250) << "recurseChildren: " << m_recurseChildren; kDebug(250) << "isLocalFile: " << m_startURL.isLocalFile(); kDebug(250) << "dirOnlyMode: " << dirOnlyMode(); if( m_recurseChildren && (!m_startURL.isLocalFile() || ! dirOnlyMode()) ) { bool wantRecurseUrl = false; /* look if the url is in the list for url to recurse */ for ( KUrl::List::Iterator it = m_openChildrenURLs.begin(); it != m_openChildrenURLs.end(); ++it ) { /* it is only interesting that the url _is_in_ the list. */ if( (*it).equals( url, KUrl::CompareWithoutTrailingSlash ) ) wantRecurseUrl = true; } K3FileTreeViewItem *nextChild = 0; kDebug(250) << "Recursing " << url.prettyUrl() << "? " << wantRecurseUrl; if( wantRecurseUrl && currParent ) { /* now walk again through the tree and populate the children to get +-signs */ /* This is the starting point. The visible folder has finished, processing the children has not yet started */ nextChild = static_cast (static_cast(currParent)->firstChild()); if( ! nextChild ) { /* This happens if there is no child at all */ kDebug( 250 ) << "No children to recuse"; } /* Since we have listed the children to recurse, we can remove the entry * in the list of the URLs to see the children. */ m_openChildrenURLs.removeAll(url); } if( nextChild ) /* This implies that idx > -1 */ { /* Next child is defined. We start a dirlister job on every child item * which is a directory to find out how much children are in the child * of the last opened dir */ /* Skip non directory entries */ while( nextChild ) { if( nextChild->isDir() && ! nextChild->alreadyListed()) { - KFileItem *kfi = nextChild->fileItem(); - if( kfi && kfi->isReadable()) + const KFileItem kfi = nextChild->fileItem(); + if( !kfi.isNull() && kfi.isReadable()) { - KUrl recurseUrl = kfi->url(); + KUrl recurseUrl = kfi.url(); kDebug(250) << "Starting to recurse NOW " << recurseUrl.prettyUrl(); openUrl( recurseUrl, true ); } } nextChild = static_cast(nextChild->nextSibling()); // kDebug(250) << "Next child " << m_nextChild; } } } else { kDebug(250) << "skipping to recurse in complete-slot"; } } /* This slot is called when a treeviewitem is expanded in the gui */ bool KFileTreeBranch::populate( const KUrl& url, K3FileTreeViewItem *currItem ) { bool ret = false; if( ! currItem ) return ret; kDebug(250) << "Populating <" << url.prettyUrl() << ">"; /* Add this url to the list of urls to recurse for children */ if( m_recurseChildren ) { m_openChildrenURLs.append( url ); kDebug(250) << "Appending to list " << url.prettyUrl(); } if( ! currItem->alreadyListed() ) { /* start the lister */ ret = openUrl( url, true ); } else { kDebug(250) << "Children already existing in treeview!"; slCompleted( url ); ret = true; } return ret; } #include "kfiletreebranch.moc" diff --git a/kfile/kfiletreebranch.h b/kfile/kfiletreebranch.h index b47355f90c..66b9b0b4c8 100644 --- a/kfile/kfiletreebranch.h +++ b/kfile/kfiletreebranch.h @@ -1,240 +1,240 @@ /* This file is part of the KDE project Copyright (C) 2000 David Faure 2000 Carsten Pfeiffer 2001 Klaas Freitag 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 kfile_tree_branch_h #define kfile_tree_branch_h class QColorGroup; #include #include #include #include #include #include class KUrl; class K3FileTreeView; /** * This is the branch class of the K3FileTreeView, which represents one * branch in the treeview. Every branch has a root which is an url. The branch * lists the files under the root. Every branch uses its own dirlister and can * have its own filter etc. * * @short Branch object for K3FileTreeView object. * */ class KFILE_EXPORT KFileTreeBranch : public KDirLister { Q_OBJECT public: /** * constructs a branch for K3FileTreeView. Does not yet start to list it. * @param url start url of the branch. * @param name the name of the branch, which is displayed in the first column of the treeview. * @param pix is a pixmap to display as an icon of the branch. * @param showHidden flag to make hidden files visible or not. * @param branchRoot is the K3FileTreeViewItem to use as the root of the * branch, with the default 0 meaning to let KFileTreeBranch create * it for you. */ KFileTreeBranch( K3FileTreeView*, const KUrl& url, const QString& name, const QPixmap& pix, bool showHidden = false, K3FileTreeViewItem *branchRoot = 0 ); /** * @returns the root url of the branch. */ KUrl rootUrl() const; /** * sets a K3FileTreeViewItem as root widget for the branch. * That must be created outside of the branch. All KFileTreeViewItems * the branch is allocating will become children of that object. * @param r the K3FileTreeViewItem to become the root item. */ virtual void setRoot( K3FileTreeViewItem *r ); /** * @returns the root item. */ K3FileTreeViewItem *root( ); /** * @returns the name of the branch. */ QString name() const; /** * sets the name of the branch. */ virtual void setName( const QString n ); /* * returns the current root item pixmap set in the constructor. The root * item pixmap defaults to the icon for directories. * @see openPixmap() */ QPixmap pixmap() const; /* * returns the current root item pixmap set by setOpenPixmap() * which is displayed if the branch is expanded. * The root item pixmap defaults to the icon for directories. * @see pixmap() * Note that it depends on K3FileTreeView::showFolderOpenPximap weather * open pixmap are displayed or not. */ QPixmap openPixmap() const; /** * @returns whether the items in the branch show their file extensions in the * tree or not. See setShowExtensions for more information. */ bool showExtensions( ) const; /** * sets the root of the branch open or closed. */ void setOpen( bool setopen = true ); /** * sets if children recursion is wanted or not. If this is switched off, the * child directories of a just opened directory are not listed internally. * That means that it can not be determined if the sub directories are * expandable or not. If this is switched off there will be no call to * setExpandable. * @param t set to true to switch on child recursion */ void setChildRecurse( bool t=true ); /** * @returns if child recursion is on or off. * @see setChildRecurse */ bool childRecurse(); public Q_SLOTS: /** * populates a branch. This method must be called after a branch was added * to a K3FileTreeView using method addBranch. * @param url is the url of the root item where the branch starts. * @param currItem is the current parent. */ virtual bool populate( const KUrl &url, K3FileTreeViewItem* currItem ); /** * sets printing of the file extensions on or off. If you pass false to this * slot, all items of this branch will not show their file extensions in the * tree. * @param visible flags if the extensions should be visible or not. */ virtual void setShowExtensions( bool visible = true ); void setOpenPixmap( const QPixmap& pix ); protected: /** * allocates a K3FileTreeViewItem for the branch * for new items. */ virtual K3FileTreeViewItem *createTreeViewItem( K3FileTreeViewItem *parent, - KFileItem *fileItem ); + const KFileItem &fileItem ); public: /** * find the according K3FileTreeViewItem by an url */ virtual K3FileTreeViewItem *findTVIByUrl( const KUrl& ); Q_SIGNALS: /** * emitted with the item of a directory which was finished to populate */ void populateFinished( K3FileTreeViewItem * ); /** * emitted with a list of new or updated K3FileTreeViewItem which were * found in a branch. Note that this signal is emitted very often and may slow * down the performance of the treeview ! */ void newTreeViewItems( KFileTreeBranch*, const K3FileTreeViewItemList& ); /** * emitted with the exact count of children for a directory. */ void directoryChildCount( K3FileTreeViewItem* item, int count ); private Q_SLOTS: - void slotRefreshItems( const KFileItemList& ); + void slotRefreshItems( const QList >& ); void addItems( const KFileItemList& ); void slCompleted( const KUrl& ); void slotCanceled( const KUrl& ); void slotListerStarted( const KUrl& ); - void slotDeleteItem( KFileItem* ); + void slotDeleteItem( const KFileItem& ); void slotDirlisterClear(); void slotDirlisterClearUrl( const KUrl& url ); void slotRedirect( const KUrl& oldUrl, const KUrl&newUrl ); private: - K3FileTreeViewItem *parentKFTVItem( KFileItem *item ); + K3FileTreeViewItem *parentKFTVItem( const KFileItem &item ); static void deleteChildrenOf( Q3ListViewItem *parent ); - K3FileTreeViewItem* treeItemForFileItem(KFileItem *it); + K3FileTreeViewItem* treeItemForFileItem(const KFileItem &it); K3FileTreeViewItem *m_root; KUrl m_startURL; QString m_name; QPixmap m_rootIcon; QPixmap m_openRootIcon; /* this list holds the url's which children are opened. */ KUrl::List m_openChildrenURLs; /* The next two members are used for caching purposes in findTVIByURL. */ KUrl m_lastFoundURL; K3FileTreeViewItem *m_lastFoundItem; bool m_recurseChildren :1; bool m_showExtensions :1; private: class KFileTreeBranchPrivate; KFileTreeBranchPrivate *d; }; /** * List of KFileTreeBranches */ typedef Q3PtrList KFileTreeBranchList; /** * Iterator for KFileTreeBranchLists */ typedef Q3PtrListIterator KFileTreeBranchIterator; #endif diff --git a/kfile/kfileview.cpp b/kfile/kfileview.cpp index 4d45e02a70..3405ef7818 100644 --- a/kfile/kfileview.cpp +++ b/kfile/kfileview.cpp @@ -1,464 +1,448 @@ /* This file is part of the KDE libraries Copyright (C) 1998 Stephan Kulow 1998 Daniel Grana 2001 Carsten Pfeiffer 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 "kfileview.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef Unsorted // the "I hate X.h" modus #undef Unsorted #endif QDir::SortFlags KFileView::defaultSortFlags = (QDir::Name | QDir::IgnoreCase | QDir::DirsFirst); class KFileView::KFileViewPrivate { public: KFileViewPrivate() { actions = 0; dropOptions = 0; } ~KFileViewPrivate() { if( actions ) { actions->clear(); // so that the removed() signal is emitted! delete actions; } } QPointer actions; int dropOptions; }; KFileView::KFileView() :d(new KFileViewPrivate()) { m_sorting = KFileView::defaultSortFlags; sig = new KFileViewSignaler(); sig->setObjectName("view-signaller"); - m_selectedList = 0L; filesNumber = 0; dirsNumber = 0; view_mode = All; selection_mode = KFile::Single; m_viewName = i18n("Unknown View"); myOnlyDoubleClickSelectsFiles = false; } KFileView::~KFileView() { delete d; delete sig; - delete m_selectedList; } void KFileView::setParentView(KFileView *parent) { if ( parent ) { // pass all signals right to our parent - QObject::connect(sig, SIGNAL( activatedMenu(const KFileItem *, + QObject::connect(sig, SIGNAL( activatedMenu(const KFileItem &, const QPoint& ) ), - parent->sig, SIGNAL( activatedMenu(const KFileItem *, + parent->sig, SIGNAL( activatedMenu(const KFileItem &, const QPoint& ))); - QObject::connect(sig, SIGNAL( dirActivated(const KFileItem *)), - parent->sig, SIGNAL( dirActivated(const KFileItem*))); - QObject::connect(sig, SIGNAL( fileSelected(const KFileItem *)), - parent->sig, SIGNAL( fileSelected(const KFileItem*))); - QObject::connect(sig, SIGNAL( fileHighlighted(const KFileItem *) ), - parent->sig,SIGNAL(fileHighlighted(const KFileItem*))); + QObject::connect(sig, SIGNAL( dirActivated(const KFileItem &)), + parent->sig, SIGNAL( dirActivated(const KFileItem&))); + QObject::connect(sig, SIGNAL( fileSelected(const KFileItem &)), + parent->sig, SIGNAL( fileSelected(const KFileItem&))); + QObject::connect(sig, SIGNAL( fileHighlighted(const KFileItem &) ), + parent->sig,SIGNAL(fileHighlighted(const KFileItem&))); QObject::connect(sig, SIGNAL( sortingChanged( QDir::SortFlags ) ), parent->sig, SIGNAL(sortingChanged( QDir::SortFlags))); - QObject::connect(sig, SIGNAL( dropped(const KFileItem *, QDropEvent*, const KUrl::List&) ), - parent->sig, SIGNAL(dropped(const KFileItem *, QDropEvent*, const KUrl::List&))); + QObject::connect(sig, SIGNAL( dropped(const KFileItem &, QDropEvent*, const KUrl::List&) ), + parent->sig, SIGNAL(dropped(const KFileItem &, QDropEvent*, const KUrl::List&))); } } -bool KFileView::updateNumbers(const KFileItem *i) +bool KFileView::updateNumbers(const KFileItem &i) { - if (!( viewMode() & Files ) && i->isFile()) + if (!( viewMode() & Files ) && i.isFile()) return false; - if (!( viewMode() & Directories ) && i->isDir()) + if (!( viewMode() & Directories ) && i.isDir()) return false; - if (i->isDir()) + if (i.isDir()) dirsNumber++; else filesNumber++; return true; } // filter out files if we're in directory mode and count files/directories // and insert into the view void KFileView::addItemList(const KFileItemList& list) { KFileItemList::const_iterator kit = list.begin(); const KFileItemList::const_iterator kend = list.end(); for ( ; kit != kend; ++kit ) { - KFileItem *item = *kit; + const KFileItem item = *kit; if (!updateNumbers(item)) continue; insertItem(item); } } -void KFileView::insertItem( KFileItem * ) +void KFileView::insertItem( const KFileItem& ) { } QDir::SortFlags KFileView::sorting() const { return m_sorting; } void KFileView::setSorting(QDir::SortFlags new_sort) { m_sorting = new_sort; } void KFileView::clear() { - m_itemList.clear(); filesNumber = 0; dirsNumber = 0; clearView(); } bool KFileView::isReversed() const { return (m_sorting & QDir::Reversed); } void KFileView::sortReversed() { int spec = sorting(); setSorting( QDir::SortFlags( spec ^ QDir::Reversed ) ); } uint KFileView::count() const { return filesNumber + dirsNumber; } uint KFileView::numFiles() const { return filesNumber; } uint KFileView::numDirs() const { return dirsNumber; } #if 0 int KFileView::compareItems(const KFileItem *fi1, const KFileItem *fi2) const { static const QString &dirup = KGlobal::staticQString(".."); bool bigger = true; bool keepFirst = false; bool dirsFirst = ((m_sorting & QDir::DirsFirst) == QDir::DirsFirst); if (fi1 == fi2) return 0; // .. is always bigger, independent of the sort criteria if ( fi1->name() == dirup ) { bigger = false; keepFirst = dirsFirst; } else if ( fi2->name() == dirup ) { bigger = true; keepFirst = dirsFirst; } else { if ( fi1->isDir() != fi2->isDir() && dirsFirst ) { bigger = fi2->isDir(); keepFirst = true; } else { QDir::SortFlags sort = static_cast(m_sorting & QDir::SortByMask); //if (fi1->isDir() || fi2->isDir()) // sort = static_cast(KFileView::defaultSortSpec & QDir::SortByMask); switch (sort) { case QDir::Name: default: sort_by_name: if ( (m_sorting & QDir::IgnoreCase) == QDir::IgnoreCase ) bigger = (fi1->name( true ) > fi2->name( true )); else bigger = (fi1->name() > fi2->name()); break; case QDir::Time: { time_t t1 = fi1->time( KIO::UDSEntry::UDS_MODIFICATION_TIME ); time_t t2 = fi2->time( KIO::UDSEntry::UDS_MODIFICATION_TIME ); if ( t1 != t2 ) { bigger = (t1 > t2); break; } // Sort by name if both items have the same timestamp. // Don't honor the reverse flag tho. else { keepFirst = true; goto sort_by_name; } } case QDir::Size: { KIO::filesize_t s1 = fi1->size(); KIO::filesize_t s2 = fi2->size(); if ( s1 != s2 ) { bigger = (s1 > s2); break; } // Sort by name if both items have the same size. // Don't honor the reverse flag tho. else { keepFirst = true; goto sort_by_name; } } case QDir::Unsorted: bigger = true; // nothing break; } } } if (reversed && !keepFirst ) // don't reverse dirs to the end! bigger = !bigger; return (bigger ? 1 : -1); } #endif void KFileView::updateView(bool) { widget()->repaint(); } -void KFileView::updateView(const KFileItem *) +void KFileView::updateView(const KFileItem &) { } void KFileView::setCurrentItem(const QString &filename ) { if (!filename.isNull()) { - KFileItem *item; - for ( (item = firstFileItem()); item; item = nextItem( item ) ) { - if (item->name() == filename) { + KFileItem item; + for ( (item = firstFileItem()); !item.isNull(); item = nextItem( item ) ) { + if (item.name() == filename) { setCurrentItem( item ); return; } } } kDebug(kfile_area) << "setCurrentItem: no match found: " << filename; } -// KDE4 TODO: remove pointer, return by value -// KDE4 TODO: remove m_itemList -const KFileItemList * KFileView::items() const +KFileItemList KFileView::items() const { - KFileItem *item = 0L; + KFileItemList list; - // only ever use m_itemList in this method! - m_itemList.clear(); - for ( (item = firstFileItem()); item; item = nextItem( item ) ) - m_itemList.append( item ); + for ( KFileItem item = firstFileItem(); !item.isNull(); item = nextItem( item ) ) + list.append( item ); - return &m_itemList; + return list; } void KFileView::setOnlyDoubleClickSelectsFiles( bool enable ) { myOnlyDoubleClickSelectsFiles = enable; } bool KFileView::onlyDoubleClickSelectsFiles() const { return myOnlyDoubleClickSelectsFiles; } -const KFileItemList * KFileView::selectedItems() const +KFileItemList KFileView::selectedItems() const { - if ( !m_selectedList ) - m_selectedList = new KFileItemList; + KFileItemList list; - m_selectedList->clear(); - - KFileItem *item; - for ( (item = firstFileItem()); item; item = nextItem( item ) ) { + for ( KFileItem item = firstFileItem(); !item.isNull(); item = nextItem( item ) ) { if ( isSelected( item ) ) - m_selectedList->append( item ); + list.append( item ); } - return m_selectedList; + return list; } void KFileView::selectAll() { if (selection_mode == KFile::NoSelection || selection_mode== KFile::Single) return; - KFileItem *item = 0L; - for ( (item = firstFileItem()); item; item = nextItem( item ) ) + for ( KFileItem item = firstFileItem(); !item.isNull(); item = nextItem( item ) ) setSelected( item, true ); } void KFileView::invertSelection() { - KFileItem *item = 0L; - for ( (item = firstFileItem()); item; item = nextItem( item ) ) + for ( KFileItem item = firstFileItem(); !item.isNull(); item = nextItem( item ) ) setSelected( item, !isSelected( item ) ); } void KFileView::setSelectionMode( KFile::SelectionMode sm ) { selection_mode = sm; } KFile::SelectionMode KFileView::selectionMode() const { return selection_mode; } void KFileView::setViewMode( KFileView::ViewMode vm ) { view_mode = vm; } KFileView::ViewMode KFileView::viewMode() const { return view_mode; } QString KFileView::viewName() const { return m_viewName; } void KFileView::setViewName( const QString& name ) { m_viewName = name; } -void KFileView::removeItem( const KFileItem *item ) +void KFileView::removeItem( const KFileItem &item ) { - if ( !item ) - return; + if ( item.isNull() ) + return; - if ( item->isDir() ) + if ( item.isDir() ) dirsNumber--; else filesNumber--; - - if ( m_selectedList ) - m_selectedList->removeAll( const_cast( item ) ); } void KFileView::listingCompleted() { // empty default impl. } KActionCollection * KFileView::actionCollection() const { if ( !d->actions ) { d->actions = new KActionCollection( widget() ); d->actions->setObjectName( "KFileView::d->actions" ); } return d->actions; } KFileViewSignaler * KFileView::signaler() const { return sig; } void KFileView::readConfig( KConfigGroup *) { } void KFileView::writeConfig( KConfigGroup *) { } QString KFileView::sortingKey( const QString& value, bool isDir, QDir::SortFlags SortFlags ) { bool reverse = SortFlags & QDir::Reversed; bool dirsFirst = SortFlags & QDir::DirsFirst; char start = (isDir && dirsFirst) ? (reverse ? '2' : '0') : '1'; QString result = (SortFlags & QDir::IgnoreCase) ? value.toLower() : value; return result.prepend( QLatin1Char(start) ); } QString KFileView::sortingKey( KIO::filesize_t value, bool isDir, QDir::SortFlags SortFlags) { bool reverse = SortFlags & QDir::Reversed; bool dirsFirst = SortFlags & QDir::DirsFirst; char start = (isDir && dirsFirst) ? (reverse ? '2' : '0') : '1'; return KIO::number( value ).rightJustified( 24, '0' ).prepend( QLatin1Char(start) ); } void KFileView::setDropOptions(int options) { d->dropOptions = options; } int KFileView::dropOptions() { return d->dropOptions; } int KFileView::autoOpenDelay() { return (QApplication::startDragTime() * 3) / 2; } #include "kfileview.moc" diff --git a/kfile/kfileview.h b/kfile/kfileview.h index 0368eee7fb..0ad31885ac 100644 --- a/kfile/kfileview.h +++ b/kfile/kfileview.h @@ -1,432 +1,427 @@ // -*- c++ -*- /* This file is part of the KDE libraries Copyright (C) 1997 Stephan Kulow Copyright (C) 2001 Carsten Pfeiffer 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 KFILEVIEW_H #define KFILEVIEW_H class QPoint; class QWidget; class QDropEvent; class KActionCollection; #include "kfile_export.h" #include "kfileitem.h" #include "kfile.h" /** * internal class to make easier to use signals possible * @internal **/ class KFILE_EXPORT KFileViewSignaler : public QObject { Q_OBJECT public: /** * Call this method when an item is selected (depends on single click / * double click configuration). Emits the appropriate signal. **/ - void activate( const KFileItem *item ) { - if ( item->isDir() ) + void activate( const KFileItem &item ) { + if ( item.isDir() ) dirActivated( item ); else fileSelected( item ); } /** * emits the highlighted signal for item. Call this in your view class * whenever the selection changes. */ - void highlightFile(const KFileItem *i) { fileHighlighted(i); } + void highlightFile(const KFileItem &i) { fileHighlighted(i); } - void activateMenu( const KFileItem *i, const QPoint& pos ) { + void activateMenu( const KFileItem &i, const QPoint& pos ) { activatedMenu( i, pos ); } void changeSorting( QDir::SortFlags sorting ) { sortingChanged( sorting ); } - void dropURLs(const KFileItem *i, QDropEvent*e, const KUrl::List&urls) { + void dropURLs(const KFileItem &i, QDropEvent*e, const KUrl::List&urls) { dropped(i, e, urls); } Q_SIGNALS: - void dirActivated(const KFileItem*); + void dirActivated(const KFileItem&); void sortingChanged( QDir::SortFlags ); /** * the item maybe be 0L, indicating that we're in multiselection mode and * the selection has changed. */ - void fileHighlighted(const KFileItem*); - void fileSelected(const KFileItem*); - void activatedMenu( const KFileItem *i, const QPoint& ); - void dropped(const KFileItem *, QDropEvent*, const KUrl::List&); + void fileHighlighted(const KFileItem&); + void fileSelected(const KFileItem&); + void activatedMenu( const KFileItem &i, const QPoint& ); + void dropped(const KFileItem &, QDropEvent*, const KUrl::List&); }; /** * This class defines an interface to all file views. Its intent is * to allow to switch the view of the files in the selector very easily. * It defines some pure virtual functions, that must be implemented to * make a file view working. * * Since this class is not a widget, but it's meant to be added to other * widgets, its most important function is widget. This should return * a pointer to the implemented widget. * * @short A base class for views of the KDE file selector * @author Stephan Kulow **/ class KFILE_EXPORT KFileView { public: KFileView(); /** * Destructor */ virtual ~KFileView(); /** * inserts a list of items. **/ void addItemList(const KFileItemList &list); /** * a pure virtual function to get a QWidget, that can be added to * other widgets. This function is needed to make it possible for * derived classes to derive from other widgets. **/ virtual QWidget *widget() = 0; /** * ### As const-method, to be fixed in 3.0 */ QWidget *widget() const { return const_cast(this)->widget(); } /** * Sets @p filename the current item in the view, if available. */ void setCurrentItem( const QString &filename ); /** * Reimplement this to set @p item the current item in the view, e.g. * the item having focus. */ - virtual void setCurrentItem( const KFileItem *item ) = 0; + virtual void setCurrentItem( const KFileItem &item ) = 0; /** * @returns the "current" KFileItem, e.g. where the cursor is. - * Returns 0L when there is no current item (e.g. in an empty view). + * Returns an null file item when there is no current item (e.g. in an empty view). * Subclasses have to implement this. */ - virtual KFileItem *currentFileItem() const = 0; + virtual KFileItem currentFileItem() const = 0; /** * Clears the view and all item lists. */ virtual void clear(); /** * does a repaint of the view. * * The default implementation calls * \code * widget()->repaint(f) * \endcode **/ virtual void updateView(bool f = true); - virtual void updateView(const KFileItem*); + virtual void updateView(const KFileItem&); /** * Removes an item from the list; has to be implemented by the view. * Call KFileView::removeItem( item ) after removing it. */ - virtual void removeItem(const KFileItem *item); + virtual void removeItem(const KFileItem &item); /** * This hook is called when all items of the currently listed directory * are listed and inserted into the view, i.e. there won't come any new * items anymore. */ virtual void listingCompleted(); /** * Returns the sorting order of the internal list. Newly added files * are added through this sorting. */ QDir::SortFlags sorting() const; /** * Sets the sorting order of the view. * * Default is QDir::Name | QDir::IgnoreCase | QDir::DirsFirst * Override this in your subclass and sort accordingly (usually by * setting the sorting-key for every item and telling QIconView * or QListView to sort. * * A view may choose to use a different sorting than QDir::Name, Time * or Size. E.g. to sort by mimetype or any possible string. Set the * sorting to QDir::Unsorted for that and do the rest internally. * * @see sortingKey */ virtual void setSorting(QDir::SortFlags sort); /** * Tells whether the current items are in reversed order (shortcut to * sorting() & QDir::Reversed). */ bool isReversed() const; void sortReversed(); /** * @returns the number of dirs and files **/ uint count() const; /** * @returns the number of files. **/ uint numFiles() const; /** * @returns the number of directories **/ uint numDirs() const; virtual void setSelectionMode( KFile::SelectionMode sm ); virtual KFile::SelectionMode selectionMode() const; enum ViewMode { Files = 1, Directories = 2, All = Files | Directories }; virtual void setViewMode( ViewMode vm ); virtual ViewMode viewMode() const; /** * @returns the localized name of the view, which could be displayed * somewhere, e.g. in a menu, where the user can choose between views. * @see setViewName */ QString viewName() const; /** * Sets the name of the view, which could be displayed somewhere. * E.g. "Image Preview". */ void setViewName( const QString& name ); virtual void setParentView(KFileView *parent); /** * The derived view must implement this function to add * the file in the widget. * * Make sure to call this implementation, i.e. * KFileView::insertItem( i ); * */ - virtual void insertItem( KFileItem *i); + virtual void insertItem( const KFileItem &i); /** * pure virtual function, that should be implemented to clear * the view. At this moment the list is already empty **/ virtual void clearView() = 0; /** * pure virtual function, that should be implemented to make item i * visible, i.e. by scrolling the view appropriately. */ - virtual void ensureItemVisible( const KFileItem *i ) = 0; + virtual void ensureItemVisible( const KFileItem &i ) = 0; /** * Clears any selection, unhighlights everything. Must be implemented by * the view. */ virtual void clearSelection() = 0; /** * Selects all items. You may want to override this, if you can implement * it more efficiently than calling setSelected() with every item. * This works only in Multiselection mode of course. */ virtual void selectAll(); /** * Inverts the current selection, i.e. selects all items, that were up to * now not selected and deselects the other. */ virtual void invertSelection(); /** * Tells the view that it should highlight the item. * This function must be implemented by the view. **/ - virtual void setSelected(const KFileItem *, bool enable) = 0; + virtual void setSelected(const KFileItem &, bool enable) = 0; /** * @returns whether the given item is currently selected. * Must be implemented by the view. */ - virtual bool isSelected( const KFileItem * ) const = 0; + virtual bool isSelected( const KFileItem & ) const = 0; /** * @returns all currently highlighted items. */ - const KFileItemList * selectedItems() const; + KFileItemList selectedItems() const; /** * @returns all items currently available in the current sort-order */ - const KFileItemList * items() const; + KFileItemList items() const; - virtual KFileItem * firstFileItem() const = 0; - virtual KFileItem * nextItem( const KFileItem * ) const = 0; - virtual KFileItem * prevItem( const KFileItem * ) const = 0; + virtual KFileItem firstFileItem() const = 0; + virtual KFileItem nextItem( const KFileItem & ) const = 0; + virtual KFileItem prevItem( const KFileItem & ) const = 0; /** * This is a KFileDialog specific hack: we want to select directories with * single click, but not files. But as a generic class, we have to be able * to select files on single click as well. * * This gives us the opportunity to do both. * * Every view has to decide when to call select( item ) when a file was * single-clicked, based on onlyDoubleClickSelectsFiles(). */ void setOnlyDoubleClickSelectsFiles( bool enable ); /** * @returns whether files (not directories) should only be select()ed by * double-clicks. * @see setOnlyDoubleClickSelectsFiles */ bool onlyDoubleClickSelectsFiles() const; /** * increases the number of dirs and files. * @returns true if the item fits the view mode */ - bool updateNumbers(const KFileItem *i); + bool updateNumbers(const KFileItem &i); /** * @returns the view-specific action-collection. Every view should * add its actions here (if it has any) to make them available to * e.g. the KDirOperator's popup-menu. */ virtual KActionCollection * actionCollection() const; KFileViewSignaler * signaler() const; virtual void readConfig( KConfigGroup *); virtual void writeConfig( KConfigGroup *); /** * Various options for drag and drop support. * These values can be or'd together. * @li @p AutoOpenDirs Automatically open directory after hovering above it * for a short while while dragging. */ enum DropOptions { AutoOpenDirs = 1 }; /** * Specify DND options. See DropOptions for details. * All options are disabled by default. */ virtual void setDropOptions(int options); /** * Returns the DND options in effect. * See DropOptions for details. */ int dropOptions(); /** * This method calculates a QString from the given parameters, that is * suitable for sorting with e.g. QIconView or QListView. Their * Item-classes usually have a setKey( const QString& ) method or a virtual * method QString key() that is used for sorting. * * @param value Any string that should be used as sort criterion * @param isDir Tells whether the key is computed for an item representing * a directory (directories are usually sorted before files) * @param SortFlags An ORed combination of QDir::SortFlag flags. * Currently, the values IgnoreCase, Reversed and * DirsFirst are taken into account. */ static QString sortingKey( const QString& value, bool isDir, QDir::SortFlags SortFlags); /** * An overloaded method that takes not a QString, but a number as sort * criterion. You can use this for file-sizes or dates/times for example. * If you use a time_t, you need to cast that to KIO::filesize_t because * of ambiguity problems. */ static QString sortingKey( KIO::filesize_t value, bool isDir,QDir::SortFlags SortFlags); /** * @internal * delay before auto opening a directory */ static int autoOpenDelay(); protected: /** * @internal * class to distribute the signals **/ KFileViewSignaler *sig; private: static QDir::SortFlags defaultSortFlags; QDir::SortFlags m_sorting; QString m_viewName; /** * counters **/ uint filesNumber; uint dirsNumber; ViewMode view_mode; KFile::SelectionMode selection_mode; - // never use! It's only guaranteed to contain valid items in the items() - // method! - mutable KFileItemList m_itemList; - - mutable KFileItemList *m_selectedList; bool myOnlyDoubleClickSelectsFiles; private: class KFileViewPrivate; KFileViewPrivate* const d; }; #endif // KFILEINFOLISTWIDGET_H diff --git a/kfile/kfilewidget.cpp b/kfile/kfilewidget.cpp index 586fdc85bc..a5cfe1c46c 100644 --- a/kfile/kfilewidget.cpp +++ b/kfile/kfilewidget.cpp @@ -1,2123 +1,2123 @@ // -*- c++ -*- /* This file is part of the KDE libraries Copyright (C) 1997, 1998 Richard Moore 1998 Stephan Kulow 1998 Daniel Grana 1999,2000,2001,2002,2003 Carsten Pfeiffer 2003 Clarence Dang 2007 David Faure 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 "kfilewidget.h" #include "kfileplacesview.h" #include "kfileplacesmodel.h" #include "kfilebookmarkhandler.h" #include "kurlcombobox.h" #include "kurlnavigator.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class KFileWidgetPrivate { public: KFileWidgetPrivate( KFileWidget* q ) : boxLayout(0), labeledCustomWidget(0), bottomCustomWidget(0), inAccept(false), q(q) { } void updateLocationWhatsThis(); void updateAutoSelectExtension(); void initSpeedbar(); void initGUI(); void readConfig(const KConfigGroup &configGroup); void writeConfig(KConfigGroup &configGroup); void setNonExtSelection(); void setLocationText(const QString&); void appendExtension(KUrl &url); void updateLocationEditExtension(const QString &); void updateFilter(); KUrl::List& parseSelectedUrls(); /** * Parses the string "line" for files. If line doesn't contain any ", the * whole line will be interpreted as one file. If the number of " is odd, * an empty list will be returned. Otherwise, all items enclosed in " " * will be returned as correct urls. */ KUrl::List tokenize(const QString& line) const; /** * Reads the recent used files and inserts them into the location combobox */ void readRecentFiles( KConfig * ); /** * Saves the entries from the location combobox. */ void saveRecentFiles( KConfig * ); /** * called when an item is highlighted/selected in multiselection mode. * handles setting the locationEdit. */ void multiSelectionChanged(); /** * Returns the URL which represents the directory of \a url. If * \a url already is a directory, then just \a url is returned. */ KUrl directoryUrl(const KUrl& url) const; // the last selected url KUrl url; // the selected filenames in multiselection mode -- FIXME QString filenames; // the name of the filename set by setSelection QString selection; // now following all kind of widgets, that I need to rebuild // the geometry management QBoxLayout *boxLayout; QGridLayout *lafBox; QVBoxLayout *vbox; QLabel *locationLabel; // @deprecated remove in KDE4 -- err, remove what? QLabel *filterLabel; KUrlNavigator *urlNavigator; KPushButton *okButton, *cancelButton; KFilePlacesView *placesView; QSplitter *placesViewSplitter; QWidget *labeledCustomWidget; QWidget *bottomCustomWidget; // Automatically Select Extension stuff QCheckBox *autoSelectExtCheckBox; bool autoSelectExtChecked; // whether or not the _user_ has checked the above box QString extension; // current extension for this filter QList statJobs; KUrl::List urlList; //the list of selected urls QStringList mimetypes; //the list of possible mimetypes to save as // indicates if the location edit should be kept or cleared when changing // directories bool keepLocation; // the KDirOperators view is set in KFileWidget::show(), so to avoid // setting it again and again, we have this nice little boolean :) bool hasView; bool hasDefaultFilter; // necessary for the operationMode bool autoDirectoryFollowing; bool inAccept; // true between beginning and end of accept() KFileWidget::OperationMode operationMode; // The file class used for KRecentDirs QString fileClass; KFileBookmarkHandler *bookmarkHandler; KActionMenu* bookmarkButton; KConfigGroup *viewConfigGroup; KToolBar *toolbar; KUrlComboBox *locationEdit; KDirOperator *ops; KFileFilterCombo *filterWidget; KFileWidget* q; }; K_GLOBAL_STATIC(KUrl, lastDirectory) // to set the start path static const char autocompletionWhatsThisText[] = I18N_NOOP("While typing in the text area, you may be presented " "with possible matches. " "This feature can be controlled by clicking with the right mouse button " "and selecting a preferred mode from the Text Completion menu.") ""; KFileWidget::KFileWidget( const KUrl& startDir, QWidget *parent ) : QWidget(parent), KAbstractFileWidget(), d(new KFileWidgetPrivate(this)) { // TODO move most of this code for the KFileWidgetPrivate constructor d->keepLocation = false; d->operationMode = Opening; d->bookmarkHandler = 0; d->hasDefaultFilter = false; d->hasView = false; d->okButton = new KPushButton(KStandardGuiItem::ok(), this); d->okButton->setDefault( true ); d->cancelButton = new KPushButton(KStandardGuiItem::cancel(), this); // The dialog shows them d->okButton->hide(); d->cancelButton->hide(); d->autoSelectExtCheckBox = 0; // delayed loading d->autoSelectExtChecked = false; d->placesView = 0; // delayed loading d->toolbar = new KToolBar(this, true); d->toolbar->setObjectName("KFileWidget::toolbar"); d->toolbar->setMovable(false); KFilePlacesModel *model = new KFilePlacesModel(this); d->urlNavigator = new KUrlNavigator(model, d->directoryUrl(startDir), d->toolbar); d->urlNavigator->setPlacesSelectorVisible(false); KUrl u; QString text; KUrlComboBox *pathCombo = d->urlNavigator->editor(); #ifdef Q_WS_WIN foreach( const QFileInfo &drive,QFSFileEngine::drives() ) { u.setPath( drive.filePath() ); text = i18n("Drive: %1", u.toLocalFile() ); pathCombo->addDefaultUrl( u, KIO::pixmapForUrl( u, 0, K3Icon::Small ), text ); } #else u.setPath( QDir::rootPath() ); text = i18n("Root Folder: %1", u.toLocalFile() ); pathCombo->addDefaultUrl( u, KIO::pixmapForUrl( u, 0, K3Icon::Small ), text ); #endif u.setPath( QDir::homePath() ); text = i18n("Home Folder: %1", u.path( KUrl::AddTrailingSlash ) ); pathCombo->addDefaultUrl( u, KIO::pixmapForUrl( u, 0, K3Icon::Small ), text ); KUrl docPath; docPath.setPath( KGlobalSettings::documentPath() ); if ( (u.path(KUrl::AddTrailingSlash) != docPath.path(KUrl::AddTrailingSlash)) && QDir(docPath.path(KUrl::AddTrailingSlash)).exists() ) { text = i18n("Documents: %1", docPath.path( KUrl::AddTrailingSlash ) ); pathCombo->addDefaultUrl( docPath, KIO::pixmapForUrl( docPath, 0, K3Icon::Small ), text ); } u.setPath( KGlobalSettings::desktopPath() ); text = i18n("Desktop: %1", u.path( KUrl::AddTrailingSlash ) ); pathCombo->addDefaultUrl( u, KIO::pixmapForUrl( u, 0, K3Icon::Small ), text ); d->url = getStartUrl( startDir, d->fileClass ); d->selection = d->url.url(); // If local, check it exists. If not, go up until it exists. if ( d->url.isLocalFile() ) { if ( !QFile::exists( d->url.toLocalFile() ) ) { d->url = d->url.upUrl(); QDir dir( d->url.toLocalFile() ); while ( !dir.exists() ) { d->url = d->url.upUrl(); dir.setPath( d->url.toLocalFile() ); } } } d->ops = new KDirOperator(d->url, this ); d->ops->setObjectName( "KFileWidget::ops" ); d->ops->setOnlyDoubleClickSelectsFiles( true ); connect(d->ops, SIGNAL(urlEntered(const KUrl&)), SLOT(urlEntered(const KUrl&))); - connect(d->ops, SIGNAL(fileHighlighted(const KFileItem *)), - SLOT(fileHighlighted(const KFileItem *))); - connect(d->ops, SIGNAL(fileSelected(const KFileItem *)), - SLOT(fileSelected(const KFileItem *))); + connect(d->ops, SIGNAL(fileHighlighted(const KFileItem &)), + SLOT(fileHighlighted(const KFileItem &))); + connect(d->ops, SIGNAL(fileSelected(const KFileItem &)), + SLOT(fileSelected(const KFileItem &))); connect(d->ops, SIGNAL(finishedLoading()), SLOT(slotLoadingFinished())); d->ops->setupMenu(KDirOperator::SortActions | KDirOperator::FileActions | KDirOperator::ViewActions); KActionCollection *coll = d->ops->actionCollection(); // add nav items to the toolbar // // NOTE: The order of the button icons here differs from that // found in the file manager and web browser, but has been discussed // and agreed upon on the kde-core-devel mailing list: // // http://lists.kde.org/?l=kde-core-devel&m=116888382514090&w=2 // d->toolbar->addAction( coll->action( "up" ) ); coll->action( "up" )->setWhatsThis(i18n("Click this button to enter the parent folder.

" "For instance, if the current location is file:/home/%1 clicking this " "button will take you to file:/home.
", KUser().loginName() )); d->toolbar->addAction( coll->action( "back" ) ); coll->action( "back" )->setWhatsThis(i18n("Click this button to move backwards one step in the browsing history.")); d->toolbar->addAction( coll->action( "forward" ) ); coll->action( "forward" )->setWhatsThis(i18n("Click this button to move forward one step in the browsing history.")); d->toolbar->addAction( coll->action( "reload" ) ); coll->action( "reload" )->setWhatsThis(i18n("Click this button to reload the contents of the current location.")); coll->action( "mkdir" )->setShortcut( QKeySequence(Qt::Key_F10) ); d->toolbar->addAction( coll->action( "mkdir" ) ); coll->action( "mkdir" )->setWhatsThis(i18n("Click this button to create a new folder.")); KToggleAction *showSidebarAction = new KToggleAction(i18n("Show Places Navigation Panel"), this); coll->addAction("toggleSpeedbar", showSidebarAction); showSidebarAction->setShortcut( QKeySequence(Qt::Key_F9) ); connect( showSidebarAction, SIGNAL( toggled( bool ) ), SLOT( toggleSpeedbar( bool )) ); KToggleAction *showBookmarksAction = new KToggleAction(i18n("Show Bookmarks"), this); coll->addAction("toggleBookmarks", showBookmarksAction); connect( showBookmarksAction, SIGNAL( toggled( bool ) ), SLOT( toggleBookmarks( bool )) ); KActionMenu *menu = new KActionMenu( KIcon("configure"), i18n("Options"), this); coll->addAction("extra menu", menu); menu->setWhatsThis(i18n("This is the preferences menu for the file dialog. " "Various options can be accessed from this menu including:
    " "
  • how files are sorted in the list
  • " "
  • types of view, including icon and list
  • " "
  • showing of hidden files
  • " "
  • the Places navigation panel
  • " "
  • file previews
  • " "
  • separating folders from files
")); menu->addAction( coll->action( "sorting menu" )); menu->addSeparator(); coll->action( "short view" )->setShortcut( QKeySequence(Qt::Key_F6) ); menu->addAction( coll->action( "short view" )); coll->action( "detailed view" )->setShortcut( QKeySequence(Qt::Key_F7) ); menu->addAction( coll->action( "detailed view" )); menu->addSeparator(); coll->action( "show hidden" )->setShortcut( QKeySequence(Qt::Key_F8) ); menu->addAction( coll->action( "show hidden" )); menu->addAction( showSidebarAction ); menu->addAction( showBookmarksAction ); coll->action( "preview" )->setShortcut( QKeySequence(Qt::Key_F11) ); menu->addAction( coll->action( "preview" )); menu->setDelayed( false ); connect( menu->menu(), SIGNAL( aboutToShow() ), d->ops, SLOT( updateSelectionDependentActions() )); d->toolbar->addAction( menu ); d->toolbar->addWidget(d->urlNavigator); // FIXME KAction port - add capability //d->toolbar->setItemAutoSized (PATH_COMBO); d->toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly); d->toolbar->setMovable(false); KUrlCompletion *pathCompletionObj = new KUrlCompletion( KUrlCompletion::DirCompletion ); pathCombo->setCompletionObject( pathCompletionObj ); pathCombo->setAutoDeleteCompletionObject( true ); connect( d->urlNavigator, SIGNAL( urlChanged( const KUrl& )), this, SLOT( enterUrl( const KUrl& ) )); QString whatsThisText; // the Location label/edit d->locationLabel = new QLabel(i18n("&Location:"), this); d->locationEdit = new KUrlComboBox(KUrlComboBox::Files, true, this); connect( d->locationEdit, SIGNAL( textChanged( const QString& ) ), SLOT( slotLocationChanged( const QString& )) ); d->updateLocationWhatsThis(); d->locationLabel->setBuddy(d->locationEdit); d->locationEdit->setFocus(); KUrlCompletion *fileCompletionObj = new KUrlCompletion( KUrlCompletion::FileCompletion ); QString dir = d->url.url(KUrl::AddTrailingSlash); d->urlNavigator->setUrl( d->directoryUrl( dir ) ); fileCompletionObj->setDir( dir ); d->locationEdit->setCompletionObject( fileCompletionObj ); d->locationEdit->setAutoDeleteCompletionObject( true ); connect( fileCompletionObj, SIGNAL( match( const QString& ) ), SLOT( fileCompletion( const QString& )) ); connect(d->locationEdit, SIGNAL( returnPressed( const QString& )), this, SLOT( locationAccepted( const QString& ) )); // the Filter label/edit whatsThisText = i18n("This is the filter to apply to the file list. " "File names that do not match the filter will not be shown.

" "You may select from one of the preset filters in the " "drop down menu, or you may enter a custom filter " "directly into the text area.

" "Wildcards such as * and ? are allowed.

"); d->filterLabel = new QLabel(i18n("&Filter:"), this); d->filterLabel->setWhatsThis(whatsThisText); d->filterWidget = new KFileFilterCombo(this); d->filterWidget->setWhatsThis(whatsThisText); d->filterLabel->setBuddy(d->filterWidget); connect(d->filterWidget, SIGNAL(filterChanged()), SLOT(slotFilterChanged())); // the Automatically Select Extension checkbox // (the text, visibility etc. is set in updateAutoSelectExtension(), which is called by readConfig()) d->autoSelectExtCheckBox = new QCheckBox (this); d->autoSelectExtCheckBox->setStyleSheet(QString("QCheckBox { spacing-top: %1px; }").arg(KDialog::spacingHint())); connect(d->autoSelectExtCheckBox, SIGNAL(clicked()), SLOT(slotAutoSelectExtClicked())); d->initGUI(); // activate GM KSharedConfig::Ptr config = KGlobal::config(); d->readRecentFiles(config.data()); d->viewConfigGroup=new KConfigGroup(config,ConfigGroup); d->ops->setViewConfig(*d->viewConfigGroup); d->readConfig(* d->viewConfigGroup); setSelection(d->selection); } KFileWidget::~KFileWidget() { KSharedConfig::Ptr config = KGlobal::config(); config->sync(); delete d->bookmarkHandler; // Should be deleted before ops! delete d->ops; delete d->viewConfigGroup; delete d; } void KFileWidget::setLocationLabel(const QString& text) { d->locationLabel->setText(text); } void KFileWidget::setFilter(const QString& filter) { int pos = filter.indexOf('/'); // Check for an un-escaped '/', if found // interpret as a MIME filter. if (pos > 0 && filter[pos - 1] != '\\') { QStringList filters = filter.split(" ", QString::SkipEmptyParts); //QStringList::split( " ", filter ); setMimeFilter( filters ); return; } // Strip the escape characters from // escaped '/' characters. QString copy (filter); for (pos = 0; (pos = copy.indexOf("\\/", pos)) != -1; ++pos) copy.remove(pos, 1); d->ops->clearFilter(); d->filterWidget->setFilter(copy); d->ops->setNameFilter(d->filterWidget->currentFilter()); d->hasDefaultFilter = false; d->filterWidget->setEditable( true ); d->updateAutoSelectExtension (); } QString KFileWidget::currentFilter() const { return d->filterWidget->currentFilter(); } void KFileWidget::setMimeFilter( const QStringList& mimeTypes, const QString& defaultType ) { d->mimetypes = mimeTypes; d->filterWidget->setMimeFilter( mimeTypes, defaultType ); QStringList types = d->filterWidget->currentFilter().split(" ",QString::SkipEmptyParts); //QStringList::split(" ", d->filterWidget->currentFilter()); types.append( QLatin1String( "inode/directory" )); d->ops->clearFilter(); d->ops->setMimeFilter( types ); d->hasDefaultFilter = !defaultType.isEmpty(); d->filterWidget->setEditable( !d->hasDefaultFilter || d->operationMode != Saving ); d->updateAutoSelectExtension (); } void KFileWidget::clearFilter() { d->mimetypes.clear(); d->filterWidget->setFilter( QString() ); d->ops->clearFilter(); d->hasDefaultFilter = false; d->filterWidget->setEditable( true ); d->updateAutoSelectExtension (); } QString KFileWidget::currentMimeFilter() const { int i = d->filterWidget->currentIndex(); if (d->filterWidget->showsAllTypes()) i--; if ((i >= 0) && (i < (int) d->mimetypes.count())) return d->mimetypes[i]; return QString(); // The "all types" item has no mimetype } KMimeType::Ptr KFileWidget::currentFilterMimeType() { return KMimeType::mimeType( currentMimeFilter() ); } void KFileWidget::setPreviewWidget(KPreviewWidgetBase *w) { d->ops->setPreviewWidget(w); d->ops->clearHistory(); d->hasView = true; } KUrl KFileWidget::getCompleteUrl(const QString &_url) { QString url = KShell::tildeExpand(_url); KUrl u; if ( KUrl::isRelativeUrl(url) ) // only a full URL isn't relative. Even /path is. { if (!url.isEmpty() && !QDir::isRelativePath(url) ) // absolute path u.setPath( url ); else { u = d->ops->url(); u.addPath( url ); // works for filenames and relative paths u.cleanPath(); // fix "dir/.." } } else // complete URL u = url; return u; } // Called by KFileDialog void KFileWidget::slotOk() { kDebug(kfile_area) << "slotOk\n"; // a list of all selected files/directories (if any) // can only be used if the user didn't type any filenames/urls himself const QList items = d->ops->selectedItems(); if ( (mode() & KFile::Directory) != KFile::Directory ) { if ( d->locationEdit->currentText().trimmed().isEmpty() ) { if (items.isEmpty() ) { QString msg; if ( d->operationMode == Saving ) msg = i18n("Please specify the filename to save to."); else msg = i18n("Please select the file to open."); KMessageBox::information(this, msg); return; } // weird case: the location edit is empty, but there are // highlighted files else { bool multi = (mode() & KFile::Files) != 0; QString endQuote = QLatin1String("\" "); QString name, files; foreach (KFileItem fileItem, items) { name = fileItem.name(); if ( multi ) { name.prepend( QLatin1Char( '"' ) ); name.append( endQuote ); } files.append( name ); } d->setLocationText( files ); return; } } } bool dirOnly = d->ops->dirOnlyMode(); // we can use our kfileitems, no need to parse anything if ( !d->locationEdit->lineEdit()->isModified() && !(items.isEmpty() && !dirOnly) ) { d->urlList.clear(); d->filenames.clear(); if ( dirOnly ) { d->url = d->ops->url(); } else { if ( !(mode() & KFile::Files) ) {// single selection d->url = items.first().url(); } else { // multi (dirs and/or files) d->url = d->ops->url(); KUrl::List urlList; foreach (KFileItem item, items) { urlList.append(item.url()); } d->urlList = urlList; } } KUrl url = KIO::NetAccess::mostLocalUrl(d->url,topLevelWidget()); if ( (mode() & KFile::LocalOnly) == KFile::LocalOnly && !url.isLocalFile() ) { // ### after message freeze, add message for directories! KMessageBox::sorry( this, i18n("You can only select local files."), i18n("Remote Files Not Accepted") ); return; } d->url = url; emit accepted(); return; } KUrl selectedUrl; if ( (mode() & KFile::Files) == KFile::Files ) {// multiselection mode QString locationText = d->locationEdit->currentText(); if ( locationText.contains( '/' )) { // relative path? -> prepend the current directory KUrl u( d->ops->url(), KShell::tildeExpand(locationText)); if ( u.isValid() ) selectedUrl = u; else selectedUrl = d->ops->url(); } else // simple filename -> just use the current URL selectedUrl = d->ops->url(); } else { selectedUrl = getCompleteUrl(d->locationEdit->currentText()); // appendExtension() may change selectedUrl d->appendExtension (selectedUrl); } if ( !selectedUrl.isValid() ) { KMessageBox::sorry( this, i18n("%1\ndoes not appear to be a valid URL.\n", d->url.url()), i18n("Invalid URL") ); return; } KUrl url = KIO::NetAccess::mostLocalUrl(selectedUrl,topLevelWidget()); if ( (mode() & KFile::LocalOnly) == KFile::LocalOnly && !url.isLocalFile() ) { KMessageBox::sorry( this, i18n("You can only select local files."), i18n("Remote Files Not Accepted") ); return; } d->url = url; // d->url is a correct URL now if ( (mode() & KFile::Directory) == KFile::Directory ) { kDebug(kfile_area) << "Directory"; bool done = true; if ( d->url.isLocalFile() ) { if ( d->locationEdit->currentText().trimmed().isEmpty() ) { QFileInfo info( d->url.toLocalFile() ); if ( info.isDir() ) { d->filenames.clear(); d->urlList.clear(); d->urlList.append( d->url ); emit accepted(); } else if (!info.exists() && (mode() & KFile::File) != KFile::File) { // directory doesn't exist, create and enter it if ( d->ops->mkdir( d->url.url(), true )) return; else emit accepted(); } else { // d->url is not a directory, // maybe we are in File(s) | Directory mode if ( (mode() & KFile::File) == KFile::File || (mode() & KFile::Files) == KFile::Files ) done = false; } } else // Directory mode, with file[s]/dir[s] selected { if ( mode() & KFile::ExistingOnly ) { if ( d->ops->dirOnlyMode() ) { KUrl fullURL(d->url, d->locationEdit->currentText()); if ( QFile::exists( fullURL.toLocalFile() ) ) { d->url = fullURL; d->filenames.clear(); d->urlList.clear(); emit accepted(); return; } else // doesn't exist -> reject return; } } d->filenames = d->locationEdit->currentText(); emit accepted(); // what can we do? } } else { // FIXME: remote directory, should we allow that? // qDebug( "**** Selected remote directory: %s", d->url.url().toLatin1().constData()); d->filenames.clear(); d->urlList.clear(); d->urlList.append( d->url ); if ( mode() & KFile::ExistingOnly ) done = false; else emit accepted(); } if ( done ) return; } if (!KAuthorized::authorizeUrlAction("open", KUrl(), d->url)) { QString msg = KIO::buildErrorString(KIO::ERR_ACCESS_DENIED, d->url.prettyUrl()); KMessageBox::error( this, msg); return; } KIO::StatJob *job = 0L; d->statJobs.clear(); d->filenames = KShell::tildeExpand(d->locationEdit->currentText()); if ( (mode() & KFile::Files) == KFile::Files && !d->locationEdit->currentText().contains( '/' )) { kDebug(kfile_area) << "Files\n"; KUrl::List list = d->parseSelectedUrls(); for ( KUrl::List::ConstIterator it = list.begin(); it != list.end(); ++it ) { if (!KAuthorized::authorizeUrlAction("open", KUrl(), *it)) { QString msg = KIO::buildErrorString(KIO::ERR_ACCESS_DENIED, (*it).prettyUrl()); KMessageBox::error( this, msg); return; } } for ( KUrl::List::ConstIterator it = list.begin(); it != list.end(); ++it ) { job = KIO::stat( *it, !(*it).isLocalFile() ); job->ui()->setWindow (topLevelWidget()); KIO::Scheduler::scheduleJob( job ); d->statJobs.append( job ); connect( job, SIGNAL( result(KJob *) ), SLOT( slotStatResult( KJob *) )); } return; } job = KIO::stat(d->url,!d->url.isLocalFile()); job->ui()->setWindow (topLevelWidget()); d->statJobs.append( job ); connect(job, SIGNAL(result(KJob*)), SLOT(slotStatResult(KJob*))); } // FIXME : count all errors and show messagebox when d->statJobs.count() == 0 // in case of an error, we cancel the whole operation (clear d->statJobs and // don't call accept) void KFileWidget::slotStatResult(KJob* job) { kDebug(kfile_area) << "slotStatResult"; KIO::StatJob *sJob = static_cast( job ); if ( !d->statJobs.removeAll( sJob ) ) { return; } int count = d->statJobs.count(); // errors mean in general, the location is no directory ;/ // Can we be sure that it is exististant at all? (pfeiffer) if (sJob->error() && count == 0 && !d->ops->dirOnlyMode()) { emit accepted(); return; } KIO::UDSEntry t = sJob->statResult(); if (t.isDir()) { if ( d->ops->dirOnlyMode() ) { d->filenames.clear(); d->urlList.clear(); emit accepted(); } else // in File[s] mode, directory means error -> cd into it { if ( count == 0 ) { d->locationEdit->clearEditText(); d->locationEdit->lineEdit()->setModified( false ); setUrl( sJob->url() ); } } d->statJobs.clear(); return; } else if ( d->ops->dirOnlyMode() ) { return; // ### error message? } kDebug(kfile_area) << "filename " << sJob->url().url(); if ( count == 0 ) emit accepted(); } void KFileWidget::accept() { d->inAccept = true; // parseSelectedUrls() checks that *lastDirectory = d->ops->url(); if (!d->fileClass.isEmpty()) KRecentDirs::add(d->fileClass, d->ops->url().url()); // clear the topmost item, we insert it as full path later on as item 1 d->locationEdit->setItemText( 0, QString() ); KUrl::List list = selectedUrls(); QList::const_iterator it = list.begin(); for ( ; it != list.end(); ++it ) { const KUrl& url = *it; // we strip the last slash (-1) because KUrlComboBox does that as well // when operating in file-mode. If we wouldn't , dupe-finding wouldn't // work. QString file = url.isLocalFile() ? url.path(KUrl::RemoveTrailingSlash) : url.prettyUrl(KUrl::RemoveTrailingSlash); // remove dupes for ( int i = 1; i < d->locationEdit->count(); i++ ) { if ( d->locationEdit->itemText( i ) == file ) { d->locationEdit->removeItem( i-- ); break; } } d->locationEdit->insertItem( 1,file); } KSharedConfig::Ptr config = KGlobal::config(); config->setForceGlobal( true ); KConfigGroup grp(config,ConfigGroup); d->writeConfig(grp); config->setForceGlobal( false ); d->saveRecentFiles(config.data()); config->sync(); addToRecentDocuments(); if ( (mode() & KFile::Files) != KFile::Files ) // single selection emit fileSelected(d->url.url()); d->ops->close(); } -void KFileWidget::fileHighlighted(const KFileItem *i) +void KFileWidget::fileHighlighted(const KFileItem &i) { - if (i && i->isDir()) + if (!i.isNull() && i.isDir()) return; if ( (d->ops->mode() & KFile::Files) != KFile::Files ) { - if ( !i ) + if ( i.isNull() ) return; - d->url = i->url(); + d->url = i.url(); if ( !d->locationEdit->hasFocus() ) { // don't disturb while editing - d->setLocationText( i->name() ); + d->setLocationText( i.name() ); } emit fileHighlighted(d->url.url()); } else { d->multiSelectionChanged(); emit selectionChanged(); } } -void KFileWidget::fileSelected(const KFileItem *i) +void KFileWidget::fileSelected(const KFileItem &i) { - if (i && i->isDir()) + if (!i.isNull() && i.isDir()) return; if ( (d->ops->mode() & KFile::Files) != KFile::Files ) { - if ( !i ) + if ( i.isNull() ) return; - d->url = i->url(); - d->setLocationText( i->name() ); + d->url = i.url(); + d->setLocationText( i.name() ); } else { d->multiSelectionChanged(); emit selectionChanged(); } slotOk(); } // I know it's slow to always iterate thru the whole filelist // (d->ops->selectedItems()), but what can we do? void KFileWidgetPrivate::multiSelectionChanged() { if ( locationEdit->hasFocus() ) // don't disturb return; locationEdit->lineEdit()->setModified( false ); const QList list = ops->selectedItems(); if ( list.isEmpty() ) { locationEdit->clearEditText(); return; } static const QString &begin = KGlobal::staticQString(" \""); QString text; foreach (KFileItem fileItem, list) { text.append( begin ).append( fileItem.name() ).append( QLatin1Char( '"' ) ); } setLocationText( text.trimmed() ); } KUrl KFileWidgetPrivate::directoryUrl(const KUrl& url) const { KFileItem item(S_IFDIR, KFileItem::Unknown, url); item.refresh(); return item.isDir() ? url : url.upUrl(); } void KFileWidgetPrivate::setLocationText( const QString& text ) { // setCurrentItem() will cause textChanged() being emitted, // so slotLocationChanged() will be called. Make sure we don't clear // the KDirOperator's view-selection in there QObject::disconnect( locationEdit, SIGNAL( textChanged( const QString& ) ), q, SLOT( slotLocationChanged( const QString& ) ) ); locationEdit->setCurrentIndex( 0 ); QObject::connect( locationEdit, SIGNAL( textChanged( const QString& ) ), q, SLOT( slotLocationChanged( const QString& )) ); locationEdit->setEditText( text ); // don't change selection when user has clicked on an item if ( operationMode == KFileWidget::Saving && !locationEdit->isVisible()) setNonExtSelection(); } void KFileWidgetPrivate::updateLocationWhatsThis() { QString whatsThisText; if (operationMode == KFileWidget::Saving) { whatsThisText = "" + i18n("This is the name to save the file as.") + i18n (autocompletionWhatsThisText); } else if (ops->mode() & KFile::Files) { whatsThisText = "" + i18n("This is the list of files to open. More than " "one file can be specified by listing several " "files, separated by spaces.") + i18n (autocompletionWhatsThisText); } else { whatsThisText = "" + i18n("This is the name of the file to open.") + i18n (autocompletionWhatsThisText); } locationLabel->setWhatsThis(whatsThisText); locationEdit->setWhatsThis(whatsThisText); } void KFileWidgetPrivate::initSpeedbar() { placesView = new KFilePlacesView( q ); placesView->setModel(new KFilePlacesModel(placesView)); placesView->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); placesView->setObjectName( QLatin1String( "url bar" ) ); QObject::connect( placesView, SIGNAL( urlChanged( const KUrl& )), q, SLOT( enterUrl( const KUrl& )) ); // need to set the current url of the urlbar manually (not via urlEntered() // here, because the initial url of KDirOperator might be the same as the // one that will be set later (and then urlEntered() won't be emitted). // ### REMOVE THIS when KDirOperator's initial URL (in the c'tor) is gone. placesView->setUrl( url ); placesViewSplitter->insertWidget( 0, placesView ); } void KFileWidgetPrivate::initGUI() { delete boxLayout; // deletes all sub layouts boxLayout = new QVBoxLayout( q); boxLayout->setMargin(0); // no additional margin to the already existing boxLayout->setSpacing(0); boxLayout->addWidget(toolbar, 0, Qt::AlignTop); placesViewSplitter = new QSplitter(q); placesViewSplitter->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); placesViewSplitter->setChildrenCollapsible(false); boxLayout->addWidget(placesViewSplitter); vbox = new QVBoxLayout(); vbox->setMargin(0); QWidget *vboxWidget = new QWidget(); vboxWidget->setLayout(vbox); placesViewSplitter->insertWidget(0, vboxWidget); vbox->addWidget(ops, 4); vbox->addSpacing(KDialog::spacingHint()); lafBox = new QGridLayout(); // The default minimum width of the location editor and the filter widget // is so huge, that it is no possible for the user to adjust the width // of the speedbar, hence it will be reduced. locationEdit->setMinimumWidth(40); filterWidget->setMinimumWidth(40); lafBox->setSpacing(KDialog::spacingHint()); lafBox->addWidget(locationLabel, 0, 0, Qt::AlignVCenter); lafBox->addWidget(locationEdit, 0, 1, Qt::AlignVCenter); lafBox->addWidget(okButton, 0, 2, Qt::AlignVCenter); lafBox->addWidget(filterLabel, 1, 0, Qt::AlignVCenter); lafBox->addWidget(filterWidget, 1, 1, Qt::AlignVCenter); lafBox->addWidget(cancelButton, 1, 2, Qt::AlignVCenter); lafBox->setColumnStretch(1, 4); vbox->addLayout(lafBox); // add the Automatically Select Extension checkbox vbox->addWidget(autoSelectExtCheckBox); q->setTabOrder(ops, autoSelectExtCheckBox); q->setTabOrder(autoSelectExtCheckBox, locationEdit); q->setTabOrder(locationEdit, filterWidget); q->setTabOrder(filterWidget, okButton); q->setTabOrder(okButton, cancelButton); q->setTabOrder(cancelButton, urlNavigator); q->setTabOrder(urlNavigator, ops); q->setTabOrder(cancelButton, urlNavigator); q->setTabOrder(urlNavigator, ops); } void KFileWidget::slotFilterChanged() { QString filter = d->filterWidget->currentFilter(); d->ops->clearFilter(); if ( filter.indexOf( '/' ) > -1 ) { QStringList types = filter.split(" ",QString::SkipEmptyParts); //QStringList::split( " ", filter ); types.prepend( "inode/directory" ); d->ops->setMimeFilter( types ); } else d->ops->setNameFilter( filter ); d->ops->updateDir(); d->updateAutoSelectExtension(); emit filterChanged( filter ); } void KFileWidget::setUrl(const KUrl& url, bool clearforward) { d->selection.clear(); d->ops->setUrl( url, clearforward); } // Protected void KFileWidget::urlEntered(const KUrl& url) { QString filename = d->locationEdit->currentText(); d->selection.clear(); KUrlComboBox* pathCombo = d->urlNavigator->editor(); if ( pathCombo->count() != 0 ) { // little hack pathCombo->setUrl( url ); } bool blocked = d->locationEdit->blockSignals( true ); d->locationEdit->setCurrentIndex( 0 ); if ( d->keepLocation ) d->locationEdit->setEditText( filename ); d->locationEdit->blockSignals( blocked ); d->urlNavigator->setUrl( d->directoryUrl( url ) ); QString dir = url.url(KUrl::AddTrailingSlash); static_cast( d->locationEdit->completionObject() )->setDir( dir ); if ( d->placesView ) d->placesView->setUrl( url ); } void KFileWidget::locationAccepted( const QString& url ) { setSelection( url ); slotOk(); } void KFileWidget::enterUrl( const KUrl& url ) { setUrl( url ); } void KFileWidget::enterUrl( const QString& url ) { setUrl( KUrl( KUrlCompletion::replacedPath( url, true, true )) ); } void KFileWidget::setSelection(const QString& url) { kDebug(kfile_area) << "setSelection " << url; if (url.isEmpty()) { d->selection.clear(); return; } KUrl u = getCompleteUrl(url); if (!u.isValid()) { // if it still is kWarning() << url << " is not a correct argument for setSelection!"; return; } // Honor protocols that do not support directory listing if (!KProtocolManager::supportsListing(u)) return; /* we strip the first / from the path to avoid file://usr which means * / on host usr */ KFileItem i(KFileItem::Unknown, KFileItem::Unknown, u, true ); // KFileItem i(u.path()); kDebug(kfile_area) << "KFileItem " << u.path() << " " << i.isDir() << " " << u.isLocalFile() << " " << QFile::exists( u.path() ); if ( i.isDir() && u.isLocalFile() && QFile::exists( u.path() ) ) { // trust isDir() only if the file is // local (we cannot stat non-local urls) and if it exists! // (as KFileItem does not check if the file exists or not // -> the statbuffer is undefined -> isDir() is unreliable) (Simon) setUrl(u, true); } else { QString filename = u.url(); int sep = filename.lastIndexOf('/'); if (sep >= 0) { // there is a / in it KUrl dir(u); dir.setQuery( QString() ); dir.setFileName( QString() ); setUrl(dir, true ); // filename must be decoded, or "name with space" would become // "name%20with%20space", so we use KUrl::fileName() filename = u.fileName(); kDebug(kfile_area) << "filename " << filename; d->selection = filename; d->setLocationText( filename ); // tell the line edit that it has been edited // otherwise we won't know this was set by the user // and it will be ignored if there has been an // auto completion. this caused bugs where automcompletion // would start, the user would pick something from the // history and then hit Ok only to get the autocompleted // selection. OOD->OPS. d->locationEdit->lineEdit()->setModified( true ); } d->url = d->ops->url(); d->url.addPath(filename); } } void KFileWidget::slotLoadingFinished() { if ( !d->selection.isNull() ) d->ops->setCurrentItem( d->selection ); } void KFileWidget::fileCompletion( const QString& match ) { if ( match.isEmpty() && d->ops->view() ) d->ops->view()->clearSelection(); else d->ops->setCurrentItem( match ); } void KFileWidget::slotLocationChanged( const QString& text ) { if ( text.isEmpty() && d->ops->view() ) d->ops->view()->clearSelection(); d->updateFilter(); } KUrl KFileWidget::selectedUrl() const { if ( d->inAccept ) return d->url; else return KUrl(); } KUrl::List KFileWidget::selectedUrls() const { KUrl::List list; if ( d->inAccept ) { if ( (d->ops->mode() & KFile::Files) == KFile::Files ) list = d->parseSelectedUrls(); else list.append( d->url ); } return list; } KUrl::List& KFileWidgetPrivate::parseSelectedUrls() { if ( filenames.isEmpty() ) { return urlList; } urlList.clear(); if ( filenames.contains( '/' )) { // assume _one_ absolute filename static const QString &prot = KGlobal::staticQString(":/"); KUrl u; if ( filenames.indexOf( prot ) != -1 ) u = filenames; else u.setPath( filenames ); if ( u.isValid() ) urlList.append( u ); else KMessageBox::error( q, i18n("The chosen filenames do not\n" "appear to be valid."), i18n("Invalid Filenames") ); } else urlList = tokenize( filenames ); filenames.clear(); // indicate that we parsed that one return urlList; } // FIXME: current implementation drawback: a filename can't contain quotes KUrl::List KFileWidgetPrivate::tokenize( const QString& line ) const { KUrl::List urls; KUrl u( ops->url() ); QString name; const int count = line.count( QLatin1Char( '"' ) ); if ( count == 0 ) { // no " " -> assume one single file u.setFileName( line ); if ( u.isValid() ) urls.append( u ); return urls; } if ( (count % 2) == 1 ) { // odd number of " -> error KMessageBox::sorry(q, i18n("The requested filenames\n" "%1\n" "do not appear to be valid;\n" "make sure every filename is enclosed in double quotes.", line), i18n("Filename Error")); return urls; } int start = 0; int index1 = -1, index2 = -1; while ( true ) { index1 = line.indexOf( '"', start ); index2 = line.indexOf( '"', index1 + 1 ); if ( index1 < 0 ) break; // get everything between the " " name = line.mid( index1 + 1, index2 - index1 - 1 ); u.setFileName( name ); if ( u.isValid() ) urls.append( u ); start = index2 + 1; } return urls; } QString KFileWidget::selectedFile() const { if ( d->inAccept ) { const KUrl url = KIO::NetAccess::mostLocalUrl(d->url,topLevelWidget()); if (url.isLocalFile()) return url.path(); else { KMessageBox::sorry( const_cast(this), i18n("You can only select local files."), i18n("Remote Files Not Accepted") ); } } return QString(); } QStringList KFileWidget::selectedFiles() const { QStringList list; if ( d->inAccept ) { if ( (d->ops->mode() & KFile::Files) == KFile::Files ) { KUrl::List urls = d->parseSelectedUrls(); QList::const_iterator it = urls.begin(); while ( it != urls.end() ) { KUrl url = KIO::NetAccess::mostLocalUrl(*it,topLevelWidget()); if ( url.isLocalFile() ) list.append( url.path() ); ++it; } } else { // single-selection mode if ( d->url.isLocalFile() ) list.append( d->url.path() ); } } return list; } KUrl KFileWidget::baseUrl() const { return d->ops->url(); } void KFileWidget::showEvent(QShowEvent* event) { if ( !d->hasView ) { // delayed view-creation d->ops->setView(KFile::Default); d->ops->view()->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); d->ops->view()->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); d->hasView = true; } d->ops->clearHistory(); QList sizes = d->placesViewSplitter->sizes(); if (sizes.count() == 2) { // restore width of speedbar KConfigGroup configGroup( KGlobal::config(), ConfigGroup ); const int speedbarWidth = configGroup.readEntry( SpeedbarWidth, 100 ); const int availableWidth = sizes[0] + sizes[1]; sizes[0] = speedbarWidth; sizes[1] = availableWidth - speedbarWidth; d->placesViewSplitter->setSizes( sizes ); } QWidget::showEvent(event); } void KFileWidget::setMode( KFile::Modes m ) { d->ops->setMode(m); if ( d->ops->dirOnlyMode() ) { d->filterWidget->setDefaultFilter( i18n("*|All Folders") ); } else { d->filterWidget->setDefaultFilter( i18n("*|All Files") ); } d->updateAutoSelectExtension(); } KFile::Modes KFileWidget::mode() const { return d->ops->mode(); } void KFileWidgetPrivate::readConfig( const KConfigGroup &configGroup) { ops->readConfig(configGroup); KUrlComboBox *combo = urlNavigator->editor(); combo->setUrls( configGroup.readPathListEntry( RecentURLs ), KUrlComboBox::RemoveTop ); combo->setMaxItems( configGroup.readEntry( RecentURLsNumber, DefaultRecentURLsNumber ) ); combo->setUrl( ops->url() ); autoDirectoryFollowing = configGroup.readEntry( AutoDirectoryFollowing, DefaultDirectoryFollowing ); KGlobalSettings::Completion cm = (KGlobalSettings::Completion) configGroup.readEntry( PathComboCompletionMode, static_cast( KGlobalSettings::completionMode() ) ); if ( cm != KGlobalSettings::completionMode() ) combo->setCompletionMode( cm ); cm = (KGlobalSettings::Completion) configGroup.readEntry( LocationComboCompletionMode, static_cast( KGlobalSettings::completionMode() ) ); if ( cm != KGlobalSettings::completionMode() ) locationEdit->setCompletionMode( cm ); // show or don't show the speedbar q->toggleSpeedbar( configGroup.readEntry( ShowSpeedbar, true ) ); // show or don't show the bookmarks q->toggleBookmarks( configGroup.readEntry(ShowBookmarks, false) ); // does the user want Automatically Select Extension? autoSelectExtChecked = configGroup.readEntry (AutoSelectExtChecked, DefaultAutoSelectExtChecked); updateAutoSelectExtension(); // should the URL navigator use the breadcrumb navigation? urlNavigator->setUrlEditable( !configGroup.readEntry(BreadcrumbNavigation, true) ); int w1 = q->minimumSize().width(); int w2 = toolbar->sizeHint().width() + 10; if (w1 < w2) q->setMinimumWidth(w2); //restoreDialogSize( d->fileWidget->viewConfigGroup() ); } void KFileWidgetPrivate::writeConfig(KConfigGroup &configGroup) { KUrlComboBox *pathCombo = urlNavigator->editor(); configGroup.writePathEntry( RecentURLs, pathCombo->urls() ); //saveDialogSize( configGroup, KConfigBase::Persistent | KConfigBase::Global ); configGroup.writeEntry( PathComboCompletionMode, static_cast(pathCombo->completionMode()) ); configGroup.writeEntry( LocationComboCompletionMode, static_cast(locationEdit->completionMode()) ); const bool showSpeedbar = placesView && !placesView->isHidden(); configGroup.writeEntry( ShowSpeedbar, showSpeedbar ); if (showSpeedbar) { QList sizes = placesViewSplitter->sizes(); Q_ASSERT( sizes.count() > 0 ); configGroup.writeEntry( SpeedbarWidth, sizes[0] ); } configGroup.writeEntry( ShowSpeedbar, placesView && !placesView->isHidden() ); configGroup.writeEntry( ShowBookmarks, bookmarkHandler != 0 ); configGroup.writeEntry( AutoSelectExtChecked, autoSelectExtChecked ); configGroup.writeEntry( BreadcrumbNavigation, !urlNavigator->isUrlEditable() ); ops->writeConfig(configGroup); } void KFileWidgetPrivate::readRecentFiles( KConfig *kc ) { KConfigGroup cg( kc, ConfigGroup ); locationEdit->setMaxItems( cg.readEntry( RecentFilesNumber, DefaultRecentURLsNumber ) ); locationEdit->setUrls( cg.readPathListEntry( RecentFiles ), KUrlComboBox::RemoveBottom ); locationEdit->insertItem(0, QString()); // dummy item without pixmap locationEdit->setCurrentIndex( 0 ); } void KFileWidgetPrivate::saveRecentFiles( KConfig *kc ) { KConfigGroup cg(kc, ConfigGroup ); cg.writePathEntry( RecentFiles, locationEdit->urls() ); } KPushButton * KFileWidget::okButton() const { return d->okButton; } KPushButton * KFileWidget::cancelButton() const { return d->cancelButton; } // Called by KFileDialog void KFileWidget::slotCancel() { d->ops->close(); KSharedConfig::Ptr config = KGlobal::config(); config->setForceGlobal( true ); KConfigGroup grp(config,ConfigGroup); d->writeConfig(grp); config->setForceGlobal( false ); } void KFileWidget::setKeepLocation( bool keep ) { d->keepLocation = keep; } bool KFileWidget::keepsLocation() const { return d->keepLocation; } void KFileWidget::setOperationMode( OperationMode mode ) { d->operationMode = mode; d->keepLocation = (mode == Saving); d->filterWidget->setEditable( !d->hasDefaultFilter || mode != Saving ); if ( mode == Opening ) // don't use KStandardGuiItem::open() here which has trailing ellipsis! d->okButton->setGuiItem( KGuiItem( i18n( "&Open" ), "document-open") ); else if ( mode == Saving ) { d->okButton->setGuiItem( KStandardGuiItem::save() ); d->setNonExtSelection(); } else d->okButton->setGuiItem( KStandardGuiItem::ok() ); d->updateLocationWhatsThis(); d->updateAutoSelectExtension(); } KFileWidget::OperationMode KFileWidget::operationMode() const { return d->operationMode; } void KFileWidget::slotAutoSelectExtClicked() { kDebug (kfile_area) << "slotAutoSelectExtClicked(): " << d->autoSelectExtCheckBox->isChecked() << endl; // whether the _user_ wants it on/off d->autoSelectExtChecked = d->autoSelectExtCheckBox->isChecked(); // update the current filename's extension d->updateLocationEditExtension (d->extension /* extension hasn't changed */); } static QString getExtensionFromPatternList(const QStringList &patternList) { QString ret; kDebug (kfile_area) << "\tgetExtension " << patternList; QStringList::ConstIterator patternListEnd = patternList.end(); for (QStringList::ConstIterator it = patternList.begin(); it != patternListEnd; ++it) { kDebug (kfile_area) << "\t\ttry: \'" << (*it) << "\'"; // is this pattern like "*.BMP" rather than useless things like: // // README // *. // *.* // *.JP*G // *.JP? if ((*it).startsWith ("*.") && (*it).length() > 2 && (*it).indexOf('*', 2) < 0 && (*it).indexOf ('?', 2) < 0) { ret = (*it).mid (1); break; } } return ret; } static QString stripUndisplayable (const QString &string) { QString ret = string; ret.remove (':'); ret.remove ('&'); return ret; } //QString KFileWidget::currentFilterExtension() //{ // return d->extension; //} void KFileWidgetPrivate::updateAutoSelectExtension() { if (!autoSelectExtCheckBox) return; // // Figure out an extension for the Automatically Select Extension thing // (some Windows users apparently don't know what to do when confronted // with a text file called "COPYING" but do know what to do with // COPYING.txt ...) // kDebug (kfile_area) << "Figure out an extension: "; QString lastExtension = extension; extension.clear(); // Automatically Select Extension is only valid if the user is _saving_ a _file_ if ((operationMode == KFileWidget::Saving) && (ops->mode() & KFile::File)) { // // Get an extension from the filter // QString filter = filterWidget->currentFilter(); if (!filter.isEmpty()) { // e.g. "*.cpp" if (filter.indexOf ('/') < 0) { extension = getExtensionFromPatternList (filter.split(" ",QString::SkipEmptyParts)/*QStringList::split (" ", filter)*/).toLower(); kDebug (kfile_area) << "\tsetFilter-style: pattern ext=\'" << extension << "\'" << endl; } // e.g. "text/html" else { KMimeType::Ptr mime = KMimeType::mimeType (filter); if (mime) { // first try X-KDE-NativeExtension QString nativeExtension = mime->property ("X-KDE-NativeExtension").toString(); if (!nativeExtension.isEmpty() && nativeExtension.at (0) == '.') { extension = nativeExtension.toLower(); kDebug (kfile_area) << "\tsetMimeFilter-style: native ext=\'" << extension << "\'" << endl; } // no X-KDE-NativeExtension if (extension.isEmpty()) { extension = getExtensionFromPatternList (mime->patterns()).toLower(); kDebug (kfile_area) << "\tsetMimeFilter-style: pattern ext=\'" << extension << "\'" << endl; } } } } // // GUI: checkbox // QString whatsThisExtension; if (!extension.isEmpty()) { // remember: sync any changes to the string with below autoSelectExtCheckBox->setText (i18n ("Automatically select filename e&xtension (%1)", extension)); whatsThisExtension = i18n ("the extension %1", extension); autoSelectExtCheckBox->setEnabled (true); autoSelectExtCheckBox->setChecked (autoSelectExtChecked); } else { // remember: sync any changes to the string with above autoSelectExtCheckBox->setText (i18n ("Automatically select filename e&xtension")); whatsThisExtension = i18n ("a suitable extension"); autoSelectExtCheckBox->setChecked (false); autoSelectExtCheckBox->setEnabled (false); } const QString locationLabelText = stripUndisplayable (locationLabel->text()); const QString filterLabelText = stripUndisplayable (filterLabel->text()); autoSelectExtCheckBox->setWhatsThis( "" + i18n ( "This option enables some convenient features for " "saving files with extensions:
" "
    " "
  1. Any extension specified in the %1 text " "area will be updated if you change the file type " "to save in.
    " "
  2. " "
  3. If no extension is specified in the %2 " "text area when you click " "Save, %3 will be added to the end of the " "filename (if the filename does not already exist). " "This extension is based on the file type that you " "have chosen to save in.
    " "
    " "If you do not want KDE to supply an extension for the " "filename, you can either turn this option off or you " "can suppress it by adding a period (.) to the end of " "the filename (the period will be automatically " "removed)." "
  4. " "
" "If unsure, keep this option enabled as it makes your " "files more manageable." , locationLabelText, locationLabelText, whatsThisExtension) + "
" ); autoSelectExtCheckBox->show(); // update the current filename's extension updateLocationEditExtension (lastExtension); } // Automatically Select Extension not valid else { autoSelectExtCheckBox->setChecked (false); autoSelectExtCheckBox->hide(); } } // Updates the extension of the filename specified in d->locationEdit if the // Automatically Select Extension feature is enabled. // (this prevents you from accidently saving "file.kwd" as RTF, for example) void KFileWidgetPrivate::updateLocationEditExtension (const QString &lastExtension) { if (!autoSelectExtCheckBox->isChecked() || extension.isEmpty()) return; QString urlStr = locationEdit->currentText(); if (urlStr.isEmpty()) return; KUrl url = q->getCompleteUrl(urlStr); kDebug (kfile_area) << "updateLocationEditExtension (" << url << ")"; const int fileNameOffset = urlStr.lastIndexOf ('/') + 1; QString fileName = urlStr.mid (fileNameOffset); const int dot = fileName.lastIndexOf ('.'); const int len = fileName.length(); if (dot > 0 && // has an extension already and it's not a hidden file // like ".hidden" (but we do accept ".hidden.ext") dot != len - 1 // and not deliberately suppressing extension ) { // exists? KIO::UDSEntry t; if (KIO::NetAccess::stat (url, t, q->topLevelWidget())) { kDebug (kfile_area) << "\tfile exists"; if (t.isDir()) { kDebug (kfile_area) << "\tisDir - won't alter extension"; return; } // --- fall through --- } // // try to get rid of the current extension // // catch "double extensions" like ".tar.gz" if (lastExtension.length() && fileName.endsWith (lastExtension)) fileName.truncate (len - lastExtension.length()); // can only handle "single extensions" else fileName.truncate (dot); // add extension const QString newText = urlStr.left (fileNameOffset) + fileName + extension; if ( newText != locationEdit->currentText() ) { locationEdit->setItemText(locationEdit->currentIndex(),urlStr.left (fileNameOffset) + fileName + extension); locationEdit->lineEdit()->setModified (true); } } } // Updates the filter if the extension of the filename specified in d->locationEdit is changed // (this prevents you from accidently saving "file.kwd" as RTF, for example) void KFileWidgetPrivate::updateFilter() { if ((operationMode == KFileWidget::Saving) && (ops->mode() & KFile::File) ) { const QString urlStr = locationEdit->currentText(); if (urlStr.isEmpty()) return; KMimeType::Ptr mime = KMimeType::findByPath(urlStr, 0, true); if (mime && mime->name() != KMimeType::defaultMimeType()) { if (filterWidget->currentFilter() != mime->name() && filterWidget->filters().indexOf(mime->name()) != -1) filterWidget->setCurrentFilter(mime->name()); } } } // applies only to a file that doesn't already exist void KFileWidgetPrivate::appendExtension (KUrl &url) { if (!autoSelectExtCheckBox->isChecked() || extension.isEmpty()) return; QString fileName = url.fileName(); if (fileName.isEmpty()) return; kDebug (kfile_area) << "appendExtension(" << url << ")"; const int len = fileName.length(); const int dot = fileName.lastIndexOf ('.'); const bool suppressExtension = (dot == len - 1); const bool unspecifiedExtension = (dot <= 0); // don't KIO::NetAccess::Stat if unnecessary if (!(suppressExtension || unspecifiedExtension)) return; // exists? KIO::UDSEntry t; if (KIO::NetAccess::stat (url, t, q->topLevelWidget())) { kDebug (kfile_area) << "\tfile exists - won't append extension"; return; } // suppress automatically append extension? if (suppressExtension) { // // Strip trailing dot // This allows lazy people to have autoSelectExtCheckBox->isChecked // but don't want a file extension to be appended // e.g. "README." will make a file called "README" // // If you really want a name like "README.", then type "README.." // and the trailing dot will be removed (or just stop being lazy and // turn off this feature so that you can type "README.") // kDebug (kfile_area) << "\tstrip trailing dot"; url.setFileName (fileName.left (len - 1)); } // evilmatically append extension :) if the user hasn't specified one else if (unspecifiedExtension) { kDebug (kfile_area) << "\tappending extension \'" << extension << "\'..."; url.setFileName (fileName + extension); kDebug (kfile_area) << "\tsaving as \'" << url << "\'"; } } // adds the selected files/urls to 'recent documents' void KFileWidget::addToRecentDocuments() { int m = d->ops->mode(); if ( m & KFile::LocalOnly ) { QStringList files = selectedFiles(); QStringList::ConstIterator it = files.begin(); for ( ; it != files.end(); ++it ) KRecentDocument::add( *it ); } else { // urls KUrl::List urls = selectedUrls(); KUrl::List::ConstIterator it = urls.begin(); for ( ; it != urls.end(); ++it ) { if ( (*it).isValid() ) KRecentDocument::add( *it ); } } } KUrlComboBox* KFileWidget::locationEdit() const { return d->locationEdit; } KFileFilterCombo* KFileWidget::filterWidget() const { return d->filterWidget; } KActionCollection * KFileWidget::actionCollection() const { return d->ops->actionCollection(); } void KFileWidget::toggleSpeedbar( bool show ) { if ( show ) { if ( !d->placesView ) d->initSpeedbar(); d->placesView->show(); // check to see if they have a home item defined, if not show the home button KUrl homeURL; homeURL.setPath( QDir::homePath() ); KFilePlacesModel *model = static_cast(d->placesView->model()); for ( int rowIndex = 0 ; rowIndex < d->placesView->model()->rowCount() ; rowIndex++ ) { QModelIndex index = model->index(rowIndex, 0); KUrl url = model->url(index); if ( homeURL.equals( url, KUrl::CompareWithoutTrailingSlash ) ) { d->toolbar->removeAction( d->ops->actionCollection()->action( "home" ) ); break; } } } else { if (d->placesView) d->placesView->hide(); QAction* homeAction = d->ops->actionCollection()->action( "home" ); QAction* reloadAction = d->ops->actionCollection()->action( "reload" ); if ( !d->toolbar->actions().contains(homeAction) ) d->toolbar->insertAction( reloadAction, homeAction ); } static_cast(actionCollection()->action("toggleSpeedbar"))->setChecked( show ); } void KFileWidget::toggleBookmarks(bool show) { if (show) { if (d->bookmarkHandler) { return; } d->bookmarkHandler = new KFileBookmarkHandler( this ); connect( d->bookmarkHandler, SIGNAL( openUrl( const QString& )), SLOT( enterUrl( const QString& ))); d->bookmarkButton = new KActionMenu(KIcon("bookmark"),i18n("Bookmarks"), this); d->bookmarkButton->setDelayed(false); actionCollection()->addAction("bookmark", d->bookmarkButton); d->bookmarkButton->setMenu(d->bookmarkHandler->menu()); d->bookmarkButton->setWhatsThis(i18n("This button allows you to bookmark specific locations. " "Click on this button to open the bookmark menu where you may add, " "edit or select a bookmark.

" "These bookmarks are specific to the file dialog, but otherwise operate " "like bookmarks elsewhere in KDE.
")); d->toolbar->addAction(d->bookmarkButton); } else if (d->bookmarkHandler) { delete d->bookmarkHandler; d->bookmarkHandler = 0; delete d->bookmarkButton; d->bookmarkButton = 0; } static_cast(actionCollection()->action("toggleBookmarks"))->setChecked( show ); } // static KUrl KFileWidget::getStartUrl( const KUrl& startDir, QString& recentDirClass ) { recentDirClass.clear(); KUrl ret; bool useDefaultStartDir = startDir.isEmpty(); if ( !useDefaultStartDir ) { if (startDir.protocol() == "kfiledialog") { if ( startDir.query() == "?global" ) recentDirClass = QString( "::%1" ).arg( startDir.path().mid( 1 ) ); else recentDirClass = QString( ":%1" ).arg( startDir.path().mid( 1 ) ); ret = KUrl( KRecentDirs::dir(recentDirClass) ); } else { ret = startDir; // If we won't be able to list it (e.g. http), then use default if ( !KProtocolManager::supportsListing( ret ) ) useDefaultStartDir = true; } } if ( useDefaultStartDir ) { if (lastDirectory->isEmpty()) { lastDirectory->setPath(KGlobalSettings::documentPath()); KUrl home; home.setPath( QDir::homePath() ); // if there is no docpath set (== home dir), we prefer the current // directory over it. We also prefer the homedir when our CWD is // different from our homedirectory or when the document dir // does not exist if ( lastDirectory->path(KUrl::AddTrailingSlash) == home.path(KUrl::AddTrailingSlash) || QDir::currentPath() != QDir::homePath() || !QDir(lastDirectory->path(KUrl::AddTrailingSlash)).exists() ) lastDirectory->setPath(QDir::currentPath()); } ret = *lastDirectory; } return ret; } void KFileWidget::setStartDir( const KUrl& directory ) { if ( directory.isValid() ) *lastDirectory = directory; } void KFileWidgetPrivate::setNonExtSelection() { // Enhanced rename: Don't highlight the file extension. QString filename = locationEdit->currentText().trimmed(); QString extension = KMimeType::extractKnownExtension( filename ); if ( !extension.isEmpty() ) locationEdit->lineEdit()->setSelection( 0, filename.length() - extension.length() - 1 ); else { int lastDot = filename.lastIndexOf( '.' ); if ( lastDot > 0 ) locationEdit->lineEdit()->setSelection( 0, lastDot ); } } KToolBar * KFileWidget::toolBar() const { return d->toolbar; } void KFileWidget::setCustomWidget(QWidget* widget) { delete d->bottomCustomWidget; d->bottomCustomWidget = widget; // add it to the dialog, below the filter list box. // Change the parent so that this widget is a child of the main widget d->bottomCustomWidget->setParent( this ); d->vbox->addWidget( d->bottomCustomWidget ); //d->vbox->addSpacing(3); // can't do this every time... // FIXME: This should adjust the tab orders so that the custom widget // comes after the Cancel button. The code appears to do this, but the result // somehow screws up the tab order of the file path combo box. Not a major // problem, but ideally the tab order with a custom widget should be // the same as the order without one. setTabOrder(d->cancelButton, d->bottomCustomWidget); setTabOrder(d->bottomCustomWidget, d->urlNavigator); } void KFileWidget::setCustomWidget(const QString& text, QWidget* widget) { delete d->labeledCustomWidget; d->labeledCustomWidget = widget; QLabel* label = new QLabel(text, this); d->lafBox->addWidget(label, 2, 0, Qt::AlignVCenter); d->lafBox->addWidget(widget, 2, 1, Qt::AlignVCenter); } void KFileWidget::virtual_hook( int id, void* data ) { Q_UNUSED(id); Q_UNUSED(data); } #include "kfilewidget.moc" diff --git a/kfile/kfilewidget.h b/kfile/kfilewidget.h index 50b5c2d4d6..d3b19c8102 100644 --- a/kfile/kfilewidget.h +++ b/kfile/kfilewidget.h @@ -1,475 +1,475 @@ // -*- c++ -*- /* This file is part of the KDE libraries Copyright (C) 1997, 1998 Richard Moore 1998 Stephan Kulow 1998 Daniel Grana 2000,2001 Carsten Pfeiffer 2001 Frerich Raabe 2007 David Faure 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 KFILEWIDGET_H #define KFILEWIDGET_H #include "kfile_export.h" #include "kabstractfilewidget.h" #include class KJob; class KFileItem; class KFILE_EXPORT KFileWidget : public QWidget, public KAbstractFileWidget { Q_OBJECT Q_INTERFACES(KAbstractFileWidget) public: /** * Constructs a file selector widget. * * @param startDir This can either be * @li The URL of the directory to start in. * @li A KUrl() to start in the current working * directory, or the last directory where a file has been * selected. * @li An URL starting with 'kfiledialog:///<keyword>' to start in the * directory last used by a filedialog in the same application that specified * the same keyword. * @li An URL starting with 'kfiledialog:///<keyword>?global' to start * in the directory last used by a filedialog in any application that specified the * same keyword. * * @param filter A shell glob or a mime-type-filter that specifies * which files to display. * See setFilter() and setMimeFilter() for details on how to use this argument. * * @param parent The parent widget of this widget * * @param widget A widget, or a widget of widgets, for displaying custom * data in the file widget. This can be used, for example, to * display a check box with the caption "Open as read-only". * When creating this widget, you don't need to specify a parent, * since the widget's parent will be set automatically by KFileWidget. */ KFileWidget(const KUrl& startDir, QWidget *parent); /** * Destructor */ virtual ~KFileWidget(); /** * @returns The selected fully qualified filename. */ virtual KUrl selectedUrl() const; /** * @returns The list of selected URLs. */ virtual KUrl::List selectedUrls() const; /** * @returns the currently shown directory. */ virtual KUrl baseUrl() const; /** * Returns the full path of the selected file in the local filesystem. * (Local files only) */ virtual QString selectedFile() const; /** * Returns a list of all selected local files. */ virtual QStringList selectedFiles() const; /** * Sets the directory to view. * * @param url URL to show. * @param clearforward Indicates whether the forward queue * should be cleared. */ virtual void setUrl(const KUrl &url, bool clearforward = true); /** * Sets the file name to preselect to @p name * * This takes absolute URLs and relative file names. */ virtual void setSelection(const QString& name); /** * Sets the operational mode of the filedialog to @p Saving, @p Opening * or @p Other. This will set some flags that are specific to loading * or saving files. E.g. setKeepLocation() makes mostly sense for * a save-as dialog. So setOperationMode( KFileWidget::Saving ); sets * setKeepLocation for example. * * The mode @p Saving, together with a default filter set via * setMimeFilter() will make the filter combobox read-only. * * The default mode is @p Opening. * * Call this method right after instantiating KFileWidget. * * @see operationMode * @see KFileWidget::OperationMode */ virtual void setOperationMode( OperationMode ); /** * @returns the current operation mode, Opening, Saving or Other. Default * is Other. * * @see operationMode * @see KFileWidget::OperationMode */ virtual OperationMode operationMode() const; /** * Sets whether the filename/url should be kept when changing directories. * This is for example useful when having a predefined filename where * the full path for that file is searched. * * This is implicitly set when operationMode() is KFileWidget::Saving * * getSaveFileName() and getSaveUrl() set this to true by default, so that * you can type in the filename and change the directory without having * to type the name again. */ virtual void setKeepLocation( bool keep ); /** * @returns whether the contents of the location edit are kept when * changing directories. */ virtual bool keepsLocation() const; /** * Sets the filter to be used to @p filter. * * You can set more * filters for the user to select separated by '\n'. Every * filter entry is defined through namefilter|text to diplay. * If no | is found in the expression, just the namefilter is * shown. Examples: * * \code * kfile->setFilter("*.cpp|C++ Source Files\n*.h|Header files"); * kfile->setFilter("*.cpp"); * kfile->setFilter("*.cpp|Sources (*.cpp)"); * kfile->setFilter("*.cpp|" + i18n("Sources (*.cpp)")); * kfile->setFilter("*.cpp *.cc *.C|C++ Source Files\n*.h *.H|Header files"); * \endcode * * Note: The text to display is not parsed in any way. So, if you * want to show the suffix to select by a specific filter, you must * repeat it. * * If the filter contains an unescaped '/', a mimetype-filter is assumed. * If you would like a '/' visible in your filter it can be escaped with * a '\'. You can specify multiple mimetypes like this (separated with * space): * * \code * kfile->setFilter( "image/png text/html text/plain" ); * kfile->setFilter( "*.cue|CUE\\/BIN Files (*.cue)" ); * \endcode * * @see filterChanged * @see setMimeFilter */ virtual void setFilter(const QString& filter); /** * Returns the current filter as entered by the user or one of the * predefined set via setFilter(). * * @see setFilter() * @see filterChanged() */ virtual QString currentFilter() const; /** * Returns the mimetype for the desired output format. * * This is only valid if setFilterMimeType() has been called * previously. * * @see setFilterMimeType() */ virtual KMimeType::Ptr currentFilterMimeType(); /** * Sets the filter up to specify the output type. * * @param types a list of mimetypes that can be used as output format * @param defaultType the default mimetype to use as output format, if any. * If @p defaultType is set, it will be set as the current item. * Otherwise, a first item showing all the mimetypes will be created. * Typically, @p defaultType should be empty for loading and set for saving. * * Do not use in conjunction with setFilter() */ virtual void setMimeFilter( const QStringList& types, const QString& defaultType = QString() ); /** * The mimetype for the desired output format. * * This is only valid if setMimeFilter() has been called * previously. * * @see setMimeFilter() */ virtual QString currentMimeFilter() const; /** * Clears any mime- or namefilter. Does not reload the directory. */ virtual void clearFilter(); /** * Adds a preview widget and enters the preview mode. * * In this mode the dialog is split and the right part contains your * preview widget. * * Ownership is transferred to KFileWidget. You need to create the * preview-widget with "new", i.e. on the heap. * * @param w The widget to be used for the preview. */ virtual void setPreviewWidget(KPreviewWidgetBase *w); /** * Sets the mode of the dialog. * * The mode is defined as (in kfile.h): * \code * enum Mode { * File = 1, * Directory = 2, * Files = 4, * ExistingOnly = 8, * LocalOnly = 16 * }; * \endcode * You can OR the values, e.g. * \code * KFile::Modes mode = KFile::Files | * KFile::ExistingOnly | * KFile::LocalOnly ); * setMode( mode ); * \endcode */ virtual void setMode( KFile::Modes m ); /** * Returns the mode of the filedialog. * @see setMode() */ virtual KFile::Modes mode() const; /** * Sets the text to be displayed in front of the selection. * * The default is "Location". * Most useful if you want to make clear what * the location is used for. */ virtual void setLocationLabel(const QString& text); /** * Returns a pointer to the toolbar. * * You can use this to insert custom * items into it, e.g.: * \code * yourAction = new KAction( i18n("Your Action"), 0, * this, SLOT( yourSlot() ), * this, "action name" ); * yourAction->plug( kfileDialog->toolBar() ); * \endcode */ KToolBar *toolBar() const; /** * @returns a pointer to the OK-Button in the filedialog. * Note that the button is hidden and unconnected when using KFileWidget alone; * KFileDialog shows it and connects to it. */ KPushButton *okButton() const; /** * @returns a pointer to the Cancel-Button in the filedialog. * Note that the button is hidden and unconnected when using KFileWidget alone; * KFileDialog shows it and connects to it. */ KPushButton *cancelButton() const; /** * @returns the combobox used to type the filename or full location of the file. */ KUrlComboBox *locationEdit() const; /** * @returns the combobox that contains the filters */ KFileFilterCombo *filterWidget() const; /** * @returns a pointer to the action collection, holding all the used * KActions. */ KActionCollection *actionCollection() const; /** * This method implements the logic to determine the user's default directory * to be listed. E.g. the documents directory, home directory or a recently * used directory. * @param startDir A url, to be used. May use the 'kfiledialog:///keyword' and * 'kfiledialog:///keyword?global' syntax * as documented in the KFileDialog() constructor. * @param recentDirClass If the 'kfiledialog:///' syntax is used, recentDirClass * will contain the string to be used later for KRecentDir::dir() * @return The URL that should be listed by default (e.g. by KFileDialog or * KDirSelectDialog). */ static KUrl getStartUrl( const KUrl& startDir, QString& recentDirClass ); /** * @internal * Used by KDirSelectDialog to share the dialog's start directory. */ static void setStartDir( const KUrl& directory ); /** * Set a custom widget that should be added to the file dialog. * @param widget A widget, or a widget of widgets, for displaying custom * data in the file widget. This can be used, for example, to * display a check box with the caption "Open as read-only". * When creating this widget, you don't need to specify a parent, * since the widget's parent will be set automatically by KFileWidget. */ virtual void setCustomWidget(QWidget* widget); /** * Sets a custom widget that should be added below the location and the filter * editors. * @param text Label of the custom widget, which is displayed below the labels * "Location:" and "Filter:". * @param widget Any kind of widget, but preferable a combo box or a line editor * to be compliant with the location and filter layout. * When creating this widget, you don't need to specify a parent, * since the widget's parent will be set automatically by KFileWidget. */ virtual void setCustomWidget(const QString& text, QWidget* widget); /// @internal for future extensions virtual void virtual_hook( int id, void* data ); public Q_SLOTS: /** * Called when clicking ok (when this widget is used in KFileDialog) * Might or might not call accept(). */ virtual void slotOk(); virtual void accept(); virtual void slotCancel(); protected: virtual void showEvent(QShowEvent* event); Q_SIGNALS: /** * Emitted when the user selects a file. It is only emitted in single- * selection mode. The best way to get notified about selected file(s) * is to connect to the okClicked() signal inherited from KDialog * and call selectedFile(), selectedFiles(), * selectedUrl() or selectedUrls(). */ void fileSelected(const QString&); /** * Emitted when the user highlights a file. */ void fileHighlighted(const QString&); /** * Emitted when the user hilights one or more files in multiselection mode. * * Note: fileHighlighted() or fileSelected() are @em not * emitted in multiselection mode. You may use selectedItems() to * ask for the current highlighted items. * @see fileSelected */ void selectionChanged(); /** * Emitted when the filter changed, i.e. the user entered an own filter * or chose one of the predefined set via setFilter(). * * @param filter contains the new filter (only the extension part, * not the explanation), i.e. "*.cpp" or "*.cpp *.cc". * * @see setFilter() * @see currentFilter() */ void filterChanged( const QString& filter ); /** * Emitted by slotOk() (directly or asynchronously) once everything has * been done. Should be used by the caller to call accept(). */ void accepted(); private Q_SLOTS: void slotLocationChanged( const QString& text ); void urlEntered( const KUrl& ); void enterUrl( const KUrl& url ); void enterUrl( const QString& url ); void locationAccepted( const QString& url ); void slotFilterChanged(); - void fileHighlighted(const KFileItem *i); - void fileSelected(const KFileItem *i); + void fileHighlighted(const KFileItem &i); + void fileSelected(const KFileItem &i); void slotStatResult(KJob* job); void slotLoadingFinished(); void fileCompletion( const QString& ); void toggleSpeedbar( bool ); void toggleBookmarks(bool show); void slotAutoSelectExtClicked(); void addToRecentDocuments(); private: /** * Returns the absolute version of the URL specified in locationEdit. */ KUrl getCompleteUrl(const QString&); private: friend class KFileWidgetPrivate; KFileWidgetPrivate* const d; }; #endif /* KABSTRACTFILEWIDGET_H */ diff --git a/kio/kfile/kfilesharedialog.cpp b/kio/kfile/kfilesharedialog.cpp index 1dc0bed06f..aa393f0684 100644 --- a/kio/kfile/kfilesharedialog.cpp +++ b/kio/kfile/kfilesharedialog.cpp @@ -1,294 +1,294 @@ /* This file is part of the KDE project Copyright (c) 2001 David Faure Copyright (c) 2001 Laurent Montel 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 "kfilesharedialog.h" #include "kfsprocess.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class KFileSharePropsPlugin::Private { public: KVBox *m_vBox; KfsProcess *m_configProc; bool m_bAllShared; bool m_bAllUnshared; QWidget *m_widget; QRadioButton *m_rbShare; QRadioButton *m_rbUnShare; QPushButton *m_pbConfig; }; KFileSharePropsPlugin::KFileSharePropsPlugin( KPropertiesDialog *_props ) : KPropertiesDialogPlugin( _props ),d(new Private) { d->m_vBox = new KVBox(); _props->addPage( d->m_vBox, i18n("&Share") ); d->m_configProc = 0; properties->setFileSharingPage(d->m_vBox); d->m_widget = 0L; init(); } KFileSharePropsPlugin::~KFileSharePropsPlugin() { if (d->m_configProc) d->m_configProc->detach(); // Detach to prevent that we kill the process delete d; } bool KFileSharePropsPlugin::supports( const KFileItemList& items ) { // Do not show dialog if in advanced mode, // because the advanced dialog is shown already. if (KFileShare::shareMode() == KFileShare::Advanced) { kDebug() << "KFileSharePropsPlugin::supports: false because sharemode is advanced"; return false; } KFileItemList::const_iterator kit = items.begin(); const KFileItemList::const_iterator kend = items.end(); for ( ; kit != kend; ++kit ) { - bool isLocal = (*kit)->isLocalFile(); + bool isLocal = (*kit).isLocalFile(); // We only support local dirs - if ( !(*kit)->isDir() || !isLocal ) + if ( !(*kit).isDir() || !isLocal ) return false; } return true; } void KFileSharePropsPlugin::init() { // We store the main widget, so that it's possible (later) to call init() // more than once, to update the page if something changed (e.g. after // the user has been authorized) delete d->m_widget; d->m_rbShare = 0L; d->m_rbUnShare = 0L; d->m_widget = new QWidget( d->m_vBox ); QVBoxLayout * vbox = new QVBoxLayout( d->m_widget ); switch ( KFileShare::authorization() ) { case KFileShare::Authorized: { // Check if all selected dirs are in $HOME QString home = QDir::homePath(); if ( home[home.length()-1] != '/' ) home += '/'; bool ok = true; KFileItemList items = properties->items(); // We have 3 possibilities: all shared, all unshared, or mixed. d->m_bAllShared = true; d->m_bAllUnshared = true; KFileItemList::const_iterator kit = items.begin(); const KFileItemList::const_iterator kend = items.end(); for ( ; kit != kend && ok; ++kit ) { // We know it's local, see supports() - const QString path = (*kit)->url().path(); + const QString path = (*kit).url().path(); if ( !path.startsWith( home ) ) ok = false; if ( KFileShare::isDirectoryShared( path ) ) d->m_bAllUnshared = false; else d->m_bAllShared = false; } if ( !ok ) { vbox->addWidget( new QLabel( i18n( "Only folders in your home folder can be shared."), d->m_widget ), 0 ); } else { // Everything ok, show the share/unshare GUI vbox->setSpacing( KDialog::spacingHint() ); vbox->setMargin( KDialog::marginHint() ); QButtonGroup *rbGroup = new QButtonGroup( d->m_widget ); d->m_rbUnShare = new QRadioButton( i18n("Not shared"), d->m_widget ); connect( d->m_rbUnShare, SIGNAL( toggled(bool) ), SIGNAL( changed() ) ); vbox->addWidget( d->m_rbUnShare, 0 ); rbGroup->addButton( d->m_rbUnShare ); d->m_rbShare = new QRadioButton( i18n("Shared"), d->m_widget ); connect( d->m_rbShare, SIGNAL( toggled(bool) ), SIGNAL( changed() ) ); vbox->addWidget( d->m_rbShare, 0 ); rbGroup->addButton( d->m_rbShare ); // Activate depending on status if ( d->m_bAllShared ) d->m_rbShare->setChecked(true); if ( d->m_bAllUnshared ) d->m_rbUnShare->setChecked(true); // Some help text QLabel *label = new QLabel( i18n("Sharing this folder makes it available under Linux/UNIX (NFS) and Windows (Samba).") , d->m_widget ); label->setAlignment( Qt::AlignLeft | Qt::AlignVCenter); label->setWordWrap(true); vbox->addWidget( label, 0 ); KSeparator* sep=new KSeparator(d->m_widget); vbox->addWidget( sep, 0 ); label = new QLabel( i18n("You can also reconfigure file sharing authorization.") , d->m_widget ); label->setAlignment( Qt::AlignLeft | Qt::AlignVCenter); label->setWordWrap(true); vbox->addWidget( label, 0 ); d->m_pbConfig = new QPushButton( i18n("Configure File Sharing..."), d->m_widget ); connect( d->m_pbConfig, SIGNAL( clicked() ), SLOT( slotConfigureFileSharing() ) ); vbox->addWidget( d->m_pbConfig, 0, Qt::AlignHCenter ); vbox->addStretch( 10 ); } } break; case KFileShare::ErrorNotFound: vbox->addWidget( new QLabel( i18n("Error running 'filesharelist'. Check if installed and in $PATH or /usr/sbin."), d->m_widget ), 0 ); break; case KFileShare::UserNotAllowed: { vbox->setSpacing( 10 ); if (KFileShare::sharingEnabled()) { vbox->addWidget( new QLabel( i18n("You need to be authorized to share folders."), d->m_widget ), 0 ); } else { vbox->addWidget( new QLabel( i18n("File sharing is disabled."), d->m_widget ), 0 ); } QHBoxLayout* hBox = new QHBoxLayout( (QWidget *)0L ); vbox->addLayout( hBox, 0 ); d->m_pbConfig = new QPushButton( i18n("Configure File Sharing..."), d->m_widget ); connect( d->m_pbConfig, SIGNAL( clicked() ), SLOT( slotConfigureFileSharing() ) ); hBox->addWidget( d->m_pbConfig, 0, Qt::AlignHCenter ); vbox->addStretch( 10 ); // align items on top break; } case KFileShare::NotInitialized: kWarning() << "KFileShare Authorization still NotInitialized after calling authorization() - impossible"; break; } d->m_widget->show(); // In case the dialog was shown already. } void KFileSharePropsPlugin::slotConfigureFileSharing() { if (d->m_configProc) return; d->m_configProc = new KfsProcess(this); (*d->m_configProc) << KStandardDirs::findExe("kdesu") << "kcmshell" << "fileshare"; if (!d->m_configProc->start()) { delete d->m_configProc; d->m_configProc = 0; return; } connect(d->m_configProc, SIGNAL(processExited()), this, SLOT(slotConfigureFileSharingDone())); d->m_pbConfig->setEnabled(false); } void KFileSharePropsPlugin::slotConfigureFileSharingDone() { delete d->m_configProc; d->m_configProc = 0; KFileShare::readConfig(); KFileShare::readShareList(); init(); } void KFileSharePropsPlugin::applyChanges() { kDebug() << "KFileSharePropsPlugin::applyChanges"; if ( d->m_rbShare && d->m_rbUnShare ) { bool share = d->m_rbShare->isChecked(); if (share && d->m_bAllShared) return; // Nothing to do if (!share && d->m_bAllUnshared) return; // Nothing to do const KFileItemList items = properties->items(); bool ok = true; KFileItemList::const_iterator kit = items.begin(); const KFileItemList::const_iterator kend = items.end(); for ( ; kit != kend && ok; ++kit ) { - const QString path = (*kit)->url().path(); + const QString path = (*kit).url().path(); ok = setShared( path, share ); if (!ok) { if (share) KMessageBox::detailedError(properties, i18n("Sharing folder '%1' failed.", path), i18n("An error occurred while trying to share folder '%1'. " "Make sure that the Perl script 'fileshareset' is set suid root.", path)); else KMessageBox::error(properties, i18n("Unsharing folder '%1' failed.", path), i18n("An error occurred while trying to unshare folder '%1'. " "Make sure that the Perl script 'fileshareset' is set suid root.", path)); properties->abortApplying(); break; } } // Get the change back into our cached info KFileShare::readShareList(); } } bool KFileSharePropsPlugin::setShared( const QString& path, bool shared ) { kDebug() << "KFileSharePropsPlugin::setShared " << path << "," << shared; return KFileShare::setShared( path, shared ); } QWidget* KFileSharePropsPlugin::page() const { return d->m_vBox; } #include "kfilesharedialog.moc" //TODO: do we need to monitor /etc/security/fileshare.conf ? // if the user is added to the 'fileshare' group, we wouldn't be notified // Of course the config module can notify us. // TODO: listen to such notifications ;) diff --git a/kio/kfile/kmetaprops.cpp b/kio/kfile/kmetaprops.cpp index 135517a31b..cad28ee909 100644 --- a/kio/kfile/kmetaprops.cpp +++ b/kio/kfile/kmetaprops.cpp @@ -1,236 +1,236 @@ /* This file is part of the KDE libraries Copyright (C) 2001,2002 Rolf Magnus 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 "kmetaprops.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class KFileMetaPropsPlugin::KFileMetaPropsPluginPrivate { public: KFileMetaPropsPluginPrivate() {} ~KFileMetaPropsPluginPrivate() {} QWidget* m_frame; QGridLayout* m_framelayout; KFileMetaInfo m_info; // QPushButton* m_add; QList m_editWidgets; }; KFileMetaPropsPlugin::KFileMetaPropsPlugin(KPropertiesDialog* props) : KPropertiesDialogPlugin(props),d(new KFileMetaPropsPluginPrivate) { - KFileItem * fileitem = properties->item(); + KFileItem &fileitem = properties->item(); kDebug(250) << "KFileMetaPropsPlugin constructor"; - d->m_info = fileitem->metaInfo(); + d->m_info = fileitem.metaInfo(); if (!d->m_info.isValid()) { d->m_info = KFileMetaInfo(properties->kurl().path(KUrl::RemoveTrailingSlash)); - fileitem->setMetaInfo(d->m_info); + fileitem.setMetaInfo(d->m_info); } if ( properties->items().count() > 1 ) { // not yet supported // we should allow setting values for a list of files. Itt makes sense // in some cases, like the album of a list of mp3s return; } createLayout(); setDirty(true); } void KFileMetaPropsPlugin::createLayout() { - QFileInfo file_info(properties->item()->url().path()); + QFileInfo file_info(properties->item().url().path()); kDebug(250) << "KFileMetaPropsPlugin::createLayout"; // is there any valid and non-empty info at all? if ( !d->m_info.isValid() ) return; // now get a list of groups //KFileMetaInfoProvider* prov = KFileMetaInfoProvider::self(); KFileMetaInfoGroupList groupList = d->m_info.preferredGroups(); if (groupList.isEmpty()) return; QScrollArea* scrollArea = new QScrollArea; scrollArea->setFrameStyle(QFrame::NoFrame); properties->addPage(scrollArea, i18n("&Meta Info")); d->m_frame = new QWidget(scrollArea); scrollArea->setWidget(d->m_frame); scrollArea->setWidgetResizable(true); QVBoxLayout *toplayout = new QVBoxLayout(d->m_frame); toplayout->setSpacing(KDialog::spacingHint()); foreach (const KFileMetaInfoGroup& group, groupList) { //kDebug(7033) << *git; KFileMetaInfoItemList itemList = group.items(); if (itemList.isEmpty()) continue; QGroupBox *groupBox = new QGroupBox( Qt::escape(group.name()), d->m_frame); QGridLayout *grouplayout = new QGridLayout(groupBox); grouplayout->activate(); toplayout->addWidget(groupBox); KFileMetaInfoItemList readItems; KFileMetaInfoItemList editItems; foreach (const KFileMetaInfoItem& item, itemList) { if ( !item.isValid() ) continue; bool editable = file_info.isWritable() && item.isEditable(); if (editable) editItems.append( item ); else readItems.append( item ); } KFileMetaInfoWidget* w = 0L; int row = 0; // then first add the editable items to the layout foreach (const KFileMetaInfoItem& item, editItems) { QLabel* l = new QLabel(item.name() + ':', groupBox); grouplayout->addWidget(l, row, 0); l->setAlignment( Qt::AlignLeft | Qt::AlignTop ); QValidator* val = item.properties().createValidator(); if (!val) kDebug(7033) << "didn't get a validator for " << item.name() << endl; w = new KFileMetaInfoWidget(item, val, groupBox); grouplayout->addWidget(w, row, 1); d->m_editWidgets.append( w ); connect(w, SIGNAL(valueChanged(const QVariant&)), this, SIGNAL(changed())); ++row; } // and then the read only items foreach (const KFileMetaInfoItem& item, readItems) { QLabel* l = new QLabel(item.name() + ':', groupBox); grouplayout->addWidget(l, row, 0); l->setAlignment( Qt::AlignLeft | Qt::AlignTop ); w = new KFileMetaInfoWidget(item, KFileMetaInfoWidget::ReadOnly, 0L, groupBox); grouplayout->addWidget(w, row, 1); ++row; } } toplayout->addStretch(1); // the add key (disabled until fully implemented) /* d->m_add = new QPushButton(i18n("&Add"), topframe); d->m_add->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); connect(d->m_add, SIGNAL(clicked()), this, SLOT(slotAdd())); tmp->addWidget(d->m_add); // if nothing can be added, deactivate it if ( !d->m_info.supportsVariableKeys() ) { // if supportedKeys() does contain anything not in preferredKeys, // we have something addable QStringList sk = d->m_info.supportedKeys(); d->m_add->setEnabled(false); for (QStringList::Iterator it = sk.begin(); it!=sk.end(); ++it) { if ( l.find(*it)==l.end() ) { d->m_add->setEnabled(true); kDebug(250) << "**first addable key is " << (*it).toLatin1().constData() << "**"; break; } kDebug(250) << "**already existing key is " << (*it).toLatin1().constData() << "**"; } } */ } /*void KFileMetaPropsPlugin::slotAdd() { // add a lineedit for the name // insert the item in the list }*/ KFileMetaPropsPlugin::~KFileMetaPropsPlugin() { delete d; } bool KFileMetaPropsPlugin::supports( const KFileItemList& _items ) { kDebug() ; // TODO: Add support for more than one item // TODO check that KDesktopPropsPlugin is correct, i.e. that we never want metainfo for // a .desktop file? Used to be that only Application desktop files were filtered out if (KDesktopPropsPlugin::supports(_items) || KUrlPropsPlugin::supports(_items)) return false; // Having both is redundant. if (_items.count() != 1) return false; - bool metaDataEnabled = KGlobalSettings::showFilePreview(_items.first()->url()); + bool metaDataEnabled = KGlobalSettings::showFilePreview(_items.first().url()); kDebug() << "metaDataEnabled=" << metaDataEnabled; return metaDataEnabled; } void KFileMetaPropsPlugin::applyChanges() { kDebug(250) << "applying changes"; // insert the fields that changed into the info object foreach(KFileMetaInfoWidget* w, d->m_editWidgets) w->apply(); d->m_info.applyChanges(); } #include "kmetaprops.moc" diff --git a/kio/kfile/kpreviewprops.cpp b/kio/kfile/kpreviewprops.cpp index f8f222ee16..7d988b203f 100644 --- a/kio/kfile/kpreviewprops.cpp +++ b/kio/kfile/kpreviewprops.cpp @@ -1,89 +1,89 @@ /* This file is part of the KDE libraries Copyright (C) 2005 Stephan Binner 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 "kpreviewprops.h" #include #include #include #include class KPreviewPropsPlugin::KPreviewPropsPluginPrivate { public: KPreviewPropsPluginPrivate() {} ~KPreviewPropsPluginPrivate() {} }; KPreviewPropsPlugin::KPreviewPropsPlugin(KPropertiesDialog* props) : KPropertiesDialogPlugin(props),d(new KPreviewPropsPluginPrivate) { if (properties->items().count()>1) return; createLayout(); } void KPreviewPropsPlugin::createLayout() { // let the dialog create the page frame QFrame* topframe = new QFrame(); properties->addPage(topframe, i18n("P&review")); topframe->setFrameStyle(QFrame::NoFrame); QVBoxLayout* tmp = new QVBoxLayout(topframe); tmp->setMargin(0); tmp->setSpacing(0); preview = new KFileMetaPreview(topframe); tmp->addWidget(preview) ; connect( properties, SIGNAL( currentPageChanged( KPageWidgetItem *, KPageWidgetItem * ) ), SLOT( currentPageChanged( KPageWidgetItem *, KPageWidgetItem * ) ) ); } KPreviewPropsPlugin::~KPreviewPropsPlugin() { delete d; } -bool KPreviewPropsPlugin::supports( KFileItemList _items ) +bool KPreviewPropsPlugin::supports( const KFileItemList &_items ) { - bool metaDataEnabled = KGlobalSettings::showFilePreview(_items.first()->url()); - KMimeType::Ptr mt = KMimeType::findByUrl( _items.first()->url() ); + bool metaDataEnabled = KGlobalSettings::showFilePreview(_items.first().url()); + KMimeType::Ptr mt = KMimeType::findByUrl( _items.first().url() ); if ( _items.count() != 1 || !metaDataEnabled || mt->name() == "inode/directory" || mt->name().startsWith( "media/" ) ) return false; //TODO Copy everything of KFileMetaPreview::previewProviderFor() ? return true; } void KPreviewPropsPlugin::currentPageChanged( KPageWidgetItem *current, KPageWidgetItem * ) { if ( current->widget() != preview->parent() ) return; disconnect( properties, SIGNAL( currentPageChanged( KPageWidgetItem *, KPageWidgetItem * ) ), this, SLOT( currentPageChanged( KPageWidgetItem *, KPageWidgetItem * ) ) ); - preview->showPreview(properties->item()->url()); + preview->showPreview(properties->item().url()); } #include "kpreviewprops.moc" diff --git a/kio/kfile/kpreviewprops.h b/kio/kfile/kpreviewprops.h index 86bd7503fc..8a974da8e4 100644 --- a/kio/kfile/kpreviewprops.h +++ b/kio/kfile/kpreviewprops.h @@ -1,56 +1,56 @@ /* This file is part of the KDE libraries Copyright (C) 2005 Stephan Binner 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 __KPREVIEWPROPS_H__ #define __KPREVIEWPROPS_H__ #include class KFileMetaPreview; /*! * PreviewProps plugin * This plugin displays a preview of the given file */ class KIO_EXPORT KPreviewPropsPlugin : public KPropertiesDialogPlugin { Q_OBJECT public: KPreviewPropsPlugin( KPropertiesDialog *_props ); virtual ~KPreviewPropsPlugin(); /** * Tests whether a preview for the first item should be shown */ - static bool supports( KFileItemList _items ); + static bool supports( const KFileItemList& _items ); private Q_SLOTS: void currentPageChanged( KPageWidgetItem *, KPageWidgetItem * ); private: KFileMetaPreview* preview; void createLayout(); class KPreviewPropsPluginPrivate; KPreviewPropsPluginPrivate* const d; }; #endif diff --git a/kio/kfile/kpropertiesdialog.cpp b/kio/kfile/kpropertiesdialog.cpp index bd2ab1ca7f..c72893dae8 100644 --- a/kio/kfile/kpropertiesdialog.cpp +++ b/kio/kfile/kpropertiesdialog.cpp @@ -1,3572 +1,3561 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright (c) 1999, 2000 Preston Brown Copyright (c) 2000 Simon Hausmann Copyright (c) 2000 David Faure Copyright (c) 2003 Waldo Bastian 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. */ /* * kpropertiesdialog.cpp * View/Edit Properties of files, locally or remotely * * some FilePermissionsPropsPlugin-changes by * Henner Zeller * some layout management by * Bertrand Leconte * the rest of the layout management, bug fixes, adaptation to libkio, * template feature by * David Faure * More layout, cleanups, and fixes by * Preston Brown * Plugin capability, cleanups and port to KDialog by * Simon Hausmann * KDesktopPropsPlugin by * Waldo Bastian */ #include "kpropertiesdialog.h" #include #include extern "C" { #include #include #include #include #include } #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_POSIX_ACL extern "C" { # include } #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef Q_OS_WIN #include "kfilesharedialog.h" #endif #include "ui_kpropertiesdesktopbase.h" #include "ui_kpropertiesdesktopadvbase.h" #ifdef HAVE_POSIX_ACL #include "kacleditwidget.h" #endif #include #include #ifdef Q_WS_WIN # include #ifdef __GNUC__ # warning TODO: port completely to win32 #endif #endif static QString nameFromFileName(QString nameStr) { if ( nameStr.endsWith(".desktop") ) nameStr.truncate( nameStr.length() - 8 ); if ( nameStr.endsWith(".kdelnk") ) nameStr.truncate( nameStr.length() - 7 ); // Make it human-readable (%2F => '/', ...) nameStr = KIO::decodeFileName( nameStr ); return nameStr; } mode_t KFilePermissionsPropsPlugin::fperm[3][4] = { {S_IRUSR, S_IWUSR, S_IXUSR, S_ISUID}, {S_IRGRP, S_IWGRP, S_IXGRP, S_ISGID}, {S_IROTH, S_IWOTH, S_IXOTH, S_ISVTX} }; class KPropertiesDialog::KPropertiesDialogPrivate { public: KPropertiesDialogPrivate(KPropertiesDialog *qq) { q = qq; m_aborted = false; fileSharePage = 0; } ~KPropertiesDialogPrivate() { } /** * Common initialization for all constructors */ void init(); /** * Inserts all pages in the dialog. */ void insertPages(); KPropertiesDialog *q; bool m_aborted:1; QWidget* fileSharePage; /** * The URL of the props dialog (when shown for only one file) */ KUrl m_singleUrl; /** * List of items this props dialog is shown for */ KFileItemList m_items; /** * For templates */ QString m_defaultName; KUrl m_currentDir; /** * List of all plugins inserted ( first one first ) */ QList m_pageList; }; -KPropertiesDialog::KPropertiesDialog (KFileItem* item, +KPropertiesDialog::KPropertiesDialog (const KFileItem& item, QWidget* parent) : KPageDialog(parent), d(new KPropertiesDialogPrivate(this)) { - setCaption( i18n( "Properties for %1" , KIO::decodeFileName(item->url().fileName())) ); + setCaption( i18n( "Properties for %1" , KIO::decodeFileName(item.url().fileName())) ); - assert( item ); - d->m_items.append(new KFileItem(*item)); // deep copy + assert( !item.isNull() ); + d->m_items.append(item); - d->m_singleUrl = item->url(); + d->m_singleUrl = item.url(); assert(!d->m_singleUrl.isEmpty()); d->init(); } KPropertiesDialog::KPropertiesDialog (const QString& title, QWidget* parent) : KPageDialog(parent), d(new KPropertiesDialogPrivate(this)) { setCaption( i18n( "Properties for %1", title ) ); d->init(); } KPropertiesDialog::KPropertiesDialog(const KFileItemList& _items, QWidget* parent) : KPageDialog(parent), d(new KPropertiesDialogPrivate(this)) { if ( _items.count() > 1 ) setCaption( i18np( "Properties for 1 item", "Properties for %1 Selected Items", _items.count() ) ); else - setCaption( i18n( "Properties for %1" , KIO::decodeFileName(_items.first()->url().fileName())) ); + setCaption( i18n( "Properties for %1" , KIO::decodeFileName(_items.first().url().fileName())) ); assert( !_items.isEmpty() ); - d->m_singleUrl = _items.first()->url(); + d->m_singleUrl = _items.first().url(); assert(!d->m_singleUrl.isEmpty()); - // Make copies - // TODO turn m_items into a list of KFileItems by values instead of by pointers - KFileItemList::const_iterator kit = _items.begin(); - const KFileItemList::const_iterator kend = _items.end(); - for ( ; kit != kend; ++kit ) - d->m_items.append(new KFileItem(**kit)); + d->m_items = _items; d->init(); } KPropertiesDialog::KPropertiesDialog (const KUrl& _url, QWidget* parent) : KPageDialog(parent), d(new KPropertiesDialogPrivate(this)) { setCaption( i18n( "Properties for %1" , KIO::decodeFileName(_url.fileName())) ); d->m_singleUrl = _url; KIO::UDSEntry entry; KIO::NetAccess::stat(_url, entry, parent); - d->m_items.append(new KFileItem(entry, _url)); + d->m_items.append(KFileItem(entry, _url)); d->init(); } KPropertiesDialog::KPropertiesDialog (const KUrl& _tempUrl, const KUrl& _currentDir, const QString& _defaultName, QWidget* parent) : KPageDialog(parent), d(new KPropertiesDialogPrivate(this)) { setCaption( i18n( "Properties for %1" , KIO::decodeFileName(_tempUrl.fileName())) ); d->m_singleUrl = _tempUrl; d->m_defaultName = _defaultName; d->m_currentDir = _currentDir; assert(!d->m_singleUrl.isEmpty()); // Create the KFileItem for the _template_ file, in order to read from it. - d->m_items.append(new KFileItem(KFileItem::Unknown, KFileItem::Unknown, d->m_singleUrl)); + d->m_items.append(KFileItem(KFileItem::Unknown, KFileItem::Unknown, d->m_singleUrl)); d->init(); } -bool KPropertiesDialog::showDialog(KFileItem* item, QWidget* parent, +bool KPropertiesDialog::showDialog(const KFileItem& item, QWidget* parent, bool modal) { // TODO: do we really want to show the win32 property dialog? // This means we lose metainfo, support for .desktop files, etc. (DF) #ifdef Q_WS_WIN - QString localPath = item->localPath(); + QString localPath = item.localPath(); if (!localPath.isEmpty()) return showWin32FilePropertyDialog(localPath); #endif KPropertiesDialog* dlg = new KPropertiesDialog(item, parent); if (modal) { dlg->exec(); } else { dlg->close(); } return true; } bool KPropertiesDialog::showDialog(const KUrl& _url, QWidget* parent, bool modal) { #ifdef Q_WS_WIN if (_url.isLocalFile()) return showWin32FilePropertyDialog( _url.path() ); #endif KPropertiesDialog* dlg = new KPropertiesDialog(_url, parent); if (modal) { dlg->exec(); } else { dlg->close(); } return true; } bool KPropertiesDialog::showDialog(const KFileItemList& _items, QWidget* parent, bool modal) { if (_items.count()==1) { - KFileItem * item = _items.first(); - if (item->entry().count() == 0 && item->localPath().isEmpty()) // this remote item wasn't listed by a slave + const KFileItem item = _items.first(); + if (item.entry().count() == 0 && item.localPath().isEmpty()) // this remote item wasn't listed by a slave // Let's stat to get more info on the file - return KPropertiesDialog::showDialog(item->url(), parent, modal); + return KPropertiesDialog::showDialog(item.url(), parent, modal); else return KPropertiesDialog::showDialog(_items.first(), parent, modal); } KPropertiesDialog* dlg = new KPropertiesDialog(_items, parent); if (modal) { dlg->exec(); } else { dlg->close(); } return true; } void KPropertiesDialog::KPropertiesDialogPrivate::init() { q->setFaceType(KPageDialog::Tabbed); q->setButtons(KDialog::Ok | KDialog::Cancel); q->setDefaultButton(KDialog::Ok); connect(q, SIGNAL(okClicked()), q, SLOT(slotOk())); connect(q, SIGNAL(cancelClicked()), q, SLOT(slotCancel())); insertPages(); KConfigGroup group(KGlobal::config(), "KPropertiesDialog"); q->restoreDialogSize(group); } void KPropertiesDialog::showFileSharingPage() { if (d->fileSharePage) { // FIXME: this showFileSharingPage thingy looks broken! (tokoe) // showPage( pageIndex( d->fileSharePage)); } } void KPropertiesDialog::setFileSharingPage(QWidget* page) { d->fileSharePage = page; } void KPropertiesDialog::setFileNameReadOnly( bool ro ) { foreach(KPropertiesDialogPlugin *it, d->m_pageList) { KFilePropsPlugin* plugin = dynamic_cast(it); if ( plugin ) { plugin->setFileNameReadOnly( ro ); break; } } } KPropertiesDialog::~KPropertiesDialog() { - qDeleteAll(d->m_items); qDeleteAll(d->m_pageList); delete d; KConfigGroup group(KGlobal::config(), "KPropertiesDialog"); saveDialogSize(group, KConfigFlags::Persistent); } void KPropertiesDialog::insertPlugin (KPropertiesDialogPlugin* plugin) { connect (plugin, SIGNAL (changed ()), plugin, SLOT (setDirty ())); d->m_pageList.append(plugin); } KUrl KPropertiesDialog::kurl() const { return d->m_singleUrl; } -KFileItem* KPropertiesDialog::item() +KFileItem& KPropertiesDialog::item() { return d->m_items.first(); } KFileItemList KPropertiesDialog::items() const { return d->m_items; } KUrl KPropertiesDialog::currentDir() const { return d->m_currentDir; } QString KPropertiesDialog::defaultName() const { return d->m_defaultName; } bool KPropertiesDialog::canDisplay( const KFileItemList& _items ) { // TODO: cache the result of those calls. Currently we parse .desktop files far too many times return KFilePropsPlugin::supports( _items ) || KFilePermissionsPropsPlugin::supports( _items ) || KDesktopPropsPlugin::supports( _items ) || KBindingPropsPlugin::supports( _items ) || KUrlPropsPlugin::supports( _items ) || KDevicePropsPlugin::supports( _items ) || KFileMetaPropsPlugin::supports( _items ) || KPreviewPropsPlugin::supports( _items ); } void KPropertiesDialog::slotOk() { QList::const_iterator pageListIt; d->m_aborted = false; KFilePropsPlugin * filePropsPlugin = 0L; if (qobject_cast(d->m_pageList.first())) filePropsPlugin = static_cast(d->m_pageList.first()); // If any page is dirty, then set the main one (KFilePropsPlugin) as // dirty too. This is what makes it possible to save changes to a global // desktop file into a local one. In other cases, it doesn't hurt. for (pageListIt = d->m_pageList.constBegin(); pageListIt != d->m_pageList.constEnd(); ++pageListIt) { if ( (*pageListIt)->isDirty() && filePropsPlugin ) { filePropsPlugin->setDirty(); break; } } // Apply the changes in the _normal_ order of the tabs now // This is because in case of renaming a file, KFilePropsPlugin will call // KPropertiesDialog::rename, so other tab will be ok with whatever order // BUT for file copied from templates, we need to do the renaming first ! for (pageListIt = d->m_pageList.constBegin(); pageListIt != d->m_pageList.constEnd() && !d->m_aborted; ++pageListIt) { if ( (*pageListIt)->isDirty() ) { kDebug( 250 ) << "applying changes for " << (*pageListIt)->metaObject()->className(); (*pageListIt)->applyChanges(); // applyChanges may change d->m_aborted. } else { kDebug( 250 ) << "skipping page " << (*pageListIt)->metaObject()->className(); } } if ( !d->m_aborted && filePropsPlugin ) filePropsPlugin->postApplyChanges(); if ( !d->m_aborted ) { emit applied(); emit propertiesClosed(); deleteLater(); // somewhat like Qt::WA_DeleteOnClose would do. accept(); } // else, keep dialog open for user to fix the problem. } void KPropertiesDialog::slotCancel() { emit canceled(); emit propertiesClosed(); deleteLater(); done( Rejected ); } void KPropertiesDialog::KPropertiesDialogPrivate::insertPages() { if (m_items.isEmpty()) return; if ( KFilePropsPlugin::supports( m_items ) ) { KPropertiesDialogPlugin *p = new KFilePropsPlugin(q); q->insertPlugin(p); } if ( KFilePermissionsPropsPlugin::supports( m_items ) ) { KPropertiesDialogPlugin *p = new KFilePermissionsPropsPlugin(q); q->insertPlugin(p); } if ( KDesktopPropsPlugin::supports( m_items ) ) { KPropertiesDialogPlugin *p = new KDesktopPropsPlugin(q); q->insertPlugin(p); } if ( KBindingPropsPlugin::supports( m_items ) ) { KPropertiesDialogPlugin *p = new KBindingPropsPlugin(q); q->insertPlugin(p); } if ( KUrlPropsPlugin::supports( m_items ) ) { KPropertiesDialogPlugin *p = new KUrlPropsPlugin(q); q->insertPlugin(p); } if ( KDevicePropsPlugin::supports( m_items ) ) { KPropertiesDialogPlugin *p = new KDevicePropsPlugin(q); q->insertPlugin(p); } if ( KFileMetaPropsPlugin::supports( m_items ) ) { KPropertiesDialogPlugin *p = new KFileMetaPropsPlugin(q); q->insertPlugin(p); } if ( KPreviewPropsPlugin::supports( m_items ) ) { KPropertiesDialogPlugin *p = new KPreviewPropsPlugin(q); q->insertPlugin(p); } #ifndef Q_OS_WIN if ( KAuthorized::authorizeKAction("sharefile") && KFileSharePropsPlugin::supports( m_items ) ) { KPropertiesDialogPlugin *p = new KFileSharePropsPlugin(q); q->insertPlugin(p); } #endif //plugins if ( m_items.count() != 1 ) return; - KFileItem *item = m_items.first(); - QString mimetype = item->mimetype(); + const KFileItem item = m_items.first(); + const QString mimetype = item.mimetype(); if ( mimetype.isEmpty() ) return; QString query = QString::fromLatin1( "((not exist [X-KDE-Protocol]) or " " ([X-KDE-Protocol] == '%1' ) )" - ).arg(item->url().protocol()); + ).arg(item.url().protocol()); kDebug( 250 ) << "trader query: " << query; KService::List offers = KMimeTypeTrader::self()->query( mimetype, "KPropertiesDialog/Plugin", query ); foreach (const KService::Ptr ptr, offers) { KPropertiesDialogPlugin *plugin = ptr->createInstance(q); if (!plugin) continue; plugin->setObjectName(ptr->name()); q->insertPlugin(plugin); } } void KPropertiesDialog::updateUrl( const KUrl& _newUrl ) { Q_ASSERT(d->m_items.count() == 1); kDebug(250) << "KPropertiesDialog::updateUrl (pre)" << _newUrl.url(); KUrl newUrl = _newUrl; emit saveAs(d->m_singleUrl, newUrl); kDebug(250) << "KPropertiesDialog::updateUrl (post)" << newUrl.url(); d->m_singleUrl = newUrl; - d->m_items.first()->setUrl(newUrl); + d->m_items.first().setUrl(newUrl); assert(!d->m_singleUrl.isEmpty()); // If we have an Desktop page, set it dirty, so that a full file is saved locally // Same for a URL page (because of the Name= hack) foreach (KPropertiesDialogPlugin *it, d->m_pageList) { if ( qobject_cast(it) || qobject_cast(it) ) { //kDebug(250) << "Setting page dirty"; it->setDirty(); break; } } } void KPropertiesDialog::rename( const QString& _name ) { Q_ASSERT(d->m_items.count() == 1); kDebug(250) << "KPropertiesDialog::rename " << _name; KUrl newUrl; // if we're creating from a template : use currentdir if (!d->m_currentDir.isEmpty()) { newUrl = d->m_currentDir; newUrl.addPath( _name ); } else { QString tmpurl = d->m_singleUrl.url(); if ( tmpurl.at(tmpurl.length() - 1) == '/') // It's a directory, so strip the trailing slash first tmpurl.truncate( tmpurl.length() - 1); newUrl = tmpurl; newUrl.setFileName( _name ); } updateUrl( newUrl ); } void KPropertiesDialog::abortApplying() { d->m_aborted = true; } class KPropertiesDialogPlugin::KPropertiesDialogPluginPrivate { public: KPropertiesDialogPluginPrivate() { } ~KPropertiesDialogPluginPrivate() { } bool m_bDirty; }; KPropertiesDialogPlugin::KPropertiesDialogPlugin( KPropertiesDialog *_props ) : QObject( _props ),d(new KPropertiesDialogPluginPrivate) { properties = _props; fontHeight = 2*properties->fontMetrics().height(); d->m_bDirty = false; } KPropertiesDialogPlugin::~KPropertiesDialogPlugin() { delete d; } -bool KPropertiesDialogPlugin::isDesktopFile( KFileItem * _item ) +bool KPropertiesDialogPlugin::isDesktopFile( const KFileItem& _item ) { // only local files bool isLocal; - const KUrl url = _item->mostLocalUrl( isLocal ); + const KUrl url = _item.mostLocalUrl( isLocal ); if ( !isLocal ) return false; // only regular files - if ( !S_ISREG( _item->mode() ) ) + if ( !S_ISREG( _item.mode() ) ) return false; QString t( url.path() ); // only if readable FILE *f = fopen( QFile::encodeName(t), "r" ); if ( f == 0L ) return false; fclose(f); // return true if desktop file - return ( _item->mimetype() == "application/x-desktop" ); + return ( _item.mimetype() == "application/x-desktop" ); } void KPropertiesDialogPlugin::setDirty( bool b ) { d->m_bDirty = b; } void KPropertiesDialogPlugin::setDirty() { d->m_bDirty = true; } bool KPropertiesDialogPlugin::isDirty() const { return d->m_bDirty; } void KPropertiesDialogPlugin::applyChanges() { kWarning(250) << "applyChanges() not implemented in page !"; } /////////////////////////////////////////////////////////////////////////////// class KFilePropsPlugin::KFilePropsPluginPrivate { public: KFilePropsPluginPrivate() { dirSizeJob = 0L; dirSizeUpdateTimer = 0L; m_lined = 0; m_freeSpaceLabel = 0; } ~KFilePropsPluginPrivate() { if ( dirSizeJob ) dirSizeJob->kill(); } KIO::DirectorySizeJob * dirSizeJob; QTimer *dirSizeUpdateTimer; QFrame *m_frame; bool bMultiple; bool bIconChanged; bool bKDesktopMode; bool bDesktopFile; QLabel *m_freeSpaceLabel; QString mimeType; QString oldFileName; KLineEdit* m_lined; QWidget *iconArea; QWidget *nameArea; QLabel *m_sizeLabel; QPushButton *m_sizeDetermineButton; QPushButton *m_sizeStopButton; QString m_sRelativePath; bool m_bFromTemplate; /** * The initial filename */ QString oldName; }; KFilePropsPlugin::KFilePropsPlugin( KPropertiesDialog *_props ) : KPropertiesDialogPlugin( _props ),d(new KFilePropsPluginPrivate) { d->bMultiple = (properties->items().count() > 1); d->bIconChanged = false; d->bKDesktopMode = (qApp->objectName() == "kdesktop"); d->bDesktopFile = KDesktopPropsPlugin::supports(properties->items()); kDebug(250) << "KFilePropsPlugin::KFilePropsPlugin bMultiple=" << d->bMultiple; // We set this data from the first item, and we'll // check that the other items match against it, resetting when not. bool isLocal; - KFileItem * item = properties->item(); - KUrl url = item->mostLocalUrl( isLocal ); - bool isReallyLocal = item->url().isLocalFile(); + const KFileItem item = properties->item(); + KUrl url = item.mostLocalUrl( isLocal ); + bool isReallyLocal = item.url().isLocalFile(); bool bDesktopFile = isDesktopFile(item); - mode_t mode = item->mode(); - bool hasDirs = item->isDir() && !item->isLink(); + mode_t mode = item.mode(); + bool hasDirs = item.isDir() && !item.isLink(); bool hasRoot = url.path() == QLatin1String("/"); QString iconStr = KMimeType::iconNameForUrl(url, mode); QString directory = properties->kurl().directory(); QString protocol = properties->kurl().protocol(); - QString mimeComment = item->mimeComment(); - d->mimeType = item->mimetype(); - KIO::filesize_t totalSize = item->size(); + QString mimeComment = item.mimeComment(); + d->mimeType = item.mimetype(); + KIO::filesize_t totalSize = item.size(); QString magicMimeComment; if ( isLocal ) { KMimeType::Ptr magicMimeType = KMimeType::findByFileContent( url.path() ); if ( magicMimeType->name() != KMimeType::defaultMimeType() ) magicMimeComment = magicMimeType->comment(); } // Those things only apply to 'single file' mode QString filename= QString(); bool isTrash = false; bool isDevice = false; d->m_bFromTemplate = false; // And those only to 'multiple' mode uint iDirCount = hasDirs ? 1 : 0; uint iFileCount = 1-iDirCount; d->m_frame = new QFrame(); properties->addPage(d->m_frame, i18n("&General")); QVBoxLayout *vbl = new QVBoxLayout( d->m_frame ); vbl->setMargin( 0 ); vbl->setSpacing( KDialog::spacingHint() ); vbl->setObjectName( QLatin1String( "vbl" ) ); QGridLayout *grid = new QGridLayout(); // unknown rows grid->setColumnStretch(0, 0); grid->setColumnStretch(1, 0); grid->setColumnStretch(2, 1); grid->addItem(new QSpacerItem(KDialog::spacingHint(),0), 0, 1); vbl->addLayout(grid); int curRow = 0; if ( !d->bMultiple ) { QString path; if ( !d->m_bFromTemplate ) { isTrash = ( properties->kurl().protocol().toLower() == "trash" ); isDevice = ( properties->kurl().protocol().toLower() == "device" ); // Extract the full name, but without file: for local files if ( isReallyLocal ) path = properties->kurl().path(); else path = properties->kurl().prettyUrl(); } else { path = properties->currentDir().path(KUrl::AddTrailingSlash) + properties->defaultName(); directory = properties->currentDir().prettyUrl(); } if (d->bDesktopFile || KBindingPropsPlugin::supports(properties->items())) { determineRelativePath( path ); } // Extract the file name only filename = properties->defaultName(); if ( filename.isEmpty() ) { // no template - filename = item->name(); // this gives support for UDS_NAME, e.g. for kio_trash or kio_system + filename = item.name(); // this gives support for UDS_NAME, e.g. for kio_trash or kio_system } else { d->m_bFromTemplate = true; setDirty(); // to enforce that the copy happens } d->oldFileName = filename; // Make it human-readable filename = nameFromFileName( filename ); if ( d->bKDesktopMode && d->bDesktopFile ) { KDesktopFile config( url.path() ); if ( config.desktopGroup().hasKey( "Name" ) ) { filename = config.readName(); } } d->oldName = filename; } else { // Multiple items: see what they have in common const KFileItemList items = properties->items(); KFileItemList::const_iterator kit = items.begin(); const KFileItemList::const_iterator kend = items.end(); for ( ++kit /*no need to check the first one again*/ ; kit != kend; ++kit ) { - const KUrl url = (*kit)->url(); + const KUrl url = (*kit).url(); kDebug(250) << "KFilePropsPlugin::KFilePropsPlugin " << url.prettyUrl(); // The list of things we check here should match the variables defined // at the beginning of this method. if ( url.isLocalFile() != isLocal ) isLocal = false; // not all local if ( bDesktopFile && isDesktopFile(*kit) != bDesktopFile ) bDesktopFile = false; // not all desktop files - if ( (*kit)->mode() != mode ) + if ( (*kit).mode() != mode ) mode = (mode_t)0; if ( KMimeType::iconNameForUrl(url, mode) != iconStr ) iconStr = "kmultiple"; if ( url.directory() != directory ) directory.clear(); if ( url.protocol() != protocol ) protocol.clear(); - if ( !mimeComment.isNull() && (*kit)->mimeComment() != mimeComment ) + if ( !mimeComment.isNull() && (*kit).mimeComment() != mimeComment ) mimeComment.clear(); if ( isLocal && !magicMimeComment.isNull() ) { KMimeType::Ptr magicMimeType = KMimeType::findByFileContent( url.path() ); if ( magicMimeType->comment() != magicMimeComment ) magicMimeComment.clear(); } if ( isLocal && url.path() == QLatin1String("/") ) hasRoot = true; - if ( (*kit)->isDir() && !(*kit)->isLink() ) + if ( (*kit).isDir() && !(*kit).isLink() ) { iDirCount++; hasDirs = true; } else { iFileCount++; - totalSize += (*kit)->size(); + totalSize += (*kit).size(); } } } if (!isReallyLocal && !protocol.isEmpty()) { directory += ' '; directory += '('; directory += protocol; directory += ')'; } if ( !isDevice && !isTrash && (bDesktopFile || S_ISDIR(mode)) && !d->bMultiple /*not implemented for multiple*/ ) { KIconButton *iconButton = new KIconButton( d->m_frame ); int bsize = 66 + 2 * iconButton->style()->pixelMetric(QStyle::PM_ButtonMargin); iconButton->setFixedSize(bsize, bsize); iconButton->setIconSize(48); iconButton->setStrictIconSize(false); // This works for everything except Device icons on unmounted devices // So we have to really open .desktop files QString iconStr = KMimeType::findByUrl( url, mode )->iconName( url ); if ( bDesktopFile && isLocal ) { KDesktopFile config( url.path() ); KConfigGroup group = config.desktopGroup(); iconStr = group.readEntry( "Icon" ); if ( config.hasDeviceType() ) iconButton->setIconType( K3Icon::Desktop, K3Icon::Device ); else iconButton->setIconType( K3Icon::Desktop, K3Icon::Application ); } else iconButton->setIconType( K3Icon::Desktop, K3Icon::FileSystem ); iconButton->setIcon(iconStr); d->iconArea = iconButton; connect( iconButton, SIGNAL( iconChanged(const QString&) ), this, SLOT( slotIconChanged() ) ); } else { QLabel *iconLabel = new QLabel( d->m_frame ); int bsize = 66 + 2 * iconLabel->style()->pixelMetric(QStyle::PM_ButtonMargin); iconLabel->setFixedSize(bsize, bsize); iconLabel->setPixmap( KIconLoader::global()->loadIcon( iconStr, K3Icon::Desktop, 48) ); d->iconArea = iconLabel; } grid->addWidget(d->iconArea, curRow, 0, Qt::AlignLeft); if (d->bMultiple || isTrash || isDevice || hasRoot) { QLabel *lab = new QLabel(d->m_frame ); if ( d->bMultiple ) lab->setText( KIO::itemsSummaryString( iFileCount + iDirCount, iFileCount, iDirCount, 0, false ) ); else lab->setText( filename ); d->nameArea = lab; } else { d->m_lined = new KLineEdit( d->m_frame ); d->m_lined->setText(filename); d->nameArea = d->m_lined; d->m_lined->setFocus(); // Enhanced rename: Don't highlight the file extension. QString extension = KMimeType::extractKnownExtension( filename ); if ( !extension.isEmpty() ) d->m_lined->setSelection( 0, filename.length() - extension.length() - 1 ); else { int lastDot = filename.lastIndexOf('.'); if (lastDot > 0) d->m_lined->setSelection(0, lastDot); } connect( d->m_lined, SIGNAL( textChanged( const QString & ) ), this, SLOT( nameFileChanged(const QString & ) ) ); } grid->addWidget(d->nameArea, curRow++, 2); KSeparator* sep = new KSeparator( Qt::Horizontal, d->m_frame); grid->addWidget(sep, curRow, 0, 1, 3); ++curRow; QLabel *l; if ( !mimeComment.isEmpty() && !isDevice && !isTrash) { l = new QLabel(i18n("Type:"), d->m_frame ); grid->addWidget(l, curRow, 0); KHBox *box = new KHBox(d->m_frame); box->setSpacing(20); l = new QLabel(mimeComment, box ); #ifdef Q_WS_X11 //TODO: wrap for win32 or mac? QPushButton *button = new QPushButton(box); button->setIcon( KIcon(QString::fromLatin1("configure")) ); const int pixmapSize = button->style()->pixelMetric(QStyle::PM_SmallIconSize); button->setFixedSize( pixmapSize+8, pixmapSize+8 ); if ( d->mimeType == KMimeType::defaultMimeType() ) button->setToolTip(i18n("Create new file type")); else button->setToolTip(i18n("Edit file type")); connect( button, SIGNAL( clicked() ), SLOT( slotEditFileType() )); if (!KAuthorized::authorizeKAction("editfiletype")) button->hide(); #endif grid->addWidget(box, curRow++, 2); } if ( !magicMimeComment.isEmpty() && magicMimeComment != mimeComment ) { l = new QLabel(i18n("Contents:"), d->m_frame ); grid->addWidget(l, curRow, 0); l = new QLabel(magicMimeComment, d->m_frame ); grid->addWidget(l, curRow++, 2); } if ( !directory.isEmpty() ) { l = new QLabel( i18n("Location:"), d->m_frame ); grid->addWidget(l, curRow, 0); l = new KSqueezedTextLabel( directory, d->m_frame ); grid->addWidget(l, curRow++, 2); } l = new QLabel(i18n("Size:"), d->m_frame ); grid->addWidget(l, curRow, 0); d->m_sizeLabel = new QLabel( d->m_frame ); grid->addWidget( d->m_sizeLabel, curRow++, 2 ); if ( !hasDirs ) // Only files [and symlinks] { d->m_sizeLabel->setText(QString::fromLatin1("%1 (%2)").arg(KIO::convertSize(totalSize)) .arg(KGlobal::locale()->formatNumber(totalSize, 0))); d->m_sizeDetermineButton = 0L; d->m_sizeStopButton = 0L; } else // Directory { QHBoxLayout * sizelay = new QHBoxLayout(); sizelay->setSpacing(KDialog::spacingHint()); grid->addLayout( sizelay, curRow++, 2 ); // buttons d->m_sizeDetermineButton = new QPushButton( i18n("Calculate"), d->m_frame ); d->m_sizeStopButton = new QPushButton( i18n("Stop"), d->m_frame ); connect( d->m_sizeDetermineButton, SIGNAL( clicked() ), this, SLOT( slotSizeDetermine() ) ); connect( d->m_sizeStopButton, SIGNAL( clicked() ), this, SLOT( slotSizeStop() ) ); sizelay->addWidget(d->m_sizeDetermineButton, 0); sizelay->addWidget(d->m_sizeStopButton, 0); sizelay->addStretch(10); // so that the buttons don't grow horizontally // auto-launch for local dirs only, and not for '/' if ( isLocal && !hasRoot ) { d->m_sizeDetermineButton->setText( i18n("Refresh") ); slotSizeDetermine(); } else d->m_sizeStopButton->setEnabled( false ); } - if (!d->bMultiple && item->isLink()) { + if (!d->bMultiple && item.isLink()) { l = new QLabel(i18n("Points to:"), d->m_frame ); grid->addWidget(l, curRow, 0); - l = new KSqueezedTextLabel(item->linkDest(), d->m_frame ); + l = new KSqueezedTextLabel(item.linkDest(), d->m_frame ); grid->addWidget(l, curRow++, 2); } if (!d->bMultiple) // Dates for multiple don't make much sense... { - KDateTime dt = item->time(KFileItem::CreationTime); + KDateTime dt = item.time(KFileItem::CreationTime); if ( !dt.isNull() ) { l = new QLabel(i18n("Created:"), d->m_frame ); grid->addWidget(l, curRow, 0); l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame ); grid->addWidget(l, curRow++, 2); } - dt = item->time(KFileItem::ModificationTime); + dt = item.time(KFileItem::ModificationTime); if ( !dt.isNull() ) { l = new QLabel(i18n("Modified:"), d->m_frame ); grid->addWidget(l, curRow, 0); l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame ); grid->addWidget(l, curRow++, 2); } - dt = item->time(KFileItem::AccessTime); + dt = item.time(KFileItem::AccessTime); if ( !dt.isNull() ) { l = new QLabel(i18n("Accessed:"), d->m_frame ); grid->addWidget(l, curRow, 0); l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame ); grid->addWidget(l, curRow++, 2); } } if ( isLocal && hasDirs ) // only for directories { sep = new KSeparator( Qt::Horizontal, d->m_frame); grid->addWidget(sep, curRow, 0, 1, 3); ++curRow; KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath( url.path() ); if (mp) { if (mp->mountPoint() != "/") { l = new QLabel(i18n("Mounted on:"), d->m_frame ); grid->addWidget(l, curRow, 0); l = new KSqueezedTextLabel( mp->mountPoint(), d->m_frame ); grid->addWidget( l, curRow++, 2 ); } l = new QLabel(i18n("Free disk space:"), d->m_frame ); grid->addWidget(l, curRow, 0); d->m_freeSpaceLabel = new QLabel( d->m_frame ); grid->addWidget( d->m_freeSpaceLabel, curRow++, 2 ); KDiskFreeSpace * job = new KDiskFreeSpace; connect(job, SIGNAL(foundMountPoint(QString, quint64, quint64, quint64)), this, SLOT(slotFoundMountPoint(QString, quint64, quint64, quint64))); job->readDF( mp->mountPoint() ); } } vbl->addStretch(1); } // QString KFilePropsPlugin::tabName () const // { // return i18n ("&General"); // } void KFilePropsPlugin::setFileNameReadOnly( bool ro ) { if ( d->m_lined ) { d->m_lined->setReadOnly( ro ); if (ro) { // Don't put the initial focus on the line edit when it is ro properties->setButtonFocus(KDialog::Ok); } } } void KFilePropsPlugin::slotEditFileType() { #ifdef Q_WS_X11 QString mime; if ( d->mimeType == KMimeType::defaultMimeType() ) { int pos = d->oldFileName.lastIndexOf( '.' ); if ( pos != -1 ) mime = '*' + d->oldFileName.mid(pos); else mime = '*'; } else mime = d->mimeType; //TODO: wrap for win32 or mac? QString keditfiletype = QString::fromLatin1("keditfiletype"); KRun::runCommand( keditfiletype + " --parent " + QString::number( (ulong)properties->topLevelWidget()->winId()) + ' ' + KShell::quoteArg(mime), keditfiletype, keditfiletype /*unused*/, properties->topLevelWidget()); #endif } void KFilePropsPlugin::slotIconChanged() { d->bIconChanged = true; emit changed(); } void KFilePropsPlugin::nameFileChanged(const QString &text ) { properties->enableButtonOk(!text.isEmpty()); emit changed(); } void KFilePropsPlugin::determineRelativePath( const QString & path ) { // now let's make it relative QStringList dirs; if (KBindingPropsPlugin::supports(properties->items())) { d->m_sRelativePath =KGlobal::dirs()->relativeLocation("mime", path); if (d->m_sRelativePath.startsWith('/')) d->m_sRelativePath.clear(); } else { d->m_sRelativePath =KGlobal::dirs()->relativeLocation("apps", path); if (d->m_sRelativePath.startsWith('/')) { d->m_sRelativePath =KGlobal::dirs()->relativeLocation("xdgdata-apps", path); if (d->m_sRelativePath.startsWith('/')) d->m_sRelativePath.clear(); else d->m_sRelativePath = path; } } if ( d->m_sRelativePath.isEmpty() ) { if (KBindingPropsPlugin::supports(properties->items())) kWarning(250) << "Warning : editing a mimetype file out of the mimetype dirs!"; } } void KFilePropsPlugin::slotFoundMountPoint( const QString&, quint64 kBSize, quint64 /*kBUsed*/, quint64 kBAvail ) { d->m_freeSpaceLabel->setText( i18nc("Available space out of total partition size (percent used)", "%1 out of %2 (%3% used)", KIO::convertSizeFromKiB(kBAvail), KIO::convertSizeFromKiB(kBSize), 100 - (int)(100.0 * kBAvail / kBSize) )); } void KFilePropsPlugin::slotDirSizeUpdate() { KIO::filesize_t totalSize = d->dirSizeJob->totalSize(); KIO::filesize_t totalFiles = d->dirSizeJob->totalFiles(); KIO::filesize_t totalSubdirs = d->dirSizeJob->totalSubdirs(); d->m_sizeLabel->setText( i18n("Calculating... %1 (%2)\n%3, %4", KIO::convertSize(totalSize), KGlobal::locale()->formatNumber(totalSize, 0), i18np("1 file","%1 files",totalFiles), i18np("1 sub-folder","%1 sub-folders",totalSubdirs))); } void KFilePropsPlugin::slotDirSizeFinished( KJob * job ) { if (job->error()) d->m_sizeLabel->setText( job->errorString() ); else { KIO::filesize_t totalSize = d->dirSizeJob->totalSize(); KIO::filesize_t totalFiles = d->dirSizeJob->totalFiles(); KIO::filesize_t totalSubdirs = d->dirSizeJob->totalSubdirs(); d->m_sizeLabel->setText( QString::fromLatin1("%1 (%2)\n%3, %4") .arg(KIO::convertSize(totalSize)) .arg(KGlobal::locale()->formatNumber(totalSize, 0)) .arg(i18np("1 file","%1 files",totalFiles)) .arg(i18np("1 sub-folder","%1 sub-folders",totalSubdirs))); } d->m_sizeStopButton->setEnabled(false); // just in case you change something and try again :) d->m_sizeDetermineButton->setText( i18n("Refresh") ); d->m_sizeDetermineButton->setEnabled(true); d->dirSizeJob = 0; delete d->dirSizeUpdateTimer; d->dirSizeUpdateTimer = 0; } void KFilePropsPlugin::slotSizeDetermine() { d->m_sizeLabel->setText( i18n("Calculating...") ); kDebug(250) << " KFilePropsPlugin::slotSizeDetermine() properties->item()=" << properties->item(); - kDebug(250) << " URL=" << properties->item()->url().url(); + kDebug(250) << " URL=" << properties->item().url().url(); - // Must turn QList to QList... # TODO port KPropertiesDialog to QList - QList itemList; - foreach( KFileItem* it, properties->items() ) - itemList.append( *it ); - - d->dirSizeJob = KIO::directorySize( itemList ); + d->dirSizeJob = KIO::directorySize( properties->items() ); d->dirSizeUpdateTimer = new QTimer(this); connect( d->dirSizeUpdateTimer, SIGNAL( timeout() ), SLOT( slotDirSizeUpdate() ) ); d->dirSizeUpdateTimer->start(500); connect( d->dirSizeJob, SIGNAL( result( KJob * ) ), SLOT( slotDirSizeFinished( KJob * ) ) ); d->m_sizeStopButton->setEnabled(true); d->m_sizeDetermineButton->setEnabled(false); // also update the "Free disk space" display if ( d->m_freeSpaceLabel ) { bool isLocal; - KFileItem * item = properties->item(); - KUrl url = item->mostLocalUrl( isLocal ); + const KFileItem item = properties->item(); + KUrl url = item.mostLocalUrl( isLocal ); KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath( url.path() ); if (mp) { KDiskFreeSpace * job = new KDiskFreeSpace; connect(job, SIGNAL(foundMountPoint(QString, quint64, quint64, quint64)), this, SLOT(slotFoundMountPoint(QString, quint64, quint64, quint64))); job->readDF( mp->mountPoint() ); } } } void KFilePropsPlugin::slotSizeStop() { if ( d->dirSizeJob ) { KIO::filesize_t totalSize = d->dirSizeJob->totalSize(); d->m_sizeLabel->setText(i18n("At least %1", KIO::convertSize(totalSize))); d->dirSizeJob->kill(); d->dirSizeJob = 0; } if ( d->dirSizeUpdateTimer ) d->dirSizeUpdateTimer->stop(); d->m_sizeStopButton->setEnabled(false); d->m_sizeDetermineButton->setEnabled(true); } KFilePropsPlugin::~KFilePropsPlugin() { delete d; } bool KFilePropsPlugin::supports( const KFileItemList& /*_items*/ ) { return true; } void KFilePropsPlugin::applyChanges() { if ( d->dirSizeJob ) slotSizeStop(); kDebug(250) << "KFilePropsPlugin::applyChanges"; if (qobject_cast(d->nameArea)) { QString n = ((QLineEdit *) d->nameArea)->text(); // Remove trailing spaces (#4345) while ( n[n.length()-1].isSpace() ) n.truncate( n.length() - 1 ); if ( n.isEmpty() ) { KMessageBox::sorry( properties, i18n("The new file name is empty.")); properties->abortApplying(); return; } // Do we need to rename the file ? kDebug(250) << "oldname = " << d->oldName; kDebug(250) << "newname = " << n; if ( d->oldName != n || d->m_bFromTemplate ) { // true for any from-template file KIO::Job * job = 0L; KUrl oldurl = properties->kurl(); QString newFileName = KIO::encodeFileName(n); if (d->bDesktopFile && !newFileName.endsWith(".desktop") && !newFileName.endsWith(".kdelnk")) newFileName += ".desktop"; // Tell properties. Warning, this changes the result of properties->kurl() ! properties->rename( newFileName ); // Update also relative path (for apps and mimetypes) if ( !d->m_sRelativePath.isEmpty() ) determineRelativePath( properties->kurl().path() ); kDebug(250) << "New URL = " << properties->kurl().url(); kDebug(250) << "old = " << oldurl.url(); // Don't remove the template !! if ( !d->m_bFromTemplate ) // (normal renaming) job = KIO::move( oldurl, properties->kurl() ); else // Copying a template job = KIO::copy( oldurl, properties->kurl() ); connect( job, SIGNAL( result( KJob * ) ), SLOT( slotCopyFinished( KJob * ) ) ); connect( job, SIGNAL( renamed( KIO::Job *, const KUrl &, const KUrl & ) ), SLOT( slotFileRenamed( KIO::Job *, const KUrl &, const KUrl & ) ) ); // wait for job QEventLoop eventLoop; connect(this, SIGNAL(leaveModality()), &eventLoop, SLOT(quit())); eventLoop.exec(QEventLoop::ExcludeUserInputEvents); return; } properties->updateUrl(properties->kurl()); // Update also relative path (for apps and mimetypes) if ( !d->m_sRelativePath.isEmpty() ) determineRelativePath( properties->kurl().path() ); } // No job, keep going slotCopyFinished( 0L ); } void KFilePropsPlugin::slotCopyFinished( KJob * job ) { kDebug(250) << "KFilePropsPlugin::slotCopyFinished"; if (job) { // allow apply() to return emit leaveModality(); if ( job->error() ) { job->uiDelegate()->showErrorMessage(); // Didn't work. Revert the URL to the old one properties->updateUrl( static_cast(job)->srcUrls().first() ); properties->abortApplying(); // Don't apply the changes to the wrong file ! return; } } - assert( properties->item() ); - assert( !properties->item()->url().isEmpty() ); + assert( !properties->item().isNull() ); + assert( !properties->item().url().isEmpty() ); // Save the file where we can -> usually in ~/.kde/... if (KBindingPropsPlugin::supports(properties->items()) && !d->m_sRelativePath.isEmpty()) { KUrl newURL; newURL.setPath( KStandardDirs::locateLocal("mime", d->m_sRelativePath) ); properties->updateUrl( newURL ); } else if (d->bDesktopFile && !d->m_sRelativePath.isEmpty()) { kDebug(250) << "KFilePropsPlugin::slotCopyFinished " << d->m_sRelativePath; KUrl newURL; newURL.setPath( KDesktopFile::locateLocal(d->m_sRelativePath) ); kDebug(250) << "KFilePropsPlugin::slotCopyFinished path=" << newURL.path(); properties->updateUrl( newURL ); } if ( d->bKDesktopMode && d->bDesktopFile ) { // Renamed? Update Name field if ( d->oldFileName != properties->kurl().fileName() || d->m_bFromTemplate ) { KDesktopFile config( properties->kurl().path() ); KConfigGroup cg = config.desktopGroup(); QString nameStr = nameFromFileName(properties->kurl().fileName()); cg.writeEntry( "Name", nameStr ); cg.writeEntry( "Name", nameStr, KConfigBase::Persistent|KConfigBase::NLS); } } } void KFilePropsPlugin::applyIconChanges() { KIconButton *iconButton = qobject_cast(d->iconArea); if ( !iconButton || !d->bIconChanged ) return; // handle icon changes - only local files (or pseudo-local) for now // TODO: Use KTempFile and KIO::file_copy with overwrite = true KUrl url = properties->kurl(); url = KIO::NetAccess::mostLocalUrl( url, properties ); if ( url.isLocalFile()) { QString path; - if (S_ISDIR(properties->item()->mode())) + if (S_ISDIR(properties->item().mode())) { path = url.path(KUrl::AddTrailingSlash) + QString::fromLatin1(".directory"); // don't call updateUrl because the other tabs (i.e. permissions) // apply to the directory, not the .directory file. } else path = url.path(); // Get the default image QString str = KMimeType::findByUrl( url, - properties->item()->mode(), + properties->item().mode(), true )->iconName(); // Is it another one than the default ? QString sIcon; if ( str != iconButton->icon() ) sIcon = iconButton->icon(); // (otherwise write empty value) kDebug(250) << "**" << path << "**"; QFile f( path ); // If default icon and no .directory file -> don't create one if ( !sIcon.isEmpty() || f.exists() ) { if ( !f.open( QIODevice::ReadWrite ) ) { KMessageBox::sorry( 0, i18n("Could not save properties. You do not " "have sufficient access to write to %1.", path)); return; } f.close(); KDesktopFile cfg(path); kDebug(250) << "sIcon = " << (sIcon); kDebug(250) << "str = " << (str); cfg.desktopGroup().writeEntry( "Icon", sIcon ); cfg.sync(); } } } void KFilePropsPlugin::slotFileRenamed( KIO::Job *, const KUrl &, const KUrl & newUrl ) { // This is called in case of an existing local file during the copy/move operation, // if the user chooses Rename. properties->updateUrl( newUrl ); } void KFilePropsPlugin::postApplyChanges() { // Save the icon only after applying the permissions changes (#46192) applyIconChanges(); const KFileItemList items = properties->items(); const KUrl::List lst = items.urlList(); org::kde::KDirNotify::emitFilesChanged( lst.toStringList() ); } class KFilePermissionsPropsPlugin::KFilePermissionsPropsPluginPrivate { public: KFilePermissionsPropsPluginPrivate() { } ~KFilePermissionsPropsPluginPrivate() { } QFrame *m_frame; QCheckBox *cbRecursive; QLabel *explanationLabel; QComboBox *ownerPermCombo, *groupPermCombo, *othersPermCombo; QCheckBox *extraCheckbox; mode_t partialPermissions; KFilePermissionsPropsPlugin::PermissionsMode pmode; bool canChangePermissions; bool isIrregular; bool hasExtendedACL; KACL extendedACL; KACL defaultACL; bool fileSystemSupportsACLs; QComboBox *grpCombo; KLineEdit *usrEdit; KLineEdit *grpEdit; // Old permissions mode_t permissions; // Old group QString strGroup; // Old owner QString strOwner; }; #define UniOwner (S_IRUSR|S_IWUSR|S_IXUSR) #define UniGroup (S_IRGRP|S_IWGRP|S_IXGRP) #define UniOthers (S_IROTH|S_IWOTH|S_IXOTH) #define UniRead (S_IRUSR|S_IRGRP|S_IROTH) #define UniWrite (S_IWUSR|S_IWGRP|S_IWOTH) #define UniExec (S_IXUSR|S_IXGRP|S_IXOTH) #define UniSpecial (S_ISUID|S_ISGID|S_ISVTX) // synced with PermissionsTarget const mode_t KFilePermissionsPropsPlugin::permissionsMasks[3] = {UniOwner, UniGroup, UniOthers}; const mode_t KFilePermissionsPropsPlugin::standardPermissions[4] = { 0, UniRead, UniRead|UniWrite, (mode_t)-1 }; // synced with PermissionsMode and standardPermissions const char *KFilePermissionsPropsPlugin::permissionsTexts[4][4] = { { I18N_NOOP("Forbidden"), I18N_NOOP("Can Read"), I18N_NOOP("Can Read & Write"), 0 }, { I18N_NOOP("Forbidden"), I18N_NOOP("Can View Content"), I18N_NOOP("Can View & Modify Content"), 0 }, { 0, 0, 0, 0}, // no texts for links { I18N_NOOP("Forbidden"), I18N_NOOP("Can View Content & Read"), I18N_NOOP("Can View/Read & Modify/Write"), 0 } }; KFilePermissionsPropsPlugin::KFilePermissionsPropsPlugin( KPropertiesDialog *_props ) : KPropertiesDialogPlugin( _props ),d(new KFilePermissionsPropsPluginPrivate) { d->cbRecursive = 0L; d->grpCombo = 0L; d->grpEdit = 0; d->usrEdit = 0L; QString path = properties->kurl().path(KUrl::RemoveTrailingSlash); QString fname = properties->kurl().fileName(); bool isLocal = properties->kurl().isLocalFile(); bool isTrash = ( properties->kurl().protocol().toLower() == "trash" ); bool IamRoot = (geteuid() == 0); - KFileItem * item = properties->item(); - bool isLink = item->isLink(); - bool isDir = item->isDir(); // all dirs - bool hasDir = item->isDir(); // at least one dir - d->permissions = item->permissions(); // common permissions to all files + const KFileItem item = properties->item(); + bool isLink = item.isLink(); + bool isDir = item.isDir(); // all dirs + bool hasDir = item.isDir(); // at least one dir + d->permissions = item.permissions(); // common permissions to all files d->partialPermissions = d->permissions; // permissions that only some files have (at first we take everything) d->isIrregular = isIrregular(d->permissions, isDir, isLink); - d->strOwner = item->user(); - d->strGroup = item->group(); - d->hasExtendedACL = item->ACL().isExtended() || item->defaultACL().isValid(); - d->extendedACL = item->ACL(); - d->defaultACL = item->defaultACL(); + d->strOwner = item.user(); + d->strGroup = item.group(); + d->hasExtendedACL = item.ACL().isExtended() || item.defaultACL().isValid(); + d->extendedACL = item.ACL(); + d->defaultACL = item.defaultACL(); d->fileSystemSupportsACLs = false; if ( properties->items().count() > 1 ) { // Multiple items: see what they have in common const KFileItemList items = properties->items(); KFileItemList::const_iterator it = items.begin(); const KFileItemList::const_iterator kend = items.end(); for ( ++it /*no need to check the first one again*/ ; it != kend; ++it ) { - const KUrl url = (*it)->url(); + const KUrl url = (*it).url(); if (!d->isIrregular) - d->isIrregular |= isIrregular((*it)->permissions(), - (*it)->isDir() == isDir, - (*it)->isLink() == isLink); - d->hasExtendedACL = d->hasExtendedACL || (*it)->hasExtendedACL(); - if ( (*it)->isLink() != isLink ) + d->isIrregular |= isIrregular((*it).permissions(), + (*it).isDir() == isDir, + (*it).isLink() == isLink); + d->hasExtendedACL = d->hasExtendedACL || (*it).hasExtendedACL(); + if ( (*it).isLink() != isLink ) isLink = false; - if ( (*it)->isDir() != isDir ) + if ( (*it).isDir() != isDir ) isDir = false; - hasDir |= (*it)->isDir(); - if ( (*it)->permissions() != d->permissions ) + hasDir |= (*it).isDir(); + if ( (*it).permissions() != d->permissions ) { - d->permissions &= (*it)->permissions(); - d->partialPermissions |= (*it)->permissions(); + d->permissions &= (*it).permissions(); + d->partialPermissions |= (*it).permissions(); } - if ( (*it)->user() != d->strOwner ) + if ( (*it).user() != d->strOwner ) d->strOwner.clear(); - if ( (*it)->group() != d->strGroup ) + if ( (*it).group() != d->strGroup ) d->strGroup.clear(); } } if (isLink) d->pmode = PermissionsOnlyLinks; else if (isDir) d->pmode = PermissionsOnlyDirs; else if (hasDir) d->pmode = PermissionsMixed; else d->pmode = PermissionsOnlyFiles; // keep only what's not in the common permissions d->partialPermissions = d->partialPermissions & ~d->permissions; bool isMyFile = false; if (isLocal && !d->strOwner.isEmpty()) { // local files, and all owned by the same person struct passwd *myself = getpwuid( geteuid() ); if ( myself != 0L ) { isMyFile = (d->strOwner == QString::fromLocal8Bit(myself->pw_name)); } else kWarning() << "I don't exist ?! geteuid=" << geteuid(); } else { //We don't know, for remote files, if they are ours or not. //So we let the user change permissions, and //KIO::chmod will tell, if he had no right to do it. isMyFile = true; } d->canChangePermissions = (isMyFile || IamRoot) && (!isLink); // create GUI d->m_frame = new QFrame(); properties->addPage( d->m_frame, i18n("&Permissions") ); QBoxLayout *box = new QVBoxLayout( d->m_frame ); box->setMargin( 0 ); box->setSpacing( KDialog::spacingHint() ); QWidget *l; QLabel *lbl; QGroupBox *gb; QGridLayout *gl; QPushButton* pbAdvancedPerm = 0; /* Group: Access Permissions */ gb = new QGroupBox ( i18n("Access Permissions"), d->m_frame ); box->addWidget (gb); gl = new QGridLayout (gb); gl->setSpacing(KDialog::spacingHint()); gl->setMargin(KDialog::marginHint()); gl->setColumnStretch(1, 1); l = d->explanationLabel = new QLabel( "", gb ); if (isLink) d->explanationLabel->setText(i18np("This file is a link and does not have permissions.", "All files are links and do not have permissions.", properties->items().count())); else if (!d->canChangePermissions) d->explanationLabel->setText(i18n("Only the owner can change permissions.")); gl->addWidget(l, 0, 0, 1, 2); lbl = new QLabel( i18n("O&wner:"), gb); gl->addWidget(lbl, 1, 0); l = d->ownerPermCombo = new QComboBox(gb); lbl->setBuddy(l); gl->addWidget(l, 1, 1); connect(l, SIGNAL( highlighted(int) ), this, SIGNAL( changed() )); l->setWhatsThis(i18n("Specifies the actions that the owner is allowed to do.")); lbl = new QLabel( i18n("Gro&up:"), gb); gl->addWidget(lbl, 2, 0); l = d->groupPermCombo = new QComboBox(gb); lbl->setBuddy(l); gl->addWidget(l, 2, 1); connect(l, SIGNAL( highlighted(int) ), this, SIGNAL( changed() )); l->setWhatsThis(i18n("Specifies the actions that the members of the group are allowed to do.")); lbl = new QLabel( i18n("O&thers:"), gb); gl->addWidget(lbl, 3, 0); l = d->othersPermCombo = new QComboBox(gb); lbl->setBuddy(l); gl->addWidget(l, 3, 1); connect(l, SIGNAL( highlighted(int) ), this, SIGNAL( changed() )); l->setWhatsThis(i18n("Specifies the actions that all users, who are neither " "owner nor in the group, are allowed to do.")); if (!isLink) { l = d->extraCheckbox = new QCheckBox(hasDir ? i18n("Only own&er can rename and delete folder content") : i18n("Is &executable"), gb ); connect( d->extraCheckbox, SIGNAL( clicked() ), this, SIGNAL( changed() ) ); gl->addWidget(l, 4, 1); l->setWhatsThis(hasDir ? i18n("Enable this option to allow only the folder's owner to " "delete or rename the contained files and folders. Other " "users can only add new files, which requires the 'Modify " "Content' permission.") : i18n("Enable this option to mark the file as executable. This only makes " "sense for programs and scripts. It is required when you want to " "execute them.")); QLayoutItem *spacer = new QSpacerItem(0, 20, QSizePolicy::Minimum, QSizePolicy::Expanding); gl->addItem(spacer, 5, 0, 1, 3); pbAdvancedPerm = new QPushButton(i18n("A&dvanced Permissions"), gb); gl->addWidget(pbAdvancedPerm, 6, 0, 1, 2, Qt::AlignRight); connect(pbAdvancedPerm, SIGNAL( clicked() ), this, SLOT( slotShowAdvancedPermissions() )); } else d->extraCheckbox = 0; /**** Group: Ownership ****/ gb = new QGroupBox ( i18n("Ownership"), d->m_frame ); box->addWidget (gb); gl = new QGridLayout (gb); gl->setSpacing(KDialog::spacingHint()); gl->setMargin(KDialog::marginHint()); gl->addItem(new QSpacerItem(0, 10), 0, 0); /*** Set Owner ***/ l = new QLabel( i18n("User:"), gb ); gl->addWidget (l, 1, 0); /* GJ: Don't autocomplete more than 1000 users. This is a kind of random * value. Huge sites having 10.000+ user have a fair chance of using NIS, * (possibly) making this unacceptably slow. * OTOH, it is nice to offer this functionality for the standard user. */ int i, maxEntries = 1000; struct passwd *user; struct group *ge; /* File owner: For root, offer a KLineEdit with autocompletion. * For a user, who can never chown() a file, offer a QLabel. */ if (IamRoot && isLocal) { d->usrEdit = new KLineEdit( gb ); KCompletion *kcom = d->usrEdit->completionObject(); kcom->setOrder(KCompletion::Sorted); setpwent(); for (i=0; ((user = getpwent()) != 0L) && (i < maxEntries); i++) kcom->addItem(QString::fromLatin1(user->pw_name)); endpwent(); d->usrEdit->setCompletionMode((i < maxEntries) ? KGlobalSettings::CompletionAuto : KGlobalSettings::CompletionNone); d->usrEdit->setText(d->strOwner); gl->addWidget(d->usrEdit, 1, 1); connect( d->usrEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) ); } else { l = new QLabel(d->strOwner, gb); gl->addWidget(l, 1, 1); } /*** Set Group ***/ QStringList groupList; QByteArray strUser; user = getpwuid(geteuid()); if (user != 0L) strUser = user->pw_name; #ifdef Q_OS_UNIX setgrent(); for (i=0; ((ge = getgrent()) != 0L) && (i < maxEntries); i++) { if (IamRoot) groupList += QString::fromLatin1(ge->gr_name); else { /* pick the groups to which the user belongs */ char ** members = ge->gr_mem; char * member; while ((member = *members) != 0L) { if (strUser == member) { groupList += QString::fromLocal8Bit(ge->gr_name); break; } ++members; } } } endgrent(); #endif //Q_OS_UNIX /* add the effective Group to the list .. */ ge = getgrgid (getegid()); if (ge) { QString name = QString::fromLatin1(ge->gr_name); if (name.isEmpty()) name.setNum(ge->gr_gid); if (groupList.indexOf(name) == -1) groupList += name; } bool isMyGroup = groupList.contains(d->strGroup); /* add the group the file currently belongs to .. * .. if its not there already */ if (!isMyGroup) groupList += d->strGroup; l = new QLabel( i18n("Group:"), gb ); gl->addWidget (l, 2, 0); /* Set group: if possible to change: * - Offer a KLineEdit for root, since he can change to any group. * - Offer a QComboBox for a normal user, since he can change to a fixed * (small) set of groups only. * If not changeable: offer a QLabel. */ if (IamRoot && isLocal) { d->grpEdit = new KLineEdit(gb); KCompletion *kcom = new KCompletion; kcom->setItems(groupList); d->grpEdit->setCompletionObject(kcom, true); d->grpEdit->setAutoDeleteCompletionObject( true ); d->grpEdit->setCompletionMode(KGlobalSettings::CompletionAuto); d->grpEdit->setText(d->strGroup); gl->addWidget(d->grpEdit, 2, 1); connect( d->grpEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) ); } else if ((groupList.count() > 1) && isMyFile && isLocal) { d->grpCombo = new QComboBox(gb); d->grpCombo->setObjectName(QLatin1String("combogrouplist")); d->grpCombo->addItems(groupList); d->grpCombo->setCurrentIndex(groupList.indexOf(d->strGroup)); gl->addWidget(d->grpCombo, 2, 1); connect( d->grpCombo, SIGNAL( activated( int ) ), this, SIGNAL( changed() ) ); } else { l = new QLabel(d->strGroup, gb); gl->addWidget(l, 2, 1); } gl->setColumnStretch(2, 10); // "Apply recursive" checkbox if ( hasDir && !isLink && !isTrash ) { d->cbRecursive = new QCheckBox( i18n("Apply changes to all subfolders and their contents"), d->m_frame ); connect( d->cbRecursive, SIGNAL( clicked() ), this, SIGNAL( changed() ) ); box->addWidget( d->cbRecursive ); } updateAccessControls(); if ( isTrash ) { //don't allow to change properties for file into trash enableAccessControls(false); if ( pbAdvancedPerm) pbAdvancedPerm->setEnabled(false); } box->addStretch (10); } #ifdef HAVE_POSIX_ACL static bool fileSystemSupportsACL( const QByteArray& path ) { bool fileSystemSupportsACLs = false; #ifdef Q_OS_FREEBSD struct statfs buf; fileSystemSupportsACLs = ( statfs( path.data(), &buf ) == 0 ) && ( buf.f_flags & MNT_ACLS ); #else fileSystemSupportsACLs = getxattr( path.data(), "system.posix_acl_access", NULL, 0 ) >= 0 || errno == ENODATA; #endif return fileSystemSupportsACLs; } #endif void KFilePermissionsPropsPlugin::slotShowAdvancedPermissions() { bool isDir = (d->pmode == PermissionsOnlyDirs) || (d->pmode == PermissionsMixed); KDialog dlg( properties ); dlg.setModal( true ); dlg.setCaption( i18n("Advanced Permissions") ); dlg.setButtons( KDialog::Ok | KDialog::Cancel ); QLabel *l, *cl[3]; QGroupBox *gb; QGridLayout *gl; QWidget *mainw = new QWidget( &dlg ); QVBoxLayout *vbox = new QVBoxLayout(mainw); // Group: Access Permissions gb = new QGroupBox ( i18n("Access Permissions"), mainw ); vbox->addWidget(gb); gl = new QGridLayout (gb); gl->setSpacing(KDialog::spacingHint()); gl->setMargin(KDialog::marginHint()); gl->addItem(new QSpacerItem(0, 10), 0, 0); QVector theNotSpecials; l = new QLabel(i18n("Class"), gb ); gl->addWidget(l, 1, 0); theNotSpecials.append( l ); if (isDir) l = new QLabel( i18n("Show\nEntries"), gb ); else l = new QLabel( i18n("Read"), gb ); gl->addWidget (l, 1, 1); theNotSpecials.append( l ); QString readWhatsThis; if (isDir) readWhatsThis = i18n("This flag allows viewing the content of the folder."); else readWhatsThis = i18n("The Read flag allows viewing the content of the file."); l->setWhatsThis(readWhatsThis); if (isDir) l = new QLabel( i18n("Write\nEntries"), gb ); else l = new QLabel( i18n("Write"), gb ); gl->addWidget (l, 1, 2); theNotSpecials.append( l ); QString writeWhatsThis; if (isDir) writeWhatsThis = i18n("This flag allows adding, renaming and deleting of files. " "Note that deleting and renaming can be limited using the Sticky flag."); else writeWhatsThis = i18n("The Write flag allows modifying the content of the file."); l->setWhatsThis(writeWhatsThis); QString execWhatsThis; if (isDir) { l = new QLabel( i18nc("Enter folder", "Enter"), gb ); execWhatsThis = i18n("Enable this flag to allow entering the folder."); } else { l = new QLabel( i18n("Exec"), gb ); execWhatsThis = i18n("Enable this flag to allow executing the file as a program."); } l->setWhatsThis(execWhatsThis); theNotSpecials.append( l ); // GJ: Add space between normal and special modes QSize size = l->sizeHint(); size.setWidth(size.width() + 15); l->setFixedSize(size); gl->addWidget (l, 1, 3); l = new QLabel( i18n("Special"), gb ); gl->addWidget(l, 1, 4, 1, 2); QString specialWhatsThis; if (isDir) specialWhatsThis = i18n("Special flag. Valid for the whole folder, the exact " "meaning of the flag can be seen in the right hand column."); else specialWhatsThis = i18n("Special flag. The exact meaning of the flag can be seen " "in the right hand column."); l->setWhatsThis(specialWhatsThis); cl[0] = new QLabel( i18n("User"), gb ); gl->addWidget (cl[0], 2, 0); theNotSpecials.append( cl[0] ); cl[1] = new QLabel( i18n("Group"), gb ); gl->addWidget (cl[1], 3, 0); theNotSpecials.append( cl[1] ); cl[2] = new QLabel( i18n("Others"), gb ); gl->addWidget (cl[2], 4, 0); theNotSpecials.append( cl[2] ); l = new QLabel(i18n("Set UID"), gb); gl->addWidget(l, 2, 5); QString setUidWhatsThis; if (isDir) setUidWhatsThis = i18n("If this flag is set, the owner of this folder will be " "the owner of all new files."); else setUidWhatsThis = i18n("If this file is an executable and the flag is set, it will " "be executed with the permissions of the owner."); l->setWhatsThis(setUidWhatsThis); l = new QLabel(i18n("Set GID"), gb); gl->addWidget(l, 3, 5); QString setGidWhatsThis; if (isDir) setGidWhatsThis = i18n("If this flag is set, the group of this folder will be " "set for all new files."); else setGidWhatsThis = i18n("If this file is an executable and the flag is set, it will " "be executed with the permissions of the group."); l->setWhatsThis(setGidWhatsThis); l = new QLabel(i18nc("File permission", "Sticky"), gb); gl->addWidget(l, 4, 5); QString stickyWhatsThis; if (isDir) stickyWhatsThis = i18n("If the Sticky flag is set on a folder, only the owner " "and root can delete or rename files. Otherwise everybody " "with write permissions can do this."); else stickyWhatsThis = i18n("The Sticky flag on a file is ignored on Linux, but may " "be used on some systems"); l->setWhatsThis(stickyWhatsThis); mode_t aPermissions, aPartialPermissions; mode_t dummy1, dummy2; if (!d->isIrregular) { switch (d->pmode) { case PermissionsOnlyFiles: getPermissionMasks(aPartialPermissions, dummy1, aPermissions, dummy2); break; case PermissionsOnlyDirs: case PermissionsMixed: getPermissionMasks(dummy1, aPartialPermissions, dummy2, aPermissions); break; case PermissionsOnlyLinks: aPermissions = UniRead | UniWrite | UniExec | UniSpecial; aPartialPermissions = 0; break; } } else { aPermissions = d->permissions; aPartialPermissions = d->partialPermissions; } // Draw Checkboxes QCheckBox *cba[3][4]; for (int row = 0; row < 3 ; ++row) { for (int col = 0; col < 4; ++col) { QCheckBox *cb = new QCheckBox(gb); if ( col != 3 ) theNotSpecials.append( cb ); cba[row][col] = cb; cb->setChecked(aPermissions & fperm[row][col]); if ( aPartialPermissions & fperm[row][col] ) { cb->setTristate(); cb->setCheckState(Qt::PartiallyChecked); } else if (d->cbRecursive && d->cbRecursive->isChecked()) cb->setTristate(); cb->setEnabled( d->canChangePermissions ); gl->addWidget (cb, row+2, col+1); switch(col) { case 0: cb->setWhatsThis(readWhatsThis); break; case 1: cb->setWhatsThis(writeWhatsThis); break; case 2: cb->setWhatsThis(execWhatsThis); break; case 3: switch(row) { case 0: cb->setWhatsThis(setUidWhatsThis); break; case 1: cb->setWhatsThis(setGidWhatsThis); break; case 2: cb->setWhatsThis(stickyWhatsThis); break; } break; } } } gl->setColumnStretch(6, 10); #ifdef HAVE_POSIX_ACL KACLEditWidget *extendedACLs = 0; // FIXME make it work with partial entries if ( properties->items().count() == 1 ) { - QByteArray path = QFile::encodeName( properties->item()->url().path() ); + QByteArray path = QFile::encodeName( properties->item().url().path() ); d->fileSystemSupportsACLs = fileSystemSupportsACL( path ); } if ( d->fileSystemSupportsACLs ) { std::for_each( theNotSpecials.begin(), theNotSpecials.end(), std::mem_fun( &QWidget::hide ) ); extendedACLs = new KACLEditWidget( mainw ); vbox->addWidget(extendedACLs); if ( d->extendedACL.isValid() && d->extendedACL.isExtended() ) extendedACLs->setACL( d->extendedACL ); else extendedACLs->setACL( KACL( aPermissions ) ); if ( d->defaultACL.isValid() ) extendedACLs->setDefaultACL( d->defaultACL ); - if ( properties->items().first()->isDir() ) + if ( properties->items().first().isDir() ) extendedACLs->setAllowDefaults( true ); } #endif dlg.setMainWidget( mainw ); if (dlg.exec() != KDialog::Accepted) return; mode_t andPermissions = mode_t(~0); mode_t orPermissions = 0; for (int row = 0; row < 3; ++row) for (int col = 0; col < 4; ++col) { switch (cba[row][col]->checkState()) { case Qt::Checked: orPermissions |= fperm[row][col]; //fall through case Qt::Unchecked: andPermissions &= ~fperm[row][col]; break; default: // NoChange break; } } d->isIrregular = false; const KFileItemList items = properties->items(); KFileItemList::const_iterator it = items.begin(); const KFileItemList::const_iterator kend = items.end(); for ( ; it != kend; ++it ) { - if (isIrregular(((*it)->permissions() & andPermissions) | orPermissions, - (*it)->isDir(), (*it)->isLink())) { + if (isIrregular(((*it).permissions() & andPermissions) | orPermissions, + (*it).isDir(), (*it).isLink())) { d->isIrregular = true; break; } } d->permissions = orPermissions; d->partialPermissions = andPermissions; #ifdef HAVE_POSIX_ACL // override with the acls, if present if ( extendedACLs ) { d->extendedACL = extendedACLs->getACL(); d->defaultACL = extendedACLs->getDefaultACL(); d->hasExtendedACL = d->extendedACL.isExtended() || d->defaultACL.isValid(); d->permissions = d->extendedACL.basePermissions(); d->permissions |= ( andPermissions | orPermissions ) & ( S_ISUID|S_ISGID|S_ISVTX ); } #endif updateAccessControls(); emit changed(); } // QString KFilePermissionsPropsPlugin::tabName () const // { // return i18n ("&Permissions"); // } KFilePermissionsPropsPlugin::~KFilePermissionsPropsPlugin() { delete d; } bool KFilePermissionsPropsPlugin::supports( const KFileItemList& /*_items*/ ) { return true; } // sets a combo box in the Access Control frame void KFilePermissionsPropsPlugin::setComboContent(QComboBox *combo, PermissionsTarget target, mode_t permissions, mode_t partial) { combo->clear(); if (d->pmode == PermissionsOnlyLinks) { combo->addItem(i18n("Link")); combo->setCurrentIndex(0); return; } mode_t tMask = permissionsMasks[target]; int textIndex; for (textIndex = 0; standardPermissions[textIndex] != (mode_t)-1; textIndex++) if ((standardPermissions[textIndex]&tMask) == (permissions&tMask&(UniRead|UniWrite))) break; Q_ASSERT(standardPermissions[textIndex] != (mode_t)-1); // must not happen, would be irreglar for (int i = 0; permissionsTexts[(int)d->pmode][i]; i++) combo->addItem(i18n(permissionsTexts[(int)d->pmode][i])); if (partial & tMask & ~UniExec) { combo->addItem(i18n("Varying (No Change)")); combo->setCurrentIndex(3); } else combo->setCurrentIndex(textIndex); } // permissions are irregular if they cant be displayed in a combo box. bool KFilePermissionsPropsPlugin::isIrregular(mode_t permissions, bool isDir, bool isLink) { if (isLink) // links are always ok return false; mode_t p = permissions; if (p & (S_ISUID | S_ISGID)) // setuid/setgid -> irregular return true; if (isDir) { p &= ~S_ISVTX; // ignore sticky on dirs // check supported flag combinations mode_t p0 = p & UniOwner; if ((p0 != 0) && (p0 != (S_IRUSR | S_IXUSR)) && (p0 != UniOwner)) return true; p0 = p & UniGroup; if ((p0 != 0) && (p0 != (S_IRGRP | S_IXGRP)) && (p0 != UniGroup)) return true; p0 = p & UniOthers; if ((p0 != 0) && (p0 != (S_IROTH | S_IXOTH)) && (p0 != UniOthers)) return true; return false; } if (p & S_ISVTX) // sticky on file -> irregular return true; // check supported flag combinations mode_t p0 = p & UniOwner; bool usrXPossible = !p0; // true if this file could be an executable if (p0 & S_IXUSR) { if ((p0 == S_IXUSR) || (p0 == (S_IWUSR | S_IXUSR))) return true; usrXPossible = true; } else if (p0 == S_IWUSR) return true; p0 = p & UniGroup; bool grpXPossible = !p0; // true if this file could be an executable if (p0 & S_IXGRP) { if ((p0 == S_IXGRP) || (p0 == (S_IWGRP | S_IXGRP))) return true; grpXPossible = true; } else if (p0 == S_IWGRP) return true; if (p0 == 0) grpXPossible = true; p0 = p & UniOthers; bool othXPossible = !p0; // true if this file could be an executable if (p0 & S_IXOTH) { if ((p0 == S_IXOTH) || (p0 == (S_IWOTH | S_IXOTH))) return true; othXPossible = true; } else if (p0 == S_IWOTH) return true; // check that there either all targets are executable-compatible, or none return (p & UniExec) && !(usrXPossible && grpXPossible && othXPossible); } // enables/disabled the widgets in the Access Control frame void KFilePermissionsPropsPlugin::enableAccessControls(bool enable) { d->ownerPermCombo->setEnabled(enable); d->groupPermCombo->setEnabled(enable); d->othersPermCombo->setEnabled(enable); if (d->extraCheckbox) d->extraCheckbox->setEnabled(enable); if ( d->cbRecursive ) d->cbRecursive->setEnabled(enable); } // updates all widgets in the Access Control frame void KFilePermissionsPropsPlugin::updateAccessControls() { setComboContent(d->ownerPermCombo, PermissionsOwner, d->permissions, d->partialPermissions); setComboContent(d->groupPermCombo, PermissionsGroup, d->permissions, d->partialPermissions); setComboContent(d->othersPermCombo, PermissionsOthers, d->permissions, d->partialPermissions); switch(d->pmode) { case PermissionsOnlyLinks: enableAccessControls(false); break; case PermissionsOnlyFiles: enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL); if (d->canChangePermissions) d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ? i18np("This file uses advanced permissions", "These files use advanced permissions.", properties->items().count()) : ""); if (d->partialPermissions & UniExec) { d->extraCheckbox->setTristate(); d->extraCheckbox->setCheckState(Qt::PartiallyChecked); } else { d->extraCheckbox->setTristate(false); d->extraCheckbox->setChecked(d->permissions & UniExec); } break; case PermissionsOnlyDirs: enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL); // if this is a dir, and we can change permissions, don't dis-allow // recursive, we can do that for ACL setting. if ( d->cbRecursive ) d->cbRecursive->setEnabled( d->canChangePermissions && !d->isIrregular ); if (d->canChangePermissions) d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ? i18np("This folder uses advanced permissions.", "These folders use advanced permissions.", properties->items().count()) : ""); if (d->partialPermissions & S_ISVTX) { d->extraCheckbox->setTristate(); d->extraCheckbox->setCheckState(Qt::PartiallyChecked); } else { d->extraCheckbox->setTristate(false); d->extraCheckbox->setChecked(d->permissions & S_ISVTX); } break; case PermissionsMixed: enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL); if (d->canChangePermissions) d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ? i18n("These files use advanced permissions.") : ""); break; if (d->partialPermissions & S_ISVTX) { d->extraCheckbox->setTristate(); d->extraCheckbox->setCheckState(Qt::PartiallyChecked); } else { d->extraCheckbox->setTristate(false); d->extraCheckbox->setChecked(d->permissions & S_ISVTX); } break; } } // gets masks for files and dirs from the Access Control frame widgets void KFilePermissionsPropsPlugin::getPermissionMasks(mode_t &andFilePermissions, mode_t &andDirPermissions, mode_t &orFilePermissions, mode_t &orDirPermissions) { andFilePermissions = mode_t(~UniSpecial); andDirPermissions = mode_t(~(S_ISUID|S_ISGID)); orFilePermissions = 0; orDirPermissions = 0; if (d->isIrregular) return; mode_t m = standardPermissions[d->ownerPermCombo->currentIndex()]; if (m != (mode_t) -1) { orFilePermissions |= m & UniOwner; if ((m & UniOwner) && ((d->pmode == PermissionsMixed) || ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->checkState() == Qt::PartiallyChecked)))) andFilePermissions &= ~(S_IRUSR | S_IWUSR); else { andFilePermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR); if ((m & S_IRUSR) && (d->extraCheckbox->checkState() == Qt::Checked)) orFilePermissions |= S_IXUSR; } orDirPermissions |= m & UniOwner; if (m & S_IRUSR) orDirPermissions |= S_IXUSR; andDirPermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR); } m = standardPermissions[d->groupPermCombo->currentIndex()]; if (m != (mode_t) -1) { orFilePermissions |= m & UniGroup; if ((m & UniGroup) && ((d->pmode == PermissionsMixed) || ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->checkState() == Qt::PartiallyChecked)))) andFilePermissions &= ~(S_IRGRP | S_IWGRP); else { andFilePermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP); if ((m & S_IRGRP) && (d->extraCheckbox->checkState() == Qt::Checked)) orFilePermissions |= S_IXGRP; } orDirPermissions |= m & UniGroup; if (m & S_IRGRP) orDirPermissions |= S_IXGRP; andDirPermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP); } m = d->othersPermCombo->currentIndex() >= 0 ? standardPermissions[d->othersPermCombo->currentIndex()] : (mode_t)-1; if (m != (mode_t) -1) { orFilePermissions |= m & UniOthers; if ((m & UniOthers) && ((d->pmode == PermissionsMixed) || ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->checkState() == Qt::PartiallyChecked)))) andFilePermissions &= ~(S_IROTH | S_IWOTH); else { andFilePermissions &= ~(S_IROTH | S_IWOTH | S_IXOTH); if ((m & S_IROTH) && (d->extraCheckbox->checkState() == Qt::Checked)) orFilePermissions |= S_IXOTH; } orDirPermissions |= m & UniOthers; if (m & S_IROTH) orDirPermissions |= S_IXOTH; andDirPermissions &= ~(S_IROTH | S_IWOTH | S_IXOTH); } if (((d->pmode == PermissionsMixed) || (d->pmode == PermissionsOnlyDirs)) && (d->extraCheckbox->checkState() != Qt::PartiallyChecked)) { andDirPermissions &= ~S_ISVTX; if (d->extraCheckbox->checkState() == Qt::Checked) orDirPermissions |= S_ISVTX; } } void KFilePermissionsPropsPlugin::applyChanges() { mode_t orFilePermissions; mode_t orDirPermissions; mode_t andFilePermissions; mode_t andDirPermissions; if (!d->canChangePermissions) return; if (!d->isIrregular) getPermissionMasks(andFilePermissions, andDirPermissions, orFilePermissions, orDirPermissions); else { orFilePermissions = d->permissions; andFilePermissions = d->partialPermissions; orDirPermissions = d->permissions; andDirPermissions = d->partialPermissions; } QString owner, group; if (d->usrEdit) owner = d->usrEdit->text(); if (d->grpEdit) group = d->grpEdit->text(); else if (d->grpCombo) group = d->grpCombo->currentText(); if (owner == d->strOwner) owner.clear(); // no change if (group == d->strGroup) group.clear(); bool recursive = d->cbRecursive && d->cbRecursive->isChecked(); bool permissionChange = false; KFileItemList files, dirs; const KFileItemList items = properties->items(); KFileItemList::const_iterator it = items.begin(); const KFileItemList::const_iterator kend = items.end(); for ( ; it != kend; ++it ) { - if ((*it)->isDir()) { + if ((*it).isDir()) { dirs.append(*it); - if ((*it)->permissions() != (((*it)->permissions() & andDirPermissions) | orDirPermissions)) + if ((*it).permissions() != (((*it).permissions() & andDirPermissions) | orDirPermissions)) permissionChange = true; } - else if ((*it)->isFile()) { + else if ((*it).isFile()) { files.append(*it); - if ((*it)->permissions() != (((*it)->permissions() & andFilePermissions) | orFilePermissions)) + if ((*it).permissions() != (((*it).permissions() & andFilePermissions) | orFilePermissions)) permissionChange = true; } } - const bool ACLChange = ( d->extendedACL != properties->item()->ACL() ); - const bool defaultACLChange = ( d->defaultACL != properties->item()->defaultACL() ); + const bool ACLChange = ( d->extendedACL != properties->item().ACL() ); + const bool defaultACLChange = ( d->defaultACL != properties->item().defaultACL() ); if ( owner.isEmpty() && group.isEmpty() && !recursive && !permissionChange && !ACLChange && !defaultACLChange ) return; KIO::Job * job; if (files.count() > 0) { job = KIO::chmod( files, orFilePermissions, ~andFilePermissions, owner, group, false ); if ( ACLChange && d->fileSystemSupportsACLs ) job->addMetaData( "ACL_STRING", d->extendedACL.isValid()?d->extendedACL.asString():"ACL_DELETE" ); if ( defaultACLChange && d->fileSystemSupportsACLs ) job->addMetaData( "DEFAULT_ACL_STRING", d->defaultACL.isValid()?d->defaultACL.asString():"ACL_DELETE" ); connect( job, SIGNAL( result( KJob * ) ), SLOT( slotChmodResult( KJob * ) ) ); QEventLoop eventLoop; connect(this, SIGNAL(leaveModality()), &eventLoop, SLOT(quit())); eventLoop.exec(QEventLoop::ExcludeUserInputEvents); } if (dirs.count() > 0) { job = KIO::chmod( dirs, orDirPermissions, ~andDirPermissions, owner, group, recursive ); if ( ACLChange && d->fileSystemSupportsACLs ) job->addMetaData( "ACL_STRING", d->extendedACL.isValid()?d->extendedACL.asString():"ACL_DELETE" ); if ( defaultACLChange && d->fileSystemSupportsACLs ) job->addMetaData( "DEFAULT_ACL_STRING", d->defaultACL.isValid()?d->defaultACL.asString():"ACL_DELETE" ); connect( job, SIGNAL( result( KJob * ) ), SLOT( slotChmodResult( KJob * ) ) ); QEventLoop eventLoop; connect(this, SIGNAL(leaveModality()), &eventLoop, SLOT(quit())); eventLoop.exec(QEventLoop::ExcludeUserInputEvents); } } void KFilePermissionsPropsPlugin::slotChmodResult( KJob * job ) { kDebug(250) << "KFilePermissionsPropsPlugin::slotChmodResult"; if (job->error()) job->uiDelegate()->showErrorMessage(); // allow apply() to return emit leaveModality(); } class KUrlPropsPlugin::KUrlPropsPluginPrivate { public: KUrlPropsPluginPrivate() { } ~KUrlPropsPluginPrivate() { } QFrame *m_frame; KUrlRequester *URLEdit; QString URLStr; }; KUrlPropsPlugin::KUrlPropsPlugin( KPropertiesDialog *_props ) : KPropertiesDialogPlugin( _props ),d(new KUrlPropsPluginPrivate) { d->m_frame = new QFrame(); properties->addPage(d->m_frame, i18n("U&RL")); QVBoxLayout *layout = new QVBoxLayout(d->m_frame); layout->setMargin(0); layout->setSpacing(KDialog::spacingHint()); QLabel *l; l = new QLabel( d->m_frame ); l->setObjectName( QLatin1String( "Label_1" ) ); l->setText( i18n("URL:") ); layout->addWidget(l); d->URLEdit = new KUrlRequester( d->m_frame ); layout->addWidget(d->URLEdit); QString path = properties->kurl().path(); QFile f( path ); if ( !f.open( QIODevice::ReadOnly ) ) return; f.close(); KDesktopFile config( path ); const KConfigGroup dg = config.desktopGroup(); d->URLStr = dg.readPathEntry( "URL" ); if ( !d->URLStr.isEmpty() ) d->URLEdit->setUrl( KUrl(d->URLStr) ); connect( d->URLEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) ); layout->addStretch (1); } KUrlPropsPlugin::~KUrlPropsPlugin() { delete d; } // QString KUrlPropsPlugin::tabName () const // { // return i18n ("U&RL"); // } bool KUrlPropsPlugin::supports( const KFileItemList& _items ) { if ( _items.count() != 1 ) return false; - KFileItem * item = _items.first(); + const KFileItem item = _items.first(); // check if desktop file if ( !KPropertiesDialogPlugin::isDesktopFile( item ) ) return false; // open file and check type - KDesktopFile config( item->url().path() ); + KDesktopFile config( item.url().path() ); return config.hasLinkType(); } void KUrlPropsPlugin::applyChanges() { QString path = properties->kurl().path(); QFile f( path ); if ( !f.open( QIODevice::ReadWrite ) ) { KMessageBox::sorry( 0, i18n("Could not save properties. You do not have " "sufficient access to write to %1.", path)); return; } f.close(); KDesktopFile config( path ); KConfigGroup dg = config.desktopGroup(); dg.writeEntry( "Type", QString::fromLatin1("Link")); dg.writePathEntry( "URL", d->URLEdit->url().url() ); // Users can't create a Link .desktop file with a Name field, // but distributions can. Update the Name field in that case. if ( dg.hasKey("Name") ) { QString nameStr = nameFromFileName(properties->kurl().fileName()); dg.writeEntry( "Name", nameStr ); dg.writeEntry( "Name", nameStr, KConfigBase::Persistent|KConfigBase::NLS ); } } /* ---------------------------------------------------- * * KBindingPropsPlugin * * -------------------------------------------------- */ class KBindingPropsPlugin::KBindingPropsPluginPrivate { public: KBindingPropsPluginPrivate() { } ~KBindingPropsPluginPrivate() { } QFrame *m_frame; QLineEdit *commentEdit; QLineEdit *patternEdit; QLineEdit *mimeEdit; QString m_sMimeStr; QCheckBox * cbAutoEmbed; }; KBindingPropsPlugin::KBindingPropsPlugin( KPropertiesDialog *_props ) : KPropertiesDialogPlugin( _props ),d(new KBindingPropsPluginPrivate) { d->m_frame = new QFrame(); properties->addPage(d->m_frame, i18n("A&ssociation")); QBoxLayout *mainlayout = new QVBoxLayout(d->m_frame); mainlayout->setMargin(0); mainlayout->setSpacing(KDialog::spacingHint()); QLabel* tmpQLabel; tmpQLabel = new QLabel( d->m_frame ); tmpQLabel->setObjectName( QLatin1String( "Label_1" ) ); tmpQLabel->setText( i18n("Pattern ( example: *.html;*.htm )") ); tmpQLabel->setMinimumSize(tmpQLabel->sizeHint()); mainlayout->addWidget(tmpQLabel, 1); d->patternEdit = new KLineEdit( d->m_frame ); //d->patternEdit->setGeometry( 10, 40, 210, 30 ); //d->patternEdit->setText( "" ); d->patternEdit->setMaxLength( 512 ); d->patternEdit->setMinimumSize( d->patternEdit->sizeHint() ); d->patternEdit->setFixedHeight( fontHeight ); mainlayout->addWidget(d->patternEdit, 1); tmpQLabel = new QLabel( d->m_frame ); tmpQLabel->setObjectName( QLatin1String( "Label_2" ) ); tmpQLabel->setText( i18n("Mime Type") ); tmpQLabel->setMinimumSize(tmpQLabel->sizeHint()); mainlayout->addWidget(tmpQLabel, 1); d->mimeEdit = new KLineEdit( d->m_frame ); //d->mimeEdit->setGeometry( 10, 160, 210, 30 ); d->mimeEdit->setMaxLength( 256 ); d->mimeEdit->setMinimumSize( d->mimeEdit->sizeHint() ); d->mimeEdit->setFixedHeight( fontHeight ); mainlayout->addWidget(d->mimeEdit, 1); tmpQLabel = new QLabel( d->m_frame ); tmpQLabel->setObjectName( QLatin1String( "Label_3" ) ); tmpQLabel->setText( i18n("Comment") ); tmpQLabel->setMinimumSize(tmpQLabel->sizeHint()); mainlayout->addWidget(tmpQLabel, 1); d->commentEdit = new KLineEdit( d->m_frame ); //d->commentEdit->setGeometry( 10, 100, 210, 30 ); d->commentEdit->setMaxLength( 256 ); d->commentEdit->setMinimumSize( d->commentEdit->sizeHint() ); d->commentEdit->setFixedHeight( fontHeight ); mainlayout->addWidget(d->commentEdit, 1); d->cbAutoEmbed = new QCheckBox( i18n("Left click previews"), d->m_frame ); d->cbAutoEmbed->setObjectName( QLatin1String( "cbAutoEmbed" ) ); mainlayout->addWidget(d->cbAutoEmbed, 1); mainlayout->addStretch (10); mainlayout->activate(); QFile f( _props->kurl().path() ); if ( !f.open( QIODevice::ReadOnly ) ) return; f.close(); const KDesktopFile _config( _props->kurl().path() ); const KConfigGroup config = _config.desktopGroup(); QString patternStr = config.readEntry( "Patterns" ); QString iconStr = config.readEntry( "Icon" ); QString commentStr = config.readEntry( "Comment" ); d->m_sMimeStr = config.readEntry( "MimeType" ); if ( !patternStr.isEmpty() ) d->patternEdit->setText( patternStr ); if ( !commentStr.isEmpty() ) d->commentEdit->setText( commentStr ); if ( !d->m_sMimeStr.isEmpty() ) d->mimeEdit->setText( d->m_sMimeStr ); d->cbAutoEmbed->setTristate(); if ( config.hasKey( "X-KDE-AutoEmbed" ) ) d->cbAutoEmbed->setChecked( config.readEntry( "X-KDE-AutoEmbed", false ) ); else d->cbAutoEmbed->setCheckState(Qt::PartiallyChecked); connect( d->patternEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) ); connect( d->commentEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) ); connect( d->mimeEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) ); connect( d->cbAutoEmbed, SIGNAL( toggled( bool ) ), this, SIGNAL( changed() ) ); } KBindingPropsPlugin::~KBindingPropsPlugin() { delete d; } // QString KBindingPropsPlugin::tabName () const // { // return i18n ("A&ssociation"); // } bool KBindingPropsPlugin::supports( const KFileItemList& _items ) { if ( _items.count() != 1 ) return false; - KFileItem * item = _items.first(); + const KFileItem item = _items.first(); // check if desktop file if ( !KPropertiesDialogPlugin::isDesktopFile( item ) ) return false; // open file and check type - KDesktopFile config( item->url().path() ); + KDesktopFile config( item.url().path() ); return config.hasMimeTypeType(); } void KBindingPropsPlugin::applyChanges() { QString path = properties->kurl().path(); QFile f( path ); if ( !f.open( QIODevice::ReadWrite ) ) { KMessageBox::sorry( 0, i18n("Could not save properties. You do not have " "sufficient access to write to %1.", path)); return; } f.close(); KDesktopFile config( path ); KConfigGroup dg = config.desktopGroup(); dg.writeEntry( "Type", QString::fromLatin1("MimeType") ); dg.writeEntry( "Patterns", d->patternEdit->text() ); dg.writeEntry( "Comment", d->commentEdit->text() ); dg.writeEntry( "Comment", d->commentEdit->text(), KConfigBase::Persistent|KConfigBase::NLS ); // for compat dg.writeEntry( "MimeType", d->mimeEdit->text() ); if ( d->cbAutoEmbed->checkState() == Qt::PartiallyChecked ) dg.deleteEntry( "X-KDE-AutoEmbed" ); else dg.writeEntry( "X-KDE-AutoEmbed", d->cbAutoEmbed->isChecked() ); dg.sync(); } /* ---------------------------------------------------- * * KDevicePropsPlugin * * -------------------------------------------------- */ class KDevicePropsPlugin::KDevicePropsPluginPrivate { public: KDevicePropsPluginPrivate() { } ~KDevicePropsPluginPrivate() { } QFrame *m_frame; QStringList mountpointlist; QLabel *m_freeSpaceText; QLabel *m_freeSpaceLabel; QProgressBar *m_freeSpaceBar; QComboBox* device; QLabel* mountpoint; QCheckBox* readonly; KIconButton* unmounted; QStringList m_devicelist; }; KDevicePropsPlugin::KDevicePropsPlugin( KPropertiesDialog *_props ) : KPropertiesDialogPlugin( _props ),d(new KDevicePropsPluginPrivate) { d->m_frame = new QFrame(); properties->addPage(d->m_frame, i18n("De&vice")); QStringList devices; KMountPoint::List mountPoints = KMountPoint::possibleMountPoints(); for(KMountPoint::List::ConstIterator it = mountPoints.begin(); it != mountPoints.end(); ++it) { const KMountPoint::Ptr mp = (*it); QString mountPoint = mp->mountPoint(); QString device = mp->mountedFrom(); kDebug()<<"mountPoint :"<mountType() :"<mountType(); if ((mountPoint != "-") && (mountPoint != "none") && !mountPoint.isEmpty() && device != "none") { devices.append( device + QString::fromLatin1(" (") + mountPoint + QString::fromLatin1(")") ); d->m_devicelist.append(device); d->mountpointlist.append(mountPoint); } } QGridLayout *layout = new QGridLayout( d->m_frame ); layout->setMargin(0); layout->setSpacing(KDialog::spacingHint()); layout->setColumnStretch(1, 1); QLabel* label; label = new QLabel( d->m_frame ); label->setText( devices.count() == 0 ? i18n("Device (/dev/fd0):") : // old style i18n("Device:") ); // new style (combobox) layout->addWidget(label, 0, 0); d->device = new QComboBox( d->m_frame ); d->device->setObjectName( QLatin1String( "ComboBox_device" ) ); d->device->setEditable( true ); d->device->addItems( devices ); layout->addWidget(d->device, 0, 1); connect( d->device, SIGNAL( activated( int ) ), this, SLOT( slotActivated( int ) ) ); d->readonly = new QCheckBox( d->m_frame ); d->readonly->setObjectName( QLatin1String( "CheckBox_readonly" ) ); d->readonly->setText( i18n("Read only") ); layout->addWidget(d->readonly, 1, 1); label = new QLabel( d->m_frame ); label->setText( i18n("File system:") ); layout->addWidget(label, 2, 0); QLabel *fileSystem = new QLabel( d->m_frame ); layout->addWidget(fileSystem, 2, 1); label = new QLabel( d->m_frame ); label->setText( devices.count()==0 ? i18n("Mount point (/mnt/floppy):") : // old style i18n("Mount point:")); // new style (combobox) layout->addWidget(label, 3, 0); d->mountpoint = new QLabel( d->m_frame ); d->mountpoint->setObjectName( QLatin1String( "LineEdit_mountpoint" ) ); layout->addWidget(d->mountpoint, 3, 1); // show disk free d->m_freeSpaceText = new QLabel(i18n("Free disk space:"), d->m_frame ); layout->addWidget(d->m_freeSpaceText, 4, 0); d->m_freeSpaceLabel = new QLabel( d->m_frame ); layout->addWidget( d->m_freeSpaceLabel, 4, 1 ); d->m_freeSpaceBar = new QProgressBar( d->m_frame ); d->m_freeSpaceBar->setObjectName( "freeSpaceBar" ); layout->addWidget(d->m_freeSpaceBar, 5, 0, 1, 2); // we show it in the slot when we know the values d->m_freeSpaceText->hide(); d->m_freeSpaceLabel->hide(); d->m_freeSpaceBar->hide(); KSeparator* sep = new KSeparator( Qt::Horizontal, d->m_frame); layout->addWidget(sep, 6, 0, 1, 2); d->unmounted = new KIconButton( d->m_frame ); int bsize = 66 + 2 * d->unmounted->style()->pixelMetric(QStyle::PM_ButtonMargin); d->unmounted->setFixedSize(bsize, bsize); d->unmounted->setIconType(K3Icon::Desktop, K3Icon::Device); layout->addWidget(d->unmounted, 7, 0); label = new QLabel( i18n("Unmounted Icon"), d->m_frame ); layout->addWidget(label, 7, 1); layout->setRowStretch(8, 1); QString path( _props->kurl().path() ); QFile f( path ); if ( !f.open( QIODevice::ReadOnly ) ) return; f.close(); const KDesktopFile _config( path ); const KConfigGroup config = _config.desktopGroup(); QString deviceStr = config.readEntry( "Dev" ); QString mountPointStr = config.readEntry( "MountPoint" ); bool ro = config.readEntry( "ReadOnly", false ); QString unmountedStr = config.readEntry( "UnmountIcon" ); fileSystem->setText( i18n(config.readEntry("FSType").toLocal8Bit()) ); d->device->setEditText( deviceStr ); if ( !deviceStr.isEmpty() ) { // Set default options for this device (first matching entry) int index = d->m_devicelist.indexOf(deviceStr); if (index != -1) { //kDebug(250) << "found it " << index; slotActivated( index ); } } if ( !mountPointStr.isEmpty() ) { d->mountpoint->setText( mountPointStr ); updateInfo(); } d->readonly->setChecked( ro ); if ( unmountedStr.isEmpty() ) unmountedStr = KMimeType::defaultMimeTypePtr()->iconName(); // default icon d->unmounted->setIcon( unmountedStr ); connect( d->device, SIGNAL( activated( int ) ), this, SIGNAL( changed() ) ); connect( d->device, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) ); connect( d->readonly, SIGNAL( toggled( bool ) ), this, SIGNAL( changed() ) ); connect( d->unmounted, SIGNAL( iconChanged( const QString& ) ), this, SIGNAL( changed() ) ); connect( d->device, SIGNAL( textChanged( const QString & ) ), this, SLOT( slotDeviceChanged() ) ); } KDevicePropsPlugin::~KDevicePropsPlugin() { delete d; } // QString KDevicePropsPlugin::tabName () const // { // return i18n ("De&vice"); // } void KDevicePropsPlugin::updateInfo() { // we show it in the slot when we know the values d->m_freeSpaceText->hide(); d->m_freeSpaceLabel->hide(); d->m_freeSpaceBar->hide(); if ( !d->mountpoint->text().isEmpty() ) { KDiskFreeSpace * job = new KDiskFreeSpace; connect(job, SIGNAL(foundMountPoint(QString, quint64, quint64, quint64)), this, SLOT(slotFoundMountPoint(QString, quint64, quint64, quint64))); job->readDF( d->mountpoint->text() ); } } void KDevicePropsPlugin::slotActivated( int index ) { // Update mountpoint so that it matches the device that was selected in the combo d->device->setEditText( d->m_devicelist[index] ); d->mountpoint->setText( d->mountpointlist[index] ); updateInfo(); } void KDevicePropsPlugin::slotDeviceChanged() { // Update mountpoint so that it matches the typed device int index = d->m_devicelist.indexOf( d->device->currentText() ); if ( index != -1 ) d->mountpoint->setText( d->mountpointlist[index] ); else d->mountpoint->setText( QString() ); updateInfo(); } void KDevicePropsPlugin::slotFoundMountPoint( const QString&, quint64 kBSize, quint64 /*kBUsed*/, quint64 kBAvail ) { d->m_freeSpaceText->show(); d->m_freeSpaceLabel->show(); int percUsed = 100 - (int)(100.0 * kBAvail / kBSize); d->m_freeSpaceLabel->setText( i18nc("Available space out of total partition size (percent used)", "%1 out of %2 (%3% used)", KIO::convertSizeFromKiB(kBAvail), KIO::convertSizeFromKiB(kBSize), 100 - (int)(100.0 * kBAvail / kBSize) )); d->m_freeSpaceBar->setRange(0, 100); d->m_freeSpaceBar->setValue(percUsed); d->m_freeSpaceBar->show(); } bool KDevicePropsPlugin::supports( const KFileItemList& _items ) { if ( _items.count() != 1 ) return false; - KFileItem * item = _items.first(); + const KFileItem item = _items.first(); // check if desktop file if ( !KPropertiesDialogPlugin::isDesktopFile( item ) ) return false; // open file and check type - KDesktopFile config( item->url().path() ); + KDesktopFile config( item.url().path() ); return config.hasDeviceType(); } void KDevicePropsPlugin::applyChanges() { QString path = properties->kurl().path(); QFile f( path ); if ( !f.open( QIODevice::ReadWrite ) ) { KMessageBox::sorry( 0, i18n("Could not save properties. You do not have sufficient " "access to write to %1.", path)); return; } f.close(); KDesktopFile _config( path ); KConfigGroup config = _config.desktopGroup(); config.writeEntry( "Type", QString::fromLatin1("FSDevice") ); config.writeEntry( "Dev", d->device->currentText() ); config.writeEntry( "MountPoint", d->mountpoint->text() ); config.writeEntry( "UnmountIcon", d->unmounted->icon() ); kDebug(250) << "d->unmounted->icon() = " << d->unmounted->icon(); config.writeEntry( "ReadOnly", d->readonly->isChecked() ); config.sync(); } /* ---------------------------------------------------- * * KDesktopPropsPlugin * * -------------------------------------------------- */ class KDesktopPropsPlugin::KDesktopPropsPluginPrivate { public: KDesktopPropsPluginPrivate() : w( new Ui_KPropertiesDesktopBase ) , m_frame( new QFrame() ) { } ~KDesktopPropsPluginPrivate() { delete w; } Ui_KPropertiesDesktopBase* w; QWidget *m_frame; QString m_origCommandStr; QString m_terminalOptionStr; QString m_suidUserStr; QString m_dbusStartusType; bool m_terminalBool; bool m_suidBool; bool m_startupBool; bool m_systrayBool; }; KDesktopPropsPlugin::KDesktopPropsPlugin( KPropertiesDialog *_props ) : KPropertiesDialogPlugin( _props ), d( new KDesktopPropsPluginPrivate ) { properties->addPage(d->m_frame, i18n("&Application")); d->w->setupUi(d->m_frame); bool bKDesktopMode = (qApp->objectName() == "kdesktop"); if (bKDesktopMode) { // Hide Name entry d->w->nameEdit->hide(); d->w->nameLabel->hide(); } d->w->pathEdit->setMode(KFile::Directory | KFile::LocalOnly); d->w->pathEdit->lineEdit()->setAcceptDrops(false); connect( d->w->nameEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) ); connect( d->w->genNameEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) ); connect( d->w->commentEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) ); connect( d->w->commandEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) ); connect( d->w->pathEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) ); connect( d->w->browseButton, SIGNAL( clicked() ), this, SLOT( slotBrowseExec() ) ); connect( d->w->addFiletypeButton, SIGNAL( clicked() ), this, SLOT( slotAddFiletype() ) ); connect( d->w->delFiletypeButton, SIGNAL( clicked() ), this, SLOT( slotDelFiletype() ) ); connect( d->w->advancedButton, SIGNAL( clicked() ), this, SLOT( slotAdvanced() ) ); // now populate the page QString path = _props->kurl().path(); QFile f( path ); if ( !f.open( QIODevice::ReadOnly ) ) return; f.close(); KDesktopFile _config( path ); KConfigGroup config = _config.desktopGroup(); QString nameStr = _config.readName(); QString genNameStr = _config.readGenericName(); QString commentStr = _config.readComment(); QString commandStr = config.readPathEntry( "Exec" ); if (commandStr.startsWith(QLatin1String("ksystraycmd "))) { commandStr.remove(0, 12); d->m_systrayBool = true; } else d->m_systrayBool = false; d->m_origCommandStr = commandStr; QString pathStr = config.readPathEntry( "Path" ); d->m_terminalBool = config.readEntry( "Terminal", false ); d->m_terminalOptionStr = config.readEntry( "TerminalOptions" ); d->m_suidBool = config.readEntry( "X-KDE-SubstituteUID", false ); d->m_suidUserStr = config.readEntry( "X-KDE-Username" ); if( config.hasKey( "StartupNotify" )) d->m_startupBool = config.readEntry( "StartupNotify", true ); else d->m_startupBool = config.readEntry( "X-KDE-StartupNotify", true ); d->m_dbusStartusType = config.readEntry("X-DBUS-StartupType").toLower(); //Compatibility if( d->m_dbusStartusType.isEmpty() && config.hasKey("X-DCOP-ServiceType")) d->m_dbusStartusType = config.readEntry("X-DCOP-ServiceType").toLower(); QStringList mimeTypes = config.readEntry( "MimeType", QStringList(), ';' ); if ( nameStr.isEmpty() || bKDesktopMode ) { // We'll use the file name if no name is specified // because we _need_ a Name for a valid file. // But let's do it in apply, not here, so that we pick up the right name. setDirty(); } if ( !bKDesktopMode ) d->w->nameEdit->setText(nameStr); d->w->genNameEdit->setText( genNameStr ); d->w->commentEdit->setText( commentStr ); d->w->commandEdit->setText( commandStr ); d->w->pathEdit->lineEdit()->setText( pathStr ); // was: d->w->filetypeList->setFullWidth(true); // d->w->filetypeList->header()->setStretchEnabled(true, d->w->filetypeList->columns()-1); KMimeType::Ptr defaultMimetype = KMimeType::defaultMimeTypePtr(); for(QStringList::ConstIterator it = mimeTypes.begin(); it != mimeTypes.end(); ) { KMimeType::Ptr p = KMimeType::mimeType(*it); ++it; QString preference; if (it != mimeTypes.end()) { bool numeric; (*it).toInt(&numeric); if (numeric) { preference = *it; ++it; } } if (p && (p != defaultMimetype)) { QTreeWidgetItem *item = new QTreeWidgetItem(); item->setText(0, p->name()); item->setText(1, p->comment()); item->setText(2, preference); d->w->filetypeList->addTopLevelItem(item); } } d->w->filetypeList->resizeColumnToContents(0); } KDesktopPropsPlugin::~KDesktopPropsPlugin() { delete d; } void KDesktopPropsPlugin::slotAddFiletype() { KMimeTypeChooserDialog dlg( i18n("Add File Type for %1", properties->kurl().fileName()), i18n("Select one or more file types to add:"), QStringList(), // no preselected mimetypes QString(), QStringList(), KMimeTypeChooser::Comments|KMimeTypeChooser::Patterns, d->m_frame ); if (dlg.exec() == KDialog::Accepted) { foreach(QString mimetype, dlg.chooser()->mimeTypes()) { KMimeType::Ptr p = KMimeType::mimeType(mimetype); if (!p) continue; bool found = false; int count = d->w->filetypeList->topLevelItemCount(); for (int i = 0; !found && i < count; ++i) { if (d->w->filetypeList->topLevelItem(i)->text(0) == mimetype) { found = true; } } if (!found) { QTreeWidgetItem *item = new QTreeWidgetItem(); item->setText(0, p->name()); item->setText(1, p->comment()); d->w->filetypeList->addTopLevelItem(item); } d->w->filetypeList->resizeColumnToContents(0); } } emit changed(); } void KDesktopPropsPlugin::slotDelFiletype() { QTreeWidgetItem *cur = d->w->filetypeList->currentItem(); if (cur) { delete cur; emit changed(); } } void KDesktopPropsPlugin::checkCommandChanged() { if (KRun::binaryName(d->w->commandEdit->text(), true) != KRun::binaryName(d->m_origCommandStr, true)) { d->m_origCommandStr = d->w->commandEdit->text(); d->m_dbusStartusType.clear(); // Reset } } void KDesktopPropsPlugin::applyChanges() { kDebug(250) << "KDesktopPropsPlugin::applyChanges"; QString path = properties->kurl().path(); QFile f( path ); if ( !f.open( QIODevice::ReadWrite ) ) { KMessageBox::sorry( 0, i18n("Could not save properties. You do not have " "sufficient access to write to %1.", path)); return; } f.close(); // If the command is changed we reset certain settings that are strongly // coupled to the command. checkCommandChanged(); KDesktopFile _config( path ); KConfigGroup config = _config.desktopGroup(); config.writeEntry( "Type", QString::fromLatin1("Application")); config.writeEntry( "Comment", d->w->commentEdit->text() ); config.writeEntry( "Comment", d->w->commentEdit->text(), KConfigBase::Persistent|KConfigBase::NLS ); // for compat config.writeEntry( "GenericName", d->w->genNameEdit->text() ); config.writeEntry( "GenericName", d->w->genNameEdit->text(), KConfigBase::Persistent|KConfigBase::NLS ); // for compat if (d->m_systrayBool) config.writePathEntry( "Exec", d->w->commandEdit->text().prepend("ksystraycmd ") ); else config.writePathEntry( "Exec", d->w->commandEdit->text() ); config.writePathEntry( "Path", d->w->pathEdit->lineEdit()->text() ); // Write mimeTypes QStringList mimeTypes; int count = d->w->filetypeList->topLevelItemCount(); for (int i = 0; i < count; ++i) { QTreeWidgetItem *item = d->w->filetypeList->topLevelItem(i); QString preference = item->text(2); mimeTypes.append(item->text(0)); if (!preference.isEmpty()) mimeTypes.append(preference); } kDebug() << mimeTypes; config.writeEntry( "MimeType", mimeTypes, ';' ); if ( !d->w->nameEdit->isHidden() ) { QString nameStr = d->w->nameEdit->text(); config.writeEntry( "Name", nameStr ); config.writeEntry( "Name", nameStr, KConfigBase::Persistent|KConfigBase::NLS ); } config.writeEntry("Terminal", d->m_terminalBool); config.writeEntry("TerminalOptions", d->m_terminalOptionStr); config.writeEntry("X-KDE-SubstituteUID", d->m_suidBool); config.writeEntry("X-KDE-Username", d->m_suidUserStr); config.writeEntry("StartupNotify", d->m_startupBool); config.writeEntry("X-DBUS-StartupType", d->m_dbusStartusType); config.sync(); // KSycoca update needed? QString sycocaPath = KGlobal::dirs()->relativeLocation("apps", path); bool updateNeeded = !sycocaPath.startsWith('/'); if (!updateNeeded) { sycocaPath = KGlobal::dirs()->relativeLocation("xdgdata-apps", path); updateNeeded = !sycocaPath.startsWith('/'); } if (updateNeeded) KBuildSycocaProgressDialog::rebuildKSycoca(d->m_frame); } void KDesktopPropsPlugin::slotBrowseExec() { KUrl f = KFileDialog::getOpenUrl( KUrl(), QString(), d->m_frame ); if ( f.isEmpty() ) return; if ( !f.isLocalFile()) { KMessageBox::sorry(d->m_frame, i18n("Only executables on local file systems are supported.")); return; } QString path = f.path(); path = KShell::quoteArg( path ); d->w->commandEdit->setText( path ); } void KDesktopPropsPlugin::slotAdvanced() { KDialog dlg( d->m_frame ); dlg.setObjectName( "KPropertiesDesktopAdv" ); dlg.setModal( true ); dlg.setCaption( i18n("Advanced Options for %1", properties->kurl().fileName()) ); dlg.setButtons( KDialog::Ok | KDialog::Cancel ); dlg.setDefaultButton( KDialog::Ok ); Ui_KPropertiesDesktopAdvBase w; w.setupUi(dlg.mainWidget()); // If the command is changed we reset certain settings that are strongly // coupled to the command. checkCommandChanged(); // check to see if we use konsole if not do not add the nocloseonexit // because we don't know how to do this on other terminal applications KConfigGroup confGroup( KGlobal::config(), QString::fromLatin1("General") ); QString preferredTerminal = confGroup.readPathEntry("TerminalApplication", QString::fromLatin1("konsole")); bool terminalCloseBool = false; if (preferredTerminal == "konsole") { terminalCloseBool = (d->m_terminalOptionStr.contains( "--noclose" ) > 0); w.terminalCloseCheck->setChecked(terminalCloseBool); d->m_terminalOptionStr.replace( "--noclose", ""); } else { w.terminalCloseCheck->hide(); } w.terminalCheck->setChecked(d->m_terminalBool); w.terminalEdit->setText(d->m_terminalOptionStr); w.terminalCloseCheck->setEnabled(d->m_terminalBool); w.terminalEdit->setEnabled(d->m_terminalBool); w.terminalEditLabel->setEnabled(d->m_terminalBool); w.suidCheck->setChecked(d->m_suidBool); w.suidEdit->setText(d->m_suidUserStr); w.suidEdit->setEnabled(d->m_suidBool); w.suidEditLabel->setEnabled(d->m_suidBool); w.startupInfoCheck->setChecked(d->m_startupBool); w.systrayCheck->setChecked(d->m_systrayBool); if (d->m_dbusStartusType == "unique") w.dbusCombo->setCurrentIndex(2); else if (d->m_dbusStartusType == "multi") w.dbusCombo->setCurrentIndex(1); else if (d->m_dbusStartusType == "wait") w.dbusCombo->setCurrentIndex(3); else w.dbusCombo->setCurrentIndex(0); // Provide username completion up to 1000 users. KCompletion *kcom = new KCompletion; kcom->setOrder(KCompletion::Sorted); struct passwd *pw; int i, maxEntries = 1000; setpwent(); for (i=0; ((pw = getpwent()) != 0L) && (i < maxEntries); i++) kcom->addItem(QString::fromLatin1(pw->pw_name)); endpwent(); if (i < maxEntries) { w.suidEdit->setCompletionObject(kcom, true); w.suidEdit->setAutoDeleteCompletionObject( true ); w.suidEdit->setCompletionMode(KGlobalSettings::CompletionAuto); } else { delete kcom; } connect( w.terminalEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) ); connect( w.terminalCloseCheck, SIGNAL( toggled( bool ) ), this, SIGNAL( changed() ) ); connect( w.terminalCheck, SIGNAL( toggled( bool ) ), this, SIGNAL( changed() ) ); connect( w.suidCheck, SIGNAL( toggled( bool ) ), this, SIGNAL( changed() ) ); connect( w.suidEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) ); connect( w.startupInfoCheck, SIGNAL( toggled( bool ) ), this, SIGNAL( changed() ) ); connect( w.systrayCheck, SIGNAL( toggled( bool ) ), this, SIGNAL( changed() ) ); connect( w.dbusCombo, SIGNAL( highlighted( int ) ), this, SIGNAL( changed() ) ); if ( dlg.exec() == QDialog::Accepted ) { d->m_terminalOptionStr = w.terminalEdit->text().trimmed(); d->m_terminalBool = w.terminalCheck->isChecked(); d->m_suidBool = w.suidCheck->isChecked(); d->m_suidUserStr = w.suidEdit->text().trimmed(); d->m_startupBool = w.startupInfoCheck->isChecked(); d->m_systrayBool = w.systrayCheck->isChecked(); if (w.terminalCloseCheck->isChecked()) { d->m_terminalOptionStr.append(" --noclose"); } switch(w.dbusCombo->currentIndex()) { case 1: d->m_dbusStartusType = "multi"; break; case 2: d->m_dbusStartusType = "unique"; break; case 3: d->m_dbusStartusType = "wait"; break; default: d->m_dbusStartusType = "none"; break; } } } bool KDesktopPropsPlugin::supports( const KFileItemList& _items ) { if ( _items.count() != 1 ) return false; - KFileItem * item = _items.first(); + const KFileItem item = _items.first(); // check if desktop file if ( !KPropertiesDialogPlugin::isDesktopFile( item ) ) return false; // open file and check type - KDesktopFile config( item->url().path() ); + KDesktopFile config( item.url().path() ); return config.hasApplicationType() && KAuthorized::authorize("run_desktop_files") && KAuthorized::authorize("shell_access"); } #include "kpropertiesdialog.moc" diff --git a/kio/kfile/kpropertiesdialog.h b/kio/kfile/kpropertiesdialog.h index 075849e82e..2d49216610 100644 --- a/kio/kfile/kpropertiesdialog.h +++ b/kio/kfile/kpropertiesdialog.h @@ -1,657 +1,657 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright (c) 1999, 2000 Preston Brown Copyright (c) 2000 Simon Hausmann Copyright (c) 2000 David Faure 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. */ /* * This file holds the definitions for all classes used to * display a properties dialog. */ #ifndef KPROPERTIESDIALOG_H #define KPROPERTIESDIALOG_H #include #include #include #include #include class QLabel; class QLineEdit; class QCheckBox; class QPushButton; class KLineEdit; class KUrlRequester; class QPushButton; class KIconButton; class KPropertiesDialogPlugin; class QComboBox; class KJob; namespace KIO { class Job; } /** * The main properties dialog class. * A Properties Dialog is a dialog which displays various information * about a particular file or URL, or several files or URLs. * This main class holds various related classes, which are instantiated in * the form of tab entries in the tabbed dialog that this class provides. * The various tabs themselves will let the user view, and sometimes change, * information about the file or URL. * * \image html kpropertiesdialog.png "Typical KProperties Dialog" * * The best way to display the properties dialog is to use showDialog(). * Otherwise, you should use (void)new KPropertiesDialog(...) * It will take care of deleting itself when closed. * * If you are looking for more flexibility, see KFileMetaInfo and * KFileMetaInfoWidget. */ class KIO_EXPORT KPropertiesDialog : public KPageDialog { Q_OBJECT public: /** * Determine whether there are any property pages available for the * given file items. * @param _items the list of items to check. * @return true if there are any property pages, otherwise false. */ static bool canDisplay( const KFileItemList& _items ); /** * Brings up a Properties dialog, as shown above. * This is the normal constructor for * file-manager type applications, where you have a KFileItem instance * to work with. Normally you will use this * method rather than the one below. * * @param item file item whose properties should be displayed. * @param parent is the parent of the dialog widget. * @param name is the internal name. */ - explicit KPropertiesDialog( KFileItem * item, + explicit KPropertiesDialog( const KFileItem& item, QWidget* parent = 0 ); /** * \overload * * You use this constructor for cases where you have a number of items, * rather than a single item. Be careful which methods you use * when passing a list of files or URLs, since some of them will only * work on the first item in a list. * * @param _items list of file items whose properties should be displayed. * @param parent is the parent of the dialog widget. * @param name is the internal name. */ explicit KPropertiesDialog( const KFileItemList& _items, QWidget *parent = 0 ); /** * Brings up a Properties dialog. Convenience constructor for * non-file-manager applications, where you have a KUrl rather than a * KFileItem or KFileItemList. * * @param _url the URL whose properties should be displayed * @param parent is the parent of the dialog widget. * @param name is the internal name. * * IMPORTANT: This constructor, together with exec(), leads to a grave * display bug (due to KIO::stat() being run before the dialog has all the * necessary information). Do not use this combination for now. * TODO: Check if the above is still true with Qt4. * For local files with a known mimetype, simply create a KFileItem and pass * it to the other constructor. */ explicit KPropertiesDialog( const KUrl& _url, QWidget* parent = 0 ); /** * Creates a properties dialog for a new .desktop file (whose name * is not known yet), based on a template. Special constructor for * "File / New" in file-manager type applications. * * @param _tempUrl template used for reading only * @param _currentDir directory where the file will be written to * @param _defaultName something to put in the name field, * like mimetype.desktop * @param parent is the parent of the dialog widget. * @param name is the internal name. */ KPropertiesDialog( const KUrl& _tempUrl, const KUrl& _currentDir, const QString& _defaultName, QWidget* parent = 0 ); /** * Creates an empty properties dialog (for applications that want use * a standard dialog, but for things not doable via the plugin-mechanism). * * @param title is the string display as the "filename" in the caption of the dialog. * @param parent is the parent of the dialog widget. * @param name is the internal name. * @param modal tells the dialog whether it should be modal. */ explicit KPropertiesDialog(const QString& title, QWidget* parent = 0); /** * Cleans up the properties dialog and frees any associated resources, * including the dialog itself. Note that when a properties dialog is * closed it cleans up and deletes itself. */ virtual ~KPropertiesDialog(); /** * Immediately displays a Properties dialog using constructor with * the same parameters. * On MS Windows, if @p item points to a local file, native (non modal) property * dialog is displayed (@p parent and @p modal are ignored in this case). * * @return true on successful dialog displaying (can be false on win32). */ - static bool showDialog(KFileItem* item, QWidget* parent = 0, + static bool showDialog(const KFileItem& item, QWidget* parent = 0, bool modal = true); /** * Immediately displays a Properties dialog using constructor with * the same parameters. * On MS Windows, if @p _url points to a local file, native (non modal) property * dialog is displayed (@p parent and @p modal are ignored in this case). * * @return true on successful dialog displaying (can be false on win32). */ static bool showDialog(const KUrl& _url, QWidget* parent = 0, bool modal = true); /** * Immediately displays a Properties dialog using constructor with * the same parameters. * On MS Windows, if @p _items has one element and this element points * to a local file, native (non modal) property dialog is displayed * (@p parent and @p modal are ignored in this case). * * @return true on successful dialog displaying (can be false on win32). */ static bool showDialog(const KFileItemList& _items, QWidget* parent = 0, bool modal = true); /** * Adds a "3rd party" properties plugin to the dialog. Useful * for extending the properties mechanism. * * To create a new plugin type, inherit from the base class KPropertiesDialogPlugin * and implement all the methods. If you define a service .desktop file * for your plugin, you do not need to call insertPlugin(). * * @param plugin is a pointer to the KPropertiesDialogPlugin. The Properties * dialog will do destruction for you. The KPropertiesDialogPlugin \b must * have been created with the KPropertiesDialog as its parent. * @see KPropertiesDialogPlugin */ void insertPlugin (KPropertiesDialogPlugin *plugin); /** * The URL of the file that has its properties being displayed. * This is only valid if the KPropertiesDialog was created/shown * for one file or URL. * * @return a parsed URL. */ KUrl kurl() const; /** * @return the file item for which the dialog is shown * * Warning: this method returns the first item of the list. * This means that you should use this only if you are sure the dialog is used * for a single item. Otherwise, you probably want items() instead. */ - KFileItem *item(); + KFileItem& item(); /** * @return the items for which the dialog is shown */ KFileItemList items() const; /** * If the dialog is being built from a template, this method * returns the current directory. If no template, it returns QString(). * See the template form of the constructor. * * @return the current directory or QString() */ KUrl currentDir() const; /** * If the dialog is being built from a template, this method * returns the default name. If no template, it returns QString(). * See the template form of the constructor. * @return the default name or QString() */ QString defaultName() const; /** * Updates the item URL (either called by rename or because * a global apps/mimelnk desktop file is being saved) * Can only be called if the dialog applies to a single file or URL. * @param _newUrl the new URL */ void updateUrl( const KUrl& _newUrl ); /** * Renames the item to the specified name. This can only be called if * the dialog applies to a single file or URL. * @param _name new filename, encoded. * \see FilePropsDialogPlugin::applyChanges */ void rename( const QString& _name ); /** * To abort applying changes. */ void abortApplying(); /** * Shows the page that was previously set by * setFileSharingPage(), or does nothing if no page * was set yet. * \see setFileSharingPage */ void showFileSharingPage(); /** * Sets the file sharing page. * This page is shown when calling showFileSharingPage(). * * @param page the page to set * \see showFileSharingPage */ void setFileSharingPage(QWidget* page); /** * Call this to make the filename lineedit readonly, to prevent the user * from renaming the file. * \param ro true if the lineedit should be read only */ void setFileNameReadOnly( bool ro ); public Q_SLOTS: /** * Called when the user presses 'Ok'. */ virtual void slotOk(); // Deletes the PropertiesDialog instance /** * Called when the user presses 'Cancel'. */ virtual void slotCancel(); // Deletes the PropertiesDialog instance Q_SIGNALS: /** * This signal is emitted when the Properties Dialog is closed (for * example, with OK or Cancel buttons) */ void propertiesClosed(); /** * This signal is emitted when the properties changes are applied (for * example, with the OK button) */ void applied(); /** * This signal is emitted when the properties changes are aborted (for * example, with the Cancel button) */ void canceled(); /** * Emitted before changes to @p oldUrl are saved as @p newUrl. * The receiver may change @p newUrl to point to an alternative * save location. */ void saveAs(const KUrl &oldUrl, KUrl &newUrl); Q_SIGNALS: void leaveModality(); private: class KPropertiesDialogPrivate; KPropertiesDialogPrivate* const d; Q_DISABLE_COPY(KPropertiesDialog) }; /** * A Plugin in the Properties dialog * This is an abstract class. You must inherit from this class * to build a new kind of tabbed page for the KPropertiesDialog. * A plugin in itself is just a library containing code, not a dialog's page. * It's up to the plugin to insert pages into the parent dialog. * * To make a plugin available, define a service that implements the KPropertiesDialog/Plugin * servicetype, as well as the mimetypes for which the plugin should be created. * For instance, ServiceTypes=KPropertiesDialog/Plugin,text/html,application/x-mymimetype. * * You can also include X-KDE-Protocol=file if you want that plugin * to be loaded only for local files, for instance. */ class KIO_EXPORT KPropertiesDialogPlugin : public QObject { Q_OBJECT public: /** * Constructor * To insert tabs into the properties dialog, use the add methods provided by * KPageDialog (the properties dialog is a KPageDialog). */ KPropertiesDialogPlugin( KPropertiesDialog *_props ); virtual ~KPropertiesDialogPlugin(); /** * Applies all changes to the file. * This function is called when the user presses 'Ok'. The last plugin inserted * is called first. */ virtual void applyChanges(); /** * Convenience method for most ::supports methods * @return true if the file is a local, regular, readable, desktop file */ - static bool isDesktopFile( KFileItem * _item ); + static bool isDesktopFile( const KFileItem& _item ); void setDirty( bool b ); bool isDirty() const; public Q_SLOTS: void setDirty(); // same as setDirty( true ) Q_SIGNALS: /** * Emit this signal when the user changed anything in the plugin's tabs. * The hosting PropertiesDialog will call applyChanges only if the * PropsPlugin has emitted this signal or if you have called setDirty() before. */ void changed(); protected: /** * Pointer to the dialog */ KPropertiesDialog *properties; int fontHeight; private: class KPropertiesDialogPluginPrivate; KPropertiesDialogPluginPrivate* const d; }; /** * 'General' plugin * This plugin displays the name of the file, its size and access times. * @internal */ class KIO_EXPORT KFilePropsPlugin : public KPropertiesDialogPlugin { Q_OBJECT public: /** * Constructor */ KFilePropsPlugin( KPropertiesDialog *_props ); virtual ~KFilePropsPlugin(); /** * Applies all changes made. This plugin must be always the first * plugin in the dialog, since this function may rename the file which * may confuse other applyChanges functions. */ virtual void applyChanges(); /** * Tests whether the files specified by _items need a 'General' plugin. */ static bool supports( const KFileItemList& _items ); /** * Called after all plugins applied their changes */ void postApplyChanges(); void setFileNameReadOnly( bool ro ); protected Q_SLOTS: void slotEditFileType(); void slotCopyFinished( KJob * ); void slotFileRenamed( KIO::Job *, const KUrl &, const KUrl & ); void slotDirSizeUpdate(); void slotDirSizeFinished( KJob * ); void slotFoundMountPoint( const QString& mp, quint64 kBSize, quint64 kBUsed, quint64 kBAvail ); void slotSizeStop(); void slotSizeDetermine(); Q_SIGNALS: void leaveModality(); private Q_SLOTS: void nameFileChanged(const QString &text ); void slotIconChanged(); private: void determineRelativePath( const QString & path ); void applyIconChanges(); class KFilePropsPluginPrivate; KFilePropsPluginPrivate* const d; }; /** * 'Permissions' plugin * In this plugin you can modify permissions and change * the owner of a file. * @internal */ class KIO_EXPORT KFilePermissionsPropsPlugin : public KPropertiesDialogPlugin { Q_OBJECT public: enum PermissionsMode { PermissionsOnlyFiles = 0, PermissionsOnlyDirs = 1, PermissionsOnlyLinks = 2, PermissionsMixed = 3 }; enum PermissionsTarget { PermissionsOwner = 0, PermissionsGroup = 1, PermissionsOthers = 2 }; /** * Constructor */ KFilePermissionsPropsPlugin( KPropertiesDialog *_props ); virtual ~KFilePermissionsPropsPlugin(); virtual void applyChanges(); /** * Tests whether the file specified by _items needs a 'Permissions' plugin. */ static bool supports( const KFileItemList& _items ); private Q_SLOTS: void slotChmodResult( KJob * ); void slotShowAdvancedPermissions(); Q_SIGNALS: void leaveModality(); private: void setComboContent(QComboBox *combo, PermissionsTarget target, mode_t permissions, mode_t partial); bool isIrregular(mode_t permissions, bool isDir, bool isLink); void enableAccessControls(bool enable); void updateAccessControls(); void getPermissionMasks(mode_t &andFilePermissions, mode_t &andDirPermissions, mode_t &orFilePermissions, mode_t &orDirPermissions); static const mode_t permissionsMasks[3]; static const mode_t standardPermissions[4]; static const char *permissionsTexts[4][4]; static mode_t fperm[3][4]; class KFilePermissionsPropsPluginPrivate; KFilePermissionsPropsPluginPrivate* const d; }; /** * Used to edit the files containing * [Desktop Entry] * URL=.... * * Such files are used to represent a program in kicker and konqueror. * @internal */ class KIO_EXPORT KUrlPropsPlugin : public KPropertiesDialogPlugin { Q_OBJECT public: /** * Constructor */ KUrlPropsPlugin( KPropertiesDialog *_props ); virtual ~KUrlPropsPlugin(); virtual void applyChanges(); static bool supports( const KFileItemList& _items ); private: class KUrlPropsPluginPrivate; KUrlPropsPluginPrivate* const d; }; /** * Used to edit the files containing * [Desktop Entry] * Type=MimeType * @internal */ class KIO_EXPORT KBindingPropsPlugin : public KPropertiesDialogPlugin { Q_OBJECT public: /** * Constructor */ KBindingPropsPlugin( KPropertiesDialog *_props ); virtual ~KBindingPropsPlugin(); virtual void applyChanges(); static bool supports( const KFileItemList& _items ); private: class KBindingPropsPluginPrivate; KBindingPropsPluginPrivate* const d; }; /** * Properties plugin for device .desktop files * @internal */ class KIO_EXPORT KDevicePropsPlugin : public KPropertiesDialogPlugin { Q_OBJECT public: KDevicePropsPlugin( KPropertiesDialog *_props ); virtual ~KDevicePropsPlugin(); virtual void applyChanges(); static bool supports( const KFileItemList& _items ); private Q_SLOTS: void slotActivated( int ); void slotDeviceChanged(); void slotFoundMountPoint( const QString& mp, quint64 kBSize, quint64 kBUsed, quint64 kBAvail ); private: void updateInfo(); private: class KDevicePropsPluginPrivate; KDevicePropsPluginPrivate* const d; }; class Ui_KPropertiesDesktopBase; /** * Used to edit the files containing * [Desktop Entry] * Type=Application * * Such files are used to represent a program in kicker and konqueror. * @internal */ class KIO_EXPORT KDesktopPropsPlugin : public KPropertiesDialogPlugin { Q_OBJECT public: /** * Constructor */ KDesktopPropsPlugin( KPropertiesDialog *_props ); virtual ~KDesktopPropsPlugin(); virtual void applyChanges(); static bool supports( const KFileItemList& _items ); public Q_SLOTS: void slotAddFiletype(); void slotDelFiletype(); void slotBrowseExec(); void slotAdvanced(); private: void checkCommandChanged(); private: class KDesktopPropsPluginPrivate; KDesktopPropsPluginPrivate * const d; }; #endif diff --git a/kio/kio/chmodjob.cpp b/kio/kio/chmodjob.cpp index e896ce06f5..bc08d541ec 100644 --- a/kio/kio/chmodjob.cpp +++ b/kio/kio/chmodjob.cpp @@ -1,298 +1,298 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Stephan Kulow David Faure Waldo Bastian 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 "chmodjob.h" #include "job.h" #include "jobuidelegate.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "job_p.h" namespace KIO { struct ChmodInfo { KUrl url; int permissions; }; enum ChmodJobState { STATE_LISTING, STATE_CHMODING }; class ChmodJobPrivate: public KIO::JobPrivate { public: ChmodJobPrivate(const KFileItemList& lstItems, int permissions, int mask, int newOwner, int newGroup, bool recursive) : state( STATE_LISTING ) , m_permissions( permissions ) , m_mask( mask ) , m_newOwner( newOwner ) , m_newGroup( newGroup ) , m_recursive( recursive ) , m_lstItems( lstItems ) { } ChmodJobState state; int m_permissions; int m_mask; int m_newOwner; int m_newGroup; bool m_recursive; KFileItemList m_lstItems; QLinkedList m_infos; // linkedlist since we keep removing the first item void chmodNextFile(); void _k_slotEntries( KIO::Job * , const KIO::UDSEntryList & ); void _k_processList(); Q_DECLARE_PUBLIC(ChmodJob) static inline ChmodJob *newJob(const KFileItemList& lstItems, int permissions, int mask, int newOwner, int newGroup, bool recursive, bool showProgressInfo) { ChmodJob *job = new ChmodJob(*new ChmodJobPrivate(lstItems,permissions,mask, newOwner,newGroup,recursive)); job->setUiDelegate(new JobUiDelegate()); if (showProgressInfo) KIO::getJobTracker()->registerJob(job); return job; } }; } // namespace KIO using namespace KIO; ChmodJob::ChmodJob(ChmodJobPrivate &dd) : KIO::Job(dd) { QMetaObject::invokeMethod( this, "_k_processList", Qt::QueuedConnection ); } ChmodJob::~ChmodJob() { } void ChmodJobPrivate::_k_processList() { Q_Q(ChmodJob); while ( !m_lstItems.isEmpty() ) { - KFileItem * item = m_lstItems.first(); - if ( !item->isLink() ) // don't do anything with symlinks + const KFileItem item = m_lstItems.first(); + if ( !item.isLink() ) // don't do anything with symlinks { // File or directory -> remember to chmod ChmodInfo info; - info.url = item->url(); + info.url = item.url(); // This is a toplevel file, we apply changes directly (no +X emulation here) - info.permissions = ( m_permissions & m_mask ) | ( item->permissions() & ~m_mask ); + info.permissions = ( m_permissions & m_mask ) | ( item.permissions() & ~m_mask ); /*kDebug(7007) << "\n current permissions=" << QString::number(item->permissions(),8) << "\n wanted permission=" << QString::number(m_permissions,8) << "\n with mask=" << QString::number(m_mask,8) << "\n with ~mask (mask bits we keep) =" << QString::number((uint)~m_mask,8) << "\n bits we keep =" << QString::number(item->permissions() & ~m_mask,8) << "\n new permissions = " << QString::number(info.permissions,8) << endl;*/ m_infos.prepend( info ); //kDebug(7007) << "processList : Adding info for " << info.url.prettyUrl(); // Directory and recursive -> list - if ( item->isDir() && m_recursive ) + if ( item.isDir() && m_recursive ) { //kDebug(7007) << "ChmodJob::processList dir -> listing"; - KIO::ListJob * listJob = KIO::listRecursive( item->url(), false /* no GUI */ ); + KIO::ListJob * listJob = KIO::listRecursive( item.url(), false /* no GUI */ ); q->connect( listJob, SIGNAL(entries( KIO::Job *, const KIO::UDSEntryList& )), SLOT(_k_slotEntries( KIO::Job*, const KIO::UDSEntryList& ))); q->addSubjob( listJob ); return; // we'll come back later, when this one's finished } } m_lstItems.removeFirst(); } kDebug(7007) << "ChmodJob::processList -> going to STATE_CHMODING"; // We have finished, move on state = STATE_CHMODING; chmodNextFile(); } void ChmodJobPrivate::_k_slotEntries( KIO::Job*, const KIO::UDSEntryList & list ) { KIO::UDSEntryList::ConstIterator it = list.begin(); KIO::UDSEntryList::ConstIterator end = list.end(); for (; it != end; ++it) { const KIO::UDSEntry& entry = *it; const bool isLink = !entry.stringValue( KIO::UDSEntry::UDS_STRING ).isEmpty(); const QString relativePath = entry.stringValue( KIO::UDSEntry::UDS_NAME ); if ( !isLink && relativePath != ".." ) { const mode_t permissions = entry.numberValue( KIO::UDSEntry::UDS_ACCESS ); ChmodInfo info; - info.url = m_lstItems.first()->url(); // base directory + info.url = m_lstItems.first().url(); // base directory info.url.addPath( relativePath ); int mask = m_mask; // Emulate -X: only give +x to files that had a +x bit already // So the check is the opposite : if the file had no x bit, don't touch x bits // For dirs this doesn't apply if ( !entry.isDir() ) { int newPerms = m_permissions & mask; if ( (newPerms & 0111) && !(permissions & 0111) ) { // don't interfere with mandatory file locking if ( newPerms & 02000 ) mask = mask & ~0101; else mask = mask & ~0111; } } info.permissions = ( m_permissions & mask ) | ( permissions & ~mask ); /*kDebug(7007) << "\n current permissions=" << QString::number(permissions,8) << "\n wanted permission=" << QString::number(m_permissions,8) << "\n with mask=" << QString::number(mask,8) << "\n with ~mask (mask bits we keep) =" << QString::number((uint)~mask,8) << "\n bits we keep =" << QString::number(permissions & ~mask,8) << "\n new permissions = " << QString::number(info.permissions,8) << endl;*/ // Prepend this info in our todo list. // This way, the toplevel dirs are done last. m_infos.prepend( info ); } } } void ChmodJobPrivate::chmodNextFile() { Q_Q(ChmodJob); if ( !m_infos.isEmpty() ) { ChmodInfo info = m_infos.takeFirst(); // First update group / owner (if local file) // (permissions have to set after, in case of suid and sgid) if ( info.url.isLocalFile() && ( m_newOwner != -1 || m_newGroup != -1 ) ) { QString path = info.url.path(); if ( chown( QFile::encodeName(path), m_newOwner, m_newGroup ) != 0 ) { int answer = KMessageBox::warningContinueCancel( 0, i18n( "Could not modify the ownership of file %1. You have insufficient access to the file to perform the change." , path), QString(), KGuiItem(i18n("&Skip File")) ); if (answer == KMessageBox::Cancel) { q->setError( ERR_USER_CANCELED ); q->emitResult(); return; } } } kDebug(7007) << "ChmodJob::chmodNextFile chmod'ing " << info.url.prettyUrl() << " to " << QString::number(info.permissions,8) << endl; KIO::SimpleJob * job = KIO::chmod( info.url, info.permissions ); // copy the metadata for acl and default acl const QString aclString = q->queryMetaData( QLatin1String("ACL_STRING") ); const QString defaultAclString = q->queryMetaData( QLatin1String("DEFAULT_ACL_STRING") ); if ( !aclString.isEmpty() ) job->addMetaData( QLatin1String("ACL_STRING"), aclString ); if ( !defaultAclString.isEmpty() ) job->addMetaData( QLatin1String("DEFAULT_ACL_STRING"), defaultAclString ); q->addSubjob(job); } else // We have finished q->emitResult(); } void ChmodJob::slotResult( KJob * job ) { Q_D(ChmodJob); if ( job->error() ) { setError( job->error() ); setErrorText( job->errorText() ); emitResult(); return; } //kDebug(7007) << " ChmodJob::slotResult( KJob * job ) d->m_lstItems:" << d->m_lstItems.count(); switch ( d->state ) { case STATE_LISTING: removeSubjob(job); d->m_lstItems.removeFirst(); kDebug(7007) << "ChmodJob::slotResult -> processList"; d->_k_processList(); return; case STATE_CHMODING: removeSubjob(job); kDebug(7007) << "ChmodJob::slotResult -> chmodNextFile"; d->chmodNextFile(); return; default: assert(0); return; } } ChmodJob *KIO::chmod( const KFileItemList& lstItems, int permissions, int mask, const QString& owner, const QString& group, bool recursive, bool showProgressInfo ) { uid_t newOwnerID = uid_t(-1); // chown(2) : -1 means no change if ( !owner.isEmpty() ) { struct passwd* pw = getpwnam(QFile::encodeName(owner)); if ( pw == 0L ) kError(250) << " ERROR: No user " << owner << endl; else newOwnerID = pw->pw_uid; } gid_t newGroupID = gid_t(-1); // chown(2) : -1 means no change if ( !group.isEmpty() ) { struct group* g = getgrnam(QFile::encodeName(group)); if ( g == 0L ) kError(250) << " ERROR: No group " << group << endl; else newGroupID = g->gr_gid; } return ChmodJobPrivate::newJob(lstItems, permissions, mask, newOwnerID, newGroupID, recursive, showProgressInfo); } #include "chmodjob.moc" diff --git a/kio/kio/kdirlister.cpp b/kio/kio/kdirlister.cpp index 0370998e47..b824f4821b 100644 --- a/kio/kio/kdirlister.cpp +++ b/kio/kio/kdirlister.cpp @@ -1,2437 +1,2385 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis 2000 Carsten Pfeiffer 2003-2005 David Faure 2001-2006 Michael Brade 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 "kdirlister.h" #include "kdirlister_p.h" #include #include #include #include #include #include #include #include #include #include #include #include "kprotocolmanager.h" #include "kmountpoint.h" #include #include #include // Enable this to get printDebug() called often, to see the contents of the cache //#define DEBUG_CACHE // Make really sure it doesn't get activated in the final build #ifdef NDEBUG #undef DEBUG_CACHE #endif K_GLOBAL_STATIC(KDirListerCache, s_pCache) KDirListerCache::KDirListerCache() : itemsCached( 10 ) // keep the last 10 directories around { kDebug(7004) << "+KDirListerCache"; connect( &pendingUpdateTimer, SIGNAL(timeout()), this, SLOT(processPendingUpdates()) ); pendingUpdateTimer.setSingleShot( true ); connect( KDirWatch::self(), SIGNAL( dirty( const QString& ) ), this, SLOT( slotFileDirty( const QString& ) ) ); connect( KDirWatch::self(), SIGNAL( created( const QString& ) ), this, SLOT( slotFileCreated( const QString& ) ) ); connect( KDirWatch::self(), SIGNAL( deleted( const QString& ) ), this, SLOT( slotFileDeleted( const QString& ) ) ); kdirnotify = new org::kde::KDirNotify(QString(), QString(), QDBusConnection::sessionBus(), this); connect(kdirnotify, SIGNAL(FileRenamed(QString,QString)), SLOT(slotFileRenamed(QString,QString))); connect(kdirnotify, SIGNAL(FilesAdded(QString)), SLOT(slotFilesAdded(QString))); connect(kdirnotify, SIGNAL(FilesChanged(QStringList)), SLOT(slotFilesChanged(QStringList))); connect(kdirnotify, SIGNAL(FilesRemoved(QStringList)), SLOT(slotFilesRemoved(QStringList))); // The use of QUrl::url() in ~DirItem (sendSignal) crashes if the static for QRegExpEngine got deleted already, // so we need to destroy the KDirListerCache before that. qAddPostRoutine(s_pCache.destroy); } KDirListerCache::~KDirListerCache() { kDebug(7004) << "-KDirListerCache"; qDeleteAll(itemsInUse); itemsInUse.clear(); itemsCached.clear(); directoryData.clear(); if ( KDirWatch::exists() ) KDirWatch::self()->disconnect( this ); } // setting _reload to true will emit the old files and // call updateDirectory bool KDirListerCache::listDir( KDirLister *lister, const KUrl& _u, bool _keep, bool _reload ) { // like this we don't have to worry about trailing slashes any further KUrl _url(_u); _url.cleanPath(); // kill consecutive slashes _url.adjustPath(KUrl::RemoveTrailingSlash); QString urlStr = _url.url(); if ( !validUrl( lister, _url ) ) return false; #ifdef DEBUG_CACHE printDebug(); #endif kDebug(7004) << lister << " url=" << _url << " keep=" << _keep << " reload=" << _reload << endl; if ( !_keep ) { // stop any running jobs for lister stop( lister ); // clear our internal list for lister forgetDirs( lister ); - lister->d->rootFileItem = 0; + lister->d->rootFileItem = KFileItem(); } else if ( lister->d->lstDirs.contains( _url ) ) { // stop the job listing _url for this lister stop( lister, _url ); // remove the _url as well, it will be added in a couple of lines again! // forgetDirs with three args does not do this // TODO: think about moving this into forgetDirs lister->d->lstDirs.removeAll( _url ); // clear _url for lister forgetDirs( lister, _url, true ); if ( lister->d->url == _url ) - lister->d->rootFileItem = 0; + lister->d->rootFileItem = KFileItem(); } lister->d->lstDirs.append( _url ); if ( lister->d->url.isEmpty() || !_keep ) // set toplevel URL only if not set yet lister->d->url = _url; DirItem *itemU = itemsInUse.value(urlStr); DirItem *itemC; DirectoryData& dirData = directoryData[urlStr]; // find or insert if ( dirData.listersCurrentlyListing.isEmpty() ) { // if there is an update running for _url already we get into // the following case - it will just be restarted by updateDirectory(). if ( itemU ) { kDebug(7004) << "listDir: Entry already in use: " << _url; bool oldState = lister->d->complete; lister->d->complete = false; emit lister->started( _url ); - if ( !lister->d->rootFileItem && lister->d->url == _url ) + if ( lister->d->rootFileItem.isNull() && lister->d->url == _url ) lister->d->rootFileItem = itemU->rootItem; lister->addNewItems( itemU->lstItems ); lister->emitItems(); lister->d->complete = oldState; emit lister->completed( _url ); if ( lister->d->complete ) emit lister->completed(); // _url is already in use, so there is already an entry in listersCurrentlyHolding assert( !dirData.listersCurrentlyHolding.isEmpty() ); dirData.listersCurrentlyHolding.append( lister ); if ( _reload || !itemU->complete ) updateDirectory( _url ); } else if ( !_reload && (itemC = itemsCached.take(urlStr)) ) { kDebug(7004) << "listDir: Entry in cache: " << _url; itemC->decAutoUpdate(); itemsInUse.insert( urlStr, itemC ); itemU = itemC; bool oldState = lister->d->complete; lister->d->complete = false; emit lister->started( _url ); - if ( !lister->d->rootFileItem && lister->d->url == _url ) + if ( lister->d->rootFileItem.isNull() && lister->d->url == _url ) lister->d->rootFileItem = itemC->rootItem; lister->addNewItems( itemC->lstItems ); lister->emitItems(); lister->d->complete = oldState; emit lister->completed( _url ); if ( lister->d->complete ) emit lister->completed(); Q_ASSERT( dirData.listersCurrentlyHolding.isEmpty() ); dirData.listersCurrentlyHolding.append( lister ); if ( !itemC->complete ) updateDirectory( _url ); } else // dir not in cache or _reload is true { kDebug(7004) << "listDir: Entry not in cache or reloaded: " << _url; dirData.listersCurrentlyListing.append( lister ); if (_reload) itemsCached.remove( urlStr ); itemU = new DirItem( _url ); itemsInUse.insert( urlStr, itemU ); // // we have a limit of MAX_JOBS_PER_LISTER concurrently running jobs // if ( lister->numJobs() >= MAX_JOBS_PER_LISTER ) // { // pendingUpdates.insert( _url ); // } // else // { if ( lister->d->url == _url ) - lister->d->rootFileItem = 0; + lister->d->rootFileItem = KFileItem(); KIO::ListJob* job = KIO::listDir( _url, false /* no default GUI */ ); jobs.insert( job, KIO::UDSEntryList() ); lister->jobStarted( job ); lister->connectJob( job ); if ( lister->d->window ) job->ui()->setWindow( lister->d->window ); connect( job, SIGNAL( entries( KIO::Job *, const KIO::UDSEntryList & ) ), this, SLOT( slotEntries( KIO::Job *, const KIO::UDSEntryList & ) ) ); connect( job, SIGNAL( result( KJob * ) ), this, SLOT( slotResult( KJob * ) ) ); connect( job, SIGNAL( redirection( KIO::Job *, const KUrl & ) ), this, SLOT( slotRedirection( KIO::Job *, const KUrl & ) ) ); emit lister->started( _url ); // } } } else { kDebug(7004) << "listDir: Entry currently being listed: " << _url; emit lister->started( _url ); dirData.listersCurrentlyListing.append( lister ); KIO::ListJob *job = jobForUrl( urlStr ); Q_ASSERT( job ); lister->jobStarted( job ); lister->connectJob( job ); Q_ASSERT( itemU ); - if ( !lister->d->rootFileItem && lister->d->url == _url ) + if ( lister->d->rootFileItem.isNull() && lister->d->url == _url ) lister->d->rootFileItem = itemU->rootItem; lister->addNewItems( itemU->lstItems ); lister->emitItems(); } // automatic updating of directories if ( lister->d->autoUpdate ) itemU->incAutoUpdate(); return true; } bool KDirListerCache::validUrl( const KDirLister *lister, const KUrl& url ) const { if ( !url.isValid() ) { if ( lister->d->autoErrorHandling ) { QString tmp = i18n("Malformed URL\n%1", url.prettyUrl() ); KMessageBox::error( lister->d->errorParent, tmp ); } return false; } if ( !KProtocolManager::supportsListing( url ) ) { if ( lister->d->autoErrorHandling ) { QString tmp = i18n("URL cannot be listed\n%1", url.prettyUrl() ); KMessageBox::error( lister->d->errorParent, tmp ); } return false; } return true; } void KDirListerCache::stop( KDirLister *lister ) { #ifdef DEBUG_CACHE printDebug(); #endif kDebug(7004) << "lister: " << lister; bool stopped = false; QHash::iterator dirit = directoryData.begin(); const QHash::iterator dirend = directoryData.end(); for( ; dirit != dirend ; ++dirit ) { DirectoryData& dirData = dirit.value(); if ( dirData.listersCurrentlyListing.removeAll(lister) ) { // contains + removeAll in one go // lister is listing url const QString url = dirit.key(); //kDebug(7004) << " found lister in list - for " << url; stopLister(lister, url, dirData); stopped = true; } } if ( stopped ) { emit lister->canceled(); lister->d->complete = true; } // this is wrong if there is still an update running! //Q_ASSERT( lister->d->complete ); } void KDirListerCache::stop( KDirLister *lister, const KUrl& _u ) { KUrl url(_u); url.adjustPath( KUrl::RemoveTrailingSlash ); const QString urlStr = url.url(); // TODO: consider to stop all the "child jobs" of url as well kDebug(7004) << lister << " url=" << url; QHash::iterator dirit = directoryData.find(urlStr); if (dirit == directoryData.end()) return; DirectoryData& dirData = dirit.value(); if ( dirData.listersCurrentlyListing.removeAll(lister) ) { // contains + removeAll in one go stopLister(lister, urlStr, dirData); if ( lister->numJobs() == 0 ) { lister->d->complete = true; // we killed the last job for lister emit lister->canceled(); } } } // Helper for both stop() methods void KDirListerCache::stopLister(KDirLister* lister, const QString& url, DirectoryData& dirData) { KIO::ListJob *job = jobForUrl( url ); if ( job ) lister->jobDone( job ); // move lister to listersCurrentlyHolding dirData.listersCurrentlyHolding.append(lister); emit lister->canceled( KUrl( url ) ); //kDebug(7004) << "remaining list: " << listers->count() << " listers"; if ( dirData.listersCurrentlyListing.isEmpty() ) { // kill the job since it isn't used any more if ( job ) killJob( job ); } } void KDirListerCache::setAutoUpdate( KDirLister *lister, bool enable ) { // IMPORTANT: this method does not check for the current autoUpdate state! for ( KUrl::List::const_iterator it = lister->d->lstDirs.constBegin(); it != lister->d->lstDirs.constEnd(); ++it ) { DirItem* dirItem = itemsInUse.value((*it).url()); Q_ASSERT(dirItem); if ( enable ) dirItem->incAutoUpdate(); else dirItem->decAutoUpdate(); } } void KDirListerCache::forgetDirs( KDirLister *lister ) { kDebug(7004) << lister; emit lister->clear(); // clear lister->d->lstDirs before calling forgetDirs(), so that // it doesn't contain things that itemsInUse doesn't. When emitting // the canceled signals, lstDirs must not contain anything that // itemsInUse does not contain. (otherwise it might crash in findByName()). const KUrl::List lstDirsCopy = lister->d->lstDirs; lister->d->lstDirs.clear(); for ( KUrl::List::const_iterator it = lstDirsCopy.begin(); it != lstDirsCopy.end(); ++it ) { forgetDirs( lister, *it, false ); } } static bool manually_mounted(const QString& path, const KMountPoint::List& possibleMountPoints) { KMountPoint::Ptr mp = possibleMountPoints.findByPath(path); if (!mp) // not listed in fstab -> yes, manually mounted return true; const bool supermount = mp->mountType() == "supermount"; if (supermount) { return true; } // noauto -> manually mounted. Otherwise, mounted at boot time, won't be unmounted any time soon hopefully. return mp->mountOptions().contains("noauto"); } void KDirListerCache::forgetDirs( KDirLister *lister, const KUrl& _url, bool notify ) { kDebug(7004) << lister << " _url: " << _url; KUrl url( _url ); url.adjustPath( KUrl::RemoveTrailingSlash ); const QString urlStr = url.url(); DirectoryDataHash::iterator dit = directoryData.find(urlStr); Q_ASSERT(dit != directoryData.end()); DirectoryData& dirData = *dit; dirData.listersCurrentlyHolding.removeAll(lister); DirItem *item = itemsInUse.value(urlStr); Q_ASSERT(item); if ( dirData.listersCurrentlyHolding.isEmpty() && dirData.listersCurrentlyListing.isEmpty() ) { // item not in use anymore -> move into cache if complete directoryData.erase(dit); itemsInUse.remove( urlStr ); // this job is a running update KIO::ListJob *job = jobForUrl( urlStr ); if ( job ) { lister->jobDone( job ); killJob( job ); kDebug(7004) << "Killing update job for " << urlStr; emit lister->canceled( url ); if ( lister->numJobs() == 0 ) { lister->d->complete = true; emit lister->canceled(); } } if ( notify ) { lister->d->lstDirs.removeAll( url ); emit lister->clear( url ); } if ( item->complete ) { kDebug(7004) << lister << " item moved into cache: " << url; itemsCached.insert( urlStr, item ); const KMountPoint::List possibleMountPoints = KMountPoint::possibleMountPoints(KMountPoint::NeedMountOptions); // Should we forget the dir for good, or keep a watch on it? // Generally keep a watch, except when it would prevent // unmounting a removable device (#37780) const bool isLocal = item->url.isLocalFile(); bool isManuallyMounted = false; bool containsManuallyMounted = false; if (isLocal) { isManuallyMounted = manually_mounted( item->url.path(), possibleMountPoints ); if ( !isManuallyMounted ) { // Look for a manually-mounted directory inside // If there's one, we can't keep a watch either, FAM would prevent unmounting the CDROM // I hope this isn't too slow KFileItemList::const_iterator kit = item->lstItems.begin(); const KFileItemList::const_iterator kend = item->lstItems.end(); for ( ; kit != kend && !containsManuallyMounted; ++kit ) - if ( (*kit)->isDir() && manually_mounted((*kit)->url().path(), possibleMountPoints) ) + if ( (*kit).isDir() && manually_mounted((*kit).url().path(), possibleMountPoints) ) containsManuallyMounted = true; } } if ( isManuallyMounted || containsManuallyMounted ) { kDebug(7004) << "Not adding a watch on " << item->url << " because it " << ( isManuallyMounted ? "is manually mounted" : "contains a manually mounted subdir" ) << endl; item->complete = false; // set to "dirty" } else item->incAutoUpdate(); // keep watch } else { delete item; item = 0; } } if ( item && lister->d->autoUpdate ) item->decAutoUpdate(); } void KDirListerCache::updateDirectory( const KUrl& _dir ) { kDebug(7004) << _dir; QString urlStr = _dir.url(KUrl::RemoveTrailingSlash); if ( !checkUpdate( urlStr ) ) return; // A job can be running to // - only list a new directory: the listers are in listersCurrentlyListing // - only update a directory: the listers are in listersCurrentlyHolding // - update a currently running listing: the listers are in both DirectoryData& dirData = directoryData[urlStr]; QList listers = dirData.listersCurrentlyListing; QList holders = dirData.listersCurrentlyHolding; // restart the job for _dir if it is running already bool killed = false; QWidget *window = 0; KIO::ListJob *job = jobForUrl( urlStr ); if ( job ) { window = job->ui()->window(); killJob( job ); killed = true; foreach ( KDirLister *kdl, listers ) kdl->jobDone( job ); foreach ( KDirLister *kdl, holders ) kdl->jobDone( job ); } kDebug(7004) << "Killed = " << killed; // we don't need to emit canceled signals since we only replaced the job, // the listing is continuing. Q_ASSERT( listers.isEmpty() || killed ); job = KIO::listDir( _dir, false /* no default GUI */ ); jobs.insert( job, KIO::UDSEntryList() ); connect( job, SIGNAL(entries( KIO::Job *, const KIO::UDSEntryList & )), this, SLOT(slotUpdateEntries( KIO::Job *, const KIO::UDSEntryList & )) ); connect( job, SIGNAL(result( KJob * )), this, SLOT(slotUpdateResult( KJob * )) ); kDebug(7004) << "update started in " << _dir; foreach ( KDirLister *kdl, listers ) { kdl->jobStarted( job ); } if ( !holders.isEmpty() ) { if ( !killed ) { bool first = true; foreach ( KDirLister *kdl, holders ) { kdl->jobStarted( job ); if ( first && kdl->d->window ) { first = false; job->ui()->setWindow( kdl->d->window ); } emit kdl->started( _dir ); } } else { job->ui()->setWindow( window ); foreach ( KDirLister *kdl, holders ) { kdl->jobStarted( job ); } } } } bool KDirListerCache::checkUpdate( const QString& _dir ) { if ( !itemsInUse.contains(_dir) ) { DirItem *item = itemsCached[_dir]; if ( item && item->complete ) { item->complete = false; item->decAutoUpdate(); // Hmm, this debug output might include login/password from the _dir URL. //kDebug(7004) << "directory " << _dir << " not in use, marked dirty."; } //else //kDebug(7004) << "aborted, directory " << _dir << " not in cache."; return false; } else return true; } -KFileItem *KDirListerCache::itemForUrl( const KUrl& url ) const +KFileItem KDirListerCache::itemForUrl( const KUrl& url ) const { return findByUrl( 0, url ); } KFileItemList *KDirListerCache::itemsForDir( const KUrl& _dir ) const { QString urlStr = _dir.url(KUrl::RemoveTrailingSlash); DirItem *item = itemsInUse.value(urlStr); if ( !item ) item = itemsCached[urlStr]; return item ? &item->lstItems : 0; } -KFileItem *KDirListerCache::findByName( const KDirLister *lister, const QString& _name ) const +KFileItem KDirListerCache::findByName( const KDirLister *lister, const QString& _name ) const { Q_ASSERT( lister ); for ( KUrl::List::Iterator it = lister->d->lstDirs.begin(); it != lister->d->lstDirs.end(); ++it ) { - KFileItem* item = itemsInUse[(*it).url()]->lstItems.findByName( _name ); - if ( item ) + const KFileItem item = itemsInUse[(*it).url()]->lstItems.findByName( _name ); + if ( !item.isNull() ) return item; } - return 0; + return KFileItem(); } -KFileItem *KDirListerCache::findByUrl( const KDirLister *lister, const KUrl& _u ) const +KFileItem KDirListerCache::findByUrl( const KDirLister *lister, const KUrl& _u ) const { KUrl _url(_u); _url.adjustPath(KUrl::RemoveTrailingSlash); KUrl parentDir( _url ); parentDir.setPath( parentDir.directory() ); // If lister is set, check that it contains this dir if ( lister && !lister->d->lstDirs.contains( parentDir ) ) - return 0; + return KFileItem(); const KFileItemList *itemList = itemsForDir( parentDir ); if ( itemList ) return itemList->findByUrl( _url ); - return 0; + + return KFileItem(); } void KDirListerCache::slotFilesAdded( const QString &dir ) // from KDirNotify signals { kDebug(7004) << dir; updateDirectory( KUrl(dir) ); } void KDirListerCache::slotFilesRemoved( const QStringList &fileList ) // from KDirNotify signals { kDebug(7004) ; QStringList::const_iterator it = fileList.begin(); for ( ; it != fileList.end() ; ++it ) { // emit the deleteItem signal if this file was shown in any view - KFileItem *fileitem = 0; + KFileItem fileitem; KUrl url( *it ); KUrl parentDir( url ); parentDir.setPath( parentDir.directory() ); KFileItemList *lstItems = itemsForDir( parentDir ); if ( lstItems ) { for ( KFileItemList::iterator fit = lstItems->begin(), fend = lstItems->end() ; fit != fend ; ++fit ) { - if ( (*fit )->url() == url ) { + if ( (*fit ).url() == url ) { fileitem = *fit; lstItems->erase( fit ); // remove fileitem from list break; } } } // Tell the views about it before deleting the KFileItems. They might need the subdirs' // file items (see the dirtree). - if ( fileitem ) { + if ( !fileitem.isNull() ) { DirectoryDataHash::iterator dit = directoryData.find(parentDir.url()); if ( dit != directoryData.end() ) { foreach ( KDirLister *kdl, (*dit).listersCurrentlyHolding ) kdl->emitDeleteItem( fileitem ); } } // If we found a fileitem, we can test if it's a dir. If not, we'll go to deleteDir just in case. - if ( !fileitem || fileitem->isDir() ) + if ( fileitem.isNull() || fileitem.isDir() ) { // in case of a dir, check if we have any known children, there's much to do in that case // (stopping jobs, removing dirs from cache etc.) deleteDir( url ); } - - // now remove the item itself - delete fileitem; } } void KDirListerCache::slotFilesChanged( const QStringList &fileList ) // from KDirNotify signals { KUrl::List dirsToUpdate; kDebug(7004) << "only half implemented"; QStringList::const_iterator it = fileList.begin(); for ( ; it != fileList.end() ; ++it ) { KUrl url( *it ); if ( url.isLocalFile() ) { kDebug(7004) << "KDirListerCache::slotFilesChanged " << url; - KFileItem *fileitem = findByUrl( 0, url ); - if ( fileitem ) + KFileItem fileitem = findByUrl( 0, url ); + if ( !fileitem.isNull() ) { // we need to refresh the item, because e.g. the permissions can have changed. aboutToRefreshItem( fileitem ); - KFileItem oldItem = *fileitem; - fileitem->refresh(); + KFileItem oldItem = fileitem; + fileitem.refresh(); emitRefreshItem( oldItem, fileitem ); } else kDebug(7004) << "item not found"; } else { // For remote files, refresh() won't be able to figure out the new information. // Let's update the dir. KUrl dir( url ); dir.setPath( dir.directory() ); if ( !dirsToUpdate.contains( dir ) ) dirsToUpdate.prepend( dir ); } } KUrl::List::const_iterator itdir = dirsToUpdate.begin(); for ( ; itdir != dirsToUpdate.end() ; ++itdir ) updateDirectory( *itdir ); // ## TODO problems with current jobs listing/updating that dir // ( see kde-2.2.2's kdirlister ) } void KDirListerCache::slotFileRenamed( const QString &_src, const QString &_dst ) // from KDirNotify signals { KUrl src( _src ); KUrl dst( _dst ); kDebug(7004) << src << " -> " << dst; #ifdef DEBUG_CACHE printDebug(); #endif KUrl oldurl( src ); oldurl.adjustPath( KUrl::RemoveTrailingSlash ); - KFileItem *fileitem = findByUrl( 0, oldurl ); + KFileItem fileitem = findByUrl( 0, oldurl ); // If the item had a UDS_URL as well as UDS_NAME set, the user probably wants // to be updating the name only (since they can't see the URL). // Check to see if a URL exists, and if so, if only the file part has changed, // only update the name and not the underlying URL. - bool nameOnly = fileitem && !fileitem->entry().stringValue( KIO::UDSEntry::UDS_URL ).isEmpty(); + bool nameOnly = !fileitem.isNull() && !fileitem.entry().stringValue( KIO::UDSEntry::UDS_URL ).isEmpty(); nameOnly &= src.directory( KUrl::IgnoreTrailingSlash | KUrl::AppendTrailingSlash ) == dst.directory( KUrl::IgnoreTrailingSlash | KUrl::AppendTrailingSlash ); // Somehow this should only be called if src is a dir. But how could we know if it is? // (Note that looking into itemsInUse isn't good enough. One could rename a subdir in a view.) if( !nameOnly ) renameDir( src, dst ); // Now update the KFileItem representing that file or dir (not exclusive with the above!) - if ( fileitem ) + if ( !fileitem.isNull() ) { - if ( !fileitem->isLocalFile() && !fileitem->localPath().isEmpty() ) // it uses UDS_LOCAL_PATH? ouch, needs an update then + if ( !fileitem.isLocalFile() && !fileitem.localPath().isEmpty() ) // it uses UDS_LOCAL_PATH? ouch, needs an update then slotFilesChanged( QStringList() << src.url() ); else { aboutToRefreshItem( fileitem ); - KFileItem oldItem = *fileitem; + KFileItem oldItem = fileitem; if( nameOnly ) - fileitem->setName( dst.fileName() ); + fileitem.setName( dst.fileName() ); else - fileitem->setUrl( dst ); - fileitem->refreshMimeType(); + fileitem.setUrl( dst ); + fileitem.refreshMimeType(); emitRefreshItem( oldItem, fileitem ); } } #ifdef DEBUG_CACHE printDebug(); #endif } -void KDirListerCache::aboutToRefreshItem( KFileItem *fileitem ) +void KDirListerCache::aboutToRefreshItem( const KFileItem& fileitem ) { // Look whether this item was shown in any view, i.e. held by any dirlister - KUrl parentDir( fileitem->url() ); + KUrl parentDir( fileitem.url() ); parentDir.setPath( parentDir.directory() ); const QString parentDirURL = parentDir.url(); DirectoryDataHash::iterator dit = directoryData.find(parentDirURL); if (dit == directoryData.end()) return; foreach (KDirLister *kdl, (*dit).listersCurrentlyHolding) kdl->aboutToRefreshItem( fileitem ); // Also look in listersCurrentlyListing, in case the user manages to rename during a listing foreach (KDirLister *kdl, (*dit).listersCurrentlyListing) kdl->aboutToRefreshItem( fileitem ); } -void KDirListerCache::emitRefreshItem( const KFileItem& oldItem, KFileItem *fileitem ) +void KDirListerCache::emitRefreshItem( const KFileItem& oldItem, const KFileItem& fileitem ) { // Look whether this item was shown in any view, i.e. held by any dirlister - KUrl parentDir( fileitem->url() ); + KUrl parentDir( fileitem.url() ); parentDir.setPath( parentDir.directory() ); QString parentDirURL = parentDir.url(); DirectoryDataHash::iterator dit = directoryData.find(parentDirURL); if (dit == directoryData.end()) return; // Also look in listersCurrentlyListing, in case the user manages to rename during a listing foreach ( KDirLister *kdl, (*dit).listersCurrentlyHolding + (*dit).listersCurrentlyListing ) { kdl->addRefreshItem( oldItem, fileitem ); kdl->emitItems(); } } KDirListerCache* KDirListerCache::self() { return s_pCache; } // private slots // Called by KDirWatch - usually when a dir we're watching has been modified, // but it can also be called for a file. void KDirListerCache::slotFileDirty( const QString& path ) { kDebug(7004) << path; // File or dir? KDE_struct_stat buff; if ( KDE_stat( QFile::encodeName(path), &buff ) != 0 ) return; // error const bool isDir = S_ISDIR(buff.st_mode); KUrl url(path); const QString urlStr = url.url(KUrl::RemoveTrailingSlash); if (isDir) { // A dir: launch an update job if anyone cares about it if (checkUpdate(urlStr)) updateDirectory(url); } else { // A file: delay updating it, FAM is flooding us with events if (!pendingUpdates.contains(urlStr)) { KUrl dir(url); dir.setPath(dir.directory()); if (checkUpdate(dir.url())) { pendingUpdates.insert(urlStr); if (!pendingUpdateTimer.isActive()) pendingUpdateTimer.start( 500 ); } } } } void KDirListerCache::slotFileCreated( const QString& path ) // from KDirWatch { kDebug(7004) << path; // XXX: how to avoid a complete rescan here? KUrl u( path ); u.setPath( u.directory() ); updateDirectory( u ); } void KDirListerCache::slotFileDeleted( const QString& path ) // from KDirWatch { kDebug(7004) << path; KUrl u( path ); slotFilesRemoved( QStringList() << u.url() ); } void KDirListerCache::slotEntries( KIO::Job *job, const KIO::UDSEntryList &entries ) { KUrl url(joburl( static_cast(job) )); url.adjustPath(KUrl::RemoveTrailingSlash); QString urlStr = url.url(); kDebug(7004) << "new entries for " << url; DirItem *dir = itemsInUse.value(urlStr); Q_ASSERT( dir ); DirectoryDataHash::iterator dit = directoryData.find(urlStr); Q_ASSERT(dit != directoryData.end()); DirectoryData& dirData = *dit; Q_ASSERT( !dirData.listersCurrentlyListing.isEmpty() ); // check if anyone wants the mimetypes immediately bool delayedMimeTypes = true; foreach ( KDirLister *kdl, dirData.listersCurrentlyListing ) delayedMimeTypes &= kdl->d->delayedMimeTypes; KIO::UDSEntryList::const_iterator it = entries.begin(); const KIO::UDSEntryList::const_iterator end = entries.end(); for ( ; it != end; ++it ) { const QString name = (*it).stringValue( KIO::UDSEntry::UDS_NAME ); Q_ASSERT( !name.isEmpty() ); if ( name.isEmpty() ) continue; if ( name == "." ) { - Q_ASSERT( !dir->rootItem ); - dir->rootItem = new KFileItem( *it, url, delayedMimeTypes, true ); + Q_ASSERT( dir->rootItem.isNull() ); + dir->rootItem = KFileItem( *it, url, delayedMimeTypes, true ); foreach ( KDirLister *kdl, dirData.listersCurrentlyListing ) - if ( !kdl->d->rootFileItem && kdl->d->url == url ) + if ( kdl->d->rootFileItem.isNull() && kdl->d->url == url ) kdl->d->rootFileItem = dir->rootItem; } else if ( name != ".." ) { - KFileItem* item = new KFileItem( *it, url, delayedMimeTypes, true ); - Q_ASSERT( item ); + KFileItem item( *it, url, delayedMimeTypes, true ); - //kDebug(7004)<< "Adding item: " << item->url(); + //kDebug(7004)<< "Adding item: " << item.url(); dir->lstItems.append( item ); foreach ( KDirLister *kdl, dirData.listersCurrentlyListing ) kdl->addNewItem( item ); } } foreach ( KDirLister *kdl, dirData.listersCurrentlyListing ) kdl->emitItems(); } void KDirListerCache::slotResult( KJob *j ) { Q_ASSERT( j ); KIO::ListJob *job = static_cast( j ); jobs.remove( job ); KUrl jobUrl(joburl( job )); jobUrl.adjustPath(KUrl::RemoveTrailingSlash); // need remove trailing slashes again, in case of redirections QString jobUrlStr = jobUrl.url(); kDebug(7004) << "finished listing " << jobUrl; #ifdef DEBUG_CACHE printDebug(); #endif DirectoryDataHash::iterator dit = directoryData.find(jobUrlStr); Q_ASSERT(dit != directoryData.end()); DirectoryData& dirData = *dit; Q_ASSERT( !dirData.listersCurrentlyListing.isEmpty() ); QList listers = dirData.listersCurrentlyListing; // move all listers to the holding list, do it before emitting // the signals to make sure it exists in KDirListerCache in case someone // calls listDir during the signal emission Q_ASSERT( dirData.listersCurrentlyHolding.isEmpty() ); dirData.listersCurrentlyHolding = listers; dirData.listersCurrentlyListing.clear(); if ( job->error() ) { foreach ( KDirLister *kdl, listers ) { kdl->jobDone( job ); kdl->handleError( job ); emit kdl->canceled( jobUrl ); if ( kdl->numJobs() == 0 ) { kdl->d->complete = true; emit kdl->canceled(); } } } else { DirItem *dir = itemsInUse.value(jobUrlStr); Q_ASSERT( dir ); dir->complete = true; foreach ( KDirLister* kdl, listers ) { kdl->jobDone( job ); emit kdl->completed( jobUrl ); if ( kdl->numJobs() == 0 ) { kdl->d->complete = true; emit kdl->completed(); } } } // TODO: hmm, if there was an error and job is a parent of one or more // of the pending urls we should cancel it/them as well processPendingUpdates(); #ifdef DEBUG_CACHE printDebug(); #endif } void KDirListerCache::slotRedirection( KIO::Job *j, const KUrl& url ) { Q_ASSERT( j ); KIO::ListJob *job = static_cast( j ); KUrl oldUrl(job->url()); // here we really need the old url! KUrl newUrl(url); // strip trailing slashes oldUrl.adjustPath(KUrl::RemoveTrailingSlash); newUrl.adjustPath(KUrl::RemoveTrailingSlash); if ( oldUrl == newUrl ) { kDebug(7004) << "New redirection url same as old, giving up."; return; } const QString oldUrlStr = oldUrl.url(); const QString newUrlStr = newUrl.url(); kDebug(7004) << oldUrl << " -> " << newUrl; #ifdef DEBUG_CACHE printDebug(); #endif // I don't think there can be dirItems that are children of oldUrl. // Am I wrong here? And even if so, we don't need to delete them, right? // DF: redirection happens before listDir emits any item. Makes little sense otherwise. // oldUrl cannot be in itemsCached because only completed items are moved there DirItem *dir = itemsInUse.take(oldUrlStr); Q_ASSERT( dir ); DirectoryDataHash::iterator dit = directoryData.find(oldUrlStr); Q_ASSERT(dit != directoryData.end()); DirectoryData oldDirData = *dit; directoryData.erase(dit); Q_ASSERT( !oldDirData.listersCurrentlyListing.isEmpty() ); const QList listers = oldDirData.listersCurrentlyListing; Q_ASSERT( !listers.isEmpty() ); foreach ( KDirLister *kdl, listers ) { kdl->redirect(oldUrlStr, newUrl); } // when a lister was stopped before the job emits the redirection signal, the old url will // also be in listersCurrentlyHolding const QList holders = oldDirData.listersCurrentlyHolding; foreach ( KDirLister *kdl, holders ) { kdl->jobStarted( job ); // do it like when starting a new list-job that will redirect later // TODO: maybe don't emit started if there's an update running for newUrl already? emit kdl->started( oldUrl ); kdl->redirect(oldUrl, newUrl); } DirItem *newDir = itemsInUse.value(newUrlStr); if ( newDir ) { kDebug(7004) << "slotRedirection: " << newUrl << " already in use"; // only in this case there can newUrl already be in listersCurrentlyListing or listersCurrentlyHolding delete dir; // get the job if one's running for newUrl already (can be a list-job or an update-job), but // do not return this 'job', which would happen because of the use of redirectionURL() KIO::ListJob *oldJob = jobForUrl( newUrlStr, job ); // listers of newUrl with oldJob: forget about the oldJob and use the already running one // which will be converted to an updateJob DirectoryData& newDirData = directoryData[newUrlStr]; QList& curListers = newDirData.listersCurrentlyListing; if ( !curListers.isEmpty() ) { kDebug(7004) << "slotRedirection: and it is currently listed"; Q_ASSERT( oldJob ); // ?! foreach ( KDirLister *kdl, curListers ) { // listers of newUrl kdl->jobDone( oldJob ); kdl->jobStarted( job ); kdl->connectJob( job ); } // append listers of oldUrl with newJob to listers of newUrl with oldJob foreach ( KDirLister *kdl, listers ) curListers.append( kdl ); } else { curListers = listers; } if ( oldJob ) // kill the old job, be it a list-job or an update-job killJob( oldJob ); // holders of newUrl: use the already running job which will be converted to an updateJob QList& curHolders = newDirData.listersCurrentlyHolding; if ( !curHolders.isEmpty() ) { kDebug(7004) << "slotRedirection: and it is currently held."; foreach ( KDirLister *kdl, curHolders ) { // holders of newUrl kdl->jobStarted( job ); emit kdl->started( newUrl ); } // append holders of oldUrl to holders of newUrl foreach ( KDirLister *kdl, holders ) curHolders.append( kdl ); } else { curHolders = holders; } // emit old items: listers, holders. NOT: newUrlListers/newUrlHolders, they already have them listed // TODO: make this a separate method? foreach ( KDirLister *kdl, listers + holders ) { - if ( !kdl->d->rootFileItem && kdl->d->url == newUrl ) + if ( kdl->d->rootFileItem.isNull() && kdl->d->url == newUrl ) kdl->d->rootFileItem = newDir->rootItem; kdl->addNewItems( newDir->lstItems ); kdl->emitItems(); } } else if ( (newDir = itemsCached.take( newUrlStr )) ) { kDebug(7004) << "slotRedirection: " << newUrl << " is unused, but already in the cache."; delete dir; itemsInUse.insert( newUrlStr, newDir ); DirectoryData& newDirData = directoryData[newUrlStr]; newDirData.listersCurrentlyListing = listers; newDirData.listersCurrentlyHolding = holders; // emit old items: listers, holders foreach ( KDirLister *kdl, listers + holders ) { - if ( !kdl->d->rootFileItem && kdl->d->url == newUrl ) + if ( kdl->d->rootFileItem.isNull() && kdl->d->url == newUrl ) kdl->d->rootFileItem = newDir->rootItem; kdl->addNewItems( newDir->lstItems ); kdl->emitItems(); } } else { kDebug(7004) << "slotRedirection: " << newUrl << " has not been listed yet."; - delete dir->rootItem; - dir->rootItem = 0; - qDeleteAll( dir->lstItems ); + dir->rootItem = KFileItem(); dir->lstItems.clear(); dir->redirect( newUrl ); itemsInUse.insert( newUrlStr, dir ); DirectoryData& newDirData = directoryData[newUrlStr]; newDirData.listersCurrentlyListing = listers; newDirData.listersCurrentlyHolding = holders; if ( holders.isEmpty() ) { #ifdef DEBUG_CACHE printDebug(); #endif return; // only in this case the job doesn't need to be converted, } } // make the job an update job job->disconnect( this ); connect( job, SIGNAL(entries( KIO::Job *, const KIO::UDSEntryList & )), this, SLOT(slotUpdateEntries( KIO::Job *, const KIO::UDSEntryList & )) ); connect( job, SIGNAL(result( KJob * )), this, SLOT(slotUpdateResult( KJob * )) ); // FIXME: autoUpdate-Counts!! #ifdef DEBUG_CACHE printDebug(); #endif } void KDirListerCache::renameDir( const KUrl &oldUrl, const KUrl &newUrl ) { kDebug(7004) << oldUrl << " -> " << newUrl; const QString oldUrlStr = oldUrl.url(KUrl::RemoveTrailingSlash); const QString newUrlStr = newUrl.url(KUrl::RemoveTrailingSlash); // Not enough. Also need to look at any child dir, even sub-sub-sub-dir. //DirItem *dir = itemsInUse.take( oldUrlStr ); //emitRedirections( oldUrl, url ); typedef QPair ItemToInsert; QLinkedList itemsToInsert; // Look at all dirs being listed/shown QHash::iterator itu = itemsInUse.begin(); const QHash::iterator ituend = itemsInUse.end(); bool goNext; while ( itu != ituend ) { goNext = true; DirItem *dir = itu.value(); KUrl oldDirUrl ( itu.key() ); //kDebug(7004) << "itemInUse: " << oldDirUrl; // Check if this dir is oldUrl, or a subfolder of it if ( oldUrl.isParentOf( oldDirUrl ) ) { // TODO should use KUrl::cleanpath like isParentOf does QString relPath = oldDirUrl.path().mid( oldUrl.path().length() ); KUrl newDirUrl( newUrl ); // take new base if ( !relPath.isEmpty() ) newDirUrl.addPath( relPath ); // add unchanged relative path //kDebug(7004) << "KDirListerCache::renameDir new url=" << newDirUrl; // Update URL in dir item and in itemsInUse dir->redirect( newDirUrl ); itu = itemsInUse.erase( itu ); // implies ++itu itemsToInsert.append(qMakePair(newDirUrl.url(KUrl::RemoveTrailingSlash), dir)); goNext = false; // because of the implied ++itu above // Rename all items under that dir for ( KFileItemList::iterator kit = dir->lstItems.begin(), kend = dir->lstItems.end(); kit != kend ; ++kit ) { - const KUrl oldItemUrl ((*kit)->url()); + const KUrl oldItemUrl ((*kit).url()); const QString oldItemUrlStr( oldItemUrl.url(KUrl::RemoveTrailingSlash) ); KUrl newItemUrl( oldItemUrl ); newItemUrl.setPath( newDirUrl.path() ); newItemUrl.addPath( oldItemUrl.fileName() ); kDebug(7004) << "KDirListerCache::renameDir renaming " << oldItemUrlStr << " to " << newItemUrl.url(); - (*kit)->setUrl( newItemUrl ); + (*kit).setUrl( newItemUrl ); } emitRedirections( oldDirUrl, newDirUrl ); } if ( goNext ) ++itu; } // Do the inserts out of the loop to avoid messing up iterators foreach(const ItemToInsert& i, itemsToInsert) { itemsInUse.insert(i.first, i.second); } // Is oldUrl a directory in the cache? // Remove any child of oldUrl from the cache - even if the renamed dir itself isn't in it! removeDirFromCache( oldUrl ); // TODO rename, instead. } // helper for renameDir, not used for redirections from KIO::listDir(). void KDirListerCache::emitRedirections( const KUrl &oldUrl, const KUrl &newUrl ) { kDebug(7004) << oldUrl << " -> " << newUrl; const QString oldUrlStr = oldUrl.url(KUrl::RemoveTrailingSlash); const QString newUrlStr = newUrl.url(KUrl::RemoveTrailingSlash); KIO::ListJob *job = jobForUrl( oldUrlStr ); if ( job ) killJob( job ); // Check if we were listing this dir. Need to abort and restart with new name in that case. DirectoryDataHash::iterator dit = directoryData.find(oldUrlStr); if ( dit == directoryData.end() ) return; const QList listers = (*dit).listersCurrentlyListing; const QList holders = (*dit).listersCurrentlyHolding; DirectoryData& newDirData = directoryData[newUrlStr]; // Tell the world that the job listing the old url is dead. foreach ( KDirLister *kdl, listers ) { if ( job ) kdl->jobDone( job ); emit kdl->canceled( oldUrl ); } newDirData.listersCurrentlyListing += listers; // Check if we are currently displaying this directory (odds opposite wrt above) foreach ( KDirLister *kdl, holders ) { if ( job ) kdl->jobDone( job ); } newDirData.listersCurrentlyHolding += holders; directoryData.erase(dit); if ( !listers.isEmpty() ) { updateDirectory( newUrl ); // Tell the world about the new url foreach ( KDirLister *kdl, listers ) emit kdl->started( newUrl ); } // And notify the dirlisters of the redirection foreach ( KDirLister *kdl, holders ) { // ### consider replacing this block with kdl->redirect(oldUrl, newUrl)... KUrl::List& lstDirs = kdl->d->lstDirs; lstDirs[ lstDirs.indexOf( oldUrl ) ] = newUrl; if ( lstDirs.count() == 1 ) emit kdl->redirection( newUrl ); emit kdl->redirection( oldUrl, newUrl ); } } void KDirListerCache::removeDirFromCache( const KUrl& dir ) { kDebug(7004) << "KDirListerCache::removeDirFromCache " << dir; const QList cachedDirs = itemsCached.keys(); // seems slow, but there's no qcache iterator... foreach(const QString& cachedDir, cachedDirs) { if ( dir.isParentOf( KUrl( cachedDir ) ) ) itemsCached.remove( cachedDir ); } } void KDirListerCache::slotUpdateEntries( KIO::Job* job, const KIO::UDSEntryList& list ) { jobs[static_cast(job)] += list; } void KDirListerCache::slotUpdateResult( KJob * j ) { Q_ASSERT( j ); KIO::ListJob *job = static_cast( j ); KUrl jobUrl (joburl( job )); jobUrl.adjustPath(KUrl::RemoveTrailingSlash); // need remove trailing slashes again, in case of redirections QString jobUrlStr (jobUrl.url()); kDebug(7004) << "finished update " << jobUrl; DirectoryData& dirData = directoryData[jobUrlStr]; QList &listers = dirData.listersCurrentlyHolding; QList tmpLst = dirData.listersCurrentlyListing; dirData.listersCurrentlyListing.clear(); if ( !tmpLst.isEmpty() ) { if ( !listers.isEmpty() ) { foreach ( KDirLister* kdl, tmpLst ) { Q_ASSERT( !listers.contains( kdl ) ); listers.append( kdl ); } } else { listers = tmpLst; } } // once we are updating dirs that are only in the cache this will fail! Q_ASSERT( !listers.isEmpty() ); if ( job->error() ) { foreach ( KDirLister* kdl, listers ) { kdl->jobDone( job ); //don't bother the user //kdl->handleError( job ); emit kdl->canceled( jobUrl ); if ( kdl->numJobs() == 0 ) { kdl->d->complete = true; emit kdl->canceled(); } } jobs.remove( job ); // TODO: if job is a parent of one or more // of the pending urls we should cancel them processPendingUpdates(); return; } DirItem *dir = itemsInUse.value(jobUrlStr, 0); Q_ASSERT(dir); dir->complete = true; // check if anyone wants the mimetypes immediately bool delayedMimeTypes = true; foreach ( KDirLister *kdl, listers ) delayedMimeTypes &= kdl->d->delayedMimeTypes; - QHash fileItems; // fileName -> KFileItem* + QHash fileItems; // fileName -> KFileItem* // Unmark all items in url for ( KFileItemList::iterator kit = dir->lstItems.begin(), kend = dir->lstItems.end() ; kit != kend ; ++kit ) { - (*kit)->unmark(); - fileItems.insert( (*kit)->name(), *kit ); + (*kit).unmark(); + fileItems.insert( (*kit).name(), &*kit ); } - KFileItem *tmp; - KIO::UDSEntryList buf = jobs.value( job ); KIO::UDSEntryList::const_iterator it = buf.begin(); const KIO::UDSEntryList::const_iterator end = buf.end(); for ( ; it != end; ++it ) { // Form the complete url KFileItem item( *it, jobUrl, delayedMimeTypes, true ); const QString name = item.name(); Q_ASSERT( !name.isEmpty() ); // we duplicate the check for dotdot here, to avoid iterating over // all items again and checking in matchesFilter() that way. if ( name.isEmpty() || name == ".." ) continue; if ( name == "." ) { // if the update was started before finishing the original listing // there is no root item yet - if ( !dir->rootItem ) + if ( dir->rootItem.isNull() ) { - dir->rootItem = new KFileItem(item); + dir->rootItem = item; foreach ( KDirLister *kdl, listers ) - if ( !kdl->d->rootFileItem && kdl->d->url == jobUrl ) + if ( kdl->d->rootFileItem.isNull() && kdl->d->url == jobUrl ) kdl->d->rootFileItem = dir->rootItem; } continue; } // Find this item + KFileItem *tmp = 0; if ( (tmp = fileItems.value(item.name())) ) { // check if something changed for this file if ( !tmp->cmp( item ) ) { foreach ( KDirLister *kdl, listers ) - kdl->aboutToRefreshItem( tmp ); + kdl->aboutToRefreshItem( *tmp ); - //kDebug(7004) << "slotUpdateResult: file changed: " << tmp->name(); + // kDebug(7004) << "slotUpdateResult: file changed: " << tmp->name(); const KFileItem oldItem = *tmp; *tmp = item; foreach ( KDirLister *kdl, listers ) - kdl->addRefreshItem( oldItem, tmp /*must be address of item in cache*/ ); + kdl->addRefreshItem( oldItem, *tmp ); } tmp->mark(); } else // this is a new file { - //kDebug(7004) << "slotUpdateResult: new file: " << name; + // kDebug(7004) << "slotUpdateResult: new file: " << name; - KFileItem* pitem = new KFileItem(item); // we're not using kfileitem by value yet - pitem->mark(); + KFileItem pitem(item); + pitem.mark(); dir->lstItems.append( pitem ); foreach ( KDirLister *kdl, listers ) kdl->addNewItem( pitem ); } } jobs.remove( job ); deleteUnmarkedItems( listers, dir->lstItems ); foreach ( KDirLister *kdl, listers ) { kdl->emitItems(); kdl->jobDone( job ); emit kdl->completed( jobUrl ); if ( kdl->numJobs() == 0 ) { kdl->d->complete = true; emit kdl->completed(); } } // TODO: hmm, if there was an error and job is a parent of one or more // of the pending urls we should cancel it/them as well processPendingUpdates(); } // private KIO::ListJob *KDirListerCache::jobForUrl( const QString& url, KIO::ListJob *not_job ) { KIO::ListJob *job; QMap< KIO::ListJob *, KIO::UDSEntryList >::const_iterator it = jobs.begin(); while ( it != jobs.end() ) { job = it.key(); if ( joburl( job ).url(KUrl::RemoveTrailingSlash) == url && job != not_job ) return job; ++it; } return 0; } const KUrl& KDirListerCache::joburl( KIO::ListJob *job ) { if ( job->redirectionUrl().isValid() ) return job->redirectionUrl(); else return job->url(); } void KDirListerCache::killJob( KIO::ListJob *job ) { jobs.remove( job ); job->disconnect( this ); job->kill(); } void KDirListerCache::deleteUnmarkedItems( const QList& listers, KFileItemList &lstItems ) { // Find all unmarked items and delete them - QMutableListIterator kit( lstItems ); + QMutableListIterator kit( lstItems ); while ( kit.hasNext() ) { - KFileItem* item = kit.next(); - if ( !item->isMarked() ) + const KFileItem item = kit.next(); + if ( !item.isMarked() ) { //kDebug() << item->name(); foreach ( KDirLister *kdl, listers ) kdl->emitDeleteItem( item ); - if ( item->isDir() ) - deleteDir( item->url() ); + if ( item.isDir() ) + deleteDir( item.url() ); - // finally actually delete the item - delete item; kit.remove(); } } } void KDirListerCache::deleteDir( const KUrl& dirUrl ) { //kDebug() << dirUrl; // unregister and remove the children of the deleted item. // Idea: tell all the KDirListers that they should forget the dir // and then remove it from the cache. // Separate itemsInUse iteration and calls to forgetDirs (which modify itemsInUse) KUrl::List affectedItems; QHash::iterator itu = itemsInUse.begin(); const QHash::iterator ituend = itemsInUse.end(); for ( ; itu != ituend; ++itu ) { const KUrl deletedUrl( itu.key() ); if ( dirUrl.isParentOf( deletedUrl ) ) { affectedItems.append(deletedUrl); } } foreach(const KUrl& deletedUrl, affectedItems) { const QString deletedUrlStr = deletedUrl.url(); // stop all jobs for deletedUrlStr DirectoryDataHash::iterator dit = directoryData.find(deletedUrlStr); if (dit != directoryData.end()) { // we need a copy because stop modifies the list QList listers = (*dit).listersCurrentlyListing; foreach ( KDirLister *kdl, listers ) stop( kdl, deletedUrl ); // tell listers holding deletedUrl to forget about it // this will stop running updates for deletedUrl as well // we need a copy because forgetDirs modifies the list QList holders = (*dit).listersCurrentlyHolding; foreach ( KDirLister *kdl, holders ) { // lister's root is the deleted item if ( kdl->d->url == deletedUrl ) { // tell the view first. It might need the subdirs' items (which forgetDirs will delete) - if ( kdl->d->rootFileItem ) + if ( !kdl->d->rootFileItem.isNull() ) emit kdl->deleteItem( kdl->d->rootFileItem ); forgetDirs( kdl ); - kdl->d->rootFileItem = 0; + kdl->d->rootFileItem = KFileItem(); } else { const bool treeview = kdl->d->lstDirs.count() > 1; if ( !treeview ) { emit kdl->clear(); kdl->d->lstDirs.clear(); } else kdl->d->lstDirs.removeAll( deletedUrl ); forgetDirs( kdl, deletedUrl, treeview ); } } } // delete the entry for deletedUrl - should not be needed, it's in // items cached now int count = itemsInUse.remove( deletedUrlStr ); Q_ASSERT( count == 0 ); } // remove the children from the cache removeDirFromCache( dirUrl ); } // delayed updating of files, FAM is flooding us with events void KDirListerCache::processPendingUpdates() { foreach(const QString file, pendingUpdates) { kDebug(7004) << file; KUrl u(file); - KFileItem *item = findByUrl( 0, u ); // search all items - if ( item ) { + KFileItem item = findByUrl( 0, u ); // search all items + if ( !item.isNull() ) { // we need to refresh the item, because e.g. the permissions can have changed. aboutToRefreshItem( item ); - KFileItem oldItem = *item; - item->refresh(); + KFileItem oldItem = item; + item.refresh(); emitRefreshItem( oldItem, item ); } } pendingUpdates.clear(); } #ifndef NDEBUG void KDirListerCache::printDebug() { kDebug(7004) << "Items in use: "; QHash::const_iterator itu = itemsInUse.begin(); const QHash::const_iterator ituend = itemsInUse.end(); for ( ; itu != ituend ; ++itu ) { kDebug(7004) << " " << itu.key() << " URL: " << itu.value()->url - << " rootItem: " << ( itu.value()->rootItem ? itu.value()->rootItem->url() : KUrl() ) + << " rootItem: " << ( !itu.value()->rootItem.isNull() ? itu.value()->rootItem.url() : KUrl() ) << " autoUpdates refcount: " << itu.value()->autoUpdates << " complete: " << itu.value()->complete << QString(" with %1 items.").arg(itu.value()->lstItems.count()) << endl; } kDebug(7004) << "Directory data: "; DirectoryDataHash::const_iterator dit = directoryData.begin(); for ( ; dit != directoryData.end(); ++dit ) { QString list; foreach ( KDirLister* listit, (*dit).listersCurrentlyListing ) list += " 0x" + QString::number( (long)listit, 16 ); kDebug(7004) << " " << dit.key() << " " << (*dit).listersCurrentlyListing.count() << " listers: " << list; list.clear(); foreach ( KDirLister* listit, (*dit).listersCurrentlyHolding ) list += " 0x" + QString::number( (long)listit, 16 ); kDebug(7004) << " " << dit.key() << " " << (*dit).listersCurrentlyHolding.count() << " holders: " << list; } QMap< KIO::ListJob *, KIO::UDSEntryList >::Iterator jit = jobs.begin(); kDebug(7004) << "Jobs: "; for ( ; jit != jobs.end() ; ++jit ) kDebug(7004) << " " << jit.key() << " listing " << joburl( jit.key() ) << ": " << (*jit).count() << " entries."; kDebug(7004) << "Items in cache: "; const QList cachedDirs = itemsCached.keys(); foreach(const QString& cachedDir, cachedDirs) { DirItem* dirItem = itemsCached.object(cachedDir); kDebug(7004) << " " << cachedDir << " rootItem: " - << ( dirItem->rootItem ? dirItem->rootItem->url().prettyUrl() : QString("NULL") ) + << ( !dirItem->rootItem.isNull() ? dirItem->rootItem.url().prettyUrl() : QString("NULL") ) << QString(" with %1 items.").arg(dirItem->lstItems.count()) << endl; } } #endif /*********************** -- The new KDirLister -- ************************/ KDirLister::KDirLister( QObject* parent ) : QObject(parent), d(new KDirListerPrivate) { kDebug(7003) << "+KDirLister"; d->complete = true; setAutoUpdate( true ); setDirOnlyMode( false ); setShowingDotFiles( false ); setAutoErrorHandlingEnabled( true, 0 ); } KDirLister::~KDirLister() { kDebug(7003) << "-KDirLister"; // Stop all running jobs stop(); KDirListerCache::self()->forgetDirs( this ); delete d; } bool KDirLister::openUrl( const KUrl& _url, bool _keep, bool _reload ) { // emit the current changes made to avoid an inconsistent treeview if ( d->changes != NONE && _keep ) emitChanges(); d->changes = NONE; return KDirListerCache::self()->listDir( this, _url, _keep, _reload ); } void KDirLister::stop() { kDebug(7003) ; KDirListerCache::self()->stop( this ); } void KDirLister::stop( const KUrl& _url ) { kDebug(7003) << _url; KDirListerCache::self()->stop( this, _url ); } bool KDirLister::autoUpdate() const { return d->autoUpdate; } void KDirLister::setAutoUpdate( bool _enable ) { if ( d->autoUpdate == _enable ) return; d->autoUpdate = _enable; KDirListerCache::self()->setAutoUpdate( this, _enable ); } bool KDirLister::showingDotFiles() const { return d->isShowingDotFiles; } void KDirLister::setShowingDotFiles( bool _showDotFiles ) { if ( d->isShowingDotFiles == _showDotFiles ) return; d->isShowingDotFiles = _showDotFiles; d->changes ^= DOT_FILES; } bool KDirLister::dirOnlyMode() const { return d->dirOnlyMode; } void KDirLister::setDirOnlyMode( bool _dirsOnly ) { if ( d->dirOnlyMode == _dirsOnly ) return; d->dirOnlyMode = _dirsOnly; d->changes ^= DIR_ONLY_MODE; } bool KDirLister::autoErrorHandlingEnabled() const { return d->autoErrorHandling; } void KDirLister::setAutoErrorHandlingEnabled( bool enable, QWidget* parent ) { d->autoErrorHandling = enable; d->errorParent = parent; } KUrl KDirLister::url() const { return d->url; } KUrl::List KDirLister::directories() const { return d->lstDirs; } void KDirLister::emitChanges() { if ( d->changes == NONE ) return; for ( KUrl::List::Iterator it = d->lstDirs.begin(); it != d->lstDirs.end(); ++it ) { const KFileItemList* itemList = KDirListerCache::self()->itemsForDir( *it ); KFileItemList::const_iterator kit = itemList->begin(); const KFileItemList::const_iterator kend = itemList->end(); for ( ; kit != kend; ++kit ) { - if ( (*kit)->text() == "." || (*kit)->text() == ".." ) + if ( (*kit).text() == "." || (*kit).text() == ".." ) continue; bool oldMime = true, newMime = true; if ( d->changes & MIME_FILTER ) { - const QString mimetype = (*kit)->mimetype(); + const QString mimetype = (*kit).mimetype(); oldMime = doMimeFilter( mimetype, d->oldMimeFilter ) && doMimeExcludeFilter( mimetype, d->oldMimeExcludeFilter ); newMime = doMimeFilter( mimetype, d->mimeFilter ) && doMimeExcludeFilter( mimetype, d->mimeExcludeFilter ); if ( oldMime && !newMime ) { emit deleteItem( *kit ); - emit deleteItem( **kit ); continue; } } if ( d->changes & DIR_ONLY_MODE ) { // the lister switched to dirOnlyMode if ( d->dirOnlyMode ) { - if ( !(*kit)->isDir() ) + if ( !(*kit).isDir() ) { emit deleteItem( *kit ); - emit deleteItem( **kit ); } } - else if ( !(*kit)->isDir() ) + else if ( !(*kit).isDir() ) addNewItem( *kit ); continue; } - if ( (*kit)->isHidden() ) + if ( (*kit).isHidden() ) { if ( d->changes & DOT_FILES ) { // the lister switched to dot files mode if ( d->isShowingDotFiles ) addNewItem( *kit ); else { emit deleteItem( *kit ); - emit deleteItem( **kit ); } continue; } } else if ( d->changes & NAME_FILTER ) { - bool oldName = (*kit)->isDir() || + bool oldName = (*kit).isDir() || d->oldFilters.isEmpty() || - doNameFilter( (*kit)->text(), d->oldFilters ); + doNameFilter( (*kit).text(), d->oldFilters ); - bool newName = (*kit)->isDir() || + bool newName = (*kit).isDir() || d->lstFilters.isEmpty() || - doNameFilter( (*kit)->text(), d->lstFilters ); + doNameFilter( (*kit).text(), d->lstFilters ); if ( oldName && !newName ) { emit deleteItem( *kit ); - emit deleteItem( **kit ); continue; } else if ( !oldName && newName ) addNewItem( *kit ); } if ( (d->changes & MIME_FILTER) && !oldMime && newMime ) addNewItem( *kit ); } emitItems(); } d->changes = NONE; } void KDirLister::updateDirectory( const KUrl& _u ) { KDirListerCache::self()->updateDirectory( _u ); } bool KDirLister::isFinished() const { return d->complete; } -KFileItem *KDirLister::rootItem() const +KFileItem KDirLister::rootItem() const { return d->rootFileItem; } -KFileItem *KDirLister::findByUrl( const KUrl& _url ) const +KFileItem KDirLister::findByUrl( const KUrl& _url ) const { return KDirListerCache::self()->findByUrl( this, _url ); } -KFileItem *KDirLister::findByName( const QString& _name ) const +KFileItem KDirLister::findByName( const QString& _name ) const { return KDirListerCache::self()->findByName( this, _name ); } // ================ public filter methods ================ // void KDirLister::setNameFilter( const QString& nameFilter ) { if ( !(d->changes & NAME_FILTER) ) { d->oldFilters = d->lstFilters; } d->lstFilters.clear(); d->nameFilter = nameFilter; // Split on white space const QStringList list = nameFilter.split( ' ', QString::SkipEmptyParts ); for ( QStringList::const_iterator it = list.begin(); it != list.end(); ++it ) d->lstFilters.append( QRegExp(*it, Qt::CaseInsensitive, QRegExp::Wildcard ) ); d->changes |= NAME_FILTER; } QString KDirLister::nameFilter() const { return d->nameFilter; } void KDirLister::setMimeFilter( const QStringList& mimeFilter ) { if ( !(d->changes & MIME_FILTER) ) d->oldMimeFilter = d->mimeFilter; if ( mimeFilter.contains("application/octet-stream") ) // all files d->mimeFilter.clear(); else d->mimeFilter = mimeFilter; d->changes |= MIME_FILTER; } void KDirLister::setMimeExcludeFilter( const QStringList& mimeExcludeFilter ) { if ( !(d->changes & MIME_FILTER) ) d->oldMimeExcludeFilter = d->mimeExcludeFilter; d->mimeExcludeFilter = mimeExcludeFilter; d->changes |= MIME_FILTER; } void KDirLister::clearMimeFilter() { if ( !(d->changes & MIME_FILTER) ) { d->oldMimeFilter = d->mimeFilter; d->oldMimeExcludeFilter = d->mimeExcludeFilter; } d->mimeFilter.clear(); d->mimeExcludeFilter.clear(); d->changes |= MIME_FILTER; } QStringList KDirLister::mimeFilters() const { return d->mimeFilter; } bool KDirLister::matchesFilter( const QString& name ) const { return doNameFilter( name, d->lstFilters ); } bool KDirLister::matchesMimeFilter( const QString& mime ) const { return doMimeFilter( mime, d->mimeFilter ) && doMimeExcludeFilter(mime,d->mimeExcludeFilter); } // ================ protected methods ================ // -bool KDirLister::matchesFilter( const KFileItem *item ) const +bool KDirLister::matchesFilter( const KFileItem& item ) const { - Q_ASSERT( item ); + Q_ASSERT( !item.isNull() ); - if ( item->text() == ".." ) + if ( item.text() == ".." ) return false; - if ( !d->isShowingDotFiles && item->isHidden() ) + if ( !d->isShowingDotFiles && item.isHidden() ) return false; - if ( item->isDir() || d->lstFilters.isEmpty() ) + if ( item.isDir() || d->lstFilters.isEmpty() ) return true; - return matchesFilter( item->text() ); + return matchesFilter( item.text() ); } -bool KDirLister::matchesMimeFilter( const KFileItem *item ) const +bool KDirLister::matchesMimeFilter( const KFileItem& item ) const { - Q_ASSERT( item ); + Q_ASSERT( !item.isNull() ); // Don't lose time determining the mimetype if there is no filter if ( d->mimeFilter.isEmpty() && d->mimeExcludeFilter.isEmpty() ) return true; - return matchesMimeFilter( item->mimetype() ); + return matchesMimeFilter( item.mimetype() ); } bool KDirLister::doNameFilter( const QString& name, const QList& filters ) const { for ( QList::const_iterator it = filters.begin(); it != filters.end(); ++it ) if ( (*it).exactMatch( name ) ) return true; return false; } bool KDirLister::doMimeFilter( const QString& mime, const QStringList& filters ) const { if ( filters.isEmpty() ) return true; KMimeType::Ptr mimeptr = KMimeType::mimeType(mime); //kDebug(7004) << "doMimeFilter: investigating: "<name(); QStringList::const_iterator it = filters.begin(); for ( ; it != filters.end(); ++it ) if ( mimeptr->is(*it) ) return true; //else kDebug(7004) << "doMimeFilter: compared without result to "<<*it; return false; } bool KDirLister::doMimeExcludeFilter( const QString& mime, const QStringList& filters ) const { if ( filters.isEmpty() ) return true; QStringList::const_iterator it = filters.begin(); for ( ; it != filters.end(); ++it ) if ( (*it) == mime ) return false; return true; } void KDirLister::handleError( KIO::Job *job ) { if ( d->autoErrorHandling ) job->uiDelegate()->showErrorMessage(); } // ================= private methods ================= // -void KDirLister::addNewItem( KFileItem *item ) +void KDirLister::addNewItem( const KFileItem &item ) { - if ( ( d->dirOnlyMode && !item->isDir() ) || !matchesFilter( item ) ) + if ( ( d->dirOnlyMode && !item.isDir() ) || !matchesFilter( item ) ) return; // No reason to continue... bailing out here prevents a mimetype scan. if ( matchesMimeFilter( item ) ) { if ( !d->lstNewItems ) { - Q_ASSERT( !d->lstNewItemsV2 ); d->lstNewItems = new KFileItemList; - d->lstNewItemsV2 = new QList; } - Q_ASSERT( item ); + Q_ASSERT( !item.isNull() ); d->lstNewItems->append( item ); // items not filtered - d->lstNewItemsV2->append( *item ); } else { if ( !d->lstMimeFilteredItems ) { - Q_ASSERT( !d->lstMimeFilteredItemsV2 ); d->lstMimeFilteredItems = new KFileItemList; - d->lstMimeFilteredItemsV2 = new QList; } - Q_ASSERT( item ); + Q_ASSERT( !item.isNull() ); d->lstMimeFilteredItems->append( item ); // only filtered by mime - d->lstMimeFilteredItemsV2->append( *item ); } } void KDirLister::addNewItems( const KFileItemList& items ) { // TODO: make this faster - test if we have a filter at all first // DF: was this profiled? The matchesFoo() functions should be fast, w/o filters... // Of course if there is no filter and we can do a range-insertion instead of a loop, that might be good. // But that's for Qt4, not possible with QPtrList. KFileItemList::const_iterator kit = items.begin(); const KFileItemList::const_iterator kend = items.end(); for ( ; kit != kend; ++kit ) addNewItem( *kit ); } -void KDirLister::aboutToRefreshItem( const KFileItem *item ) +void KDirLister::aboutToRefreshItem( const KFileItem &item ) { // The code here follows the logic in addNewItem - if ( ( d->dirOnlyMode && !item->isDir() ) || !matchesFilter( item ) ) + if ( ( d->dirOnlyMode && !item.isDir() ) || !matchesFilter( item ) ) d->refreshItemWasFiltered = true; else if ( !matchesMimeFilter( item ) ) d->refreshItemWasFiltered = true; else d->refreshItemWasFiltered = false; } -void KDirLister::addRefreshItem( const KFileItem& oldItem, KFileItem* item ) +void KDirLister::addRefreshItem( const KFileItem& oldItem, const KFileItem& item ) { - bool isExcluded = (d->dirOnlyMode && !item->isDir()) || !matchesFilter( item ); + bool isExcluded = (d->dirOnlyMode && !item.isDir()) || !matchesFilter( item ); if ( !isExcluded && matchesMimeFilter( item ) ) { if ( d->refreshItemWasFiltered ) { if ( !d->lstNewItems ) { - Q_ASSERT( !d->lstNewItemsV2 ); d->lstNewItems = new KFileItemList; - d->lstNewItemsV2 = new QList; } - Q_ASSERT( item ); + Q_ASSERT( !item.isNull() ); d->lstNewItems->append( item ); - d->lstNewItemsV2->append( *item ); } else { if ( !d->lstRefreshItems ) { - Q_ASSERT( !d->lstRefreshItemsV2 ); - d->lstRefreshItems = new KFileItemList; - d->lstRefreshItemsV2 = new QList >; + d->lstRefreshItems = new QList >; } - Q_ASSERT( item ); - d->lstRefreshItems->append( item ); - d->lstRefreshItemsV2->append( qMakePair(oldItem, *item) ); + Q_ASSERT( !item.isNull() ); + d->lstRefreshItems->append( qMakePair(oldItem, item) ); } } else if ( !d->refreshItemWasFiltered ) { if ( !d->lstRemoveItems ) { - Q_ASSERT( !d->lstRemoveItemsV2 ); d->lstRemoveItems = new KFileItemList; - d->lstRemoveItemsV2 = new QList; } // notify the user that the mimetype of a file changed that doesn't match // a filter or does match an exclude filter - Q_ASSERT( item ); + Q_ASSERT( !item.isNull() ); d->lstRemoveItems->append( item ); - d->lstRefreshItemsV2->append( qMakePair(oldItem, *item) ); } } void KDirLister::emitItems() { KFileItemList *tmpNew = d->lstNewItems; d->lstNewItems = 0; - QList *tmpNewV2 = d->lstNewItemsV2; - d->lstNewItemsV2 = 0; - KFileItemList *tmpMime = d->lstMimeFilteredItems; d->lstMimeFilteredItems = 0; - QList *tmpMimeV2 = d->lstMimeFilteredItemsV2; - d->lstMimeFilteredItemsV2 = 0; - - KFileItemList *tmpRefresh = d->lstRefreshItems; + QList > *tmpRefresh = d->lstRefreshItems; d->lstRefreshItems = 0; - QList > *tmpRefreshV2 = d->lstRefreshItemsV2; - d->lstRefreshItemsV2 = 0; - KFileItemList *tmpRemove = d->lstRemoveItems; d->lstRemoveItems = 0; - QList *tmpRemoveV2 = d->lstRemoveItemsV2; - d->lstRemoveItemsV2 = 0; - if ( tmpNew ) { - Q_ASSERT( tmpNewV2 ); emit newItems( *tmpNew ); - emit newItems( *tmpNewV2 ); delete tmpNew; - delete tmpNewV2; } if ( tmpMime ) { - Q_ASSERT( tmpMimeV2 ); emit itemsFilteredByMime( *tmpMime ); - emit itemsFilteredByMime( *tmpMimeV2 ); delete tmpMime; - delete tmpMimeV2; } if ( tmpRefresh ) { - Q_ASSERT( tmpRefreshV2 ); emit refreshItems( *tmpRefresh ); - emit refreshItems( *tmpRefreshV2 ); delete tmpRefresh; - delete tmpRefreshV2; } if ( tmpRemove ) { - Q_ASSERT( tmpRemoveV2 ); KFileItemList::const_iterator kit = tmpRemove->begin(); const KFileItemList::const_iterator kend = tmpRemove->end(); for ( ; kit != kend; ++kit ) { emit deleteItem( *kit ); - emit deleteItem( **kit ); } delete tmpRemove; - delete tmpRemoveV2; } } -void KDirLister::emitDeleteItem( KFileItem *item ) +void KDirLister::emitDeleteItem( const KFileItem &item ) { - if ( ( d->dirOnlyMode && !item->isDir() ) || !matchesFilter( item ) ) + if ( ( d->dirOnlyMode && !item.isDir() ) || !matchesFilter( item ) ) return; // No reason to continue... bailing out here prevents a mimetype scan. if ( matchesMimeFilter( item ) ) { emit deleteItem( item ); - emit deleteItem( *item ); } } // ================ private slots ================ // void KDirLister::slotInfoMessage( KJob *, const QString& message ) { emit infoMessage( message ); } void KDirLister::slotPercent( KJob *job, unsigned long pcnt ) { d->jobData[static_cast(job)].percent = pcnt; int result = 0; KIO::filesize_t size = 0; QMap< KIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin(); while ( dataIt != d->jobData.end() ) { result += (*dataIt).percent * (*dataIt).totalSize; size += (*dataIt).totalSize; ++dataIt; } if ( size != 0 ) result /= size; else result = 100; emit percent( result ); } void KDirLister::slotTotalSize( KJob *job, qulonglong size ) { d->jobData[static_cast(job)].totalSize = size; KIO::filesize_t result = 0; QMap< KIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin(); while ( dataIt != d->jobData.end() ) { result += (*dataIt).totalSize; ++dataIt; } emit totalSize( result ); } void KDirLister::slotProcessedSize( KJob *job, qulonglong size ) { d->jobData[static_cast(job)].processedSize = size; KIO::filesize_t result = 0; QMap< KIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin(); while ( dataIt != d->jobData.end() ) { result += (*dataIt).processedSize; ++dataIt; } emit processedSize( result ); } void KDirLister::slotSpeed( KJob *job, unsigned long spd ) { d->jobData[static_cast(job)].speed = spd; int result = 0; QMap< KIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin(); while ( dataIt != d->jobData.end() ) { result += (*dataIt).speed; ++dataIt; } emit speed( result ); } uint KDirLister::numJobs() { return d->jobData.count(); } void KDirLister::jobDone( KIO::ListJob *job ) { d->jobData.remove( job ); } void KDirLister::jobStarted( KIO::ListJob *job ) { KDirListerPrivate::JobData jobData; jobData.speed = 0; jobData.percent = 0; jobData.processedSize = 0; jobData.totalSize = 0; d->jobData.insert( job, jobData ); d->complete = false; } void KDirLister::connectJob( KIO::ListJob *job ) { connect( job, SIGNAL(infoMessage( KJob *, const QString&, const QString& )), this, SLOT(slotInfoMessage( KJob *, const QString& )) ); connect( job, SIGNAL(percent( KJob *, unsigned long )), this, SLOT(slotPercent( KJob *, unsigned long )) ); connect( job, SIGNAL(totalSize( KJob *, qulonglong )), this, SLOT(slotTotalSize( KJob *, qulonglong )) ); connect( job, SIGNAL(processedSize( KJob *, qulonglong )), this, SLOT(slotProcessedSize( KJob *, qulonglong )) ); connect( job, SIGNAL(speed( KJob *, unsigned long )), this, SLOT(slotSpeed( KJob *, unsigned long )) ); } void KDirLister::setMainWindow( QWidget *window ) { d->window = window; } QWidget *KDirLister::mainWindow() { return d->window; } KFileItemList KDirLister::items( WhichItems which ) const { return itemsForDir( url(), which ); } KFileItemList KDirLister::itemsForDir( const KUrl& dir, WhichItems which ) const { KFileItemList *allItems = KDirListerCache::self()->itemsForDir( dir ); if ( !allItems ) return KFileItemList(); if ( which == AllItems ) return *allItems; else // only items passing the filters { KFileItemList result; KFileItemList::const_iterator kit = allItems->begin(); const KFileItemList::const_iterator kend = allItems->end(); for ( ; kit != kend; ++kit ) { - KFileItem *item = *kit; - bool isExcluded = (d->dirOnlyMode && !item->isDir()) || !matchesFilter( item ); + KFileItem item = *kit; + bool isExcluded = (d->dirOnlyMode && !item.isDir()) || !matchesFilter( item ); if ( !isExcluded && matchesMimeFilter( item ) ) result.append( item ); } return result; } } bool KDirLister::delayedMimeTypes() const { return d->delayedMimeTypes; } void KDirLister::setDelayedMimeTypes( bool delayedMimeTypes ) { d->delayedMimeTypes = delayedMimeTypes; } // called by KDirListerCache::slotRedirection void KDirLister::redirect( const KUrl& oldUrl, const KUrl& newUrl ) { if ( d->url.equals( oldUrl, KUrl::CompareWithoutTrailingSlash ) ) { - d->rootFileItem = 0; + d->rootFileItem = KFileItem(); d->url = newUrl; } KUrl::List& lstDirs = d->lstDirs; lstDirs[ lstDirs.indexOf( oldUrl ) ] = newUrl; if ( lstDirs.count() == 1 ) { emit clear(); emit redirection( newUrl ); emit redirection( oldUrl, newUrl ); } else { emit clear( oldUrl ); emit redirection( oldUrl, newUrl ); } } #include "kdirlister.moc" #include "kdirlister_p.moc" diff --git a/kio/kio/kdirlister.h b/kio/kio/kdirlister.h index 2891f4d775..6c8471d63e 100644 --- a/kio/kio/kdirlister.h +++ b/kio/kio/kdirlister.h @@ -1,683 +1,631 @@ /* This file is part of the KDE project Copyright (C) 1999 David Faure 2001, 2002, 2004-2006 Michael Brade 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 kdirlister_h #define kdirlister_h #include "kfileitem.h" #include "kdirnotify.h" #include #include #include class KJob; namespace KIO { class Job; class ListJob; } /** * @short Helper class for the kiojob used to list and update a directory. * * The dir lister deals with the kiojob used to list and update a directory * and has signals for the user of this class (e.g. konqueror view or * kdesktop) to create/destroy its items when asked. * * This class is independent from the graphical representation of the dir * (icon container, tree view, ...) and it stores the items (as KFileItems). * * Typical usage : * @li Create an instance. * @li Connect to at least update, clear, newItem, and deleteItem. * @li Call openUrl - the signals will be called. * @li Reuse the instance when opening a new url (openUrl). * @li Destroy the instance when not needed anymore (usually destructor). * * Advanced usage : call openUrl with _keep = true to list directories * without forgetting the ones previously read (e.g. for a tree view) * * @author Michael Brade */ class KIO_EXPORT KDirLister : public QObject { class KDirListerPrivate; friend class KDirListerPrivate; friend class KDirListerCache; Q_OBJECT Q_PROPERTY( bool autoUpdate READ autoUpdate WRITE setAutoUpdate ) Q_PROPERTY( bool showingDotFiles READ showingDotFiles WRITE setShowingDotFiles ) Q_PROPERTY( bool dirOnlyMode READ dirOnlyMode WRITE setDirOnlyMode ) Q_PROPERTY( bool autoErrorHandlingEnabled READ autoErrorHandlingEnabled ) Q_PROPERTY( bool delayedMimeTypes READ delayedMimeTypes WRITE setDelayedMimeTypes ) Q_PROPERTY( QString nameFilter READ nameFilter WRITE setNameFilter ) Q_PROPERTY( QStringList mimeFilter READ mimeFilters WRITE setMimeFilter RESET clearMimeFilter ) public: /** * Create a directory lister. */ KDirLister( QObject* parent = 0 ); /** * Destroy the directory lister. */ virtual ~KDirLister(); /** * Run the directory lister on the given url. * * This method causes KDirLister to emit _all_ the items of @p _url, in any case. * Depending on @p _keep either clear() or clear(const KUrl &) will be * emitted first. * * The newItems() signal may be emitted more than once to supply you * with KFileItems, up until the signal completed() is emitted * (and isFinished() returns true). * * @param _url the directory URL. * @param _keep if true the previous directories aren't forgotten * (they are still watched by kdirwatch and their items * are kept for this KDirLister). This is useful for e.g. * a treeview. * @param _reload indicates wether to use the cache (false) or to reread the * directory from the disk. * Use only when opening a dir not yet listed by this lister * without using the cache. Otherwise use updateDirectory. * @return true if successful, * false otherwise (e.g. invalid @p _url) */ virtual bool openUrl( const KUrl& _url, bool _keep = false, bool _reload = false ); /** * Stop listing all directories currently being listed. * * Emits canceled() if there was at least one job running. * Emits canceled( const KUrl& ) for each stopped job if * there are at least two dirctories being watched by KDirLister. */ virtual void stop(); /** * Stop listing the given directory. * * Emits canceled() if the killed job was the last running one. * Emits canceled( const KUrl& ) for the killed job if * there are at least two directories being watched by KDirLister. * No signal is emitted if there was no job running for @p _url. * @param _url the directory URL */ virtual void stop( const KUrl& _url ); /** * @return true if the "delayed mimetypes" feature was enabled * @see setDelayedMimeTypes */ bool delayedMimeTypes() const; /** * Delayed mimetypes feature: * If enabled, mime types will be fetched on demand, which leads to a * faster initial directory listing, where icons get progressively replaced * with the correct one while KMimeTypeResolver is going through the items * with unknown or imprecise mimetype (e.g. files with no extension or an * unknown extension). */ void setDelayedMimeTypes( bool delayedMimeTypes ); /** * Checks whether KDirWatch will automatically update directories. This is * enabled by default. * @return true if KDirWatch is used to automatically update directories. */ bool autoUpdate() const; /** * Enable/disable automatic directory updating, when a directory changes * (using KDirWatch). * @param enable true to enable, false to disable */ virtual void setAutoUpdate( bool enable ); /** * Check whether auto error handling is enabled. * If enabled, it will show an error dialog to the user when an * error occurs. It is turned on by default. * @return true if auto error handling is enabled, false otherwise * @see setAutoErrorHandlingEnabled() */ bool autoErrorHandlingEnabled() const; /** * Enable or disable auto error handling is enabled. * If enabled, it will show an error dialog to the user when an * error occurs. It is turned on by default. * @param enable true to enable auto error handling, false to disable * @param parent the parent widget for the error dialogs, can be 0 for * top-level * @see autoErrorHandlingEnabled() */ void setAutoErrorHandlingEnabled( bool enable, QWidget *parent ); /** * Checks whether hidden files (files beginning with a dot) will be * shown. * By default this option is disabled (hidden files will be not shown). * @return true if dot files are shown, false otherwise * @see setShowingDotFiles() */ bool showingDotFiles() const; /** * Changes the "is viewing dot files" setting. * Calls updateDirectory() if setting changed. * By default this option is disabled (hidden files will not be shown). * @param _showDotFiles true to enable showing hidden files, false to * disable * @see showingDotFiles() */ virtual void setShowingDotFiles( bool _showDotFiles ); /** * Checks whether the KDirLister only lists directories or all * files. * By default this option is disabled (all files will be shown). * @return true if setDirOnlyMode(true) was called */ bool dirOnlyMode() const; /** * Call this to list only directories. * By default this option is disabled (all files will be shown). * @param dirsOnly true to list only directories */ virtual void setDirOnlyMode( bool dirsOnly ); /** * Returns the top level URL that is listed by this KDirLister. * It might be different from the one given with openUrl() if there was a * redirection. If you called openUrl() with @p _keep == true this is the * first url opened (e.g. in a treeview this is the root). * * @return the url used by this instance to list the files. */ KUrl url() const; /** * Returns all URLs that are listed by this KDirLister. This is only * useful if you called openUrl() with @p _keep == true, as it happens in a * treeview, for example. (Note that the base url is included in the list * as well, of course.) * * @return the list of all listed URLs */ KUrl::List directories() const; /** * Actually emit the changes made with setShowingDotFiles, setDirOnlyMode, * setNameFilter and setMimeFilter. */ virtual void emitChanges(); /** * Update the directory @p _dir. This method causes KDirLister to _only_ emit * the items of @p _dir that actually changed compared to the current state in the * cache and updates the cache. * * The current implementation calls updateDirectory automatically for * local files, using KDirWatch (if autoUpdate() is true), but it might be * useful to force an update manually. * * @param _dir the directory URL */ virtual void updateDirectory( const KUrl& _dir ); /** * Returns true if no io operation is currently in progress. * @return true if finished, false otherwise */ bool isFinished() const; /** * Returns the file item of the URL. * - * Can return 0. TODO is it OK? maybe use KFileItem(void)? + * Can return an empty KFileItem. * @return the file item for url() itself (".") */ - KFileItem *rootItem() const; + KFileItem rootItem() const; /** * Find an item by its URL. * @param _url the item URL - * @return the pointer to the KFileItem + * @return the KFileItem */ - virtual KFileItem *findByUrl( const KUrl& _url ) const; + virtual KFileItem findByUrl( const KUrl& _url ) const; /** * Find an item by its name. * @param name the item name - * @return the pointer to the KFileItem + * @return the KFileItem */ - virtual KFileItem *findByName( const QString& name ) const; + virtual KFileItem findByName( const QString& name ) const; /** * Set a name filter to only list items matching this name, e.g. "*.cpp". * * You can set more than one filter by separating them with whitespace, e.g * "*.cpp *.h". * Note: the directory is not automatically reloaded. * * @param filter the new filter, QString() to disable filtering * @see matchesFilter */ virtual void setNameFilter( const QString &filter ); /** * Returns the current name filter, as set via setNameFilter() * @return the current name filter, can be QString() if filtering * is turned off */ QString nameFilter() const; /** * Set mime-based filter to only list items matching the given mimetypes. * * NOTE: setting the filter does not automatically reload directory. * Also calling this function will not affect any named filter already set. * * @param mimeList a list of mime-types. * * @see clearMimeFilter * @see matchesMimeFilter */ virtual void setMimeFilter( const QStringList &mimeList ); /** * Filtering should be done with KFileFilter. This will be implemented in a later * revision of KDirLister. This method may be removed then. * * Set mime-based exclude filter to only list items not matching the given mimetypes * * NOTE: setting the filter does not automatically reload directory. * Also calling this function will not affect any named filter already set. * * @param mimeList a list of mime-types. * @see clearMimeFilter * @see matchesMimeFilter * @internal */ void setMimeExcludeFilter(const QStringList &mimeList ); /** * Clears the mime based filter. * * @see setMimeFilter */ virtual void clearMimeFilter(); /** * Returns the list of mime based filters, as set via setMimeFilter(). * @return the list of mime based filters. Empty, when no mime filter is set. */ QStringList mimeFilters() const; /** * Checks whether @p name matches a filter in the list of name filters. * @return true if @p name matches a filter in the list, * otherwise false. * @see setNameFilter */ bool matchesFilter( const QString& name ) const; /** * Checks whether @p mime matches a filter in the list of mime types * @param mime the mimetype to find in the filter list. * @return true if @p name matches a filter in the list, * otherwise false. * @see setMimeFilter. */ bool matchesMimeFilter( const QString& mime ) const; /** * Pass the main window this object is associated with * this is used for caching authentication data * @param window the window to associate with, 0 to disassociate */ void setMainWindow( QWidget *window ); /** * Returns the main window associated with this object. * @return the associated main window, or 0 if there is none */ QWidget *mainWindow(); /** * Used by items() and itemsForDir() to specify whether you want * all items for a directory or just the filtered ones. */ enum WhichItems { AllItems = 0, FilteredItems = 1 }; /** * Returns the items listed for the current url(). * This method will NOT start listing a directory, you should only call * this when receiving the finished() signal. * * The items in the KFileItemList are references to the items used * by KDirLister, so e.g. an item gets destroyed when the deleteItem() * signal is emitted. * * @param which specifies whether the returned list will contain all entries * or only the ones that passed the nameFilter(), mimeFilter(), * etc. Note that the latter causes iteration over all the * items, filtering them. If this is too slow for you, use the * newItems() signal, sending out filtered items in chunks. * @return the items listed for the current url(). */ KFileItemList items( WhichItems which = FilteredItems ) const; /** * Returns the items listed for the given @p dir. * This method will NOT start listing @p dir, you should only call * this when receiving the finished() signal. * * The items in the KFileItemList are references to the items used * by KDirLister, so e.g. an item gets destroyed when the deleteItem() * signal is emitted. * * @param dir specifies the url for which the items should be returned. This * is only useful if you use KDirLister with multiple URLs * i.e. using bool keep = true in openUrl(). * @param which specifies whether the returned list will contain all entries * or only the ones that passed the nameFilter, mimeFilter, etc. * Note that the latter causes iteration over all the items, * filtering them. If this is too slow for you, use the * newItems() signal, sending out filtered items in chunks. * @return the items listed for @p dir. */ KFileItemList itemsForDir( const KUrl& dir, WhichItems which = FilteredItems ) const; Q_SIGNALS: /** * Tell the view that we started to list @p _url. NOTE: this does _not_ imply that there * is really a job running! I.e. KDirLister::jobs() may return an empty list. In this case * the items are taken from the cache. * * The view knows that openUrl should start it, so it might seem useless, * but the view also needs to know when an automatic update happens. * @param _url the URL to list */ void started( const KUrl& _url ); /** * Tell the view that listing is finished. There are no jobs running anymore. */ void completed(); /** * Tell the view that the listing of the directory @p _url is finished. * There might be other running jobs left. * @param _url the directory URL */ void completed( const KUrl& _url ); /** * Tell the view that the user canceled the listing. No running jobs are left. */ void canceled(); /** * Tell the view that the listing of the directory @p _url was canceled. * There might be other running jobs left. * @param _url the directory URL */ void canceled( const KUrl& _url ); /** * Signal a redirection. * Only emitted if there's just one directory to list, i.e. most * probably openUrl() has been called with @p _keep == @p false. * @param _url the new URL */ void redirection( const KUrl& _url ); /** * Signal a redirection. * @param oldUrl the original URL * @param newUrl the new URL */ void redirection( const KUrl& oldUrl, const KUrl& newUrl ); /** * Signal to clear all items. * It must always be connected to this signal to avoid doubled items! */ void clear(); /** * Signal to empty the directory @p _url. * It is only emitted if the lister is holding more than one directory. * @param _url the directory that will be emptied */ void clear( const KUrl& _url ); /** * Signal new items. - * For KDE 4 applications it is recommended using - * KDirLister::newItems(const QList& items) instead, - * as the interfaces have been adjusted using KFileItem per value - * instead of per pointer. * * So use this signal only if you want to modify original KFileItems * @param items a list of new items */ void newItems( const KFileItemList& items ); - /** - * Signal new items. - * @param items a list of new items - */ - void newItems( const QList& items ); - /** * Send a list of items filtered-out by mime-type. - * For KDE 4 applications it is recommended using - * KDirLister::itemsFilteredByMime(const QList& items) instead, - * as the interfaces have been adjusted using KFileItem per value - * instead of per pointer. * @param items the list of filtered items */ void itemsFilteredByMime( const KFileItemList& items ); - /** - * Send a list of items filtered-out by mime-type. - * @param items the list of filtered items - */ - void itemsFilteredByMime( const QList& items ); - /** * Signal an item to remove. - * For KDE 4 applications it is recommended using - * KDirLister::deleteItem(KFileItem _fileItem) instead, - * as the interfaces have been adjusted using KFileItem per value - * instead of per pointer. * - * ATTENTION: if @p _fileItem == rootItem() the directory this lister - * is holding was deleted and you HAVE to release especially the - * rootItem() of this lister, otherwise your app will CRASH!! - * The clear() signals have been emitted already. * @param _fileItem the fileItem to delete */ - void deleteItem( KFileItem *_fileItem ); - - /** - * Signal an item to remove. - * - * ATTENTION: if @p _fileItem == rootItem() the directory this lister - * is holding was deleted and you HAVE to release especially the - * rootItem() of this lister, otherwise your app will CRASH!! - * The clear() signals have been emitted already. - * @param _fileItem the fileItem to delete - */ - void deleteItem( KFileItem _fileItem ); - - /** - * Signal an item to refresh (its mimetype/icon/name has changed). - * Note: KFileItem::refresh has already been called on those items. - * For KDE 4 applications it is recommended using - * KDirLister::refreshItems(const QList& items) instead, - * as the interfaces have been adjusted using KFileItem per value - * instead of per pointer. - * - * So use this signal only if you want to modify original KFileItems - * @param items the items to refresh - */ - void refreshItems( const KFileItemList& items ); + void deleteItem( const KFileItem &_fileItem ); /** * Signal an item to refresh (its mimetype/icon/name has changed). * Note: KFileItem::refresh has already been called on those items. * @param items the items to refresh. This is a list of pairs, where * the first item in the pair is the OLD item, and the second item is the * NEW item. This allows to track which item has changed, especially after * a renaming. */ void refreshItems( const QList >& items ); /** * Emitted to display information about running jobs. * Examples of message are "Resolving host", "Connecting to host...", etc. * @param msg the info message */ void infoMessage( const QString& msg ); /** * Progress signal showing the overall progress of the KDirLister. * This allows using a progress bar very easily. (see QProgressBar) * @param percent the progress in percent */ void percent( int percent ); /** * Emitted when we know the size of the jobs. * @param size the total size in bytes */ void totalSize( KIO::filesize_t size ); /** * Regularly emitted to show the progress of this KDirLister. * @param size the processed size in bytes */ void processedSize( KIO::filesize_t size ); /** * Emitted to display information about the speed of the jobs. * @param bytes_per_second the speed in bytes/s */ void speed( int bytes_per_second ); protected: enum Changes { NONE=0, NAME_FILTER=1, MIME_FILTER=2, DOT_FILES=4, DIR_ONLY_MODE=8 }; /** * Called for every new item before emitting newItems(). * You may reimplement this method in a subclass to implement your own * filtering. * The default implementation filters out ".." and everything not matching * the name filter(s) * @return true if the item is "ok". * false if the item shall not be shown in a view, e.g. * files not matching a pattern *.cpp ( KFileItem::isHidden()) * @see matchesFilter * @see setNameFilter */ - virtual bool matchesFilter( const KFileItem * ) const; + virtual bool matchesFilter( const KFileItem& ) const; /** * Called for every new item before emitting newItems(). * You may reimplement this method in a subclass to implement your own * filtering. * The default implementation filters out ".." and everything not matching * the name filter(s) * @return true if the item is "ok". * false if the item shall not be shown in a view, e.g. * files not matching a pattern *.cpp ( KFileItem::isHidden()) * @see matchesMimeFilter * @see setMimeFilter */ - virtual bool matchesMimeFilter( const KFileItem * ) const; + virtual bool matchesMimeFilter( const KFileItem& ) const; /** * Called by the public matchesFilter() to do the * actual filtering. Those methods may be reimplemented to customize * filtering. * @param name the name to filter * @param filters a list of regular expressions for filtering */ virtual bool doNameFilter( const QString& name, const QList& filters ) const; /** * Called by the public matchesMimeFilter() to do the * actual filtering. Those methods may be reimplemented to customize * filtering. * @param mime the mime type to filter * @param filters the list of mime types to filter */ virtual bool doMimeFilter( const QString& mime, const QStringList& filters ) const; /** Reimplement to customize error handling */ virtual void handleError( KIO::Job * ); private Q_SLOTS: void slotInfoMessage( KJob *, const QString& ); void slotPercent( KJob *, unsigned long ); void slotTotalSize( KJob *, qulonglong ); void slotProcessedSize( KJob *, qulonglong ); void slotSpeed( KJob *, unsigned long ); private: bool doMimeExcludeFilter( const QString& mimeExclude, const QStringList& filters ) const; void jobStarted( KIO::ListJob * ); void connectJob( KIO::ListJob * ); void jobDone( KIO::ListJob * ); uint numJobs(); - void addNewItem( KFileItem *item ); + void addNewItem( const KFileItem& item ); void addNewItems( const KFileItemList& items ); - void aboutToRefreshItem( const KFileItem *item ); - void addRefreshItem( const KFileItem& oldItem, KFileItem *item ); + void aboutToRefreshItem( const KFileItem& item ); + void addRefreshItem( const KFileItem& oldItem, const KFileItem& item ); void emitItems(); - void emitDeleteItem( KFileItem *item ); - void redirect( const KUrl& oldUrl, const KUrl& newUrl ); + void emitDeleteItem( const KFileItem &item ); + void redirect( const KUrl& oldUrl, const KUrl& newUrl ); KDirListerPrivate* const d; }; #endif diff --git a/kio/kio/kdirlister_p.h b/kio/kio/kdirlister_p.h index 81aa848c19..da1a0780e3 100644 --- a/kio/kio/kdirlister_p.h +++ b/kio/kio/kdirlister_p.h @@ -1,376 +1,364 @@ /* This file is part of the KDE project Copyright (C) 2002-2006 Michael Brade 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 kdirlister_p_h #define kdirlister_p_h #include "kfileitem.h" #include #include #include #include #include #include #include class QTimer; class KDirLister; namespace KIO { class Job; class ListJob; } class OrgKdeKDirNotifyInterface; class KDirLister::KDirListerPrivate { public: KDirListerPrivate() { complete = false; autoUpdate = false; isShowingDotFiles = false; dirOnlyMode = false; autoErrorHandling = false; errorParent = 0; delayedMimeTypes = false; - rootFileItem = 0; + rootFileItem = KFileItem(); lstNewItems = 0; lstRefreshItems = 0; lstMimeFilteredItems = 0; lstRemoveItems = 0; - lstNewItemsV2 = 0; - lstRefreshItemsV2 = 0; - lstMimeFilteredItemsV2 = 0; - lstRemoveItemsV2 = 0; - refreshItemWasFiltered = false; changes = NONE; window = 0; } /** * List of dirs handled by this dirlister. The first entry is the base URL. * For a tree view, it contains all the dirs shown. */ KUrl::List lstDirs; // toplevel URL KUrl url; bool complete:1; bool autoUpdate:1; bool isShowingDotFiles:1; bool dirOnlyMode:1; bool delayedMimeTypes:1; bool refreshItemWasFiltered:1; bool autoErrorHandling:2; QWidget *errorParent; struct JobData { long unsigned int percent, speed; KIO::filesize_t processedSize, totalSize; }; QMap jobData; // file item for the root itself (".") - KFileItem *rootFileItem; + KFileItem rootFileItem; - KFileItemList *lstNewItems, *lstRefreshItems; + KFileItemList *lstNewItems; + QList > *lstRefreshItems; KFileItemList *lstMimeFilteredItems, *lstRemoveItems; - // In KDE 4 the interfaces have been adjusted using KFileItem - // per value instead of per pointer. For backward compatibility - // the KFileItemList signals are still supported. - QList *lstNewItemsV2; - QList > *lstRefreshItemsV2; - QList *lstMimeFilteredItemsV2, *lstRemoveItemsV2; - int changes; QWidget *window; // Main window ths lister is associated with QString nameFilter; QList lstFilters, oldFilters; QStringList mimeFilter, oldMimeFilter; QStringList mimeExcludeFilter, oldMimeExcludeFilter; }; /** * Design of the cache: * There is a single KDirListerCache for the whole process. * It holds all the items used by the dir listers (itemsInUse) * as well as a cache of the recently used items (itemsCached). * Those items are grouped by directory (a DirItem represents a whole directory). * * KDirListerCache also runs all the jobs for listing directories, whether they are for * normal listing or for updates. * For faster lookups, it also stores a hash table, which gives for a directory URL: * - the dirlisters holding that URL (listersCurrentlyHolding) * - the dirlisters currently listing that URL (listersCurrentlyListing) */ class KDirListerCache : public QObject { Q_OBJECT public: static KDirListerCache *self(); KDirListerCache(); // only called by K_GLOBAL_STATIC ~KDirListerCache(); void updateDirectory( const KUrl& dir ); - KFileItem *itemForUrl( const KUrl& url ) const; + KFileItem itemForUrl( const KUrl& url ) const; KFileItemList *itemsForDir( const KUrl& dir ) const; bool listDir( KDirLister *lister, const KUrl& _url, bool _keep, bool _reload ); // stop all running jobs for lister void stop( KDirLister *lister ); // stop just the job listing url for lister void stop( KDirLister *lister, const KUrl &_url ); void setAutoUpdate( KDirLister *lister, bool enable ); void forgetDirs( KDirLister *lister ); void forgetDirs( KDirLister *lister, const KUrl &_url, bool notify ); - KFileItem *findByName( const KDirLister *lister, const QString &_name ) const; + KFileItem findByName( const KDirLister *lister, const QString &_name ) const; // if lister is set, it is checked that the url is held by the lister - KFileItem *findByUrl( const KDirLister *lister, const KUrl &_url ) const; + KFileItem findByUrl( const KDirLister *lister, const KUrl &_url ) const; public Q_SLOTS: /** * Notify that files have been added in @p directory * The receiver will list that directory again to find * the new items (since it needs more than just the names anyway). * Connected to the DBus signal from the KDirNotify interface. */ void slotFilesAdded( const QString& urlDirectory ); /** * Notify that files have been deleted. * This call passes the exact urls of the deleted files * so that any view showing them can simply remove them * or be closed (if its current dir was deleted) * Connected to the DBus signal from the KDirNotify interface. */ void slotFilesRemoved( const QStringList& fileList ); /** * Notify that files have been changed. * At the moment, this is only used for new icon, but it could be * used for size etc. as well. * Connected to the DBus signal from the KDirNotify interface. */ void slotFilesChanged( const QStringList& fileList ); void slotFileRenamed( const QString& srcUrl, const QString& dstUrl ); private: bool validUrl( const KDirLister *lister, const KUrl& _url ) const; // helper for both stop methods struct DirectoryData; void stopLister(KDirLister* lister, const QString& url, DirectoryData& dirData); private Q_SLOTS: void slotFileDirty( const QString &_file ); void slotFileCreated( const QString &_file ); void slotFileDeleted( const QString &_file ); void slotEntries( KIO::Job *job, const KIO::UDSEntryList &entries ); void slotResult( KJob *j ); void slotRedirection( KIO::Job *job, const KUrl &url ); void slotUpdateEntries( KIO::Job *job, const KIO::UDSEntryList &entries ); void slotUpdateResult( KJob *job ); void processPendingUpdates(); private: KIO::ListJob *jobForUrl( const QString& url, KIO::ListJob *not_job = 0 ); const KUrl& joburl( KIO::ListJob *job ); void killJob( KIO::ListJob *job ); // Called when something tells us that the directory @p url has changed. // Returns true if @p url is held by some lister (meaning: do the update now) // otherwise mark the cached item as not-up-to-date for later and return false bool checkUpdate( const QString& url ); // when there were items deleted from the filesystem all the listers holding // the parent directory need to be notified, the unmarked items have to be deleted // and removed from the cache including all the children. void deleteUnmarkedItems( const QList&, KFileItemList & ); // common for slotRedirection and slotFileRenamed void renameDir( const KUrl &oldUrl, const KUrl &url ); // common for deleteUnmarkedItems and slotFilesRemoved void deleteDir( const KUrl& dirUrl ); // remove directory from cache (itemsCached), including all child dirs void removeDirFromCache( const KUrl& dir ); // helper for renameDir void emitRedirections( const KUrl &oldUrl, const KUrl &url ); - void aboutToRefreshItem( KFileItem *fileitem ); - void emitRefreshItem( const KFileItem& oldItem, KFileItem *fileitem ); + void aboutToRefreshItem( const KFileItem& fileitem ); + void emitRefreshItem( const KFileItem& oldItem, const KFileItem& fileitem ); #ifndef NDEBUG void printDebug(); #endif struct DirItem { DirItem( const KUrl &dir ) - : url(dir), rootItem(0), lstItems() + : url(dir) { autoUpdates = 0; complete = false; } ~DirItem() { if ( autoUpdates ) { if ( KDirWatch::exists() && url.isLocalFile() ) KDirWatch::self()->removeDir( url.path() ); sendSignal( false, url ); } - delete rootItem; - qDeleteAll( lstItems ); + lstItems.clear(); } void sendSignal( bool entering, const KUrl& url ) { // Note that "entering" means "start watching", and "leaving" means "stop watching" // (i.e. it's not when the user leaves the directory, it's when the directory is removed from the cache) if (entering) org::kde::KDirNotify::emitEnteredDirectory( url.url() ); else org::kde::KDirNotify::emitLeftDirectory( url.url() ); } void redirect( const KUrl& newUrl ) { if ( autoUpdates ) { if ( url.isLocalFile() ) KDirWatch::self()->removeDir( url.path() ); sendSignal( false, url ); if ( newUrl.isLocalFile() ) KDirWatch::self()->addDir( newUrl.path() ); sendSignal( true, newUrl ); } url = newUrl; - if ( rootItem ) - rootItem->setUrl( newUrl ); + if ( !rootItem.isNull() ) + rootItem.setUrl( newUrl ); } void incAutoUpdate() { if ( autoUpdates++ == 0 ) { if ( url.isLocalFile() ) KDirWatch::self()->addDir( url.path() ); sendSignal( true, url ); } } void decAutoUpdate() { if ( --autoUpdates == 0 ) { if ( url.isLocalFile() ) KDirWatch::self()->removeDir( url.path() ); sendSignal( false, url ); } else if ( autoUpdates < 0 ) autoUpdates = 0; } // number of KDirListers using autoUpdate for this dir short autoUpdates; // this directory is up-to-date bool complete; // the complete url of this directory KUrl url; // KFileItem representing the root of this directory. // Remember that this is optional. FTP sites don't return '.' in // the list, so they give no root item - KFileItem *rootItem; + KFileItem rootItem; KFileItemList lstItems; }; //static const unsigned short MAX_JOBS_PER_LISTER; QMap jobs; // an item is a complete directory QHash itemsInUse; QCache itemsCached; // Data associated with a directory url // This could be in DirItem but only in the itemsInUse dict... struct DirectoryData { // A lister can be EITHER in listersCurrentlyListing OR listersCurrentlyHolding // but NOT in both at the same time. // But both lists can have different listers at the same time; this // happens if more listers are requesting url at the same time and // one lister was stopped during the listing of files. // Listers that are currently listing this url QList listersCurrentlyListing; // Listers that are currently holding this url QList listersCurrentlyHolding; }; typedef QHash DirectoryDataHash; DirectoryDataHash directoryData; // List of files that we have changed recently QSet pendingUpdates; // The timer for doing the delayed updates QTimer pendingUpdateTimer; // the KDirNotify signals OrgKdeKDirNotifyInterface *kdirnotify; }; //const unsigned short KDirListerCache::MAX_JOBS_PER_LISTER = 5; #endif diff --git a/kio/kio/kdirmodel.cpp b/kio/kio/kdirmodel.cpp index 07dfa4b1e2..1f2c264752 100644 --- a/kio/kio/kdirmodel.cpp +++ b/kio/kio/kdirmodel.cpp @@ -1,736 +1,735 @@ /* This file is part of the KDE project Copyright (C) 2006 David Faure 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 "kdirmodel.h" #include "kdirlister.h" #include "kfileitem.h" #include #include #include #include #include #include #include #include #include #include class KDirModelNode; class KDirModelDirNode; // We create our own tree behind the scenes to have fast lookup from an item to its parent, // and also to get the children of an item fast. class KDirModelNode { public: KDirModelNode( KDirModelDirNode* parent, const KFileItem& item ) : m_item(item), m_parent(parent), m_preview() { } //KUrl url() const { return m_item->url(); } // m_item is KFileItem() for the root item const KFileItem& item() const { return m_item; } void setItem(const KFileItem& item) { m_item = item; } KDirModelDirNode* parent() const { return m_parent; } // linear search int rowNumber() const; QIcon preview() const { return m_preview; } void addPreview( const QPixmap& pix ) { m_preview.addPixmap(pix); } void setPreview( const QIcon& icn ) { m_preview = icn; } private: KFileItem m_item; KDirModelDirNode* const m_parent; QIcon m_preview; }; // Specialization for directory nodes class KDirModelDirNode : public KDirModelNode { public: KDirModelDirNode( KDirModelDirNode* parent, const KFileItem& item) : KDirModelNode( parent, item), m_childNodes(), m_childCount(KDirModel::ChildCountUnknown), m_populated(false) {} ~KDirModelDirNode() { qDeleteAll(m_childNodes); } QList m_childNodes; // If we listed the directory, the child count is known. Otherwise it can be set via setChildCount. int childCount() const { return m_childNodes.isEmpty() ? m_childCount : m_childNodes.count(); } void setChildCount(int count) { m_childCount = count; } bool isPopulated() const { return m_populated; } void setPopulated( bool populated ) { m_populated = populated; } private: int m_childCount:31; bool m_populated:1; }; int KDirModelNode::rowNumber() const { if (!m_parent) return 0; return m_parent->m_childNodes.indexOf(const_cast(this)); } //// class KDirModelPrivate { public: KDirModelPrivate( KDirModel* model ) : q(model), m_dirLister(0), m_rootNode(new KDirModelDirNode(0, KFileItem())), m_dropsAllowed(KDirModel::NoDrops) { } ~KDirModelPrivate() { delete m_rootNode; } void clear() { delete m_rootNode; m_rootNode = new KDirModelDirNode(0, KFileItem()); } // Find the row number and node for a given url. // This has to drill down from the root node. // Returns (0,0) if there is no node for this url. // If returnLastParent is set, then return the last known parent if there is no node for this url // (special case for expandToUrl) QPair nodeForUrl(const KUrl& url, bool returnLastParent = false) const; KDirModelNode* nodeForIndex(const QModelIndex& index) const; QModelIndex indexForNode(KDirModelNode* node, int rowNumber = -1 /*unknown*/) const; bool isDir(KDirModelNode* node) const { return (node == m_rootNode) || node->item().isDir(); } KDirModel* q; KDirLister* m_dirLister; KDirModelDirNode* m_rootNode; KDirModel::DropsAllowed m_dropsAllowed; // key = current known parent node (always a KDirModelDirNode but KDirModelNode is more convenient), // value = final url[s] being fetched QMap m_urlsBeingFetched; }; // If we want to support arbitrary trees like "home:/ as a child of system:/" then, -// we need to get the parent KFileItem in slotNewItems, and then we can use a QHash cache. +// we need to get the parent KFileItem in slotNewItems, and then we can use a QHash cache. // (well there isn't a parent kfileitem, rather a parent url... hmm, back to square one with hashes-of-urls..) // For now we'll assume "child url = parent url + filename" QPair KDirModelPrivate::nodeForUrl(const KUrl& _url, bool returnLastParent) const // O(n*m) { KUrl url(_url); url.adjustPath(KUrl::RemoveTrailingSlash); //kDebug(7008) << url; if (url == m_dirLister->url()) return qMakePair(0, static_cast(m_rootNode)); const QString urlStr = url.url(); KDirModelDirNode* dirNode = m_rootNode; KUrl nodeUrl = m_dirLister->url(); if ( !urlStr.startsWith(nodeUrl.url()) ) { return qMakePair(0, static_cast(0)); } for (;;) { Q_ASSERT( urlStr.startsWith(nodeUrl.url()) ); bool foundChild = false; QList::const_iterator it = dirNode->m_childNodes.begin(); const QList::const_iterator end = dirNode->m_childNodes.end(); int row = 0; for ( ; it != end ; ++it, ++row ) { const KUrl u = (*it)->item().url(); if ( u == url ) { //kDebug(7008) << "Found! " << u; return qMakePair(row, *it); } if ( urlStr.startsWith(u.url()+'/') ) { //kDebug(7008) << "going into " << node->item().url(); Q_ASSERT( isDir(*it) ); dirNode = static_cast( *it ); foundChild = true; break; } } if (!foundChild) { //kDebug(7008) << "child equal or starting with " << url << " not found"; if (returnLastParent) return qMakePair(-1 /*not implemented*/, static_cast(dirNode)); else return qMakePair(0, static_cast(0)); } nodeUrl = dirNode->item().url(); //kDebug(7008) << " " << nodeUrl; } // NOTREACHED //return qMakePair(0, static_cast(0)); } // node -> index. If rowNumber is set (or node is root): O(1). Otherwise: O(n). QModelIndex KDirModelPrivate::indexForNode(KDirModelNode* node, int rowNumber) const { if (node == m_rootNode) return QModelIndex(); Q_ASSERT(node->parent()); return q->createIndex(rowNumber == -1 ? node->rowNumber() : rowNumber, 0, node); } // index -> node. O(1) KDirModelNode* KDirModelPrivate::nodeForIndex(const QModelIndex& index) const { return index.isValid() ? static_cast(index.internalPointer()) : m_rootNode; } // We don't use QHash anymore, it's too slow. // Idea from George, to make QHash fast: - cache hash value into QUrl or KUrl // This also helps making operator== fast [which means operator== has to call qHash if cached value isn't there] // But it means invalidating the cached hash value when the url is modified, // so it can't be done in the current KUrl due to the public inheritance from QUrl. /* * This model wraps the data held by KDirLister. * * The internal pointer of the QModelIndex for a given file is the node for that file in our own tree. * E.g. index(2,0) returns a QModelIndex with row=2 internalPointer= * * Invalid parent index means root of the tree, m_rootNode */ #ifndef NDEBUG static QString debugIndex(const QModelIndex& index) { QString str; if (!index.isValid()) str = "[invalid index, i.e. root]"; else { KDirModelNode* node = static_cast(index.internalPointer()); str = "[index for " + node->item().url().pathOrUrl(); if (index.column() > 0) str += ", column " + QString::number(index.column()); str += ']'; } return str; } #endif KDirModel::KDirModel(QObject* parent) : QAbstractItemModel(parent), d(new KDirModelPrivate(this)) { setDirLister(new KDirLister(this)); } KDirModel::~KDirModel() { delete d; } void KDirModel::setDirLister(KDirLister* dirLister) { if (d->m_dirLister) { d->clear(); delete d->m_dirLister; } d->m_dirLister = dirLister; d->m_dirLister->setParent(this); - connect( d->m_dirLister, SIGNAL(newItems(QList)), - this, SLOT(slotNewItems(QList)) ); + connect( d->m_dirLister, SIGNAL(newItems(KFileItemList)), + this, SLOT(slotNewItems(KFileItemList)) ); connect( d->m_dirLister, SIGNAL(deleteItem(KFileItem)), this, SLOT(slotDeleteItem(KFileItem)) ); connect( d->m_dirLister, SIGNAL(refreshItems(QList >)), this, SLOT(slotRefreshItems(QList >)) ); connect( d->m_dirLister, SIGNAL(clear()), this, SLOT(slotClear()) ); } KDirLister* KDirModel::dirLister() const { return d->m_dirLister; } -void KDirModel::slotNewItems(const QList& items) +void KDirModel::slotNewItems(const KFileItemList& items) { // Find parent item - it's the same for all the items // TODO (if Michael Brade agrees): add parent url to the newItems signal // This way we can finally support properly trees where the urls are using different protocols. KUrl dir( items.first().url().upUrl() ); dir.adjustPath(KUrl::RemoveTrailingSlash); //kDebug(7008) << "dir=" << dir; const QPair result = d->nodeForUrl(dir); // O(n*m) Q_ASSERT(result.second); // Are you calling KDirLister::openUrl(url,true,false)? Please use expandToUrl() instead. Q_ASSERT(d->isDir(result.second)); KDirModelDirNode* dirNode = static_cast(result.second); const QModelIndex index = d->indexForNode(dirNode, result.first); // O(1) const int newItemsCount = items.count(); const int newRowCount = dirNode->m_childNodes.count() + newItemsCount; #ifndef NDEBUG // debugIndex only defined in debug mode kDebug(7008) << items.count() << " in " << dir << " index=" << debugIndex(index) << " newRowCount=" << newRowCount << endl; #endif beginInsertRows( index, newRowCount - newItemsCount, newRowCount - 1 ); // parent, first, last const KUrl::List urlsBeingFetched = d->m_urlsBeingFetched.value(dirNode); //kDebug(7008) << "urlsBeingFetched for dir" << dirNode << dir << ":" << urlsBeingFetched; QList::const_iterator it = items.begin(); const QList::const_iterator end = items.end(); for ( ; it != end ; ++it ) { const bool isDir = it->isDir(); KDirModelNode* node = isDir ? new KDirModelDirNode( dirNode, *it ) : new KDirModelNode( dirNode, *it ); dirNode->m_childNodes.append(node); if (isDir && !urlsBeingFetched.isEmpty()) { const KUrl dirUrl = it->url(); foreach(const KUrl& urlFetched, urlsBeingFetched) { if (dirUrl.isParentOf(urlFetched)) { //kDebug(7008) << "Listing found" << dirUrl << "which is a parent of fetched url" << urlFetched; const QModelIndex parentIndex = d->indexForNode(node, dirNode->m_childNodes.count()-1); Q_ASSERT(parentIndex.isValid()); emit expand(parentIndex); if (dirUrl != urlFetched) { fetchMore(parentIndex); d->m_urlsBeingFetched[node].append(urlFetched); } } } } } d->m_urlsBeingFetched.remove(dirNode); endInsertRows(); } void KDirModel::slotDeleteItem(const KFileItem& item) { //KUrl dir( item->url().upUrl() ); //dir.adjustPath(KUrl::RemoveTrailingSlash); Q_ASSERT(!item.isNull()); const QPair result = d->nodeForUrl(item.url()); // O(n*m) const int rowNumber = result.first; KDirModelNode* node = result.second; Q_ASSERT(node); if (!node) return; KDirModelDirNode* dirNode = node->parent(); Q_ASSERT(dirNode); QModelIndex parentIndex = d->indexForNode(dirNode); // O(n) beginRemoveRows( parentIndex, rowNumber, rowNumber ); dirNode->m_childNodes.removeAt(rowNumber); endRemoveRows(); } void KDirModel::slotRefreshItems(const QList >& items) { QModelIndex topLeft, bottomRight; // Solution 1: we could emit dataChanged for one row (if items.size()==1) or all rows // Solution 2: more fine-grained, actually figure out the beginning and end rows. for ( QList >::const_iterator fit = items.begin(), fend = items.end() ; fit != fend ; ++fit ) { const QModelIndex index = indexForUrl( fit->first.url() ); // O(n*m); maybe we could look up to the parent only once d->nodeForIndex(index)->setItem(fit->second); if (!topLeft.isValid() || index.row() < topLeft.row()) { topLeft = index; } if (!bottomRight.isValid() || index.row() > bottomRight.row()) { bottomRight = index; } } #ifndef NDEBUG // debugIndex only defined in debug mode kDebug(7008) << "slotRefreshItems: dataChanged(" << debugIndex(topLeft) << " - " << debugIndex(bottomRight); #endif bottomRight = bottomRight.sibling(bottomRight.row(), columnCount(QModelIndex())-1); emit dataChanged(topLeft, bottomRight); } void KDirModel::slotClear() { const int numRows = d->m_rootNode->m_childNodes.count(); beginRemoveRows( QModelIndex(), 0, numRows ); endRemoveRows(); //emit layoutAboutToBeChanged(); d->clear(); //emit layoutChanged(); } void KDirModel::itemChanged( const QModelIndex& index ) { emit dataChanged(index, index); } int KDirModel::columnCount( const QModelIndex & ) const { return ColumnCount; } QVariant KDirModel::data( const QModelIndex & index, int role ) const { if (index.isValid()) { KDirModelNode* node = static_cast(index.internalPointer()); const KFileItem& item( node->item() ); switch (role) { case Qt::DisplayRole: switch (index.column()) { case Name: return item.name(); case Size: // //return KIO::convertSize(item->size()); // Default to "file size in bytes" like in kde3's filedialog return KGlobal::locale()->formatNumber(item.size(), 0); case ModifiedTime: { KDateTime dt = item.time(KFileItem::ModificationTime); return KGlobal::locale()->formatDateTime(dt); } case Permissions: return item.permissionsString(); case Owner: return item.user(); case Group: return item.group(); case Type: return item.mimeComment(); } break; case Qt::DecorationRole: if (index.column() == Name) { if (!node->preview().isNull()) { //kDebug(7008) << item->url() << " preview found"; return node->preview(); } Q_ASSERT(!item.isNull()); //kDebug(7008) << item->url() << " overlays=" << item->overlays(); return KIcon(item.iconName(), 0, item.overlays()); } break; case Qt::TextAlignmentRole: if (index.column() == Size) { // use a right alignment for L2R and R2L languages return Qt::AlignRight; } break; case FileItemRole: return QVariant::fromValue(item); case ChildCountRole: if (!item.isDir()) return ChildCountUnknown; else { KDirModelDirNode* dirNode = static_cast(node); int count = dirNode->childCount(); if (count == ChildCountUnknown && item.isReadable()) { const QString path = item.localPath(); if (!path.isEmpty()) { QDir dir(path); count = dir.entryList(QDir::AllEntries|QDir::NoDotAndDotDot|QDir::System).count(); //kDebug(7008) << "child count for " << path << ":" << count; dirNode->setChildCount(count); } } return count; } } } return QVariant(); } void KDirModel::sort( int column, Qt::SortOrder order ) { // Not implemented - we should probably use QSortFilterProxyModel instead. return QAbstractItemModel::sort(column, order); } bool KDirModel::setData( const QModelIndex & index, const QVariant & value, int role ) { switch (role) { case Qt::DisplayRole: // TODO handle renaming here? break; case Qt::DecorationRole: if (index.column() == Name) { Q_ASSERT(index.isValid()); // Set new pixmap - e.g. preview KDirModelNode* node = static_cast(index.internalPointer()); //kDebug(7008) << "setting icon for " << node->item()->url(); Q_ASSERT(node); if (value.type() == QVariant::Icon) { const QIcon icon(qvariant_cast(value)); Q_ASSERT(!icon.isNull()); node->setPreview(icon); } else if (value.type() == QVariant::Pixmap) { node->addPreview(qvariant_cast(value)); } emit dataChanged(index, index); return true; } break; default: break; } return false; } int KDirModel::rowCount( const QModelIndex & parent ) const { KDirModelDirNode* parentNode = static_cast(d->nodeForIndex(parent)); Q_ASSERT(parentNode); const int count = parentNode->m_childNodes.count(); //kDebug(7008) << "rowCount for " << parentUrl << ": " << count;; return count; } // sibling() calls parent() and isn't virtual! So parent() should be fast... QModelIndex KDirModel::parent( const QModelIndex & index ) const { if (!index.isValid()) return QModelIndex(); KDirModelNode* childNode = static_cast(index.internalPointer()); Q_ASSERT(childNode); KDirModelNode* parentNode = childNode->parent(); Q_ASSERT(parentNode); return d->indexForNode(parentNode); // O(n) } QStringList KDirModel::mimeTypes( ) const { return QStringList() << QLatin1String("text/uri-list") << QLatin1String( "application/x-kde-cutselection" ) // TODO << QLatin1String( "text/plain" ) << QLatin1String( "application/x-kde-urilist" ); } QMimeData * KDirModel::mimeData( const QModelIndexList & indexes ) const { KUrl::List urls; foreach ( const QModelIndex &index, indexes ) { urls << d->nodeForIndex( index )->item().url(); } QMimeData *data = new QMimeData(); urls.populateMimeData( data ); return data; } // Public API; not much point in calling it internally KFileItem KDirModel::itemForIndex( const QModelIndex& index ) const { if (!index.isValid()) { - KFileItem* item = d->m_dirLister->rootItem(); - return item ? (*item) : KFileItem(); + return d->m_dirLister->rootItem(); } else { return static_cast(index.internalPointer())->item(); } } QModelIndex KDirModel::indexForItem( const KFileItem* item ) const { // Note that we can only use the URL here, not the pointer. // KFileItems can be copied. return indexForUrl(item->url()); // O(n*m) } QModelIndex KDirModel::indexForItem( const KFileItem& item ) const { // Note that we can only use the URL here, not the pointer. // KFileItems can be copied. return indexForUrl(item.url()); // O(n*m) } // url -> index. O(n*m) QModelIndex KDirModel::indexForUrl(const KUrl& url) const { const QPair result = d->nodeForUrl(url); // O(n*m) (m is the depth from the root) if (!result.second) { kWarning(7007) << url << "not found"; return QModelIndex(); } return d->indexForNode(result.second, result.first); // O(1) } QModelIndex KDirModel::index( int row, int column, const QModelIndex & parent ) const { KDirModelNode* parentNode = d->nodeForIndex(parent); // O(1) Q_ASSERT(parentNode); Q_ASSERT(d->isDir(parentNode)); KDirModelNode* childNode = static_cast(parentNode)->m_childNodes.value(row); // O(1) if (childNode) return createIndex(row, column, childNode); else return QModelIndex(); } QVariant KDirModel::headerData( int section, Qt::Orientation orientation, int role ) const { Q_UNUSED(orientation); switch (role) { case Qt::DisplayRole: switch (section) { case Name: return i18nc("@title:column","Name"); case Size: return i18nc("@title:column","Size"); case ModifiedTime: return i18nc("@title:column","Modified"); // was called "Date" in kde3's kfiledialog - which one is better? case Permissions: return i18nc("@title:column","Permissions"); case Owner: return i18nc("@title:column","Owner"); case Group: return i18nc("@title:column","Group"); case Type: return i18nc("@title:column","Type"); } } return QVariant(); } bool KDirModel::hasChildren( const QModelIndex & parent ) const { if (!parent.isValid()) return true; const KFileItem& parentItem = static_cast(parent.internalPointer())->item(); Q_ASSERT(!parentItem.isNull()); return parentItem.isDir(); } Qt::ItemFlags KDirModel::flags( const QModelIndex & index ) const { Qt::ItemFlags f = Qt::ItemIsEnabled; if (index.column() == Name) { f |= Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled; } // Allow dropping onto this item? if (d->m_dropsAllowed != NoDrops) { KFileItem item = itemForIndex(index); if (item.isDir()) { if (d->m_dropsAllowed & DropOnDirectory) { f |= Qt::ItemIsDropEnabled; } } else { // regular file item if (d->m_dropsAllowed & DropOnAnyFile) f |= Qt::ItemIsDropEnabled; else if (d->m_dropsAllowed & DropOnLocalExecutable) { if (item.isLocalFile()) { // Desktop file? if (item.mimeTypePtr()->is("application/x-desktop")) f |= Qt::ItemIsDropEnabled; // Executable, shell script ... ? else if ( QFileInfo( item.localPath() ).isExecutable() ) f |= Qt::ItemIsDropEnabled; } } } } return f; } bool KDirModel::canFetchMore( const QModelIndex & parent ) const { if (!parent.isValid()) return false; // We now have a bool KDirModelNode::m_populated, // to avoid calling fetchMore more than once on empty dirs. // But this wastes memory, and how often does someone open and re-open an empty dir in a treeview? // Maybe we can ask KDirLister "have you listed already"? (to discuss with M. Brade) KDirModelNode* node = static_cast(parent.internalPointer()); const KFileItem& item = node->item(); return item.isDir() && !static_cast(node)->isPopulated() && static_cast(node)->m_childNodes.isEmpty(); } void KDirModel::fetchMore( const QModelIndex & parent ) { if (!parent.isValid()) return; KDirModelNode* parentNode = static_cast(parent.internalPointer()); KFileItem parentItem = parentNode->item(); Q_ASSERT(!parentItem.isNull()); Q_ASSERT(parentItem.isDir()); KDirModelDirNode* dirNode = static_cast(parentNode); if( dirNode->isPopulated() ) return; dirNode->setPopulated( true ); //const KUrl url = parentItem.url(); //kDebug(7008) << "listing" << url; d->m_dirLister->openUrl(parentItem.url(), true, true); } bool KDirModel::dropMimeData( const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent ) { // Not sure we want to implement any drop handling at this level, // but for sure the default QAbstractItemModel implementation makes no sense for a dir model. Q_UNUSED(data); Q_UNUSED(action); Q_UNUSED(row); Q_UNUSED(column); Q_UNUSED(parent); return false; } void KDirModel::setDropsAllowed(DropsAllowed dropsAllowed) { d->m_dropsAllowed = dropsAllowed; } void KDirModel::expandToUrl(const KUrl& url) { const QPair result = d->nodeForUrl(url, true /*return last parent*/); // O(n*m) if (!result.second) // doesn't seem related to our base url? return; if (!(result.second->item().isNull()) && result.second->item().url() == url) { // We have it already, nothing to do kDebug(7008) << "have it already item=" <item()*/; return; } //kDebug(7008) << k_funcinfo << url << result.second << result.second->item().url() << endl; d->m_urlsBeingFetched[result.second].append(url); const QModelIndex parentIndex = d->indexForNode(result.second, result.first); Q_ASSERT(parentIndex.isValid()); fetchMore(parentIndex); } #include "kdirmodel.moc" diff --git a/kio/kio/kdirmodel.h b/kio/kio/kdirmodel.h index 648346d955..e63410e8bd 100644 --- a/kio/kio/kdirmodel.h +++ b/kio/kio/kdirmodel.h @@ -1,202 +1,202 @@ /* This file is part of the KDE project Copyright (C) 2006 David Faure 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 KDIRMODEL_H #define KDIRMODEL_H #include #include #include class KDirLister; class KDirModelPrivate; /** * @short A model for a KIO-based directory tree. * * KDirModel implements the QAbstractItemModel interface (for use with Qt's model/view widgets) * around the directory listing for one directory or a tree of directories. * * @see KDirSortFilterProxyModel * * @author David Faure * Based on work by Hamish Rodda and Pascal Letourneau */ class KIO_EXPORT KDirModel : public QAbstractItemModel { Q_OBJECT public: /** * @param parent parent qobject */ explicit KDirModel( QObject* parent = 0 ); ~KDirModel(); /** * Set the directory lister to use by this model, instead of the default KDirLister created internally. * The model takes ownership. */ void setDirLister( KDirLister* dirLister ); /** * Return the directory lister used by this model. */ KDirLister* dirLister() const; /** * Return the fileitem for a given index. This is O(1), i.e. fast. */ KFileItem itemForIndex( const QModelIndex& index ) const; /** * Return the index for a given kfileitem. This can be slow. * @deprecated use the method that takes a KFileItem by value */ KDE_DEPRECATED QModelIndex indexForItem( const KFileItem* ) const; /** * Return the index for a given kfileitem. This can be slow. */ QModelIndex indexForItem( const KFileItem& ) const; /** * Return the index for a given url. This can be slow. */ QModelIndex indexForUrl(const KUrl& url) const; /** * @short Lists subdirectories using fetchMore() as needed until the given @p url exists in the model. * * When the model is used by a treeview, call KDirLister::openUrl with the base url of the tree, * then the treeview will take care of calling fetchMore() when the user opens directories. * However if you want the tree to show a given URL (i.e. open the tree recursively until that URL), * call expandToUrl(). * Note that this is asynchronous; the necessary listing of subdirectories will take time so * the model will not immediately have this url available. * The model emits the signal expand() when an index has become available; this can be connected * to the treeview in order to let it open that index. * @param url the url of a subdirectory of the directory model */ void expandToUrl(const KUrl& url); /** * Notify the model that the item at this index has changed. * For instance because KMimeTypeResolver called determineMimeType on it. * This makes the model emit its dataChanged signal at this index, so that views repaint. * Note that for most things (renaming, changing size etc.), KDirLister's signals tell the model already. */ void itemChanged( const QModelIndex& index ); /*** * Useful "default" columns. Views can use a proxy to have more control over this. */ enum ModelColumns { Name = 0, Size, ModifiedTime, Permissions, Owner, Group, Type, ColumnCount }; /// Possible return value for data(ChildCountRole), meaning the item isn't a directory, /// or we haven't calculated its child count yet enum { ChildCountUnknown = -1 }; enum AdditionalRoles { // Note: use printf "0x%08X\n" $(($RANDOM*$RANDOM)) // to define additional roles. FileItemRole = 0x07A263FF, ///< returns the KFileItem for a given index ChildCountRole = 0x2C4D0A40 ///< returns the number of items in a directory, or ChildCountUnknown }; enum DropsAllowedFlag { NoDrops = 0, DropOnDirectory = 1, ///< allow drops on any directory DropOnAnyFile = 2, ///< allow drops on any file DropOnLocalExecutable = 4 ///< allow drops on local executables, shell scripts and desktop files. Can be used with DropOnDirectory. }; Q_DECLARE_FLAGS(DropsAllowed, DropsAllowedFlag) /// Set whether dropping onto items should be allowed, and for which kind of item /// Drops are disabled by default. void setDropsAllowed(DropsAllowed dropsAllowed); /// Reimplemented from QAbstractItemModel. Returns true for empty directories. virtual bool canFetchMore ( const QModelIndex & parent ) const; /// Reimplemented from QAbstractItemModel. Returns ColumnCount. virtual int columnCount ( const QModelIndex & parent = QModelIndex() ) const; /// Reimplemented from QAbstractItemModel. virtual QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const; /// Reimplemented from QAbstractItemModel. Not implemented yet. virtual bool dropMimeData ( const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent ); /// Reimplemented from QAbstractItemModel. Lists the subdirectory. virtual void fetchMore ( const QModelIndex & parent ); /// Reimplemented from QAbstractItemModel. virtual Qt::ItemFlags flags ( const QModelIndex & index ) const; /// Reimplemented from QAbstractItemModel. Returns true for directories. virtual bool hasChildren ( const QModelIndex & parent = QModelIndex() ) const; /// Reimplemented from QAbstractItemModel. Returns the column titles. virtual QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; /// Reimplemented from QAbstractItemModel. O(1) virtual QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const; /// Reimplemented from QAbstractItemModel. virtual QMimeData * mimeData ( const QModelIndexList & indexes ) const; /// Reimplemented from QAbstractItemModel. virtual QStringList mimeTypes () const; /// Reimplemented from QAbstractItemModel. virtual QModelIndex parent ( const QModelIndex & index ) const; /// Reimplemented from QAbstractItemModel. virtual int rowCount ( const QModelIndex & parent = QModelIndex() ) const; /// Reimplemented from QAbstractItemModel. /// Call this to set a new icon, e.g. a preview virtual bool setData ( const QModelIndex & index, const QVariant & value, int role = Qt::EditRole ); /// Reimplemented from QAbstractItemModel. Not implemented. @see KDirSortFilterProxyModel virtual void sort ( int column, Qt::SortOrder order = Qt::AscendingOrder ); Q_SIGNALS: /** * Emitted for each subdirectory that is a parent of a url passed to expandToUrl * This allows to asynchronously open a tree view down to a given directory. */ void expand(const QModelIndex& index); private Q_SLOTS: - void slotNewItems(const QList&); + void slotNewItems(const KFileItemList&); void slotDeleteItem(const KFileItem&); void slotRefreshItems(const QList >&); void slotClear(); private: // Make those private, they shouldn't be called by applications virtual bool insertRows(int , int, const QModelIndex & = QModelIndex()) { return false; } virtual bool insertColumns(int, int, const QModelIndex & = QModelIndex()) { return false; } virtual bool removeRows(int, int, const QModelIndex & = QModelIndex()) { return false; } virtual bool removeColumns(int, int, const QModelIndex & = QModelIndex()) { return false; } private: friend class KDirModelPrivate; KDirModelPrivate *const d; }; Q_DECLARE_OPERATORS_FOR_FLAGS(KDirModel::DropsAllowed) #endif /* KDIRMODEL_H */ diff --git a/kio/kio/kfilefilter.cpp b/kio/kio/kfilefilter.cpp index beb4c93505..3d551a9601 100644 --- a/kio/kio/kfilefilter.cpp +++ b/kio/kio/kfilefilter.cpp @@ -1,154 +1,154 @@ /* This file is part of the KDE libraries Copyright (c) 2001,2002 Carsten Pfeiffer This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License (LGPL) as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kfilefilter.h" #include #include #include class KSimpleFileFilterPrivate { public: KSimpleFileFilterPrivate() : m_filterDotFiles( true ), m_filterSpecials( true ), m_modeFilter( 0 ) { } QStringList m_mimeFilters; bool m_filterDotFiles :1; bool m_filterSpecials :1; mode_t m_modeFilter; }; KSimpleFileFilter::KSimpleFileFilter() : d( new KSimpleFileFilterPrivate ) { } KSimpleFileFilter::~KSimpleFileFilter() { delete d; } void KSimpleFileFilter::setFilterDotFiles( bool filter ) { d->m_filterDotFiles = filter; } bool KSimpleFileFilter::filterDotFiles() const { return d->m_filterDotFiles; } void KSimpleFileFilter::setFilterSpecials( bool filter ) { d->m_filterSpecials = filter; } bool KSimpleFileFilter::filterSpecials() const { return d->m_filterSpecials; } void KSimpleFileFilter::setNameFilters( const QStringList& nameFilters, Qt::CaseSensitivity caseSensitive ) { m_nameFilters.clear(); foreach ( const QString &filter, nameFilters ) m_nameFilters.append(QRegExp(filter, caseSensitive, QRegExp::Wildcard)); } void KSimpleFileFilter::setMimeFilters( const QStringList& mimeFilters ) { d->m_mimeFilters = mimeFilters; } QStringList KSimpleFileFilter::mimeFilters() const { return d->m_mimeFilters; } void KSimpleFileFilter::setModeFilter( mode_t mode ) { d->m_modeFilter = mode; } mode_t KSimpleFileFilter::modeFilter() const { return d->m_modeFilter; } -bool KSimpleFileFilter::passesFilter( const KFileItem *item ) const +bool KSimpleFileFilter::passesFilter( const KFileItem &item ) const { - const QString& name = item->name(); + const QString& name = item.name(); - if ( d->m_filterDotFiles && item->isHidden() ) + if ( d->m_filterDotFiles && item.isHidden() ) return false; if ( d->m_filterSpecials && (name == "." || name == "..") ) return false; - if ( d->m_modeFilter && !(d->m_modeFilter & item->mode()) ) + if ( d->m_modeFilter && !(d->m_modeFilter & item.mode()) ) return false; if ( !d->m_mimeFilters.isEmpty() ) { // correct or guessed mimetype -- we don't mind - KMimeType::Ptr mime = item->mimeTypePtr(); + KMimeType::Ptr mime = item.mimeTypePtr(); bool ok = false; QStringList::ConstIterator it = d->m_mimeFilters.begin(); for ( ; it != d->m_mimeFilters.end(); ++it ) { if ( mime->is(*it) ) { // match! ok = true; break; } } if ( !ok ) return false; } if ( !m_nameFilters.isEmpty() ) { bool ok = false; QList::const_iterator it = m_nameFilters.begin(); const QList::const_iterator end = m_nameFilters.end(); for ( ; it != end; ++it ) { if ( (*it).exactMatch( name ) ) { // match! ok = true; break; } } if ( !ok ) return false; } return true; // passes the filter! } void KFileFilter::virtual_hook( int, void* ) { /*BASE::virtual_hook( id, data );*/ } void KSimpleFileFilter::virtual_hook( int id, void* data ) { KFileFilter::virtual_hook( id, data ); } diff --git a/kio/kio/kfilefilter.h b/kio/kio/kfilefilter.h index 1b05cef1ab..df47118c03 100644 --- a/kio/kio/kfilefilter.h +++ b/kio/kio/kfilefilter.h @@ -1,152 +1,152 @@ /* This file is part of the KDE libraries Copyright (c) 2001,2002 Carsten Pfeiffer This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License (LGPL) as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KFILEFILTER_H #define KFILEFILTER_H #include #include #include #include #include class QRegExp; class KFileItem; /** * A KFileFilter is a simple base class for file filters. Just * reimplement passesFilter(). * @short Base class for file filters. */ class KIO_EXPORT KFileFilter //krazy:exclude=dpointer (just provides an interface) { public: virtual ~KFileFilter() {} //krazy:exclude=inlines (just provides an interface) /** * Checks the given @p item. * @param item the item to filter * @return true if the @p item passes the filter, false otherwise */ - virtual bool passesFilter( const KFileItem *item ) const = 0; + virtual bool passesFilter( const KFileItem &item ) const = 0; protected: virtual void virtual_hook( int id, void* data ); }; class KSimpleFileFilterPrivate; /** * A simple file filter that can filter hidden dot files, by name, * by mime type and by mode. * @short A simple file filter. */ class KIO_EXPORT KSimpleFileFilter : public KFileFilter { public: /** * Creates a new filter. By default, it filters only hidden dot files * and "." and "..". */ KSimpleFileFilter(); virtual ~KSimpleFileFilter(); /** * Enable or disable filtering hidden dot files. * This option is enabled by default. * @param filter true to enable filtering dot files, false to * disable * @see filterDotFiles */ virtual void setFilterDotFiles( bool filter ); /** * Checks whether filtering dot files is enabled. * This option is enabled by default. * @return true if filtering is enabled, false otherwise * @see setFilterDotFiles */ bool filterDotFiles() const; /** * Filters "." and "..", default is true. * @param filter true to enable, false otherwise */ virtual void setFilterSpecials( bool filter ); /** * Checks whether it filters "." and "..", default is true. * @return true if enabled, false otherwise */ bool filterSpecials() const; /** * Sets a list of regular expressions to filter by name. * The file will only pass if its name matches one of the regular * expressions. The regular expressions are using wildcard matching. * @param nameFilters a list of regular expressions * @param caseSensitive Qt::CaseSensitive or Qt::CaseInsensitive */ virtual void setNameFilters( const QStringList& nameFilters, Qt::CaseSensitivity caseSensitive = Qt::CaseSensitive ); /** * Sets a list of mime filters. A file can only pass if its * mime type is contained in this list. * @param mimeFilters the list of mime types * @see setMimeFilter */ virtual void setMimeFilters( const QStringList& mimeFilters ); /** * Returns the list of mime types. * @return the list of mime types * @see mimeFilter */ QStringList mimeFilters() const; /** * Sets the mode filter. If the @p mode is 0, the filter is * disabled. * When enabled, a file will only pass if the files mode * ANDed with @p mode is not zero. * @param mode the new mode. 0 to disable * @see modeFilter */ virtual void setModeFilter( mode_t mode ); /** * Returns the mode filter, as set by setModeFilter(). * @return the mode filter, 0 if disabled * @see setModeFilter */ mode_t modeFilter() const; /** * Checks the given @p item. * @param item the item to filter * @return true if the @p item passes the filter, false otherwise */ - virtual bool passesFilter( const KFileItem *item ) const; + virtual bool passesFilter( const KFileItem &item ) const; protected: QList m_nameFilters; protected: virtual void virtual_hook( int id, void* data ); private: KSimpleFileFilterPrivate * const d; }; #endif // KFILEFILTER_H diff --git a/kio/kio/kfileitem.cpp b/kio/kio/kfileitem.cpp index 50e1b0dab0..8b725fded9 100644 --- a/kio/kio/kfileitem.cpp +++ b/kio/kio/kfileitem.cpp @@ -1,1324 +1,1331 @@ /* This file is part of the KDE project Copyright (C) 1999-2006 David Faure 2001 Carsten Pfeiffer 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 "kfileitem.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class KFileItemPrivate : public QSharedData { public: KFileItemPrivate(const KIO::UDSEntry& entry, mode_t mode, mode_t permissions, const KUrl& url, bool urlIsDirectory, bool delayedMimeTypes) : m_entry( entry ), m_url( url ), m_pMimeType( 0 ), m_fileMode( mode ), m_permissions( permissions ), m_bMarked( false ), m_bLink( false ), m_bIsLocalUrl( url.isLocalFile() ), m_bMimeTypeKnown( false ), m_delayedMimeTypes( delayedMimeTypes ), m_hidden( Auto ) { if (entry.count() != 0) { readUDSEntry( urlIsDirectory ); } else { m_strName = url.fileName(); m_strText = KIO::decodeFileName( m_strName ); } init(); } ~KFileItemPrivate() { } - //KFileItemPrivate(const KFileItemPrivate& other); +// KFileItemPrivate(const KFileItemPrivate& other); /** * Computes the text and mode from the UDSEntry * Called by constructor, but can be called again later * Nothing does that anymore though (I guess some old KonqFileItem did) * so it's not a protected method of the public class anymore. */ void init(); KIO::filesize_t size() const; KDateTime time( KFileItem::FileTimes which ) const; bool cmp( const KFileItemPrivate & item ) const; /** * Extracts the data from the UDSEntry member and updates the KFileItem * accordingly. */ void readUDSEntry( bool _urlIsDirectory ); /** * Parses the given permission set and provides it for access() */ QString parsePermissions( mode_t perm ) const; /** * The UDSEntry that contains the data for this fileitem, if it came from a directory listing. */ mutable KIO::UDSEntry m_entry; /** * The url of the file */ KUrl m_url; /** * The text for this item, i.e. the file name without path, */ QString m_strName; /** * The text for this item, i.e. the file name without path, decoded * ('%%' becomes '%', '%2F' becomes '/') */ QString m_strText; /** * The filename in lower case (to speed up sorting) */ mutable QString m_strLowerCaseName; /** * The mimetype of the file */ mutable KMimeType::Ptr m_pMimeType; /** * The file mode */ mode_t m_fileMode; /** * The permissions */ mode_t m_permissions; /** * Marked : see mark() */ bool m_bMarked:1; /** * Whether the file is a link */ bool m_bLink:1; /** * True if local file */ bool m_bIsLocalUrl:1; mutable bool m_bMimeTypeKnown:1; bool m_delayedMimeTypes:1; // Auto: check leading dot. enum { Auto, Hidden, Shown } m_hidden:3; // For special case like link to dirs over FTP QString m_guessedMimeType; mutable QString m_access; QMap m_extra; // DEPRECATED mutable KFileMetaInfo m_metaInfo; enum { NumFlags = KFileItem::CreationTime + 1 }; mutable KDateTime m_time[3]; }; void KFileItemPrivate::init() { m_access.clear(); // metaInfo = KFileMetaInfo(); // determine mode and/or permissions if unknown // TODO: delay this until requested if ( m_fileMode == KFileItem::Unknown || m_permissions == KFileItem::Unknown ) { mode_t mode = 0; if ( m_url.isLocalFile() ) { /* directories may not have a slash at the end if * we want to stat() them; it requires that we * change into it .. which may not be allowed * stat("/is/unaccessible") -> rwx------ * stat("/is/unaccessible/") -> EPERM H.Z. * This is the reason for the -1 */ KDE_struct_stat buf; QByteArray path = QFile::encodeName(m_url.path( KUrl::RemoveTrailingSlash )); if ( KDE_lstat( path.data(), &buf ) == 0 ) { mode = buf.st_mode; if ( S_ISLNK( mode ) ) { m_bLink = true; if ( KDE_stat( path.data(), &buf ) == 0 ) mode = buf.st_mode; else // link pointing to nowhere (see kio/file/file.cc) mode = (S_IFMT-1) | S_IRWXU | S_IRWXG | S_IRWXO; } // While we're at it, store the times m_time[ KFileItem::ModificationTime ].setTime_t(buf.st_mtime); m_time[ KFileItem::AccessTime ].setTime_t(buf.st_atime); if ( m_fileMode == KFileItem::Unknown ) m_fileMode = mode & S_IFMT; // extract file type if ( m_permissions == KFileItem::Unknown ) m_permissions = mode & 07777; // extract permissions } } } } // let's see if we can just copy everything (default copy ctor) -#if 0 +/* KFileItemPrivate::KFileItemPrivate(const KFileItemPrivate& other) : QSharedData(other) { Q_ASSERT(this != &other); m_entry = other.m_entry; m_url = other.m_url; m_bIsLocalUrl = other.m_bIsLocalUrl; m_strName = other.m_strName; m_strText = other.m_strText; m_fileMode = other.m_fileMode; m_permissions = other.m_permissions; m_bLink = other.m_bLink; m_pMimeType = other.m_pMimeType; m_strLowerCaseName = other.m_strLowerCaseName; m_bMimeTypeKnown = other.m_bMimeTypeKnown; m_hidden = other.m_hidden; m_guessedMimeType = other.m_guessedMimeType; m_access = other.m_access; m_metaInfo = other.m_metaInfo; for ( int i = 0; i < NumFlags; i++ ) m_time[i] = other.m_time[i]; // note: m_extra is NOT copied, as we'd have no control over who is // deleting the data or not. // We had a mimetype previously (probably), so we need to re-determine it - determineMimeType(); +// determineMimeType(); } -#endif - +*/ void KFileItemPrivate::readUDSEntry( bool _urlIsDirectory ) { // extract fields from the KIO::UDS Entry m_fileMode = m_entry.numberValue( KIO::UDSEntry::UDS_FILE_TYPE ); m_permissions = m_entry.numberValue( KIO::UDSEntry::UDS_ACCESS ); m_strName = m_entry.stringValue( KIO::UDSEntry::UDS_NAME ); m_strText = KIO::decodeFileName( m_strName ); const QString urlStr = m_entry.stringValue( KIO::UDSEntry::UDS_URL ); const bool UDS_URL_seen = !urlStr.isEmpty(); if ( UDS_URL_seen ) { m_url = KUrl( urlStr ); if ( m_url.isLocalFile() ) m_bIsLocalUrl = true; } const QString mimeTypeStr = m_entry.stringValue( KIO::UDSEntry::UDS_MIME_TYPE ); m_bMimeTypeKnown = !mimeTypeStr.isEmpty(); if ( m_bMimeTypeKnown ) m_pMimeType = KMimeType::mimeType( mimeTypeStr ); m_guessedMimeType = m_entry.stringValue( KIO::UDSEntry::UDS_GUESSED_MIME_TYPE ); m_bLink = !m_entry.stringValue( KIO::UDSEntry::UDS_LINK_DEST ).isEmpty(); // we don't store the link dest const int hiddenVal = m_entry.numberValue( KIO::UDSEntry::UDS_HIDDEN, -1 ); m_hidden = hiddenVal == 1 ? Hidden : ( hiddenVal == 0 ? Shown : Auto ); // avoid creating these QStrings again and again static const QString& dot = KGlobal::staticQString("."); if ( _urlIsDirectory && !UDS_URL_seen && !m_strName.isEmpty() && m_strName != dot ) m_url.addPath( m_strName ); } inline //because it is used only in one place KIO::filesize_t KFileItemPrivate::size() const { // Extract it from the KIO::UDSEntry long long fieldVal = m_entry.numberValue( KIO::UDSEntry::UDS_SIZE, -1 ); if ( fieldVal != -1 ) { return fieldVal; } // If not in the KIO::UDSEntry, or if UDSEntry empty, use stat() [if local URL] if ( m_bIsLocalUrl ) { KDE_struct_stat buf; if ( KDE_stat( QFile::encodeName(m_url.path(KUrl::RemoveTrailingSlash)), &buf ) == 0 ) return buf.st_size; } return 0; } KDateTime KFileItemPrivate::time( KFileItem::FileTimes mappedWhich ) const { if ( !m_time[mappedWhich].isNull() ) return m_time[mappedWhich]; // Extract it from the KIO::UDSEntry long long fieldVal = -1; switch ( mappedWhich ) { case KFileItem::ModificationTime: fieldVal = m_entry.numberValue( KIO::UDSEntry::UDS_MODIFICATION_TIME, -1 ); break; case KFileItem::AccessTime: fieldVal = m_entry.numberValue( KIO::UDSEntry::UDS_ACCESS_TIME, -1 ); break; case KFileItem::CreationTime: fieldVal = m_entry.numberValue( KIO::UDSEntry::UDS_CREATION_TIME, -1 ); break; } if ( fieldVal != -1 ) { m_time[mappedWhich].setTime_t( fieldVal ); return m_time[mappedWhich]; } // If not in the KIO::UDSEntry, or if UDSEntry empty, use stat() [if local URL] if ( m_bIsLocalUrl ) { KDE_struct_stat buf; if ( KDE_stat( QFile::encodeName(m_url.path(KUrl::RemoveTrailingSlash)), &buf ) == 0 ) { m_time[KFileItem::ModificationTime].setTime_t(buf.st_mtime); m_time[KFileItem::AccessTime].setTime_t(buf.st_atime); m_time[KFileItem::CreationTime] = KDateTime(); return m_time[mappedWhich]; } } return KDateTime(); } inline //because it is used only in one place bool KFileItemPrivate::cmp( const KFileItemPrivate & item ) const { return ( m_strName == item.m_strName && m_bIsLocalUrl == item.m_bIsLocalUrl && m_fileMode == item.m_fileMode && m_permissions == item.m_permissions && m_entry.stringValue( KIO::UDSEntry::UDS_USER ) == item.m_entry.stringValue( KIO::UDSEntry::UDS_USER ) && m_entry.stringValue( KIO::UDSEntry::UDS_GROUP ) == item.m_entry.stringValue( KIO::UDSEntry::UDS_GROUP ) && m_bLink == item.m_bLink && m_hidden == item.m_hidden && size() == item.size() && time(KFileItem::ModificationTime) == item.time(KFileItem::ModificationTime) && m_entry.stringValue( KIO::UDSEntry::UDS_ICON_NAME ) == item.m_entry.stringValue( KIO::UDSEntry::UDS_ICON_NAME ) ); // Don't compare the mimetypes here. They might not be known, and we don't want to // do the slow operation of determining them here. } inline //because it is used only in one place QString KFileItemPrivate::parsePermissions(mode_t perm) const { static char buffer[ 12 ]; char uxbit,gxbit,oxbit; if ( (perm & (S_IXUSR|S_ISUID)) == (S_IXUSR|S_ISUID) ) uxbit = 's'; else if ( (perm & (S_IXUSR|S_ISUID)) == S_ISUID ) uxbit = 'S'; else if ( (perm & (S_IXUSR|S_ISUID)) == S_IXUSR ) uxbit = 'x'; else uxbit = '-'; if ( (perm & (S_IXGRP|S_ISGID)) == (S_IXGRP|S_ISGID) ) gxbit = 's'; else if ( (perm & (S_IXGRP|S_ISGID)) == S_ISGID ) gxbit = 'S'; else if ( (perm & (S_IXGRP|S_ISGID)) == S_IXGRP ) gxbit = 'x'; else gxbit = '-'; if ( (perm & (S_IXOTH|S_ISVTX)) == (S_IXOTH|S_ISVTX) ) oxbit = 't'; else if ( (perm & (S_IXOTH|S_ISVTX)) == S_ISVTX ) oxbit = 'T'; else if ( (perm & (S_IXOTH|S_ISVTX)) == S_IXOTH ) oxbit = 'x'; else oxbit = '-'; // Include the type in the first char like kde3 did; people are more used to seeing it, // even though it's not really part of the permissions per se. if (m_fileMode != KFileItem::Unknown && S_ISDIR(m_fileMode)) buffer[0] = 'd'; else if (m_bLink) buffer[0] = 'l'; else buffer[0] = '-'; buffer[1] = ((( perm & S_IRUSR ) == S_IRUSR ) ? 'r' : '-' ); buffer[2] = ((( perm & S_IWUSR ) == S_IWUSR ) ? 'w' : '-' ); buffer[3] = uxbit; buffer[4] = ((( perm & S_IRGRP ) == S_IRGRP ) ? 'r' : '-' ); buffer[5] = ((( perm & S_IWGRP ) == S_IWGRP ) ? 'w' : '-' ); buffer[6] = gxbit; buffer[7] = ((( perm & S_IROTH ) == S_IROTH ) ? 'r' : '-' ); buffer[8] = ((( perm & S_IWOTH ) == S_IWOTH ) ? 'w' : '-' ); buffer[9] = oxbit; // if (hasExtendedACL()) if (m_entry.contains(KIO::UDSEntry::UDS_EXTENDED_ACL)) { buffer[10] = '+'; buffer[11] = 0; } else { buffer[10] = 0; } return QString::fromLatin1(buffer); } /////// KFileItem::KFileItem() : d(0) { } KFileItem::KFileItem( const KIO::UDSEntry& entry, const KUrl& url, bool delayedMimeTypes, bool urlIsDirectory ) : d(new KFileItemPrivate(entry, KFileItem::Unknown, KFileItem::Unknown, url, urlIsDirectory, delayedMimeTypes)) { } KFileItem::KFileItem( mode_t mode, mode_t permissions, const KUrl& url, bool delayedMimeTypes ) : d(new KFileItemPrivate(KIO::UDSEntry(), mode, permissions, url, false, delayedMimeTypes)) { } KFileItem::KFileItem( const KUrl &url, const QString &mimeType, mode_t mode ) : d(new KFileItemPrivate(KIO::UDSEntry(), mode, KFileItem::Unknown, url, false, false)) { d->m_bMimeTypeKnown = !mimeType.isEmpty(); if (d->m_bMimeTypeKnown) d->m_pMimeType = KMimeType::mimeType( mimeType ); } KFileItem::KFileItem(const KFileItem& other) : d(other.d) { } KFileItem::~KFileItem() { } void KFileItem::refresh() { d->m_fileMode = KFileItem::Unknown; d->m_permissions = KFileItem::Unknown; d->m_pMimeType = 0; d->m_metaInfo = KFileMetaInfo(); d->m_hidden = KFileItemPrivate::Auto; // Basically, we can't trust any information we got while listing. // Everything could have changed... // Clearing m_entry makes it possible to detect changes in the size of the file, // the time information, etc. d->m_entry.clear(); d->init(); } void KFileItem::refreshMimeType() { d->m_pMimeType = 0; d->m_bMimeTypeKnown = false; } void KFileItem::setUrl( const KUrl &url ) { d->m_url = url; setName( url.fileName() ); } void KFileItem::setName( const QString& name ) { d->m_strName = name; d->m_strText = KIO::decodeFileName( d->m_strName ); } QString KFileItem::linkDest() const { // Extract it from the KIO::UDSEntry const QString linkStr = d->m_entry.stringValue( KIO::UDSEntry::UDS_LINK_DEST ); if ( !linkStr.isEmpty() ) return linkStr; // If not in the KIO::UDSEntry, or if UDSEntry empty, use readlink() [if local URL] if ( d->m_bIsLocalUrl ) { char buf[1000]; int n = readlink( QFile::encodeName(d->m_url.path( KUrl::RemoveTrailingSlash )), buf, sizeof(buf)-1 ); if ( n != -1 ) { buf[ n ] = 0; return QFile::decodeName( buf ); } } return QString(); } QString KFileItem::localPath() const { if ( d->m_bIsLocalUrl ) { return d->m_url.path(); } // Extract the local path from the KIO::UDSEntry return d->m_entry.stringValue( KIO::UDSEntry::UDS_LOCAL_PATH ); } KIO::filesize_t KFileItem::size() const { return d->size(); } bool KFileItem::hasExtendedACL() const { // Check if the field exists; its value doesn't matter return d->m_entry.contains(KIO::UDSEntry::UDS_EXTENDED_ACL); } KACL KFileItem::ACL() const { if ( hasExtendedACL() ) { // Extract it from the KIO::UDSEntry const QString fieldVal = d->m_entry.stringValue( KIO::UDSEntry::UDS_ACL_STRING ); if ( !fieldVal.isEmpty() ) return KACL( fieldVal ); } // create one from the basic permissions return KACL( d->m_permissions ); } KACL KFileItem::defaultACL() const { // Extract it from the KIO::UDSEntry const QString fieldVal = d->m_entry.stringValue( KIO::UDSEntry::UDS_DEFAULT_ACL_STRING ); if ( !fieldVal.isEmpty() ) return KACL(fieldVal); else return KACL(); } KDateTime KFileItem::time( FileTimes which ) const { return d->time(which); } time_t KFileItem::time( unsigned int which ) const { switch (which) { case KIO::UDSEntry::UDS_ACCESS_TIME: return d->time(AccessTime).toTime_t(); case KIO::UDSEntry::UDS_CREATION_TIME: return d->time(CreationTime).toTime_t(); case KIO::UDSEntry::UDS_MODIFICATION_TIME: default: return d->time(ModificationTime).toTime_t(); } } QString KFileItem::user() const { QString userName = d->m_entry.stringValue( KIO::UDSEntry::UDS_USER ); if ( userName.isEmpty() && d->m_bIsLocalUrl ) { #ifdef Q_WS_WIN QFileInfo a(d->m_url.path( KUrl::RemoveTrailingSlash )); userName = a.owner(); d->m_entry.insert( KIO::UDSEntry::UDS_USER, userName ); #else KDE_struct_stat buff; if ( KDE_lstat( QFile::encodeName(d->m_url.path( KUrl::RemoveTrailingSlash )), &buff ) == 0) // get uid/gid of the link, if it's a link { struct passwd *pwuser = getpwuid( buff.st_uid ); if ( pwuser != 0 ) { userName = QString::fromLocal8Bit(pwuser->pw_name); d->m_entry.insert( KIO::UDSEntry::UDS_USER, userName ); } } #endif } return userName; } QString KFileItem::group() const { QString groupName = d->m_entry.stringValue( KIO::UDSEntry::UDS_GROUP ); if (groupName.isEmpty() && d->m_bIsLocalUrl ) { #ifdef Q_WS_WIN QFileInfo a(d->m_url.path( KUrl::RemoveTrailingSlash )); groupName = a.group(); d->m_entry.insert( KIO::UDSEntry::UDS_GROUP, groupName ); #else KDE_struct_stat buff; if ( KDE_lstat( QFile::encodeName(d->m_url.path( KUrl::RemoveTrailingSlash )), &buff ) == 0) // get uid/gid of the link, if it's a link { struct group *ge = getgrgid( buff.st_gid ); if ( ge != 0 ) { groupName = QString::fromLocal8Bit(ge->gr_name); if (groupName.isEmpty()) groupName.sprintf("%d",ge->gr_gid); } else groupName.sprintf("%d",buff.st_gid); d->m_entry.insert( KIO::UDSEntry::UDS_GROUP, groupName ); } #endif } return groupName; } QString KFileItem::mimetype() const { KFileItem * that = const_cast(this); return that->determineMimeType()->name(); } KMimeType::Ptr KFileItem::determineMimeType() const { if ( !d->m_pMimeType || !d->m_bMimeTypeKnown ) { bool isLocalUrl; KUrl url = mostLocalUrl(isLocalUrl); d->m_pMimeType = KMimeType::findByUrl( url, d->m_fileMode, isLocalUrl ); Q_ASSERT(d->m_pMimeType); //kDebug() << "finding mimetype for " << url.url() << " : " << d->m_pMimeType->name(); d->m_bMimeTypeKnown = true; } return d->m_pMimeType; } bool KFileItem::isMimeTypeKnown() const { // The mimetype isn't known if determineMimeType was never called (on-demand determination) // or if this fileitem has a guessed mimetype (e.g. ftp symlink) - in which case // it always remains "not fully determined" return d->m_bMimeTypeKnown && d->m_guessedMimeType.isEmpty(); } QString KFileItem::mimeComment() const { KMimeType::Ptr mType = determineMimeType(); bool isLocalUrl; KUrl url = mostLocalUrl(isLocalUrl); KMimeType::Ptr mime = mimeTypePtr(); if (isLocalUrl && mime->is("application/x-desktop")) { KDesktopFile cfg( url.path() ); QString comment = cfg.desktopGroup().readEntry( "Comment" ); if (!comment.isEmpty()) return comment; } QString comment = mType->comment( url ); //kDebug() << "finding comment for " << url.url() << " : " << d->m_pMimeType->name(); if (!comment.isEmpty()) return comment; else return mType->name(); } static QString iconFromDesktopFile(const QString& path) { KDesktopFile cfg( path ); const KConfigGroup group = cfg.desktopGroup(); const QString icon = cfg.readIcon(); const QString type = cfg.readPath(); if ( type == "FSDevice" ) { const QString unmount_icon = group.readEntry( "UnmountIcon" ); const QString dev = cfg.readDevice(); if ( !icon.isEmpty() && !unmount_icon.isEmpty() && !dev.isEmpty() ) { KMountPoint::Ptr mountPoint = KMountPoint::currentMountPoints().findByDevice(dev); if (!mountPoint) // not mounted? return unmount_icon; } } else if ( type == "Link" ) { const QString emptyIcon = group.readEntry( "EmptyIcon" ); if ( !emptyIcon.isEmpty() ) { const QString u = group.readPathEntry( "URL" ); const KUrl url( u ); if ( url.protocol() == "trash" ) { // We need to find if the trash is empty, preferrably without using a KIO job. // So instead kio_trash leaves an entry in its config file for us. KConfig trashConfig( "trashrc", KConfig::OnlyLocal ); if ( trashConfig.group("Status").readEntry( "Empty", true ) ) { return emptyIcon; } } } } return icon; } QString KFileItem::iconName() const { const QString iconName = d->m_entry.stringValue( KIO::UDSEntry::UDS_ICON_NAME ); if (!iconName.isEmpty()) return iconName; bool isLocalUrl; KUrl url = mostLocalUrl(isLocalUrl); KMimeType::Ptr mime = mimeTypePtr(); if (isLocalUrl && mime->is("application/x-desktop")) { QString icon = iconFromDesktopFile(url.path()); if (!icon.isEmpty()) return icon; } //kDebug() << "finding icon for " << url.url() << " : " << mimeTypePtr()->name(); return mime->iconName(url); } QStringList KFileItem::overlays() const { QStringList names; if ( d->m_bLink ) { names.append("link"); } if ( !S_ISDIR( d->m_fileMode ) // Locked dirs have a special icon, use the overlay for files only && !isReadable()) { names.append("lock"); } if ( isHidden() ) { names.append("hidden"); } if( S_ISDIR( d->m_fileMode ) && d->m_bIsLocalUrl) { if (KSambaShare::instance()->isDirectoryShared( d->m_url.path() ) || KNFSShare::instance()->isDirectoryShared( d->m_url.path() )) { //kDebug()<<"KFileShare::isDirectoryShared : "<m_url.path(); names.append("share"); } } if ( d->m_pMimeType && d->m_pMimeType->is("application/x-gzip") && d->m_url.fileName().endsWith( QLatin1String( ".gz" ) ) ) { names.append("zio"); } return names; } QPixmap KFileItem::pixmap( int _size, int _state ) const { const QString iconName = d->m_entry.stringValue( KIO::UDSEntry::UDS_ICON_NAME ); if ( !iconName.isEmpty() ) return DesktopIcon(iconName, _size, _state); if ( !d->m_pMimeType ) { if ( S_ISDIR( d->m_fileMode ) ) { static const QString * defaultFolderIcon = 0; if ( !defaultFolderIcon ) { const KMimeType::Ptr mimeType = KMimeType::mimeType( "inode/directory" ); if ( mimeType ) defaultFolderIcon = &KGlobal::staticQString( mimeType->iconName() ); else kWarning(7000) << "No mimetype for inode/directory could be found. Check your installation."; } if ( defaultFolderIcon ) return DesktopIcon( *defaultFolderIcon, _size, _state ); } return DesktopIcon( "unknown", _size, _state ); } KMimeType::Ptr mime; // Use guessed mimetype if the main one hasn't been determined for sure if ( !d->m_bMimeTypeKnown && !d->m_guessedMimeType.isEmpty() ) mime = KMimeType::mimeType( d->m_guessedMimeType ); else mime = d->m_pMimeType; // Support for gzipped files: extract mimetype of contained file // See also the relevant code in overlays, which adds the zip overlay. if ( mime->name() == "application/x-gzip" && d->m_url.fileName().endsWith( QLatin1String( ".gz" ) ) ) { KUrl sf; sf.setPath( d->m_url.path().left( d->m_url.path().length() - 3 ) ); //kDebug() << "KFileItem::pixmap subFileName=" << subFileName; mime = KMimeType::findByUrl( sf, 0, d->m_bIsLocalUrl ); } bool isLocalUrl; KUrl url = mostLocalUrl(isLocalUrl); QPixmap p = KIconLoader::global()->loadMimeTypeIcon( mime->iconName( url ), K3Icon::Desktop, _size, _state ); //kDebug() << "finding pixmap for " << url.url() << " : " << mime->name(); if (p.isNull()) kWarning() << "Pixmap not found for mimetype " << d->m_pMimeType->name(); return p; } bool KFileItem::isReadable() const { /* struct passwd * user = getpwuid( geteuid() ); bool isMyFile = (QString::fromLocal8Bit(user->pw_name) == d->m_user); // This gets ugly for the group.... // Maybe we want a static QString for the user and a static QStringList // for the groups... then we need to handle the deletion properly... */ if (d->m_permissions != KFileItem::Unknown) { // No read permission at all if ( !(S_IRUSR & d->m_permissions) && !(S_IRGRP & d->m_permissions) && !(S_IROTH & d->m_permissions) ) return false; // Read permissions for all: save a stat call if ( (S_IRUSR|S_IRGRP|S_IROTH) & d->m_permissions ) return true; } // Or if we can't read it [using ::access()] - not network transparent if ( d->m_bIsLocalUrl && ::access( QFile::encodeName(d->m_url.path()), R_OK ) == -1 ) return false; return true; } bool KFileItem::isWritable() const { /* struct passwd * user = getpwuid( geteuid() ); bool isMyFile = (QString::fromLocal8Bit(user->pw_name) == d->m_user); // This gets ugly for the group.... // Maybe we want a static QString for the user and a static QStringList // for the groups... then we need to handle the deletion properly... */ if (d->m_permissions != KFileItem::Unknown) { // No write permission at all if ( !(S_IWUSR & d->m_permissions) && !(S_IWGRP & d->m_permissions) && !(S_IWOTH & d->m_permissions) ) return false; } // Or if we can't read it [using ::access()] - not network transparent if ( d->m_bIsLocalUrl && ::access( QFile::encodeName(d->m_url.path()), W_OK ) == -1 ) return false; return true; } bool KFileItem::isHidden() const { if ( d->m_hidden != KFileItemPrivate::Auto ) return d->m_hidden == KFileItemPrivate::Hidden; if ( !d->m_url.isEmpty() ) return d->m_url.fileName()[0] == '.'; else // should never happen return d->m_strName[0] == '.'; } bool KFileItem::isDir() const { if ( d->m_fileMode == KFileItem::Unknown ) { kDebug() << " KFileItem::isDir can't say -> false "; return false; // can't say for sure, so no } return (S_ISDIR(d->m_fileMode)); /* if (!S_ISDIR(d->m_fileMode)) { if (d->m_url.isLocalFile()) { KMimeType::Ptr ptr=KMimeType::findByUrl(d->m_url,0,true,true); if ((ptr!=0) && (ptr->is("directory/inode"))) return true; } return false } else return true;*/ } bool KFileItem::isFile() const { return !isDir(); } bool KFileItem::acceptsDrops() const { // A directory ? if ( S_ISDIR( mode() ) ) { return isWritable(); } // But only local .desktop files and executables if ( !d->m_bIsLocalUrl ) return false; if ( mimetype() == "application/x-desktop") return true; // Executable, shell script ... ? if ( QFileInfo(d->m_url.path()).isExecutable() ) return true; return false; } QString KFileItem::getStatusBarInfo() const { QString text = d->m_strText; const QString comment = mimeComment(); if ( d->m_bLink ) { text += ' '; if ( comment.isEmpty() ) text += i18n ( "(Symbolic Link to %1)", linkDest() ); else text += i18n("(%1, Link to %2)", comment, linkDest()); } else if ( S_ISREG( d->m_fileMode ) ) { text += QString(" (%1, %2)").arg( comment, KIO::convertSize( size() ) ); } else { text += QString(" (%1)").arg( comment ); } return text; } QString KFileItem::getToolTipText(int maxcount) const { // we can return QString() if no tool tip should be shown QString tip; KFileMetaInfo info = metaInfo(); // the font tags are a workaround for the fact that the tool tip gets // screwed if the color scheme uses white as default text color const char* start = ""; const char* mid = ""; const char* end = ""; tip = ""; tip += start + i18n("Name:") + mid + text() + end; tip += start + i18n("Type:") + mid; QString type = Qt::escape(mimeComment()); if ( d->m_bLink ) { tip += i18n("Link to %1 (%2)", linkDest(), type) + end; } else tip += type + end; if ( !S_ISDIR ( d->m_fileMode ) ) tip += start + i18n("Size:") + mid + QString("%1 (%2)").arg(KIO::convertSize(size())) .arg(KGlobal::locale()->formatNumber(size(), 0)) + end; tip += start + i18n("Modified:") + mid + timeString( KFileItem::ModificationTime ) + end #ifndef Q_WS_WIN //TODO: show win32-specific permissions +start + i18n("Owner:") + mid + user() + " - " + group() + end + start + i18n("Permissions:") + mid + permissionsString() + end #endif ; if (info.isValid()) { tip += ""; QStringList keys = info.preferredKeys(); // now the rest QStringList::Iterator it = keys.begin(); for (int count = 0; count 50) { s.truncate(47); s.append("..."); } if ( !s.isEmpty() ) { count++; tip += start + Qt::escape( item.name() ) + ':' + mid + Qt::escape( s ) + end; } } } } tip += "
                    
"; //kDebug() << "making this the tool tip rich text:\n"; //kDebug() << tip; return tip; } void KFileItem::run( QWidget* parentWidget ) const { (void) new KRun( d->m_url, parentWidget, d->m_fileMode, d->m_bIsLocalUrl ); } bool KFileItem::cmp( const KFileItem & item ) const { return d->cmp(*item.d); } bool KFileItem::operator==(const KFileItem& other) const { // is this enough? return d == other.d; } bool KFileItem::operator!=(const KFileItem& other) const { return d != other.d; } void KFileItem::setUDSEntry( const KIO::UDSEntry& _entry, const KUrl& _url, bool _delayedMimeTypes, bool _urlIsDirectory ) { d->m_entry = _entry; d->m_url = _url; d->m_strName.clear(); d->m_strText.clear(); d->m_strLowerCaseName.clear(); d->m_pMimeType = 0; d->m_fileMode = KFileItem::Unknown; d->m_permissions = KFileItem::Unknown; d->m_bMarked = false; d->m_bLink = false; d->m_bIsLocalUrl = _url.isLocalFile(); d->m_bMimeTypeKnown = false; d->m_hidden = KFileItemPrivate::Auto; d->m_guessedMimeType.clear(); d->m_metaInfo = KFileMetaInfo(); d->m_delayedMimeTypes = _delayedMimeTypes; d->readUDSEntry( _urlIsDirectory ); d->init(); } KFileItem::operator QVariant() const { return qVariantFromValue(*this); } void KFileItem::setExtraData( const void *key, void *value ) { if ( !key ) return; d->m_extra.insert( key, value ); // replaces the value of key if already there } const void * KFileItem::extraData( const void *key ) const { return d->m_extra.value( key, 0 ); } void KFileItem::removeExtraData( const void *key ) { d->m_extra.remove( key ); } QString KFileItem::permissionsString() const { if (d->m_access.isNull() && d->m_permissions != KFileItem::Unknown) d->m_access = d->parsePermissions( d->m_permissions ); return d->m_access; } // check if we need to cache this QString KFileItem::timeString( FileTimes which ) const { return KGlobal::locale()->formatDateTime( d->time(which) ); } QString KFileItem::timeString( unsigned int which ) const { switch (which) { case KIO::UDSEntry::UDS_ACCESS_TIME: return timeString(AccessTime); case KIO::UDSEntry::UDS_CREATION_TIME: return timeString(CreationTime); case KIO::UDSEntry::UDS_MODIFICATION_TIME: default: return timeString(ModificationTime); } } void KFileItem::setMetaInfo( const KFileMetaInfo & info ) const { d->m_metaInfo = info; } KFileMetaInfo KFileItem::metaInfo(bool autoget, int) const { if (autoget && !d->m_metaInfo.isValid()) { bool isLocalUrl; KUrl url(mostLocalUrl(isLocalUrl)); if (KGlobalSettings::showFilePreview(url)) d->m_metaInfo = KFileMetaInfo(url);//, mimetype() ); } return d->m_metaInfo; } void KFileItem::assign( const KFileItem & item ) { *this = item; } KUrl KFileItem::mostLocalUrl(bool &local) const { QString local_path = localPath(); if ( !local_path.isEmpty() ) { local = true; KUrl url; url.setPath(local_path); return url; } else { local = d->m_bIsLocalUrl; return d->m_url; } } QDataStream & operator<< ( QDataStream & s, const KFileItem & a ) { // We don't need to save/restore anything that refresh() invalidates, // since that means we can re-determine those by ourselves. s << a.d->m_url; s << a.d->m_strName; s << a.d->m_strText; return s; } QDataStream & operator>> ( QDataStream & s, KFileItem & a ) { s >> a.d->m_url; s >> a.d->m_strName; s >> a.d->m_strText; a.d->m_bIsLocalUrl = a.d->m_url.isLocalFile(); a.d->m_bMimeTypeKnown = false; a.refresh(); return s; } KUrl KFileItem::url() const { return d->m_url; } mode_t KFileItem::permissions() const { return d->m_permissions; } mode_t KFileItem::mode() const { return d->m_fileMode; } bool KFileItem::isLink() const { return d->m_bLink; } bool KFileItem::isLocalFile() const { return d->m_bIsLocalUrl; } QString KFileItem::text() const { return d->m_strText; } QString KFileItem::name( bool lowerCase ) const { if ( !lowerCase ) return d->m_strName; else if ( d->m_strLowerCaseName.isNull() ) d->m_strLowerCaseName = d->m_strName.toLower(); return d->m_strLowerCaseName; } /* * Mimetype handling. * * Initial state: m_pMimeType = 0. * When mimeTypePtr() is called first: fast mimetype determination, * might either find an accurate mimetype (-> Final state), otherwise we * set m_pMimeType but not m_bMimeTypeKnown (-> Intermediate state) * Intermediate state: determineMimeType() does the real determination -> Final state. * * If delayedMimeTypes isn't set, then we always go to the Final state directly. */ KMimeType::Ptr KFileItem::mimeTypePtr() const { if (!d->m_pMimeType) { // On-demand fast (but not always accurate) mimetype determination Q_ASSERT(!d->m_url.isEmpty()); bool isLocalUrl; KUrl url = mostLocalUrl(isLocalUrl); int accuracy; d->m_pMimeType = KMimeType::findByUrl( url, d->m_fileMode, isLocalUrl, // use fast mode if delayed mimetype determination can refine it later d->m_delayedMimeTypes, &accuracy ); // If we used the "fast mode" (no sniffing), and we didn't get a perfect (extension-based) match, // then determineMimeType will be able to do better. const bool canDoBetter = d->m_delayedMimeTypes && accuracy < 100; //kDebug() << "finding mimetype for" << url() << ":" << m_pMimeType->name(); d->m_bMimeTypeKnown = !canDoBetter; } return d->m_pMimeType; } KIO::UDSEntry KFileItem::entry() const { return d->m_entry; } bool KFileItem::isMarked() const { return d->m_bMarked; } void KFileItem::mark() { d->m_bMarked = true; } void KFileItem::unmark() { d->m_bMarked = false; } KFileItem& KFileItem::operator=(const KFileItem& other) { d = other.d; return *this; } bool KFileItem::isNull() const { return d == 0; } +KFileItemList::KFileItemList() +{ +} -KFileItem* KFileItemList::findByName( const QString& fileName ) const +KFileItemList::KFileItemList( const QList &items ) + : QList( items ) +{ +} + +KFileItem KFileItemList::findByName( const QString& fileName ) const { const_iterator it = begin(); const const_iterator itend = end(); for ( ; it != itend ; ++it ) { - if ( (*it)->name() == fileName ) { + if ( (*it).name() == fileName ) { return *it; } } - return 0; + return KFileItem(); } -KFileItem* KFileItemList::findByUrl( const KUrl& url ) const { +KFileItem KFileItemList::findByUrl( const KUrl& url ) const { const_iterator it = begin(); const const_iterator itend = end(); for ( ; it != itend ; ++it ) { - if ( (*it)->url() == url ) { + if ( (*it).url() == url ) { return *it; } } - return 0; + return KFileItem(); } KUrl::List KFileItemList::urlList() const { KUrl::List lst; const_iterator it = begin(); const const_iterator itend = end(); for ( ; it != itend ; ++it ) { - lst.append( (*it)->url() ); + lst.append( (*it).url() ); } return lst; } diff --git a/kio/kio/kfileitem.h b/kio/kio/kfileitem.h index 7fd4a0908b..21d43ee354 100644 --- a/kio/kio/kfileitem.h +++ b/kio/kio/kfileitem.h @@ -1,595 +1,604 @@ /* This file is part of the KDE project Copyright (C) 1999-2006 David Faure 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 KFILEITEM_H #define KFILEITEM_H #include #include #include #include #include #include #include #include #include class KFileItemPrivate; /** * A KFileItem is a generic class to handle a file, local or remote. * In particular, it makes it easier to handle the result of KIO::listDir * (UDSEntry isn't very friendly to use). * It includes many file attributes such as mimetype, icon, text, mode, link... * * KFileItem is implicitly shared, i.e. it can be used as a value and copied around at almost no cost. */ class KIO_EXPORT KFileItem { public: enum { Unknown = (mode_t) - 1 }; /** * The timestamps associated with a file. * - ModificationTime: the time the file's contents were last modified * - AccessTime: the time the file was last accessed (last read or written to) * - CreationTime: the time the file was created */ enum FileTimes { // warning: don't change without looking at the Private class ModificationTime = 0, AccessTime = 1, CreationTime = 2 //ChangeTime }; /** * Null KFileItem. Doesn't represent any file, only exists for convenience. * * NOTE KDE 4.0 when porting from KFileItem* to KFileItem&: * '(KFileItem*)a==0' becomes '(KFileItem)a.isNull()' */ KFileItem(); /** * Creates an item representing a file, from a UDSEntry. * This is the preferred constructor when using KIO::listDir(). * * @param entry the KIO entry used to get the file, contains info about it * @param url the file url * @param delayedMimeTypes specifies if the mimetype of the given * URL should be determined immediately or on demand. * See the bool delayedMimeTypes in the KDirLister constructor. * @param urlIsDirectory specifies if the url is just the directory of the * fileitem and the filename from the UDSEntry should be used. */ KFileItem( const KIO::UDSEntry& entry, const KUrl& url, bool delayedMimeTypes = false, bool urlIsDirectory = false ); /** * Creates an item representing a file, from all the necessary info for it. * @param mode the file mode (according to stat() (e.g. S_IFDIR...) * Set to KFileItem::Unknown if unknown. For local files, KFileItem will use stat(). * @param permissions the access permissions * If you set both the mode and the permissions, you save a ::stat() for * local files. * Set to KFileItem::Unknown if you don't know the mode or the permission. * @param url the file url * * @param delayedMimeTypes specify if the mimetype of the given URL * should be determined immediately or on demand */ KFileItem( mode_t mode, mode_t permissions, const KUrl& url, bool delayedMimeTypes = false ); /** * Creates an item representing a file, for which the mimetype is already known. * @param url the file url * @param mimeType the name of the file's mimetype * @param mode the mode (S_IFDIR...) */ KFileItem( const KUrl &url, const QString &mimeType, mode_t mode ); /** * Copy constructor */ KFileItem(const KFileItem& other); /** * Assignment operator */ KFileItem& operator=(const KFileItem& other); /** * Destructs the KFileItem. Extra data set via setExtraData() * is not deleted. */ ~KFileItem(); /** * Throw away and re-read (for local files) all information about the file. * This is called when the _file_ changes. */ void refresh(); /** * Re-reads mimetype information. * This is called when the mimetype database changes. */ void refreshMimeType(); /** * Returns the url of the file. * @return the url of the file */ KUrl url() const; /** * Sets the item's URL. Do not call unless you know what you are doing! * (used for example when an item got renamed). * @param url the item's URL */ void setUrl( const KUrl &url ); /** * Sets the item's name (i.e. the filename). * This is automatically done by setUrl, to set the name from the URL's fileName(). * This method is provided for some special cases like relative paths as names (KFindPart) * @param name the item's name */ void setName( const QString &name ); /** * Returns the permissions of the file (stat.st_mode containing only permissions). * @return the permissions of the file */ mode_t permissions() const; /** * Returns the access permissions for the file as a string. * @return the access persmission as string */ QString permissionsString() const; /** * Tells if the file has extended access level information ( Posix ACL ) * @return true if the file has extend ACL information or false if it hasn't */ bool hasExtendedACL() const; /** * Returns the access control list for the file. * @return the access control list as a KACL */ KACL ACL() const; /** * Returns the default access control list for the directory. * @return the default access control list as a KACL */ KACL defaultACL() const; /** * Returns the file type (stat.st_mode containing only S_IFDIR, S_IFLNK, ...). * @return the file type */ mode_t mode() const; /** * Returns the owner of the file. * @return the file's owner */ QString user() const; /** * Returns the group of the file. * @return the file's group */ QString group() const; /** * Returns true if this item represents a link in the UNIX sense of * a link. * @return true if the file is a link */ bool isLink() const; /** * Returns true if this item represents a directory. * @return true if the item is a directory */ bool isDir() const; /** * Returns true if this item represents a file (and not a a directory) * @return true if the item is a file */ bool isFile() const; /** * Checks whether the file or directory is readable. In some cases * (remote files), we may return true even though it can't be read. * @return true if the file can be read - more precisely, * false if we know for sure it can't */ bool isReadable() const; /** * Checks whether the file or directory is writable. In some cases * (remote files), we may return true even though it can't be written to. * @return true if the file or directory can be written to - more precisely, * false if we know for sure it can't */ bool isWritable() const; /** * Checks whether the file is hidden. * @return true if the file is hidden. */ bool isHidden() const; /** * Returns the link destination if isLink() == true. * @return the link destination. QString() if the item is not a link */ QString linkDest() const; /** * Returns the local path if isLocalFile() == true or the KIO item has * a UDS_LOCAL_PATH atom. * @return the item local path, or QString() if not known */ QString localPath() const; /** * Returns the size of the file, if known. * @return the file size, or 0 if not known */ KIO::filesize_t size() const; /** * Requests the modification, access or creation time, depending on @p which. * @param which the timestamp * @return the time asked for, (time_t)0 if not available * @see timeString() */ KDateTime time( FileTimes which ) const; KDE_DEPRECATED time_t time( unsigned int which ) const; /** * Requests the modification, access or creation time as a string, depending * on @p which. * @param which the timestamp * @returns a formatted string of the requested time. * @see time */ QString timeString( FileTimes which = ModificationTime ) const; KDE_DEPRECATED QString timeString( unsigned int which) const; /** * Returns true if the file is a local file. * @return true if the file is local, false otherwise */ bool isLocalFile() const; /** * Returns the text of the file item. * It's not exactly the filename since some decoding happens ('%2F'->'/'). * @return the text of the file item */ QString text() const; /** * Return the name of the file item (without a path). * Similar to text(), but unencoded, i.e. the original name. * @param lowerCase if true, the name will be returned in lower case, * which is useful to speed up sorting by name, case insensitively. * @return the file's name */ QString name( bool lowerCase = false ) const; /** * Returns the mimetype of the file item. * If @p delayedMimeTypes was used in the constructor, this will determine * the mimetype first. Equivalent to determineMimeType()->name() * @return the mime type of the file */ QString mimetype() const; /** * Returns the mimetype of the file item. * If delayedMimeTypes was used in the constructor, this will determine * the mimetype first. * @return the mime type */ KMimeType::Ptr determineMimeType() const; /** * Returns the currently known mimetype of the file item. * This will not try to determine the mimetype if unknown. * @return the known mime type */ KMimeType::Ptr mimeTypePtr() const; /** * @return true if we have determined the mimetype of this file already, * i.e. if determineMimeType() will be fast. Otherwise it will have to * find what the mimetype is, which is a possibly slow operation; usually * this is delayed until necessary. */ bool isMimeTypeKnown() const; /** * Returns the descriptive comment for this mime type, or * the mime type itself if none is present. * @return the mime type description, or the mime type itself */ QString mimeComment() const; /** * Returns the full path name to the icon that represents * this mime type. * @return iconName the name of the file's icon */ QString iconName() const; /** * Returns a pixmap representing the file. * @param _size Size for the pixmap in pixels. Zero will return the * globally configured default size. * @param _state The state of the icon: K3Icon::DefaultState, * K3Icon::ActiveState or K3Icon::DisabledState. * @return the pixmap */ QPixmap pixmap( int _size, int _state=0 ) const; /** * Returns the overlays (bitfield of K3Icon::*Overlay flags) that are used * for this item's pixmap. Overlays are used to show for example, whether * a file can be modified. * @return the overlays of the pixmap */ QStringList overlays() const; /** * Returns the string to be displayed in the statusbar, * e.g. when the mouse is over this item * @return the status bar information */ QString getStatusBarInfo() const; /** * Returns the string to be displayed in the tool tip when the mouse * is over this item. This may load a plugin to determine additional * information specific to the mimetype of the file. * * @param maxcount the maximum number of entries shown * @return the tool tip string */ QString getToolTipText(int maxcount = 6) const; /** * Returns true if files can be dropped over this item. * Contrary to popular belief, not only dirs will return true :) * Executables, .desktop files, will do so as well. * @return true if you can drop files over the item * * @deprecated This logic is application-dependent, the behavior described above * mostly makes sense for file managers only. * KDirModel has setDropsAllowed for similar (but configurable) logic. */ KDE_DEPRECATED bool acceptsDrops() const; /** * Let's "KRun" this file ! * (e.g. when file is clicked or double-clicked or return is pressed) */ void run( QWidget* parentWidget = 0 ) const; /** * Returns the UDS entry. Used by the tree view to access all details * by position. * @return the UDS entry */ KIO::UDSEntry entry() const; /** * Used when updating a directory. marked == seen when refreshing. * @return true if the file item is marked */ bool isMarked() const; /** * Marks the item. * @see isMarked() */ void mark(); /** * Unmarks the item. * @see isMarked() */ void unmark(); /** * Somewhat like a comparison operator, but more explicit, * and it can detect that two kfileitems are equal even when they do * not share the same internal pointer - e.g. when KDirLister compares * fileitems after listing a directory again, to detect changes. * @param item the item to compare * @return true if all values are equal */ bool cmp( const KFileItem & item ) const; bool operator==(const KFileItem& other) const; bool operator!=(const KFileItem& other) const; /** * Converts this KFileItem to a QVariant, this allows to use KFileItem * in QVariant() constructor */ operator QVariant() const; /** * This allows to associate some "extra" data to a KFileItem. As one * KFileItem can be used by several objects (often views) which all need * to add some data, you have to use a key to reference your extra data * within the KFileItem. * * That way a KFileItem can hold and provide access to all those views * separately. * * I.e. a KFileIconView that associates a KFileIconViewItem (an item suitable * for use with QIconView) does * * \code * kfileItem->setExtraData( this, iconViewItem ); * \endcode * * and can later access the iconViewItem by doing * * \code * KFileIconViewItem *iconViewItem = static_cast( kfileItem->extraData( this )); * \endcode * * This is usually more efficient then having every view associate data to * items by using a separate QDict or QMap. * * Note: you have to remove and destroy the data you associated yourself * when you don't need it anymore! * * @param key the key of the extra data * @param value the value of the extra data * @see extraData * @see removeExtraData * * @deprecated use model/view (KDirModel) and you won't need this anymore */ KDE_DEPRECATED void setExtraData( const void *key, void *value ); /** * Retrieves the extra data with the given @p key. * @param key the key of the extra data * @return the extra data associated to an item with @p key via * setExtraData. 0L if nothing was associated with @p key. * @see extraData * * @deprecated use model/view (KDirModel) and you won't need this anymore */ KDE_DEPRECATED const void * extraData( const void *key ) const; /** * Removes the extra data associated with an item via @p key. * @param key the key of the extra data to remove * * @deprecated use model/view (KDirModel) and you won't need this anymore */ KDE_DEPRECATED void removeExtraData( const void *key ); /** * Sets the metainfo of this item to @p info. * * Made const to avoid deep copy. * @param info the new meta info */ void setMetaInfo( const KFileMetaInfo & info ) const; /** * Returns the metainfo of this item. * @param autoget if true, the metainfo will automatically be created * @param what ignored */ KFileMetaInfo metaInfo(bool autoget = true, int what = KFileMetaInfo::Fastest) const; /** * @deprecated simply use '=' */ KDE_DEPRECATED void assign( const KFileItem & item ); /** * Reinitialize KFileItem with a new UDSEntry. * * Note: extra-data set with setExtraData() is not changed or deleted, so * be careful what you do! * * KDirListerCache uses it to save new/delete calls by updating existing * items that are otherwise not needed anymore. * * @param entry the UDSEntry to assign to this KFileItem * @param url the file url * @param delayedMimeTypes specifies if the mimetype of the given * URL should be determined immediately or on demand * @param urlIsDirectory specifies if the url is just the directory of the * fileitem and the filename from the UDSEntry should be used. * * @deprecated why not just create another KFileItem and use operator=, * now that it's a value class? */ KDE_DEPRECATED void setUDSEntry( const KIO::UDSEntry& entry, const KUrl& url, bool delayedMimeTypes = false, bool urlIsDirectory = false ); /** * Tries to give a local URL for this file item if possible. * The given boolean indicates if the returned url is local or not. */ KUrl mostLocalUrl(bool &local) const; // KDE4 TODO: bool* local = 0 /** * Return true if default-constructed */ bool isNull() const; private: QSharedDataPointer d; private: KIO_EXPORT friend QDataStream & operator<< ( QDataStream & s, const KFileItem & a ); KIO_EXPORT friend QDataStream & operator>> ( QDataStream & s, KFileItem & a ); }; Q_DECLARE_METATYPE(KFileItem) +Q_CORE_EXPORT uint qHash(const QString &key); +inline uint qHash(const KFileItem& item){ return qHash(item.url().url()); } + /** * List of KFileItems * This class adds a few helper methods to QList */ -class KIO_EXPORT KFileItemList : public QList // TODO QList +class KIO_EXPORT KFileItemList : public QList { public: + /// Creates an empty list of file items + KFileItemList(); + + /// Creates a new KFileItemList from a QList of file @p items + KFileItemList( const QList &items ); + /// Find a KFileItem by name and return it. /// @return the item with the given name, or 0 if none was found - KFileItem* findByName( const QString& fileName ) const; + KFileItem findByName( const QString& fileName ) const; /// Find a KFileItem by URL and return it. /// @return the item with the given URL, or 0 if none was found - KFileItem* findByUrl( const KUrl& url ) const; + KFileItem findByUrl( const KUrl& url ) const; /// @return the list of URLs that those items represent KUrl::List urlList() const; }; KIO_EXPORT QDataStream & operator<< ( QDataStream & s, const KFileItem & a ); KIO_EXPORT QDataStream & operator>> ( QDataStream & s, KFileItem & a ); #endif diff --git a/kio/tests/kdirlistertest.cpp b/kio/tests/kdirlistertest.cpp index a4ddaeecab..fd291c46b3 100644 --- a/kio/tests/kdirlistertest.cpp +++ b/kio/tests/kdirlistertest.cpp @@ -1,303 +1,304 @@ /* This file is part of the KDE project Copyright (C) 2007 David Faure 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 "kdirlistertest.h" #include "kdirlistertest.moc" #include #include QTEST_KDEMAIN( KDirListerTest, NoGUI ) #include #include "kiotesthelper.h" #include void KDirListerTest::initTestCase() { s_referenceTimeStamp = QDateTime::currentDateTime().addSecs( -120 ); // 2 minutes ago // Create test data: /* * PATH/toplevelfile_1 * PATH/toplevelfile_2 * PATH/toplevelfile_3 * PATH/subdir * PATH/subdir/testfile * PATH/subdir/subsubdir * PATH/subdir/subsubdir/testfile */ const QString path = m_tempDir.name(); createTestFile(path+"toplevelfile_1"); createTestFile(path+"toplevelfile_2"); createTestFile(path+"toplevelfile_3"); createTestDirectory(path+"subdir"); createTestDirectory(path+"subdir/subsubdir"); // Hmmpf. qRegisterMetaType(); } void KDirListerTest::testOpenUrl() { m_items.clear(); const QString path = m_tempDir.name(); QSignalSpy spyStarted(&m_dirLister, SIGNAL(started(KUrl))); QSignalSpy spyClear(&m_dirLister, SIGNAL(clear())); QSignalSpy spyClearKUrl(&m_dirLister, SIGNAL(clear(KUrl))); QSignalSpy spyCompleted(&m_dirLister, SIGNAL(completed())); QSignalSpy spyCompletedKUrl(&m_dirLister, SIGNAL(completed(KUrl))); QSignalSpy spyCanceled(&m_dirLister, SIGNAL(canceled())); QSignalSpy spyCanceledKUrl(&m_dirLister, SIGNAL(canceled(KUrl))); connect(&m_dirLister, SIGNAL(newItems(KFileItemList)), this, SLOT(slotNewItems(KFileItemList))); // The call to openUrl itself, emits started m_dirLister.openUrl(KUrl(path), false, false /*no reload*/); QCOMPARE(spyStarted.count(), 1); QCOMPARE(spyCompleted.count(), 0); QCOMPARE(spyCompletedKUrl.count(), 0); QCOMPARE(spyCanceled.count(), 0); QCOMPARE(spyCanceledKUrl.count(), 0); QCOMPARE(spyClear.count(), 1); QCOMPARE(spyClearKUrl.count(), 0); QCOMPARE(m_items.count(), 0); // then wait for completed qDebug("waiting for completed"); connect(&m_dirLister, SIGNAL(completed()), this, SLOT(exitLoop())); enterLoop(); QCOMPARE(spyStarted.count(), 1); QCOMPARE(spyCompleted.count(), 1); QCOMPARE(spyCompletedKUrl.count(), 1); QCOMPARE(spyCanceled.count(), 0); QCOMPARE(spyCanceledKUrl.count(), 0); QCOMPARE(spyClear.count(), 1); QCOMPARE(spyClearKUrl.count(), 0); QCOMPARE(m_items.count(), 4); disconnect(&m_dirLister, 0, this, 0); } // This test assumes testOpenUrl was run before. So m_dirLister is holding the items already. void KDirListerTest::testOpenUrlFromCache() { m_items.clear(); const QString path = m_tempDir.name(); QSignalSpy spyStarted(&m_dirLister, SIGNAL(started(KUrl))); QSignalSpy spyClear(&m_dirLister, SIGNAL(clear())); QSignalSpy spyClearKUrl(&m_dirLister, SIGNAL(clear(KUrl))); QSignalSpy spyCompleted(&m_dirLister, SIGNAL(completed())); QSignalSpy spyCompletedKUrl(&m_dirLister, SIGNAL(completed(KUrl))); QSignalSpy spyCanceled(&m_dirLister, SIGNAL(canceled())); QSignalSpy spyCanceledKUrl(&m_dirLister, SIGNAL(canceled(KUrl))); connect(&m_dirLister, SIGNAL(newItems(KFileItemList)), this, SLOT(slotNewItems(KFileItemList))); // The call to openUrl itself, emits started, the items, and completed, since it's all in the cache // ### Maybe we should get rid of this behavior difference... m_dirLister.openUrl(KUrl(path), false, false /*no reload*/); // TODO turn those bools into an enum QCOMPARE(spyStarted.count(), 1); QCOMPARE(spyCompleted.count(), 1); QCOMPARE(spyCompletedKUrl.count(), 1); QCOMPARE(spyCanceled.count(), 0); QCOMPARE(spyCanceledKUrl.count(), 0); QCOMPARE(spyClear.count(), 1); QCOMPARE(spyClearKUrl.count(), 0); QCOMPARE(m_items.count(), 4); // Get into the case where another dirlister is holding the items { m_items.clear(); KDirLister secondDirLister; QSignalSpy spyStarted(&secondDirLister, SIGNAL(started(KUrl))); QSignalSpy spyClear(&secondDirLister, SIGNAL(clear())); QSignalSpy spyClearKUrl(&secondDirLister, SIGNAL(clear(KUrl))); QSignalSpy spyCompleted(&secondDirLister, SIGNAL(completed())); QSignalSpy spyCompletedKUrl(&secondDirLister, SIGNAL(completed(KUrl))); QSignalSpy spyCanceled(&secondDirLister, SIGNAL(canceled())); QSignalSpy spyCanceledKUrl(&secondDirLister, SIGNAL(canceled(KUrl))); connect(&secondDirLister, SIGNAL(newItems(KFileItemList)), this, SLOT(slotNewItems(KFileItemList))); secondDirLister.openUrl(KUrl(path), false, false /*no reload*/); // TODO turn those bools into an enum QCOMPARE(spyStarted.count(), 1); QCOMPARE(spyCompleted.count(), 1); QCOMPARE(spyCompletedKUrl.count(), 1); QCOMPARE(spyCanceled.count(), 0); QCOMPARE(spyCanceledKUrl.count(), 0); QCOMPARE(spyClear.count(), 1); QCOMPARE(spyClearKUrl.count(), 0); QCOMPARE(m_items.count(), 4); } disconnect(&m_dirLister, 0, this, 0); } // This test assumes testOpenUrl was run before. So m_dirLister is holding the items already. void KDirListerTest::testNewItems() { QCOMPARE(m_items.count(), 4); const QString path = m_tempDir.name(); QSignalSpy spyStarted(&m_dirLister, SIGNAL(started(KUrl))); QSignalSpy spyClear(&m_dirLister, SIGNAL(clear())); QSignalSpy spyClearKUrl(&m_dirLister, SIGNAL(clear(KUrl))); QSignalSpy spyCompleted(&m_dirLister, SIGNAL(completed())); QSignalSpy spyCompletedKUrl(&m_dirLister, SIGNAL(completed(KUrl))); QSignalSpy spyCanceled(&m_dirLister, SIGNAL(canceled())); QSignalSpy spyCanceledKUrl(&m_dirLister, SIGNAL(canceled(KUrl))); connect(&m_dirLister, SIGNAL(newItems(KFileItemList)), this, SLOT(slotNewItems(KFileItemList))); QTest::qWait(1000); // We need a 1s timestamp difference on the dir, otherwise FAM won't notice anything. kDebug() << "Creating new file"; QFile file(path+"toplevelfile_new"); QVERIFY(file.open(QIODevice::WriteOnly)); file.write("foo"); file.close(); int numTries = 0; // Give time for KDirWatch to notify us while (m_items.count() == 4) { QVERIFY(++numTries < 10); QTest::qWait(200); } //kDebug() << "numTries=" << numTries; QCOMPARE(m_items.count(), 5); QCOMPARE(spyStarted.count(), 1); // Updates call started QCOMPARE(spyCompleted.count(), 1); // and completed QCOMPARE(spyCompletedKUrl.count(), 1); QCOMPARE(spyCanceled.count(), 0); QCOMPARE(spyCanceledKUrl.count(), 0); QCOMPARE(spyClear.count(), 0); QCOMPARE(spyClearKUrl.count(), 0); disconnect(&m_dirLister, 0, this, 0); } // This test assumes testOpenUrl was run before. So m_dirLister is holding the items already. void KDirListerTest::testRefreshItems() { m_refreshedItems.clear(); const QString path = m_tempDir.name(); QSignalSpy spyStarted(&m_dirLister, SIGNAL(started(KUrl))); QSignalSpy spyClear(&m_dirLister, SIGNAL(clear())); QSignalSpy spyClearKUrl(&m_dirLister, SIGNAL(clear(KUrl))); QSignalSpy spyCompleted(&m_dirLister, SIGNAL(completed())); QSignalSpy spyCompletedKUrl(&m_dirLister, SIGNAL(completed(KUrl))); QSignalSpy spyCanceled(&m_dirLister, SIGNAL(canceled())); QSignalSpy spyCanceledKUrl(&m_dirLister, SIGNAL(canceled(KUrl))); - connect(&m_dirLister, SIGNAL(refreshItems(KFileItemList)), this, SLOT(slotRefreshItems(KFileItemList))); + connect(&m_dirLister, SIGNAL(refreshItems(const QList > &)), + this, SLOT(slotRefreshItems(const QList > &))); QFile file(path+"toplevelfile_2"); QVERIFY(file.open(QIODevice::Append)); file.write("foo"); file.close(); // KDirWatch doesn't make this work when using FAM :( //KDirWatch::self()->setDirty(path+"toplevelfile_2"); // hack KDirWatch::self()->setDirty(path); // with only the file, we get into the new fast path that doesn't even emit started... int numTries = 0; // Give time for KDirWatch to notify us while (m_refreshedItems.isEmpty()) { QVERIFY(++numTries < 10); QTest::qWait(200); } QCOMPARE(spyStarted.count(), 1); // Updates (to a directory) call started... QCOMPARE(spyCompleted.count(), 1); // and completed QCOMPARE(spyCompletedKUrl.count(), 1); QCOMPARE(spyCanceled.count(), 0); QCOMPARE(spyCanceledKUrl.count(), 0); QCOMPARE(spyClear.count(), 0); QCOMPARE(spyClearKUrl.count(), 0); QCOMPARE(m_refreshedItems.count(), 1); - KFileItem* newItem = m_refreshedItems.first(); - QCOMPARE(newItem->size(), KIO::filesize_t(11 /*Hello world*/ + 3 /*foo*/)); + QPair entry = m_refreshedItems.first(); + QCOMPARE(entry.second.size(), KIO::filesize_t(11 /*Hello world*/ + 3 /*foo*/)); disconnect(&m_dirLister, 0, this, 0); } // This test assumes testOpenUrl was run before. So m_dirLister is holding the items already. void KDirListerTest::testDeleteItem() { const QString path = m_tempDir.name(); - qRegisterMetaType("KFileItem*"); - QSignalSpy spyDeleteItem(&m_dirLister, SIGNAL(deleteItem(KFileItem))); - connect(&m_dirLister, SIGNAL(deleteItem(KFileItem)), this, SLOT(exitLoop())); + qRegisterMetaType("KFileItem"); + QSignalSpy spyDeleteItem(&m_dirLister, SIGNAL(deleteItem(const KFileItem&))); + connect(&m_dirLister, SIGNAL(deleteItem(const KFileItem&)), this, SLOT(exitLoop())); //kDebug() << "Removing " << path+"toplevelfile_1"; QFile::remove(path+"toplevelfile_1"); // the remove() doesn't always trigger kdirwatch in stat mode, if this all happens in the same second KDirWatch::self()->setDirty(path); if (spyDeleteItem.count() == 0) { qDebug("waiting for deleteItem"); enterLoop(); } // OK now kdirlister told us the file was deleted, let's try a re-listing m_items.clear(); connect(&m_dirLister, SIGNAL(newItems(KFileItemList)), this, SLOT(slotNewItems(KFileItemList))); m_dirLister.openUrl(KUrl(path), false, false /*no reload*/); QCOMPARE(m_items.count(), 4); disconnect(&m_dirLister, 0, this, 0); } void KDirListerTest::testOpenAndStop() { m_items.clear(); const QString path = "/"; // better not use a directory that we already listed! QSignalSpy spyStarted(&m_dirLister, SIGNAL(started(KUrl))); QSignalSpy spyClear(&m_dirLister, SIGNAL(clear())); QSignalSpy spyClearKUrl(&m_dirLister, SIGNAL(clear(KUrl))); QSignalSpy spyCompleted(&m_dirLister, SIGNAL(completed())); QSignalSpy spyCompletedKUrl(&m_dirLister, SIGNAL(completed(KUrl))); QSignalSpy spyCanceled(&m_dirLister, SIGNAL(canceled())); QSignalSpy spyCanceledKUrl(&m_dirLister, SIGNAL(canceled(KUrl))); connect(&m_dirLister, SIGNAL(newItems(KFileItemList)), this, SLOT(slotNewItems(KFileItemList))); m_dirLister.openUrl(KUrl(path), false, false /*no reload*/); m_dirLister.stop(); // we should also test stop(KUrl(path))... QCOMPARE(spyStarted.count(), 1); // The call to openUrl itself, emits started QCOMPARE(spyCompleted.count(), 0); // we had time to stop before the job even started QCOMPARE(spyCompletedKUrl.count(), 0); QCOMPARE(spyCanceled.count(), 1); QCOMPARE(spyCanceledKUrl.count(), 1); QCOMPARE(spyClear.count(), 1); QCOMPARE(spyClearKUrl.count(), 0); QCOMPARE(m_items.count(), 0); // we had time to stop before the job even started disconnect(&m_dirLister, 0, this, 0); } void KDirListerTest::enterLoop() { qDebug("enterLoop"); m_eventLoop.exec(QEventLoop::ExcludeUserInputEvents); } void KDirListerTest::exitLoop() { qDebug("exitLoop"); m_eventLoop.quit(); } void KDirListerTest::slotNewItems(const KFileItemList& lst) { m_items += lst; } -void KDirListerTest::slotRefreshItems(const KFileItemList& lst) +void KDirListerTest::slotRefreshItems(const QList > & lst) { m_refreshedItems += lst; } diff --git a/kio/tests/kdirlistertest.h b/kio/tests/kdirlistertest.h index f9ad0e55ea..74ea55d80f 100644 --- a/kio/tests/kdirlistertest.h +++ b/kio/tests/kdirlistertest.h @@ -1,59 +1,59 @@ /* This file is part of the KDE project Copyright (C) 2007 David Faure 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 KDIRLISTERTEST_H #define KDIRLISTERTEST_H #include #include #include #include #include class KDirListerTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void testOpenUrl(); void testOpenUrlFromCache(); void testNewItems(); void testRefreshItems(); void testDeleteItem(); void testOpenAndStop(); // TODO test filtering // TODO test concurrent listings from two KDirListers protected Q_SLOTS: // 'more private than private slots' - i.e. not seen by qtestlib void exitLoop(); void slotNewItems(const KFileItemList&); - void slotRefreshItems(const KFileItemList&); + void slotRefreshItems(const QList >&); private: void enterLoop(); private: QEventLoop m_eventLoop; KTempDir m_tempDir; KDirLister m_dirLister; KFileItemList m_items; - KFileItemList m_refreshedItems; + QList > m_refreshedItems; }; #endif diff --git a/kio/tests/kdirlistertest_gui.cpp b/kio/tests/kdirlistertest_gui.cpp index a4dd2bd52c..d81fb57c04 100644 --- a/kio/tests/kdirlistertest_gui.cpp +++ b/kio/tests/kdirlistertest_gui.cpp @@ -1,167 +1,167 @@ /* This file is part of the KDE desktop environment Copyright (C) 2001, 2002 Michael Brade 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 #include #include #include #include #include #include #include "kdirlistertest_gui.h" #include KDirListerTest::KDirListerTest( QWidget *parent ) : QWidget( parent ) { lister = new KDirLister(this); debug = new PrintSignals; QVBoxLayout* layout = new QVBoxLayout( this ); QPushButton* startH = new QPushButton( "Start listing Home", this ); QPushButton* startR= new QPushButton( "Start listing Root", this ); QPushButton* test = new QPushButton( "Many", this ); QPushButton* startT = new QPushButton( "tarfile", this ); layout->addWidget( startH ); layout->addWidget( startR ); layout->addWidget( startT ); layout->addWidget( test ); resize( layout->sizeHint() ); connect( startR, SIGNAL( clicked() ), SLOT( startRoot() ) ); connect( startH, SIGNAL( clicked() ), SLOT( startHome() ) ); connect( startT, SIGNAL( clicked() ), SLOT( startTar() ) ); connect( test, SIGNAL( clicked() ), SLOT( test() ) ); connect( lister, SIGNAL( started( const KUrl & ) ), debug, SLOT( started( const KUrl & ) ) ); connect( lister, SIGNAL( completed() ), debug, SLOT( completed() ) ); connect( lister, SIGNAL( completed( const KUrl & ) ), debug, SLOT( completed( const KUrl & ) ) ); connect( lister, SIGNAL( canceled() ), debug, SLOT( canceled() ) ); connect( lister, SIGNAL( canceled( const KUrl & ) ), debug, SLOT( canceled( const KUrl & ) ) ); connect( lister, SIGNAL( redirection( const KUrl & ) ), debug, SLOT( redirection( const KUrl & ) ) ); connect( lister, SIGNAL( redirection( const KUrl &, const KUrl & ) ), debug, SLOT( redirection( const KUrl &, const KUrl & ) ) ); connect( lister, SIGNAL( clear() ), debug, SLOT( clear() ) ); connect( lister, SIGNAL( newItems( const KFileItemList & ) ), debug, SLOT( newItems( const KFileItemList & ) ) ); connect( lister, SIGNAL( itemsFilteredByMime( const KFileItemList & ) ), debug, SLOT( itemsFilteredByMime( const KFileItemList & ) ) ); - connect( lister, SIGNAL( deleteItem( KFileItem * ) ), - debug, SLOT( deleteItem( KFileItem * ) ) ); - connect( lister, SIGNAL( refreshItems( const KFileItemList & ) ), - debug, SLOT( refreshItems( const KFileItemList & ) ) ); + connect( lister, SIGNAL( deleteItem( const KFileItem & ) ), + debug, SLOT( deleteItem( const KFileItem & ) ) ); + connect( lister, SIGNAL( refreshItems( const QList > & ) ), + debug, SLOT( refreshItems( const QList > & ) ) ); connect( lister, SIGNAL( infoMessage( const QString& ) ), debug, SLOT( infoMessage( const QString& ) ) ); connect( lister, SIGNAL( percent( int ) ), debug, SLOT( percent( int ) ) ); connect( lister, SIGNAL( totalSize( KIO::filesize_t ) ), debug, SLOT( totalSize( KIO::filesize_t ) ) ); connect( lister, SIGNAL( processedSize( KIO::filesize_t ) ), debug, SLOT( processedSize( KIO::filesize_t ) ) ); connect( lister, SIGNAL( speed( int ) ), debug, SLOT( speed( int ) ) ); connect( lister, SIGNAL( completed() ), this, SLOT( completed() ) ); } KDirListerTest::~KDirListerTest() { } void KDirListerTest::startHome() { KUrl home( QDir::homePath() ); lister->openUrl( home, false, false ); // lister->stop(); } void KDirListerTest::startRoot() { KUrl root( QDir::rootPath() ); lister->openUrl( root, true, true ); // lister->stop( root ); } void KDirListerTest::startTar() { KUrl root( QDir::homePath()+"/aclocal_1.tgz" ); lister->openUrl( root, true, true ); // lister->stop( root ); } void KDirListerTest::test() { KUrl home( QDir::homePath() ); KUrl root( QDir::rootPath() ); #ifdef Q_WS_WIN lister->openUrl( home, true, false ); lister->openUrl( root, true, true ); #else /* lister->openUrl( home, true, false ); lister->openUrl( root, true, true ); lister->openUrl( KUrl("file:/etc"), true, true ); lister->openUrl( root, true, true ); lister->openUrl( KUrl("file:/dev"), true, true ); lister->openUrl( KUrl("file:/tmp"), true, true ); lister->openUrl( KUrl("file:/usr/include"), true, true ); lister->updateDirectory( KUrl("file:/usr/include") ); lister->updateDirectory( KUrl("file:/usr/include") ); lister->openUrl( KUrl("file:/usr/"), true, true ); */ lister->openUrl( KUrl("file:/dev"), true, true ); #endif } void KDirListerTest::completed() { if ( lister->url().path() == QDir::rootPath() ) { - KFileItem* item = lister->findByUrl( KUrl( QDir::tempPath() ) ); - if ( item ) - kDebug() << "Found " << QDir::tempPath() << ": " << item; + const KFileItem item = lister->findByUrl( KUrl( QDir::tempPath() ) ); + if ( !item.isNull() ) + kDebug() << "Found " << QDir::tempPath() << ": " << item.name(); else kWarning() << QDir::tempPath() << " not found! Bug in findByURL?"; } } int main ( int argc, char *argv[] ) { KCmdLineArgs::init( argc, argv, "kdirlistertest", 0, ki18n("kdirlistertest"), 0); KApplication app; KDirListerTest *test = new KDirListerTest( 0 ); test->show(); return app.exec(); } #include "kdirlistertest_gui.moc" diff --git a/kio/tests/kdirlistertest_gui.h b/kio/tests/kdirlistertest_gui.h index 15ba9a1c45..73b5d8c478 100644 --- a/kio/tests/kdirlistertest_gui.h +++ b/kio/tests/kdirlistertest_gui.h @@ -1,121 +1,121 @@ /* This file is part of the KDE desktop environment Copyright (C) 2001, 2002 Michael Brade 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 _KDIRLISTERTEST_GUI_H_ #define _KDIRLISTERTEST_GUI_H_ #include #include #include #include #include using namespace std; class PrintSignals : public QObject { Q_OBJECT public: PrintSignals() : QObject() { } public Q_SLOTS: void started( const KUrl &url ) { cout << "*** started( " << url.url().toLocal8Bit().data() << " )" << endl; } void canceled() { cout << "canceled()" << endl; } void canceled( const KUrl& url ) { cout << "*** canceled( " << url.prettyUrl().toLocal8Bit().data() << " )" << endl; } void completed() { cout << "*** completed()" << endl; } void completed( const KUrl& url ) { cout << "*** completed( " << url.prettyUrl().toLocal8Bit().data() << " )" << endl; } void redirection( const KUrl& url ) { cout << "*** redirection( " << url.prettyUrl().toLocal8Bit().data() << " )" << endl; } void redirection( const KUrl& src, const KUrl& dest ) { cout << "*** redirection( " << src.prettyUrl().toLocal8Bit().data() << ", " << dest.prettyUrl().toLocal8Bit().data() << " )" << endl; } void clear() { cout << "*** clear()" << endl; } void newItems( const KFileItemList& items ) { cout << "*** newItems: " << endl; KFileItemList::const_iterator it, itEnd = items.constEnd(); for ( it = items.constBegin() ; it != itEnd ; ++it ) - cout << (*it) << " " << (*it)->name().toLocal8Bit().data() << endl; + cout << (*it).name().toLocal8Bit().data() << endl; } - void deleteItem( KFileItem* item ) + void deleteItem( const KFileItem& item ) { - cout << "*** deleteItem: " << item->url().prettyUrl().toLocal8Bit().data() << endl; + cout << "*** deleteItem: " << item.url().prettyUrl().toLocal8Bit().data() << endl; } void itemsFilteredByMime( const KFileItemList& ) { cout << "*** itemsFilteredByMime: " << endl; // TODO } - void refreshItems( const KFileItemList& ) + void refreshItems( const QList >& ) { cout << "*** refreshItems: " << endl; // TODO } void infoMessage( const QString& msg ) { cout << "*** infoMessage: " << msg.toLocal8Bit().data() << endl; } void percent( int percent ) { cout << "*** percent: " << percent << endl; } void totalSize( KIO::filesize_t size ) { cout << "*** totalSize: " << (long)size << endl; } void processedSize( KIO::filesize_t size ) { cout << "*** processedSize: " << (long)size << endl; } void speed( int bytes_per_second ) { cout << "*** speed: " << bytes_per_second << endl; } }; class KDirListerTest : public QWidget { Q_OBJECT public: KDirListerTest( QWidget *parent=0 ); ~KDirListerTest(); public Q_SLOTS: void startRoot(); void startHome(); void startTar(); void test(); void completed(); private: KDirLister *lister; PrintSignals *debug; }; #endif