diff options
Diffstat (limited to 'kio/kfile')
99 files changed, 33538 insertions, 0 deletions
diff --git a/kio/kfile/ChangeLog b/kio/kfile/ChangeLog new file mode 100644 index 000000000..bb43f16da --- /dev/null +++ b/kio/kfile/ChangeLog @@ -0,0 +1,725 @@ +Sat Feb 26 00:26:55 2000 Carsten Pfeiffer <[email protected]> + + * kdiroperator.cpp: + lottsa changes, e.g. action handling more clear now. + fixed completed item not clearning the previous selection + + * kfileviewitem.cpp: + fixed unreadable pixmap not shown, when files were deleted + + * kfiledialog.cpp (selectedURLs): + re-enabled the hack to support multi-selection, until we have something + better + + * kcombiview.*: + forward the sorting to the right view (or should it apply to the dir- + view as well?) Sort of broken tho. + +Sun Feb 20 01:50:44 2000 Carsten Pfeiffer <[email protected]> + + * kdiroperator.*, kfiledialog.* (saveConfig): + implemented loading, saving and applying configuration + + * kfiledialog.cpp (setURL): + KDirComboBox is now a combo for recent directories as well as the + root-dir, home-dir and Desktop-dir. + Recent dirs will be saved in kdeglobals. + +Fri Feb 18 23:35:04 2000 Carsten Pfeiffer <[email protected]> + + * kfilefilter.cpp (eventFilter): + intercept Key_Return and Key_Enter in the filter-combo and emit + filterChanged instead of letting the dialog close + +Thu Feb 17 19:09:54 2000 Carsten Pfeiffer <[email protected]> + + * kfiledialog.{cpp,h} (KDirComboBox): + Added KDirComboBox and replaced the directory combobox with it. It + even does something now :) Items need to be indented tho. + + * kfilereader.cpp, kdiroperator.{cpp,h}: + fixed showHidden default + +Tue Feb 15 14:21:41 2000 Carsten Pfeiffer <[email protected]> + + * kfile.h (class KFile): + added a small class that contains all the enums now. + cleaned up the enums (hope not too many apps will be broken) + added flags for "Existing files only" and "local files only" + + * all views (setSelected()): + replaced highlightItem(item) with setSelected(item, bool) which makes + it more suitable and more consistent with Qt. + added selectionChanged() method (necessary for multiselection) + + * kfileview.*: + added invertSelection() + + * kfiledialog.cpp: + made it work with multiselection + added static methods for multiselection + added getOpenURL(), getOpenURLs and getSaveURL() + + * kdiroperator.cpp (setSorting): + added setSorting() and sorting() to keep sorting when switching views + a few cosmetic and KAction changes + +Sun Feb 13 00:45:12 2000 Carsten Pfeiffer <[email protected]> + + * kfiledialog.cpp (completion): + small fix: completion and auto-directory following works also + without protocol-part in the location. + +Sat Feb 12 15:30:40 2000 Carsten Pfeiffer <[email protected]> + + * kfileview.h: + made setSorting() and sortReversed() virtual so that subclasses + can intercept that to show sorting order + + * kfiledetailview.cpp,h (setSortIndicator): + enable header-clicking again to set the sorting + +Fri Feb 11 12:17:59 2000 Carsten Pfeiffer <[email protected]> + + * kfiledialog.cpp (KFileDialog): + fixed filefilter not being applied in constructor + +Thu Feb 10 17:06:36 2000 Carsten Pfeiffer <[email protected]> + + * kdiroperator.cpp (connectView): + - Now that KToggleAction is fixed, I can commit the new stuff: + Offer Actions for all the common functionality, i.e. sorting, setting + the view, home(), cdUp(), back, forward, etc. + All actions are exposed through a QActionCollection + + BTW, I'd really like to have a way to change the state of an action + without it calling the associated slot. For now I use blockSignals() + (thanks, Simon), but this sucks. + + - renamed fileList to fileView (that was an old relic of KDE 1) + + * kfiledialog.*: + - make use of the new Actions and fill the toolbar again (up, back, + forward, reload, home). + + - the combo in the toolbar is going to change, it does nothing now + (only shows the Root and Desktop dirs with a nice icon). + +Thu Feb 10 12:59:29 2000 Carsten Pfeiffer <[email protected]> + + * kdiroperator.cpp (insertNewFiles): + aahhh, finally fixed that infinite loop in KFileView::mergeLists + clear the view before calling view->addItemList( currentContents ); + + * kfilereader.cpp (setURL): + don't disable dirWatch update (setAutoUpdate) when setting a remote URL + +Fri Feb 4 12:42:37 2000 Carsten Pfeiffer <[email protected]> + + * kfiledetailview.cpp (insertItem): + - don't let QListView sort the items + I disabled clicking at the headers for now, will fix that later + - don't flicker on mimetype-pixmap update + +Thu Feb 3 12:15:16 2000 Carsten Pfeiffer <[email protected]> + + * kfileview.h + all views where necessary + added selectAll() + added isSelected( const KFileViewItem * ) + added const KFileViewItemList * selectedItems() + added const KFileViewItemList * items() + + spiced up documentation of KFileView a bit + +Sun Jan 30 22:20:14 2000 Carsten Pfeiffer <[email protected]> + + * kfileviewitem.cpp (pixmap): + fixed some issues for different pixmap sizes + + * kfileiconview.{h,cpp} (setPixmapSize): + added configurability for different pixmap sizes + +Sun Jan 30 16:49:12 2000 Carsten Pfeiffer <[email protected]> + + * kfileview.h + all views: + A view can now have a name (setViewName() and viewName()) + This is useful to differentiate views and to display the names in a + popupmenu for switching between views. + +Sun Jan 30 12:41:04 2000 Werner Trobin <[email protected]> + The preview mode works again. It's very similar to the Qt-2.1 QFD + preview mode - but it's not totally the same. There are some rough + edges which have to be ironed out, but it works :) + For further information, see kfilepreview.* + Note: kpreview and so on are obsolete (IMHO) + +Sat Jan 29 15:33:37 2000 Carsten Pfeiffer <[email protected]> + + Time to add some changelogs again, all the recent hard work of the + restructuring is only in CVS-log... + + * kfileview.cpp (compareItems): + - added support to disable sorting directories first (QDir::DirsFirst) + - added support to sort by date and to sort reversed + - removed unused findCompletion method + - sort case insensitive by default + - some optimization for mergeList + - fixed infinite loop in mergeLists, when the lists are the same + (I think another one is still there, but I'm tired now) + - changed setSortMode stuff: Switching-mode replaced with + sortReversed(). The enum Switching will be removed, soon. + - made setSortMode public + + * kfileviewitem.cpp: + - added time_t mTime() to enable sorting by modification time + + * kdiroperator.cpp: + - offer sorting options in popupmenu + - use checkAccess before creating directories. I guess this will again + change when the new kio will be used, tho. + - show progress widget at the bottom (otherwise the header of e.g. + the detail-view would be covered). + - Added LGPL copyright header + + * kfilewidget.cpp (connectSignals): + - connect to all signals of KDirOperator + - directly connect SIGNAL to SIGNAL -> removed slotFileHighlighted etc. + - fixed some sorting/merging bugs + + * {kfiledialog,kdiroperator}.{cpp,h}; + - support for disabling chdir (some apps may not want KFileDialog / + KFileReader change the working directory under their feet) + - Added LGPL header in cpp-file + + * kfilereader.cpp (stat): + - fixed: some special files were recognized as directories + + * kfstest.cpp: + - added test for KFileWidget (widget -> KFileWidget, + diroperator -> KDirOperator) + +Sun Oct 31 00:56:23 1999 Carsten Pfeiffer <[email protected]> + + * kfileinfo.cpp (readUserInfo): Don't call getpwnam() and getgrgid() + all the time over and over again! This opens and parses /etc/passwd + or /etc/group with every call! + Now we load /etc/passwd and /etc/group once and store the interesting + stuff in static QMaps, which need to be freed with KFileInfo::cleanup() + cleanup() is called from KFileBaseDialog's destructor. + + This speeds up KFileDialog a LOT! + +Sat Oct 23 01:55:00 1999 Carsten Pfeiffer <[email protected]> + + * kfiledetaillist.cpp (key): hopefully implemented correct mapping + from KFileInfo to QListViewItem and back. I had a hard time convincing + QListView that KFileInfoContents knows best where to insert an item + Now the detaillist is even usable :) + Fixed a problem with selection and highlighting + Disable clicking on listview headers that can't be used for sorting + + * kfileinfocontents.cpp (setSorting): call insertItem() with a correct + (is it?) index, instead of -1 + +Thu Oct 21 23:18:54 1999 Carsten Pfeiffer <[email protected]> + + * kfiledialog.cpp (init): removed "[development only] from tooltip + (sorry to break the translations, but this HAD to go) + + * don't update anything when leaving the configure-dialog with Cancel + +Wed Oct 20 15:07:16 1999 Carsten Pfeiffer <[email protected]> + + * kfileinfo.cpp,h: implemented KFileInfo::filePath() and fixed + KFileInfo::absURL() not being set in some cases + + * kdir.cpp: fixed a buglet in the path (double slashes) + + * kfiledetaillist.cpp (KFileDetailList): improved selecting files + (single click, double click, Return-key) + but the mapping from QListViewItem to KFileInfo is still quite broken + +1999-06-09 Woohyun JANG <[email protected]> + + * kdir.cpp: used QString::local8Bit() instead of QString::ascii() + for file names and directory names. + + * kfiledialog.cpp: used QStringList instead of QStrIList. + +1999-01-14 Stephan Kulow <[email protected]> + + * kfiledialog.cpp: fixed some GUI problems and moved all default + configurations into config-kfile.h. + Changed some default values, so that users that never looked into + the config dialog gets a nice suprise with 1.1 ;-) + + * kfiledialog.cpp: changed selectedFile to return decoded path instead + of encoded one + +Thu Jan 7 23:14:39 1999 Carsten Pfeiffer <[email protected]> + + * kfilesimpleview.cpp (keyPressEvent): fixed segfault on keypress, + when there were no files at all in the list + +1998-12-19 Stephan Kulow <[email protected]> + + * kfiledialog.cpp: fixing an ugly bug when "." is used as argument + +Mon Dec 14 23:00:41 1998 Carsten Pfeiffer <[email protected]> + + * kfilebookmark.cpp: renamed class KBookmark to KFileBookmark to + avoid problems with KFM's KBookmark class. Renamed bookmark.* to + kfilebookmark.*, too and changed all occurrences of bookmark.* to + kfilebookmark.* (especially all the .po-files) + +Wed Dec 2 15:59:13 1998 Carsten Pfeiffer <[email protected]> + + * kfilesimpleview.cpp: Fixed some more keyboard navigation bugs. + Added method isColCompletelyVisible( int col ), now you can scroll + perfectly to make items completely visible. + Moreover, in resizeEvent() the number of columns was not calculated + correctly in a special case. + And the currently selected item is rehighlighted correctly after + resizing, now. + +1998-10-12 Jochen K�pper <[email protected]> + + * kfiledialog.cpp (okPressed): Changed okPressed to store the correct + filename before leaving the dialog. + +1998-06-07 Stephan Kulow <[email protected]> + + * kfilesimpleview.cpp: added some checks to prevent division with + zero using the latest qt snapshots + + * kfilesimpleview.cpp: added a repaint call after a resize + + * kfiledialog.cpp: new experimental button order + + * kfiledialog.cpp: added lastDirectory to remember the last visited + directory for next open + +1998-05-24 Stephan Kulow <[email protected]> + + * kfiledialog.cpp: use setSelection also for the initial filename + given + + * kfiledialog.cpp: introduced KFileFilter to make an abstraction + for the used filter widget. Currently only QComboBox is supported, + but this may change in the future + +1998-05-20 Stephan Kulow <[email protected]> + + * kfiledialog.cpp: changed the accelerator for completion + to CTRL-T + + * kfiledialog.cpp: fixed the setSelection() feature + +1998-05-19 Stephan Kulow <[email protected]> + + * kfiledialog.h: added setSelection + +1998-05-18 Stephan Kulow <[email protected]> + + * kfiledialog.cpp: bugfixes + +1998-05-15 Stephan Kulow <[email protected]> + + * kfileinfocontents.cpp: some more changes and speed ups + (caching and some other little things) + +1998-05-14 Stephan Kulow <[email protected]> + + * kfileinfocontents.cpp: added addItemList + + * kfileinfocontents.h: introduced KFileInfoContentsSignaler + + * kfileinfocontents.cpp: some more speed improvment + +1998-05-10 Stephan Kulow <[email protected]> + + * kfiledialog.cpp: tried to speed up the refresh + +1998-04-17 Stephan Kulow <[email protected]> + + * kfiledetaillist.cpp: implemented the date field + + * kfiledetaillist.cpp: made the columns wider + + * kfileinfocontents.cpp: use the new icons by Mark + +Thu Apr 16 10:51:24 1998 Daniel Grana <[email protected]> + + * kfiledialog.*: some small fixes concerning preview + + * kfileinfocontents.cpp: fixed sorting bug in preview + + * kfilepreview.*: small bug fixes + + * kpreview.*: small bug fixes + + * added some documentation + +1998-04-15 Stephan Kulow <[email protected]> + + * kfilepreview.cpp: use a list box instead of simple view + + * kfiledialog.cpp: fixed an ugly bug + +1998-04-14 Stephan Kulow <[email protected]> + + * kfiledialogconf.cpp: removed the width/height sliders + + * kfiledialog.cpp: save the width and height on exit + + * kfiledialogconf.cpp: added more guys to the about box + + * kfiledialog.h: removed init*Children. They were useless + + * kfiledialog.cpp: set the default size to a useful value + +Mon Apr 6 17:30:18 1998 Daniel Grana <[email protected]> + + * kfilepreview.*: restructured previewing, one can now easily + use custom previewers, dynamically + + * kpreview.*: changes for allowing custom previewers, two previewers + are hardcoded so far + + * kfiledialog.*: changes changes for allowing custom previewers + + * kfstest.cpp: changes to preview mode + + * xview.*: QimageIO module allowing the visual-schnauzer generated + images to be loaded + +1998-04-06 Stephan Kulow <[email protected]> + + * debug.h: added debugC. An empty macro to hide debug output + +Thu Apr 2 19:39:37 1998 Daniel Grana <[email protected]> + + * kpreview.*: widget which for now shows some info about a + file/folder along with the first 20-30 lines of it + + * kfilepreview.*: added a new view which has a preview of any text + file in the right part + + * kfstest.cpp: added the new mode "preview" which shows the above + view + +1998-03-30 Stephan Kulow <[email protected]> + + * kfilesimpleview.cpp: fixed highlightning + + * kfiledialog.cpp: some fixes for the dir selection + + * kfiledialog.h: Moved KFileDialog into KFileBaseDialog and made + KFileDialog and KDirDialog a derived class of KFileBaseDialog to + make this virtual functions work + +Mon Mar 30 17:53:20 1998 Daniel Grana <[email protected]> + + * kcombiview.cpp: bug fixes for completion & corresponding + highlighting + + * kfiledialog.cpp: small bug fix, too much copying into location + + * kfileinfocontents.cpp: completion slightly remodeled, should + work in all views now + + * kfileinfocontents.h: changed nameList to case insensitive + list + +1998-03-28 Stephan Kulow <[email protected]> + + * kfiledialog.cpp: changed the filter separator to \n. + This looks nicer in the source code of the call + +Sat Mar 28 14:49:00 1998 Stephan Kulow <[email protected]> + + * kfiledialog.cpp: changed the meaning of the dirName argument + + * kfiledialog.h: added getShowFilter + +Thu Mar 26 12:47:42 1998 Stephan Kulow <[email protected]> + + * kfilesimpleview.cpp: improved scrolling in simple view + + * kfileinfocontents.cpp: add a / after a found dir + + * kfiledialog.cpp: fixed bug for !showFilter + +Wed Mar 25 18:39:09 1998 Daniel Grana <[email protected]> + + * kfileinfocontents.cpp: completion now working + + * kcombiview.cpp: changed behavior for completion, it + now highlights completed directory and file + +Tue Mar 24 16:08:46 1998 Daniel Grana <[email protected]> + + * kfileinfocontents.cpp: sorting now fully working + + * kfiledialog.cpp: modifications for sorting in the on the + fly reconfiguration + + * kfileinfocontents.*: modification to the constructor + to pass along the sorting + + * kfiledetaillist.*: modification to the constructor + to pass along the sorting + + * kcombiview.*: modification to the constructor + to pass along the sorting + + * kfilesimpleview.*: modification to the constructor + to pass along the sorting + +Tue Mar 24 10:45:15 1998 Daniel Grana <[email protected]> + + * kfileinfocontents.cpp: sorting fixed, the feature of keeping + directories grouped is still missing though + +Mon Mar 23 22:59:18 1998 Stephan Kulow <[email protected]> + + * kfiledetaillist.h: added acceptsFiles() and acceptsDirs() to + make dirs-only views useful + + * kfileinfocontents.cpp: OK, completion is back again, but + currently not working, because the code is missing ;-) + +Mon Mar 23 00:08:02 1998 Stephan Kulow <[email protected]> + + * kfiledialog.cpp: moved all GM related things into initGUI() to + make recreation possible + +Sun Mar 22 00:22:46 1998 Stephan Kulow <[email protected]> + + * kfiledialog.cpp: use KShellProcess now + + * kfiledialog.h: added virtual function initFileList to made + KFileDialog customable + + * kfiledialog.cpp: show a combo box, in case more filters are given + + * kfiledialog.cpp: some bug fixes. I can't get the sorting to work + + * Makefile.am: install some toolbar pixmaps + + * kfiledetailview.cpp: added pixmaps to the detail view + + * Kfiledialog.cpp: made KComboView customable through virtual + functions + + * kcombiview.cpp: took out the completion for now, added + kcombiview and some little changes in setCurrentItem + + * kdirlistbox.cpp: fixed the use of single click + + * kdir.h: moved the header files a little bit to remove + some dependencies + + * kdirlistbox.cpp: KDirListBox is now a KFileInfoContents too + + * kfiledialog.cpp: another change in the API. It uses now QSignal. + I didn't liked the old way + +1998-03-21 Stephan Kulow <[email protected]> + + * kfiledialog.cpp: implemented mixDirsAndFiles. Need some work and currently + only supported by the simple view + +Sat Mar 21 01:00:48 1998 Stephan Kulow <[email protected]> + + * kfilesimpleview.cpp: added pixmaps to indicate access on the file + + * kfilesimpleview.cpp: improved keyboard navigation + + * kfilesimpleview.cpp: first almost working simple view + + * kfilesimpleview.cpp: started implementing a simple view. Needs + still some work + + +Fri Mar 20 22:42:31 1998 Stephan Kulow <[email protected]> + + * kfileinfocontents.h: bugfixes + + * kfileinfo.cpp: KFileInfo is no longer a derived class of + QFileInfo. This should reduce memory use too + + * kfileinfocontents.h: moved the actual sorting in + KFileInfoContents. Derived classes are for viewing only + + * kfiledialog.h: fixed some header files locations + +1998-03-20 Stephan Kulow <[email protected]> + + * kfileinfo.cpp: show lockedfolder.xpm for folders, that are not + allowed to enter or read + + * kfiledialog.cpp: bug fixes + +Fri Mar 20 13:10:11 1998 Daniel Grana <[email protected]> + * kfilgdialog.*, remodeled the configuration dialog to reflect the + current possible settings + * kfiledetailList.cpp: added the PageUp PageDown navigation + +1998-03-19 Stephan Kulow <[email protected]> + + * kfiledialog.cpp: fixed the forward/back back reported by + Stefan Tafener + +1998-03-18 Stephan Kulow <[email protected]> + + * kfiledialog.cpp: set the initial focus to the line edit + + * kfiledialog.cpp: use kapp->invokeHTMLHelp now + + * kfiledialog.h: removed treeList, since it's not implemented + +Wed Mar 18 02:56:32 1998 Stephan Kulow <[email protected]> + + * kfiledetaillist.cpp: fixed sorting again + +1998-03-17 Stephan Kulow <[email protected]> + + * added a virtual class KInfoListContent to make an abstraction + for the file contents. Currently there is just one implementation + KFileDetailList + +1998-03-16 Stephan Kulow <[email protected]> + + * kfiledialog.cpp: don't accept the first entry of the combo + box as a file name + + * kfiledialog.cpp: added an accelerator for completion, since + KCombo no longer emits such a thing (currently CTRL-A) + + * kdir.cpp: disable parent button in / + + * kfiledialog.cpp: fixed layout of mkdir dialog + + * kdir.cpp: use currentDir instead of homeDir as default + + * kfiledialog.cpp: added member acceptOnlyExisting and set it + for now to always false. I guess, we need an extra parameter for this + + * kfiledialog.cpp: changed dirList and fileList to fix the + focus handling + + * kfileinfolistwidget.cpp: added focus handling + + * kfileinfolistwidget.cpp: added keyevent handling to handle + cursor and enter + + * kfiledialog.cpp: changed the filter edit to a QLineEdit, since + we don't need the completion, but the tabing for focus changes + +Mon Mar 16 11:36:07 1998 Daniel Grana <[email protected]> + * added create directory, pops up a modal dialog, should add + a mkdir method to kdir in near future + +Mon Mar 16 20:04:00 1998 Martin Jones <[email protected]> + * Added booktoken.* to remove dependancy on khtmlw and jscript + +Thu Mar 12 09:32:06 1998 Daniel Grana <[email protected]> + * worked on the dir and file completion, should do both now, + text in the location box will be added as much as possible + (right now it's the lowest denominator of dir&file&location-text) + * added sorting for the detailed list, so far only name and size + sorting implemented + * added single click selection for directories + * added a status line, which shows the number of directories and + files showed + +Thu Mar 12 00:36:05 1998 Stephan Kulow <[email protected]> + + * kfiledialog.cpp: made a library out of the file selector + * kfiledialog.cpp: added an extra parameter acceptURLs to seperate + between getOpenFileName and getOpenFileURL + +Sun Feb 15 23:13:47 1998 Richard Moore <[email protected]> + + * More work on the bookmarks - they should work properly now, you + must create the directory ~/.kde/share/apps/kdeui to store the + bookmarks in. + + * Regenerated docs + +Thu Feb 12 17:27:51 1998 Stephan Kulow <[email protected]> + + * kfileinfo.cpp: added determination of group and other things to + be display the correct values in the kfileinfolistwidget + +Thu Feb 12 16:01:44 1998 Stephan Kulow <[email protected]> + + * kfiledialog.cpp: removed the #ifdef code. Now the combo box for + the path is the only option + +Tue Feb 10 01:09:16 1998 Richard Moore <[email protected]> + + * Added details widget - this is currently selected by a config + file entry, but it there should be a toolbar button. Many changes + to kfiledialog to allow the switch (need an abstract fileview class). + +Fri Feb 6 18:08:14 1998 Stephan Kulow <[email protected]> + + * kfiledialog.cpp: replaced the location lineedit with an combo + box. Currently configurable with a compiler define. + +Fri Feb 6 17:07:26 1998 Stephan Kulow <[email protected]> + + * kfiledialog.cpp: fixed the en- and decoding of URLs. Now it's + possible to move into directories called "sdasd#sdsd" for example + +Sat Jan 24 17:18:10 1998 Mario Weilguni <[email protected]> + + * fixed a bug in kdir.cpp/parsePermissions() + + * implemented error handling for KFM URL errors + +Tue Jan 20 00:51:55 1998 Stephan Kulow <[email protected]> + + * kfiledialog.cpp: some fixes to make kfm support more robust + +Mon Jan 19 01:10:11 1998 Stephan Kulow <[email protected]> + + * kdir.cpp: re-added ftp support + + * kfiledialog.cpp: take care of the case, when the user enters a + complete filename + + * kdir.cpp: added isReadable() to indicate, that the dir is not + correct + + * kfiledialog.cpp: played a little bit with the geometry management + + +Sun Jan 18 15:00:06 1998 Stephan Kulow <[email protected]> + + * kfiledialog.cpp: - back/forward work now as expected + - show the correct filter + - the combo box works now as expected + + * kdir.cpp: check if the directory is correct (for local + files). If not, go back to the old value + + * kfiledialog.cpp: - disable parent button, when in root + - treat the case, that the URL ends with "/" + - strip white spaces out of the location text + + * kfileinfo.cpp: don't insert broken links + + * kfiledialog.cpp: just set the dir, if it's different + from the already set one + +Sun Jan 18 11:53:32 1998 Mario Weilguni <[email protected]> + + * symbolic links to subdirectories are now correctly reported as + directories + + * symbolic links are show as italic text + + * The toolbar button "Home" works now as expected diff --git a/kio/kfile/Makefile.am b/kio/kfile/Makefile.am new file mode 100644 index 000000000..a63306189 --- /dev/null +++ b/kio/kfile/Makefile.am @@ -0,0 +1,78 @@ +# This file is part of the KDE libraries +# Copyright (C) 1997 Stephan Kulow ([email protected]) + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. + +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this library; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. + + +INCLUDES= -I$(srcdir)/../libltdl/ -I$(top_srcdir) -I$(top_srcdir)/kdefx \ + -I$(top_builddir)/kio/kio -I$(top_srcdir)/kio/bookmarks $(all_includes) $(LIBART_CFLAGS) + +noinst_LTLIBRARIES = libkfile.la + +METASOURCES = AUTO + +#SUBDIRS = . acl_prop_page + +include_HEADERS = kfiledialog.h kencodingfiledialog.h\ + kdiroperator.h kfileview.h kfilefiltercombo.h \ + kfiledetailview.h kcombiview.h kdiskfreesp.h \ + kfileiconview.h krecentdocument.h \ + kurlrequester.h kfilepreview.h kfile.h \ + kurlcombobox.h kurlrequesterdlg.h kopenwith.h kpropsdlg.h \ + kicondialog.h kdirsize.h kpreviewwidgetbase.h kimagefilepreview.h kfilesharedlg.h \ + kfiletreeview.h kfiletreeviewitem.h kfiletreebranch.h \ + kdirselectdialog.h kurlbar.h kpropertiesdialog.h knotifydialog.h \ + kcustommenueditor.h knotifywidgetbase.h + +noinst_HEADERS = config-kfile.h krecentdirs.h kmetaprops.h \ + kfilebookmarkhandler.h kfilemetainfowidget.h kopenwith_p.h \ + kfilespeedbar.h kpreviewprops.h kacleditwidget.h kacleditwidget_p.h images.h + +libkfile_la_SOURCES = \ + kfilefiltercombo.cpp \ + kfileview.cpp kfileiconview.cpp \ + krecentdocument.cpp kfiledialog.cpp kdiroperator.cpp \ + kfiledetailview.cpp kcombiview.cpp kurlrequester.cpp \ + kfilepreview.cpp kurlcombobox.cpp kurlrequesterdlg.cpp \ + kopenwith.cpp kpropertiesdialog.cpp kicondialog.cpp kdirsize.cpp \ + krecentdirs.cpp kdiskfreesp.cpp kimagefilepreview.cpp kfilesharedlg.cpp \ + kurlbar.cpp kmetaprops.cpp kpreviewprops.cpp \ + kfiletreeview.cpp kfiletreeviewitem.cpp kfiletreebranch.cpp \ + kdirselectdialog.cpp kfilebookmarkhandler.cpp \ + kfilemetainfowidget.cpp kcustommenueditor.cpp knotifywidgetbase.ui \ + knotifydialog.cpp kfilespeedbar.cpp kpreviewwidgetbase.cpp \ + kfilemetapreview.cpp kpropertiesdesktopbase.ui \ + kpropertiesdesktopadvbase.ui kpropertiesmimetypebase.ui \ + kencodingfiledialog.cpp kacleditwidget.cpp + +libkfile_la_COMPILE_FIRST = $(srcdir)/../kio/kdirnotify_stub.h + +EXTRA_DIST = NOTES + +# convenience lib - no _LDFLAGS or _LIBADD ! + +servicetype_DATA = kpropsdlgplugin.desktop +servicetypedir = $(kde_servicetypesdir) + + +#Can't be a module, we need to link to it for readConfig +#kde_module_LTLIBRARIES = libkfileshare.la +#libkfileshare_la_SOURCES = kfileshare.cpp +#libkfileshare_la_LIBADD = libkfile.la +#libkfileshare_la_LDFLAGS = -module $(KDE_PLUGIN) +#kde_services_DATA = kfilesharepropsplugin.desktop + +include $(top_srcdir)/admin/Doxyfile.am diff --git a/kio/kfile/NOTES b/kio/kfile/NOTES new file mode 100644 index 000000000..c3fb6fbcb --- /dev/null +++ b/kio/kfile/NOTES @@ -0,0 +1,100 @@ +Rewrite +======= + +Here is the result of a long discussion between the kfile developers +(Carsten) and the konqueror developers (Simon and David), about the plans +for more integration between kfile and konqueror. 16/08/2000. + + + + KDirLister -----(1)---------------------> KFileView (3) + <----(2)------ [Signaller] --- | | + | | + | | + | | + | | + KFileIconView KFileListView + (4) (5) + + +(1) Gives items found when listing, and the key for each item + (KDirLister has all the sorting code) +(2) KFileView inherited classes emit requests for listing a new + directory (with a bool for treeviews), and emit requests for + the mimetype of a given item. If all KFileView inherited classes + are QScrollViews, then it could even implement the "ask for mimetype + of the visible icons first" algorithm, currently in KonqIconView. +(3) KFileView, the base class for any view, knows about KFileItem, has + signals for dropped(), popupMenu(list of actions provided by the view), + has a QWidget * canvas() method, xOffset() and yOffset() +(4) KFileIconView holds a QPtrDict to look up a QIconViewItem quickly from a + given KFileItem. This will help for e.g. deleteItems and refreshItems. +(5) KFileListView holds a QPtrDict to find the QListViewItem for a + KFileItem. It implements different modes if we want the tree view in + kfile ? + + + KFileChooserWidget + +This (base) class is the container widget that can contain any kfileview and +switch between them. It is embeddable into an application that wants a widget +for choosing a file or directory. Features listing, selecting, popupmenu for +switching modes, and a virtual createView( viewmode ) method, which +only knows about the builtin views, in this class. +It knows the current URL, has setURL(), and triggers the listing, connecting +KFileView's requests to KDirLister's methods. + + KFileManagerWidget + +This class inherits from KFileChooserWidget and adds the file management +operations. It uses a KFileOperations class (taken from the current +KonqOperations) for all KIO operations, and it uses KFileUndo, taken from +the KonqUndo stuff. The popupMenu method is reimplemented to add those +operations to it. + + KFileWidget + +This class is the full widget that can be seen in the dialog. It has the +toolbar buttons, the combo, etc. It embeds a KFileChooserWidget or a +KFileManagerWidget, the latter being a specialisation of the former. + +The Konqueror side of things +============================ + + KonqFileIconView + +Inherits KFileIconView and adds image preview - unless we want it in +KFileIconView, and the line-up-icons algorithm, etc. + + KonqFileListView + +Inherits KFileListView to add more stuff if needed. The mc-like text view +could be implemented here as well, unless we want it in kfile. Same for the +tree view. + + KonqFileManagerWidget + +This class inherits KFileManagerWidget and adds the konqueror stuff, like +the enableAction signal. It also reimplements createView to create the +konqueror versions of the views. + + KonqDirPart + +This class inherits KParts::ReadOnlyPart, and provides a BrowserExtension. +It integrates KonqFileManagerWidget as its KParts widget, and provides +KActions for it. + +Important : + +Make sure to take kfind into account. It would be nice if it could use the +same views as konqueror, to have access to all the view modes, as well as +image preview, etc. + +Unrelated: +To Add +====== + +Mime Mappings? + +Filter= ( Mime Type | shell glob list ) +Mime Type -> shell glob list diff --git a/kio/kfile/TODO b/kio/kfile/TODO new file mode 100644 index 000000000..b7bf74ea5 --- /dev/null +++ b/kio/kfile/TODO @@ -0,0 +1,17 @@ +TODO-List for the KFileDialog and associated classes (in order of importance) + +- are more messageboxes necessary? +- KFD's default dirs configurable +- drag&drop support for the views (somehow into baseclass KFileView? +- KFileView::setSelected( const QRegExp& ) +- separate KDirOperator, location-combo, filter-combo etc. from KFileDialog + to another widget (everything besides OK/Cancel buttons). +- implement in-place editing (rename) + make it configurable in all views +- make a simple fileview based on QListBox +- implement KFile::PreviewInfo +- signal KFileView::selectionCleared() + +If anyone wants to implement some of those, please do. In case of questions, +don't hesitate to ask me. + +Carsten Pfeiffer <[email protected]> diff --git a/kio/kfile/config-kfile.h b/kio/kfile/config-kfile.h new file mode 100644 index 000000000..3666c571b --- /dev/null +++ b/kio/kfile/config-kfile.h @@ -0,0 +1,32 @@ +#ifndef CONFIG_KFILE_H +#define CONFIG_KFILE_H + +const int kfile_area = 250; + +#define DefaultViewStyle QString::fromLatin1("SimpleView") +#define DefaultPannerPosition 40 +#define DefaultMixDirsAndFiles false +#define DefaultShowStatusLine false +#define DefaultShowHidden false +#define DefaultCaseInsensitive true +#define DefaultDirsFirst true +#define DefaultSortReversed false +#define DefaultRecentURLsNumber 15 +#define DefaultDirectoryFollowing true +#define DefaultAutoSelectExtChecked true +#define ConfigGroup QString::fromLatin1("KFileDialog Settings") +#define RecentURLs QString::fromLatin1("Recent URLs") +#define RecentFiles QString::fromLatin1("Recent Files") +#define RecentURLsNumber QString::fromLatin1("Maximum of recent URLs") +#define RecentFilesNumber QString::fromLatin1("Maximum of recent files") +#define DialogWidth QString::fromLatin1("Width (%1)") +#define DialogHeight QString::fromLatin1("Height (%1)") +#define ConfigShowStatusLine QString::fromLatin1("ShowStatusLine") +#define AutoDirectoryFollowing QString::fromLatin1("Automatic directory following") +#define PathComboCompletionMode QString::fromLatin1("PathCombo Completionmode") +#define LocationComboCompletionMode QString::fromLatin1("LocationCombo Completionmode") +#define ShowSpeedbar QString::fromLatin1("Show Speedbar") +#define ShowBookmarks QString::fromLatin1("Show Bookmarks") +#define AutoSelectExtChecked QString::fromLatin1("Automatically select filename extension") + +#endif diff --git a/kio/kfile/images.h b/kio/kfile/images.h new file mode 100644 index 000000000..0c7bd1c37 --- /dev/null +++ b/kio/kfile/images.h @@ -0,0 +1,277 @@ +#ifdef USE_POSIX_ACL +#ifndef _QEMBED_1804289383 +#define _QEMBED_1804289383 +#include <qimage.h> +#include <qdict.h> +static const QRgb group_grey_data[] = { + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42484848,0xc39b9b9b,0xeab1b1b1,0xce9d9d9d,0x5a4d4d4d,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x563b3b3b,0xfdaeaeae,0xffcfcfcf,0xffcccccc,0xffcecece, + 0xffbababa,0x62393939,0x0,0x0,0x0,0x0,0x0,0x0,0x4525252,0x9383838,0x0,0xd0515151,0xff969696,0xff959595, + 0xff969696,0xff959595,0xff969696,0xdd505050,0x6000000,0x0,0x0,0x0,0xa191919,0x908f8f8f,0xebc1c1c1,0xf6c6c6c6,0xc0a1a1a1,0xf74f4f4f, + 0xff626262,0xff6a6a6a,0xff6c6c6c,0xff6a6a6a,0xff636363,0xfb4a4a4a,0x1a000000,0x0,0x0,0x0,0xa3828282,0xffdfdfdf,0xffdedede,0xffdddddd, + 0xffe0e0e0,0xffa4a4a4,0xff636363,0xff666666,0xff6a6a6a,0xff676767,0xff5f5f5f,0xe6494949,0xd000000,0x0,0x0,0x21232323,0xfca2a2a2,0xffc3c3c3, + 0xffc6c6c6,0xffc6c6c6,0xffc4c4c4,0xffbababa,0xff717171,0xff7e7e7e,0xff7e7e7e,0xff7d7d7d,0xfe6f6f6f,0x812b2b2b,0x0,0x0,0x0,0x3e303030, + 0xffa6a6a6,0xffb7b7b7,0xffbdbdbd,0xffbebebe,0xffb9b9b9,0xffacacac,0xff808080,0xff8f8f8f,0xff939393,0xff909090,0xf86b6b6b,0x34202020,0x0,0x0, + 0x0,0x1c191919,0xf8a5a5a5,0xffc2c2c2,0xffcccccc,0xffcecece,0xffc5c5c5,0xffbababa,0xff888888,0xffa5a5a5,0xffa4a4a4,0xffa5a5a5,0xffa1a1a1,0xd3515151, + 0x8030303,0x0,0x0,0x0,0x8f6f6f6f,0xffd3d3d3,0xffe2e2e2,0xffe3e3e3,0xffdbdbdb,0xff9b9b9b,0xff6f6f6f,0xff727272,0xff6e6e6e,0xff717171, + 0xff707070,0xff606060,0x62363636,0x0,0x0,0x0,0x6e5b5b5b,0xffb4b4b4,0xffd4d4d4,0xffdadada,0xffcecece,0xff737373,0xff656565,0xff676767, + 0xff696969,0xff676767,0xff636363,0xff5d5d5d,0xc44b4b4b,0x0,0x0,0x27343434,0xf5a5a5a5,0xffd1d1d1,0xffd2d2d2,0xffd1d1d1,0xffd1d1d1,0xffc3c3c3, + 0xff7b7b7b,0xff6f6f6f,0xff727272,0xff6f6f6f,0xff696969,0xff626262,0xf7606060,0x16050505,0x0,0xa07d7d7d,0xffb8b8b8,0xffc0c0c0,0xffc2c2c2,0xffc1c1c1, + 0xffc1c1c1,0xffbbbbbb,0xffa6a6a6,0xff868686,0xff858585,0xff838383,0xff838383,0xff878787,0xe95e5e5e,0x19050505,0xd141414,0xf0a2a2a2,0xffbdbdbd,0xffc6c6c6, + 0xffcbcbcb,0xffcbcbcb,0xffc8c8c8,0xffc0c0c0,0xffb8b8b8,0xef8b8b8b,0xd6707070,0xd26a6a6a,0xbb595959,0x82363636,0x24070707,0x0,0x44505050,0xffc0c0c0, + 0xffc7c7c7,0xffd2d2d2,0xffd9d9d9,0xffdadada,0xffd4d4d4,0xffcacaca,0xffc1c1c1,0xc8979797,0x3000000,0x4000000,0x0,0x0,0x0,0x0, + 0x2b393939,0xeeaaaaaa,0xffdadada,0xffe4e4e4,0xffeaeaea,0xffeaeaea,0xffe4e4e4,0xffdddddd,0xffd1d1d1,0xae797979,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x1e0f0f0f,0x76575757,0xae898989,0xc49c9c9c,0xc6a0a0a0,0xbc9a9a9a,0x98808080,0x57414141,0xb000000,0x0,0x0, + 0x0,0x0,0x0,0x0 +}; + +/* Generated by qembed */ +static const QRgb group_data[] = { + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4223731d,0xc37fbb7c,0xea9bca98,0xce86b982,0x5a316e2c,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x56146610,0xfd8fce8e,0xffbae4bb,0xffb7e2b7,0xffbae3ba, + 0xff9ed89d,0x62166112,0x0,0x0,0x0,0x0,0x0,0x0,0x4003ca5,0x9003171,0x0,0xd0198b17,0xff6ac468,0xff6ec665, + 0xff70c865,0xff6ec665,0xff6ac468,0xdd1a8918,0x6000000,0x0,0x0,0x0,0xa001333,0x905b8cc4,0xeb9fc0e4,0xf6a8c5e4,0xc07a9dc9,0xf7108e1e, + 0xff2eb113,0xff42be17,0xff49c216,0xff44be17,0xff31b214,0xfb109301,0x1a000000,0x0,0x0,0x0,0xa3497ebb,0xffc6def8,0xffc6ddf6,0xffc5ddf6, + 0xffc9e0f8,0xff77abd2,0xff31af18,0xff50cd00,0xff5cd400,0xff52ce00,0xff3bbe00,0xe6218f03,0xd000000,0x0,0x0,0x21042043,0xfc61a0e3,0xff97c3f0, + 0xff9cc7f1,0xff9cc8f1,0xff98c4f0,0xff85b8ef,0xff469d88,0xff7de517,0xff87ed10,0xff7ce417,0xfe5bca14,0x811b5304,0x0,0x0,0x0,0x3e082e58, + 0xff60a7ed,0xff7fbaef,0xff8ac3f1,0xff8bc4f1,0xff83beef,0xff6caeec,0xff4e98b3,0xff88d54a,0xff98e343,0xff89d64a,0xf846a630,0x340c4100,0x0,0x0, + 0x0,0x1c021631,0xf867a7e3,0xff90c9f4,0xffa1d6f8,0xffa4d8f8,0xff96cef5,0xff82bef2,0xff65aca9,0xff8ad576,0xff86d276,0xff88d377,0xff80d072,0xd3279310, + 0x8000700,0x0,0x0,0x0,0x8f3f6f9f,0xffaedcf9,0xffc7effe,0xffc9f0fe,0xffbbe5fc,0xff73b7c4,0xff45ba25,0xff51c61e,0xff50c617,0xff51c61c, + 0xff4bc120,0xff30b50c,0x62206705,0x0,0x0,0x0,0x6e1d5899,0xff81b2e7,0xffb4d7f4,0xffbddcf7,0xffa8cef4,0xff4698a0,0xff45c407,0xff53ce00, + 0xff59d200,0xff54cf00,0xff46c600,0xff34bb00,0xc42d9105,0x0,0x0,0x270a305f,0xf567a5e3,0xffaed3f4,0xffafd4f5,0xffaed4f4,0xffaed3f4,0xff94c2f3, + 0xff4fa782,0xff6cdf00,0xff74e400,0xff6cdf00,0xff5ad300,0xff43c401,0xf742b110,0x16060b00,0x0,0xa0407dba,0xff80bcf1,0xff8ec5f2,0xff92c8f2,0xff91c8f2, + 0xff90c6f2,0xff86bff0,0xff67a8e6,0xff80e02c,0xff95f615,0xff8aee18,0xff7de323,0xff76db33,0xe951a31a,0x19040a00,0xd000f28,0xf066a4de,0xff88c4f2,0xff97cef5, + 0xffa0d4f7,0xffa0d5f7,0xff9ad0f6,0xff8dc7f3,0xff7ebcf2,0xef6ea88b,0xd679b12f,0xd271a82d,0xbb5c9221,0x8235600c,0x24060d02,0x0,0x44184c89,0xff8cc6f4, + 0xff99d0f6,0xffabddfa,0xffb7e6fc,0xffb8e6fc,0xffaee0fb,0xff9ed4f7,0xff90c9f3,0xc86697c9,0x3000000,0x4000000,0x0,0x0,0x0,0x0, + 0x2b0e3664,0xee7caed8,0xffb9e4fc,0xffcaf1fe,0xffd5f6ff,0xffd6f7ff,0xffcbf2fe,0xffbee8fc,0xffa9d6f9,0xae5378a0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x1e020c1d,0x7634547a,0xae6a88a8,0xc4849bb4,0xc689a0b8,0xbc7d9ab7,0x98627f9f,0x572c3e56,0xb000000,0x0,0x0, + 0x0,0x0,0x0,0x0 +}; + +static const QRgb mask_data[] = { + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11c84a00,0x1000000,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x68d14e00,0xffda6400,0x72bf4700,0x3000000,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14d04d00,0xefda6400,0xfffec300,0xf2d86300,0x24742b00, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98d14e00,0xfff3b537,0xfffffed6, + 0xfff3b537,0xa5c04800,0x6000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30cf4d00,0xfbe17803, + 0xfff1e7ad,0xffcacaba,0xfff1e7ac,0xfce07803,0x42973800,0x1000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0xc4d35300,0xfffad45c,0xffc0c0b2,0xff979797,0xffb2b2a9,0xfffad45a,0xccca5000,0xa000000,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x58d14e00,0xffe89410,0xfffffcbd,0xffc6c6af,0xff929292,0xffbdbda8,0xfffffcb9,0xffe8930e,0x69b04100,0x2000000,0x0,0x0,0x0, + 0x0,0x0,0x8cb4c00,0xe3d76000,0xfffeec6c,0xffffffbe,0xffe2e2b2,0xff878787,0xffd5d5a8,0xffffffb7,0xfffeeb62,0xe8d35e00,0x17491b00,0x0, + 0x0,0x0,0x0,0x0,0x84d14e00,0xfff0b41a,0xfffffea3,0xffffffa8,0xfffbfba7,0xff7b7b76,0xfff6f6a0,0xffffff9c,0xfffffe8e,0xfff0b414, + 0x92bd4600,0x4000000,0x0,0x0,0x0,0x20d04d00,0xf7dd7400,0xfffff85e,0xffffff89,0xffffff8b,0xffffff8a,0xffc3c376,0xffffff82,0xffffff7b, + 0xffffff71,0xfffff746,0xf9db7300,0x32873200,0x0,0x0,0x0,0xa8d25200,0xfff6d518,0xffffff61,0xffffff65,0xffffff65,0xffecec60,0xff9f9f52, + 0xffe6e657,0xffffff54,0xffffff4c,0xffffff44,0xfff6d50e,0xb4c54c00,0x8000000,0x0,0x40d04e00,0xffe49001,0xfffffd2b,0xffffff3a,0xffffff3d,0xffffff3d, + 0xff9b9b33,0xff272727,0xff84842a,0xffffff2e,0xffffff28,0xffffff22,0xfffffd18,0xffe49000,0x52a33c00,0x2000000,0xd0d55b00,0xfffcef07,0xffffff17,0xffffff1a, + 0xffffff1b,0xffffff1b,0xffeaea19,0xff8c8c16,0xffdbdb13,0xffffff10,0xffffff0d,0xffffff0a,0xffffff07,0xfffcef02,0xd7ce5800,0xc000000,0xe4d55d00,0xffe49204, + 0xffe4940a,0xffe4950c,0xffe4960d,0xffe4950c,0xffe4950c,0xffe4940a,0xffe49308,0xffe49307,0xffe49205,0xffe39204,0xffe39102,0xffe39100,0xead05b00,0x1c000000, + 0x13582100,0x3c702900,0x40692700,0x40692700,0x40692700,0x40692700,0x40692700,0x40692700,0x40692700,0x40692700,0x40692700,0x40692700,0x40692700,0x40692700, + 0x29290f00,0xc000000,0x0,0x2000000,0x2000000,0x2000000,0x2000000,0x2000000,0x2000000,0x2000000,0x2000000,0x2000000,0x2000000,0x2000000, + 0x2000000,0x2000000,0x2000000,0x0 +}; + +static const QRgb others_grey_data[] = { + 0x0,0x0,0x0,0xa4c4c4c,0x5d676767,0x777c7c7c,0x3d555555,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x17535353,0xd2afafaf,0xffebebeb,0xffe5e5e5,0xfec2c2c2,0x906d6d6d,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0xa09c9c9c,0xfff1f1f1,0xfff5f5f5,0xffe6e6e6,0xffd4d4d4,0xffbebebe,0x4c424242,0x117b7b7b, + 0x357d7d7d,0x177c7c7c,0x0,0x0,0x0,0x0,0x0,0x606060,0xe4b4b4b4,0xffe1e1e1,0xffe4e4e4,0xffdcdcdc,0xffcecece,0xffc0c0c0, + 0xd9858585,0xf3d5d5d5,0xffe6e6e6,0xf8cdcdcd,0x8c828282,0x2030303,0x0,0x0,0x0,0x0,0xdcaaaaaa,0xffd0d0d0,0xffd2d2d2,0xffcecece, + 0xffc5c5c5,0xffa3a3a3,0xffe8e8e8,0xfffbfbfb,0xfff4f4f4,0xffeaeaea,0xffe0e0e0,0x6f767676,0x0,0x0,0x0,0x0,0x7e848484,0xffc3c3c3, + 0xffc3c3c3,0xffbfbfbf,0xffaeaeae,0xffaaaaaa,0xfff1f1f1,0xfff2f2f2,0xffefefef,0xffe6e6e6,0xffe0e0e0,0xcba6a6a6,0x0,0x0,0x0,0xc3f3f3f, + 0xb8858585,0xff8f8f8f,0xff969696,0xff919191,0xff787878,0xffa5a5a5,0xffe7e7e7,0xffe8e8e8,0xffe5e5e5,0xffe1e1e1,0xffdcdcdc,0xd4a6a6a6,0x0,0x0, + 0x0,0xa1959595,0xffdbdbdb,0xffe3e3e3,0xff999999,0xff7a7a7a,0xffb9b9b9,0xffb5b5b5,0xffdedede,0xffe0e0e0,0xffdfdfdf,0xffdbdbdb,0xffd1d1d1,0x8e898989, + 0x0,0x0,0x28363636,0xfcb2b2b2,0xffdadada,0xffededed,0xfff5f5f5,0xffd5d5d5,0xfff5f5f5,0xffcbcbcb,0xffb7b7b7,0xffd2d2d2,0xffd3d3d3,0xffc8c8c8, + 0xffb2b2b2,0x78979797,0x0,0x0,0x694b4b4b,0xffafafaf,0xffc8c8c8,0xffd1d1d1,0xffd8d8d8,0xffdbdbdb,0xffb9b9b9,0xffd7d7d7,0xffe4e4e4,0xffc3c3c3, + 0xffa0a0a0,0xffb9b9b9,0xffd2d2d2,0xffdbdbdb,0x5e979797,0x0,0x70434343,0xff9c9c9c,0xffadadad,0xffb6b6b6,0xffbbbbbb,0xffbbbbbb,0xffb0b0b0,0xffdfdfdf, + 0xfff5f5f5,0xfff8f8f8,0xfff2f2f2,0xfff8f8f8,0xfff6f6f6,0xffe3e3e3,0xe6bababa,0x90b0b0b,0x30232323,0xfb767676,0xff939393,0xff9a9a9a,0xff9f9f9f,0xff969696, + 0xffbbbbbb,0xffdadada,0xffe3e3e3,0xffe8e8e8,0xffeaeaea,0xffe9e9e9,0xffe5e5e5,0xffdedede,0xffc8c8c8,0x41363636,0x0,0x5a2e2e2e,0xde5b5b5b,0xfe707070, + 0xff7a7a7a,0xff727272,0xffb3b3b3,0xffcbcbcb,0xffd1d1d1,0xffd6d6d6,0xffd8d8d8,0xffd7d7d7,0xffd3d3d3,0xffcecece,0xffbfbfbf,0x57424242,0x0,0x0, + 0x4000000,0x20050505,0x33070707,0x3b181818,0xf2959595,0xffbababa,0xffbfbfbf,0xffc3c3c3,0xffc5c5c5,0xffc4c4c4,0xffc1c1c1,0xffbcbcbc,0xfe9f9f9f,0x321d1d1d, + 0x0,0x0,0x0,0x0,0x0,0x0,0x5c5f5f5f,0xf08a8a8a,0xffa7a7a7,0xffb0b0b0,0xffb2b2b2,0xffb0b0b0,0xffa9a9a9,0xf98e8e8e, + 0x874c4c4c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf101010,0x4a2b2b2b,0x65424242,0x6c4a4a4a,0x67444444, + 0x542e2e2e,0x200f0f0f,0x0,0x0 +}; + +static const QRgb others_data[] = { + 0x0,0x0,0x0,0xa804618,0x5d95643a,0x77a77c52,0x3d855126,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x17964f11,0xd2cfb190,0xfff8efdf,0xffffeccb,0xfeedce98,0x909b703f,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0xa0d29e66,0xfffff7e3,0xfffff8ec,0xffffedce,0xffffe0a9,0xfff5cd88,0x4c6f4316,0x11f72300, + 0x35fb2b00,0x17f82200,0x0,0x0,0x0,0x0,0x0,0xc15b00,0xe4ebbc7d,0xffffeac4,0xffffecca,0xffffe5b9,0xfffedb9e,0xfff4d38d, + 0xd9c76844,0xf3f6b8b4,0xfffcdad0,0xf8f5b3a6,0x8cba524a,0x2070000,0x0,0x0,0x0,0x0,0xdce9b56b,0xfffedda3,0xffffdea6,0xfffedb9e, + 0xfff8d592,0xffdf9f67,0xfffadad6,0xfffffaf8,0xfffef0ea,0xfffee2d6,0xfffed4c3,0x6fac4f41,0x0,0x0,0x0,0x0,0x7ecd933c,0xfff7d390, + 0xfff6d390,0xfff3d08b,0xffe6c377,0xffe29c73,0xfffeece4,0xfffeeee7,0xfffee9e0,0xfffddecf,0xfff9d8c8,0xcbd0927d,0x0,0x0,0x0,0xc007f00, + 0xb85db05a,0xffbbae64,0xffd4ab58,0xffd0a952,0xffba9636,0xffd9a571,0xfffddfd2,0xfffee0d3,0xfffdddce,0xfffbd8c8,0xfff6d2c2,0xd4d3927a,0x0,0x0, + 0x0,0xa171b973,0xffd4e9ce,0xffd4f2d5,0xffb2a680,0xffdb301a,0xff9cde94,0xffd3c097,0xfffbd4c2,0xfffad7c6,0xfff9d6c6,0xfff6d2c0,0xfff8c2ab,0x8ec26d51, + 0x0,0x0,0x28026b11,0xfc9ed391,0xffd4efc6,0xffeaf8e2,0xfff2fcee,0xffe5cdc6,0xfff4fcee,0xffb5cbe1,0xffd19da5,0xfff9c2ab,0xfff8c5ae,0xfff9b398, + 0xffd6918f,0x785982d5,0x0,0x0,0x691d792e,0xff9edc82,0xffbdeaa7,0xffc8edb6,0xffd0f0c0,0xffd4f2c4,0xffa8cac2,0xffbdccf2,0xffcfdefa,0xffbcb5d2, + 0xff9a91b0,0xffaba8ca,0xffaaccfb,0xffc4d1f2,0x5e6184ce,0x0,0x70196d2d,0xff88d663,0xff9dde7c,0xffa8e28a,0xffaee493,0xffb0e493,0xff89add8,0xffc6dbf8, + 0xffebf5ff,0xfff1f7ff,0xffe6f3ff,0xfff2f8ff,0xffeef6ff,0xffcfe0f8,0xe694b1e1,0x9000317,0x30004626,0xfb57b03d,0xff7ed651,0xff86d75e,0xff8cd966,0xff7cc16b, + 0xff84aff2,0xffb6dafe,0xffc8e4ff,0xffd2eaff,0xffd6ebff,0xffd4eaff,0xffcce6ff,0xffbee0ff,0xff96c2fb,0x410e1f5e,0x0,0x5a104c2a,0xde3b882f,0xfe51aa37, + 0xff5cb83c,0xff4e9c49,0xff70a8f6,0xff98cdff,0xffa4d3ff,0xffaed7ff,0xffb1d8ff,0xffb0d8ff,0xffa8d4ff,0xff9dcfff,0xff81bcfe,0x57132571,0x0,0x0, + 0x4000001,0x2000020a,0x3300080f,0x3b000d31,0xf24a83e0,0xff76bdff,0xff80c2ff,0xff88c5ff,0xff8cc6ff,0xff8ac6ff,0xff84c2ff,0xff7abeff,0xfe5694e9,0x32030738, + 0x0,0x0,0x0,0x0,0x0,0x0,0x5c1429ab,0xf03d77d8,0xff57a3f7,0xff62b0fe,0xff65b2ff,0xff63b1fe,0xff5aa6f9,0xf9427edb, + 0x87152a83,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf000021,0x4a020554,0x650b167a,0x6c0e1d86,0x670c187d, + 0x5403065a,0x2000001e,0x0,0x0 +}; + +static const QRgb user_green_data[] = { + 0x0,0x0,0x0,0x0,0x5029,0x6c1c6e21,0xe332aa3b,0xf83ac841,0xf838c83f,0xda369a3b,0x5a145819,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x7e1a6c1e,0xff32da39,0xff3de341,0xff3ee045,0xff3ee042,0xff3de345,0xff27d930,0x68125817, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f013105,0xf721a328,0xff22de27,0xff23dd27,0xff26dc26,0xff26dc2a,0xff22de27, + 0xff22de27,0xee268c2b,0x12001402,0x0,0x0,0x0,0x0,0x0,0x0,0x5c0b590f,0xff19b51d,0xff1ecc1e,0xff1dd31d,0xff22d41e, + 0xff1ed41e,0xff1cd21c,0xff1dcb21,0xff18b01b,0x4d093d0d,0x0,0x0,0x0,0x0,0x0,0x0,0x640f5f13,0xff18bc18,0xff1ec61a, + 0xff1ed119,0xff22d519,0xff22d519,0xff22ce1a,0xff1fc31b,0xff16be1a,0x5a0e4211,0x0,0x0,0x0,0x0,0x0,0x0,0x38033507, + 0xff1db91d,0xff21d818,0xff24e616,0xff2aec16,0xff2aec16,0xff24e416,0xff21d418,0xfd20b020,0x35031b05,0x0,0x0,0x0,0x0,0x0, + 0x0,0x2000000,0xbb2a702c,0xff2df018,0xff3af41c,0xff48f620,0xff46fa1c,0xff39f21a,0xff23ef13,0xb929632c,0x4000000,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x2e012703,0xfc279129,0xff3fe729,0xff5ff537,0xff5bf632,0xff2ef11f,0xf6298f2e,0x29010d02,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x6000e07,0xc41f7721,0xff1ee01e,0xff29dd2d,0xff22de27,0xff22de27,0xff2bdf34,0xff1ddf21,0xab227226, + 0x1000000,0x0,0x0,0x0,0x0,0x0,0x0,0x6d136117,0xff1bc31b,0xff1ee01e,0xff24e020,0xff25e220,0xff25e220,0xff20e020, + 0xff20dc20,0xff1bbf1e,0x561b571f,0x0,0x0,0x0,0x0,0x0,0x8001205,0xe2268e29,0xff1eca1a,0xff23d41a,0xff23d81a,0xff23d81a, + 0xff23d81a,0xff23d81a,0xff24d11b,0xff1fc71b,0xd12a882c,0x4000000,0x0,0x0,0x0,0x0,0x4a0e5012,0xff1cc818,0xff21d119,0xff25db17, + 0xff25e117,0xff24e616,0xff24e616,0xff27df19,0xff21da18,0xff22cf19,0xff18c418,0x3a173d19,0x0,0x0,0x0,0x0,0x982b732c,0xff1ed915, + 0xff25dd17,0xff28ec14,0xff32f018,0xff34f21a,0xff32f218,0xff2af016,0xff28ea14,0xff20db17,0xff1bd317,0x85316f34,0x0,0x0,0x0,0x0, + 0xbf318131,0xff22e818,0xff2aea16,0xff39ef1b,0xff44fa1a,0xff4dfa1e,0xff4dfa1e,0xff3ff71b,0xff30f016,0xff2ae616,0xff20e616,0xb3337936,0x0,0x0, + 0x0,0x0,0x51134b16,0xed369834,0xff2ef11f,0xff54f828,0xff67fc2c,0xff6bfb33,0xff6ffc30,0xff5ffc28,0xff48f925,0xff30f41c,0xec33a135,0x561b3d1e, + 0x0,0x0,0x0,0x0,0x0,0x18000802,0x60113b14,0x9d315f33,0xbb417143,0xc6467848,0xc6467a48,0xbc417942,0x9e3c663e,0x64224225, + 0x1c020604,0x0,0x0,0x0 +}; + +static const QRgb user_grey_data[] = { + 0x0,0x0,0x0,0x0,0x404040,0x6c6e6e6e,0xe3b0b0b0,0xf8cecece,0xf8cccccc,0xdaa6a6a6,0x5a575757,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x7e6b6b6b,0xffd6d6d6,0xffe6e6e6,0xffe4e4e4,0xffe4e4e4,0xffe6e6e6,0xffcccccc,0x68555555, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f282828,0xf79d9d9d,0xffcccccc,0xffcdcdcd,0xffcecece,0xffcecece,0xffcccccc, + 0xffcccccc,0xee8f8f8f,0x12101010,0x0,0x0,0x0,0x0,0x0,0x0,0x5c515151,0xffa5a5a5,0xffbbbbbb,0xffc0c0c0,0xffc1c1c1, + 0xffc1c1c1,0xffbfbfbf,0xffbababa,0xffa0a0a0,0x4d383838,0x0,0x0,0x0,0x0,0x0,0x0,0x64585858,0xffaaaaaa,0xffb4b4b4, + 0xffbcbcbc,0xffbfbfbf,0xffbfbfbf,0xffbababa,0xffb2b2b2,0xffa9a9a9,0x5a404040,0x0,0x0,0x0,0x0,0x0,0x0,0x382e2e2e, + 0xffababab,0xffc0c0c0,0xffc9c9c9,0xffcfcfcf,0xffcecece,0xffc8c8c8,0xffbdbdbd,0xfda6a6a6,0x35181818,0x0,0x0,0x0,0x0,0x0, + 0x0,0x2000000,0xbb7c7c7c,0xffd3d3d3,0xffd9d9d9,0xffdfdfdf,0xffdedede,0xffd7d7d7,0xffcecece,0xb9717171,0x4000000,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x2e202020,0xfc939393,0xffdadada,0xfff0f0f0,0xffededed,0xffdadada,0xf6949494,0x290c0c0c,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x60c0c0c,0xc4787878,0xffcbcbcb,0xffd2d2d2,0xffcccccc,0xffcccccc,0xffd4d4d4,0xffc9c9c9,0xab777777, + 0x1000000,0x0,0x0,0x0,0x0,0x0,0x0,0x6d5d5d5d,0xffb2b2b2,0xffcbcbcb,0xffcccccc,0xffcecece,0xffcecece,0xffcccccc, + 0xffcacaca,0xffafafaf,0x565c5c5c,0x0,0x0,0x0,0x0,0x0,0x80f0f0f,0xe2909090,0xffb7b7b7,0xffbebebe,0xffc1c1c1,0xffc1c1c1, + 0xffc1c1c1,0xffc1c1c1,0xffbdbdbd,0xffb5b5b5,0xd18f8f8f,0x4000000,0x0,0x0,0x0,0x0,0x4a4c4c4c,0xffb3b3b3,0xffbbbbbb,0xffc2c2c2, + 0xffc7c7c7,0xffc9c9c9,0xffc9c9c9,0xffc6c6c6,0xffc1c1c1,0xffb9b9b9,0xffb0b0b0,0x3a434343,0x0,0x0,0x0,0x0,0x987e7e7e,0xffbfbfbf, + 0xffc4c4c4,0xffcdcdcd,0xffd3d3d3,0xffd6d6d6,0xffd5d5d5,0xffd1d1d1,0xffcbcbcb,0xffc2c2c2,0xffbbbbbb,0x85808080,0x0,0x0,0x0,0x0, + 0xbf8e8e8e,0xffcccccc,0xffcccccc,0xffd5d5d5,0xffdddddd,0xffe0e0e0,0xffe0e0e0,0xffdbdbdb,0xffd2d2d2,0xffcacaca,0xffcacaca,0xb38a8a8a,0x0,0x0, + 0x0,0x0,0x514b4b4b,0xeda4a4a4,0xffdadada,0xffe7e7e7,0xffededed,0xfff1f1f1,0xfff0f0f0,0xffeaeaea,0xffe4e4e4,0xffdadada,0xeca9a9a9,0x56474747, + 0x0,0x0,0x0,0x0,0x0,0x18070707,0x603e3e3e,0x9d747474,0xbb8e8e8e,0xc6989898,0xc69a9a9a,0xbc959595,0x9e828282,0x64505050, + 0x1c070707,0x0,0x0,0x0 +}; + +static const QRgb user_data[] = { + 0x0,0x0,0x0,0x0,0x7f,0x6c2c68af,0xe384abdb,0xf8b2ccea,0xf8aecae9,0xda7ba3d1,0x5a20508d,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x7e2a66ac,0xffb8d4f3,0xffd2e5f9,0xffd0e3f8,0xffcfe3f8,0xffd3e5f9,0xffa7c9f0,0x681d4e8c, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f02244d,0xf75c9ade,0xffa6cbf2,0xffa7ccf2,0xffa9cef2,0xffa9cdf2,0xffa6ccf2, + 0xffa6ccf2,0xee4d8bd0,0x12000d1f,0x0,0x0,0x0,0x0,0x0,0x0,0x5c124c8f,0xff60a4ea,0xff88bcee,0xff8fc1f0,0xff92c4f0, + 0xff92c3f0,0xff8ec0f0,0xff85baee,0xff579fe9,0x4d0f3460,0x0,0x0,0x0,0x0,0x0,0x0,0x64185598,0xff67acec,0xff7ab8ee, + 0xff87c1f1,0xff8cc5f2,0xff8cc5f2,0xff84c0f0,0xff76b6ed,0xff63a9ee,0x5a173d69,0x0,0x0,0x0,0x0,0x0,0x0,0x38052a56, + 0xff6caee9,0xff8cc6f3,0xff9cd2f6,0xffa5d8f8,0xffa4d7f8,0xff99d0f6,0xff88c3f2,0xfd68a7e4,0x3505152b,0x0,0x0,0x0,0x0,0x0, + 0x0,0x2000000,0xbb447bb3,0xffabdbfa,0xffb6e4fc,0xffc0ecfe,0xffbeebfe,0xffb3e2fb,0xffa2d6f9,0xb9426e9f,0x4000000,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x2e011c3e,0xfc5491d2,0xffbce2f8,0xffe1f6fe,0xffdcf5fe,0xffb9e0fb,0xf65790d0,0x29010b17,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x6000017,0xc43276be,0xffa2cbf3,0xffb0d1f3,0xffa6cbf2,0xffa6cbf2,0xffb4d2f4,0xff9ec9f3,0xab3774b7, + 0x1000000,0x0,0x0,0x0,0x0,0x0,0x0,0x6d1f599b,0xff76b3ed,0xffa3ccf3,0xffa5cff3,0xffa7d0f4,0xffa7d0f4,0xffa5cef3, + 0xffa2ccf2,0xff71afed,0x562c578c,0x0,0x0,0x0,0x0,0x0,0x800071e,0xe24f8fd0,0xff7ebaef,0xff8bc4f1,0xff90c7f2,0xff8fc7f2, + 0xff8fc6f2,0xff8fc6f2,0xff89c2f0,0xff7bb8ee,0xd1538dca,0x4000000,0x0,0x0,0x0,0x0,0x4a164781,0xff77b8ef,0xff85c2f1,0xff90caf4, + 0xff98cff5,0xff9cd2f6,0xff9bd1f6,0xff97cef5,0xff8ec8f3,0xff82bff0,0xff72b2ee,0x3a244062,0x0,0x0,0x0,0x0,0x98447db7,0xff8ac5f4, + 0xff94ccf4,0xffa1d6f8,0xffabddfa,0xffb0e0fb,0xffafe0fb,0xffa8dbfa,0xff9ed4f7,0xff90c9f4,0xff83c0f3,0x854d7cb2,0x0,0x0,0x0,0x0, + 0xbf5a8fc1,0xffa1d2f6,0xffa0d6f7,0xffafe0fa,0xffbceafe,0xffc2eefe,0xffc2eefe,0xffb9e8fd,0xffaaddfa,0xff9ed3f6,0xff9dd0f6,0xb35a88ba,0x0,0x0, + 0x0,0x0,0x511f4776,0xed77a7d1,0xffb8e0fb,0xffcff1fe,0xffdaf8ff,0xffe2f9ff,0xffe0f9ff,0xffd5f6ff,0xffcaeefe,0xffb8e1fc,0xec7ca9d6,0x562c4462, + 0x0,0x0,0x0,0x0,0x0,0x1800030d,0x601b3a61,0x9d4f7198,0xbb6e8cad,0xc67d96b3,0xc67d98b6,0xbc7494b5,0x9e6180a3,0x64364c69, + 0x1c040409,0x0,0x0,0x0 +}; + +static const QRgb yes_data[] = { + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x11049c00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x1005a200,0x800ba600,0xd512a700,0xe1009d00,0x34007700,0x1000000,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x8009500,0x78009b00,0xf0039f03,0xe2009800,0x46003700,0xbb009300,0xeb11a111,0x35006f00,0x1000000,0x0,0x0, + 0x0,0x0,0x0,0x8008d00,0x70009300,0xe819ab19,0xff3ec63e,0xd4099109,0x45002c00,0xf000000,0x6f008900,0xff33d633,0xeb0a9e0a,0x35006800, + 0x1000000,0x0,0x0,0x4008900,0x6c008a00,0xe8099e09,0xff2cd52c,0xff3bd93b,0xcb048404,0x3c001c00,0xc000000,0x0,0x28007a00,0xff0abc0a, + 0xff10f410,0xeb019701,0x35006100,0x5006d00,0x60008100,0xdc009800,0xff06d206,0xff13fd13,0xff11ce11,0xc2017801,0x32000b00,0xa000000,0x0,0x0, + 0x1000000,0xde009100,0xff00f700,0xff00eb00,0xeb008e00,0xde008700,0xff00c400,0xff00f500,0xff00f700,0xff00c100,0xae006700,0x30000b00,0x8000000,0x0, + 0x0,0x0,0x0,0x97007100,0xff00dc00,0xff00eb00,0xff00e500,0xff00e700,0xff00eb00,0xff00eb00,0xff00a800,0xa2005c00,0x29000000,0x6000000, + 0x0,0x0,0x0,0x0,0x0,0x4c006600,0xff00af00,0xff00de00,0xff00de00,0xff00de00,0xff00dc00,0xff009600,0x96005100,0x24000000, + 0x5000000,0x0,0x0,0x0,0x0,0x0,0x0,0xa004e00,0xf8008200,0xff00d200,0xff00d200,0xff00ce00,0xfc008700,0x80004100, + 0x21000000,0x4000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbb005f00,0xff00c300,0xff00c100,0xf6007500, + 0x76003800,0x1d000000,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f005800,0xff009700, + 0xf4006d00,0x6c003200,0x19000000,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x28005300,0xed005900,0x5a002800,0x16000000,0x1000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x1000000,0x15000000,0x12000000,0x1000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0 +}; + +static const QRgb yespartial_data[] = { + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x114e4e4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x10515151,0x80535353,0xd5535353,0xe14e4e4e,0x343b3b3b,0x1000000,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x84a4a4a,0x784d4d4d,0x9e515151,0xe24c4c4c,0x461b1b1b,0xbb494949,0xeb595959,0x35373737,0x1000000,0x0,0x0, + 0x0,0x0,0x0,0x8464646,0x70494949,0xe8626262,0x93828282,0xc34d4d4d,0x45161616,0xf000000,0x6f444444,0x8c848484,0xcf545454,0x35343434, + 0x1000000,0x0,0x0,0x4444444,0x6c454545,0xe5535353,0x57808080,0x5e8a8a8a,0xcb444444,0x3c0e0e0e,0xc000000,0x0,0x283d3d3d,0xa7636363, + 0x32828282,0xeb4c4c4c,0x35303030,0x5363636,0x60404040,0xdc4c4c4c,0x756c6c6c,0x2b888888,0x7b6f6f6f,0xc23c3c3c,0x32050505,0xa000000,0x0,0x0, + 0x1000000,0xde484848,0x3c7b7b7b,0x1f757575,0xeb474747,0xde434343,0x80626262,0x3c7a7a7a,0x337b7b7b,0x56606060,0xae333333,0x30050505,0x8000000,0x0, + 0x0,0x0,0x0,0x97383838,0x4c6e6e6e,0xa757575,0x3727272,0x24737373,0x2b757575,0x27757575,0x75545454,0x952e2e2e,0x29000000,0x6000000, + 0x0,0x0,0x0,0x0,0x0,0x4c333333,0x8e575757,0x1c6f6f6f,0x46f6f6f,0xb6f6f6f,0x2b6e6e6e,0x994b4b4b,0x85282828,0x24000000, + 0x5000000,0x0,0x0,0x0,0x0,0x0,0x0,0xa272727,0xdc414141,0x3d696969,0x11696969,0x1e676767,0x91434343,0x77202020, + 0x21000000,0x4000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbb2f2f2f,0x5d616161,0x36606060,0x893a3a3a, + 0x761c1c1c,0x1d000000,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f2c2c2c,0x9a4b4b4b, + 0x9b363636,0x6c191919,0x19000000,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x28292929,0xed2c2c2c,0x5a141414,0x16000000,0x1000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x1000000,0x15000000,0x12000000,0x1000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0 +}; + +static struct EmbedImage { + int width, height, depth; + const unsigned char *data; + int numColors; + const QRgb *colorTable; + bool alpha; + const char *name; +} embed_image_vec[] = { + { 16, 16, 32, (const unsigned char*)group_grey_data, 0, 0, TRUE, "group-grey" }, + { 16, 16, 32, (const unsigned char*)group_data, 0, 0, TRUE, "group" }, + { 16, 16, 32, (const unsigned char*)mask_data, 0, 0, TRUE, "mask" }, + { 16, 16, 32, (const unsigned char*)others_grey_data, 0, 0, TRUE, "others-grey" }, + { 16, 16, 32, (const unsigned char*)others_data, 0, 0, TRUE, "others" }, + { 16, 16, 32, (const unsigned char*)user_green_data, 0, 0, TRUE, "user-green" }, + { 16, 16, 32, (const unsigned char*)user_grey_data, 0, 0, TRUE, "user-grey" }, + { 16, 16, 32, (const unsigned char*)user_data, 0, 0, TRUE, "user" }, + { 16, 16, 32, (const unsigned char*)yes_data, 0, 0, TRUE, "yes" }, + { 16, 16, 32, (const unsigned char*)yespartial_data, 0, 0, TRUE, "yespartial" }, + { 0, 0, 0, 0, 0, 0, 0, 0 } +}; + +static const QImage& qembed_findImage( const QString& name ) +{ + static QDict<QImage> dict; + QImage* img = dict.find( name ); + if ( !img ) { + for ( int i = 0; embed_image_vec[i].data; i++ ) { + if ( strcmp(embed_image_vec[i].name, name.latin1()) == 0 ) { + img = new QImage((uchar*)embed_image_vec[i].data, + embed_image_vec[i].width, + embed_image_vec[i].height, + embed_image_vec[i].depth, + (QRgb*)embed_image_vec[i].colorTable, + embed_image_vec[i].numColors, + QImage::BigEndian ); + if ( embed_image_vec[i].alpha ) + img->setAlphaBuffer( TRUE ); + dict.insert( name, img ); + break; + } + } + if ( !img ) { + static QImage dummy; + return dummy; + } + } + return *img; +} + +#endif +#endif diff --git a/kio/kfile/kacleditwidget.cpp b/kio/kfile/kacleditwidget.cpp new file mode 100644 index 000000000..41a5af7bd --- /dev/null +++ b/kio/kfile/kacleditwidget.cpp @@ -0,0 +1,1054 @@ +/*************************************************************************** + * Copyright (C) 2005 by Sean Harmer <[email protected]> * + * Till Adam <[email protected]> * + * * + * This program 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 program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + + +#include "kacleditwidget.h" +#include "kacleditwidget_p.h" + +#ifdef USE_POSIX_ACL + +#include <qpainter.h> +#include <qptrlist.h> +#include <qvbox.h> +#include <qhbox.h> +#include <qpushbutton.h> +#include <qvbuttongroup.h> +#include <qradiobutton.h> +#include <qcombobox.h> +#include <qlabel.h> +#include <qcheckbox.h> +#include <qlayout.h> +#include <qwidgetstack.h> +#include <qheader.h> + +#include <klocale.h> +#include <kfileitem.h> +#include <kdebug.h> +#include <kdialog.h> +#include <kdialogbase.h> + +#ifdef HAVE_ACL_LIBACL_H +# include <acl/libacl.h> +#endif +extern "C" { +#include <pwd.h> +#include <grp.h> +} +#include <assert.h> + +#include "images.h" + +static struct { + const char* label; + const char* pixmapName; + QPixmap* pixmap; +} s_itemAttributes[] = { + { I18N_NOOP( "Owner" ), "user-grey", 0 }, + { I18N_NOOP( "Owning Group" ), "group-grey", 0 }, + { I18N_NOOP( "Others" ), "others-grey", 0 }, + { I18N_NOOP( "Mask" ), "mask", 0 }, + { I18N_NOOP( "Named User" ), "user", 0 }, + { I18N_NOOP( "Named Group" ), "group", 0 }, +}; + +KACLEditWidget::KACLEditWidget( QWidget *parent, const char *name ) + :QWidget( parent, name ) +{ + QHBox *hbox = new QHBox( parent ); + hbox->setSpacing( KDialog::spacingHint() ); + m_listView = new KACLListView( hbox, "acl_listview" ); + connect( m_listView, SIGNAL( selectionChanged() ), + this, SLOT( slotUpdateButtons() ) ); + QVBox *vbox = new QVBox( hbox ); + vbox->setSpacing( KDialog::spacingHint() ); + m_AddBtn = new QPushButton( i18n( "Add Entry..." ), vbox, "add_entry_button" ); + connect( m_AddBtn, SIGNAL( clicked() ), m_listView, SLOT( slotAddEntry() ) ); + m_EditBtn = new QPushButton( i18n( "Edit Entry..." ), vbox, "edit_entry_button" ); + connect( m_EditBtn, SIGNAL( clicked() ), m_listView, SLOT( slotEditEntry() ) ); + m_DelBtn = new QPushButton( i18n( "Delete Entry" ), vbox, "delete_entry_button" ); + connect( m_DelBtn, SIGNAL( clicked() ), m_listView, SLOT( slotRemoveEntry() ) ); + QWidget *spacer = new QWidget( vbox ); + spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Expanding ); + slotUpdateButtons(); +} + +void KACLEditWidget::slotUpdateButtons() +{ + bool atLeastOneIsNotDeletable = false; + bool atLeastOneIsNotAllowedToChangeType = false; + int selectedCount = 0; + QListViewItemIterator it( m_listView, QListViewItemIterator::Selected ); + while ( KACLListViewItem *item = dynamic_cast<KACLListViewItem*>( it.current() ) ) { + ++it; ++selectedCount; + if ( !item->isDeletable() ) + atLeastOneIsNotDeletable = true; + if ( !item->isAllowedToChangeType() ) + atLeastOneIsNotAllowedToChangeType = true; + } + m_EditBtn->setEnabled( selectedCount && !atLeastOneIsNotAllowedToChangeType ); + m_DelBtn->setEnabled( selectedCount && !atLeastOneIsNotDeletable ); +} + +KACL KACLEditWidget::getACL() const +{ + return m_listView->getACL(); +} + +KACL KACLEditWidget::getDefaultACL() const +{ + return m_listView->getDefaultACL(); +} + +void KACLEditWidget::setACL( const KACL &acl ) +{ + return m_listView->setACL( acl ); +} + +void KACLEditWidget::setDefaultACL( const KACL &acl ) +{ + return m_listView->setDefaultACL( acl ); +} + +void KACLEditWidget::setAllowDefaults( bool value ) +{ + m_listView->setAllowDefaults( value ); +} + +void KACLEditWidget::setReadOnly( bool on ) +{ + m_listView->setEnabled( !on ); + m_AddBtn->setEnabled( !on ); + if ( !on ) + slotUpdateButtons(); +} + +KACLListViewItem::KACLListViewItem( QListView* parent, + KACLListView::EntryType _type, + unsigned short _value, bool defaults, + const QString& _qualifier ) + : KListViewItem( parent, parent->lastItem() ), // we want to append + type( _type ), value( _value ), isDefault( defaults ), + qualifier( _qualifier ), isPartial( false ) +{ + m_pACLListView = dynamic_cast<KACLListView*>( parent ); + repaint(); +} + + +KACLListViewItem::~ KACLListViewItem() +{ + +} + +QString KACLListViewItem::key( int, bool ) const +{ + QString key; + if ( !isDefault ) + key = "A"; + else + key = "B"; + switch ( type ) + { + case KACLListView::User: + key += "A"; + break; + case KACLListView::Group: + key += "B"; + break; + case KACLListView::Others: + key += "C"; + break; + case KACLListView::Mask: + key += "D"; + break; + case KACLListView::NamedUser: + key += "E" + text( 1 ); + break; + case KACLListView::NamedGroup: + key += "F" + text( 1 ); + break; + default: + key += text( 0 ); + break; + } + return key; +} + +void KACLListViewItem::paintCell( QPainter* p, const QColorGroup &cg, + int column, int width, int alignment ) +{ + QColorGroup mycg = cg; + if ( isDefault ) { + mycg.setColor( QColorGroup::Text, QColor( 0, 0, 255 ) ); + } + if ( isPartial ) { + QFont font = p->font(); + font.setItalic( true ); + mycg.setColor( QColorGroup::Text, QColor( 100, 100, 100 ) ); + p->setFont( font ); + } + KListViewItem::paintCell( p, mycg, column, width, alignment ); + + KACLListViewItem *below =0; + if ( itemBelow() ) + below = static_cast<KACLListViewItem*>( itemBelow() ); + const bool lastUser = type == KACLListView::NamedUser && below && below->type == KACLListView::NamedGroup; + const bool lastNonDefault = !isDefault && below && below->isDefault; + if ( type == KACLListView::Mask || lastUser || lastNonDefault ) + { + p->setPen( QPen( Qt::gray, 0, QPen::DotLine ) ); + if ( type == KACLListView::Mask ) + p->drawLine( 0, 0, width - 1, 0 ); + p->drawLine( 0, height() - 1, width - 1, height() - 1 ); + } +} + + +void KACLListViewItem::updatePermPixmaps() +{ + unsigned int partialPerms = value; + + if ( value & ACL_READ ) + setPixmap( 2, m_pACLListView->getYesPixmap() ); + else if ( partialPerms & ACL_READ ) + setPixmap( 2, m_pACLListView->getYesPartialPixmap() ); + else + setPixmap( 2, QPixmap() ); + + if ( value & ACL_WRITE ) + setPixmap( 3, m_pACLListView->getYesPixmap() ); + else if ( partialPerms & ACL_WRITE ) + setPixmap( 3, m_pACLListView->getYesPartialPixmap() ); + else + setPixmap( 3, QPixmap() ); + + if ( value & ACL_EXECUTE ) + setPixmap( 4, m_pACLListView->getYesPixmap() ); + else if ( partialPerms & ACL_EXECUTE ) + setPixmap( 4, m_pACLListView->getYesPartialPixmap() ); + else + setPixmap( 4, QPixmap() ); +} + +void KACLListViewItem::repaint() +{ + int idx = 0; + switch ( type ) + { + case KACLListView::User: + idx = KACLListView::OWNER_IDX; + break; + case KACLListView::Group: + idx = KACLListView::GROUP_IDX; + break; + case KACLListView::Others: + idx = KACLListView::OTHERS_IDX; + break; + case KACLListView::Mask: + idx = KACLListView::MASK_IDX; + break; + case KACLListView::NamedUser: + idx = KACLListView::NAMED_USER_IDX; + break; + case KACLListView::NamedGroup: + idx = KACLListView::NAMED_GROUP_IDX; + break; + default: + idx = KACLListView::OWNER_IDX; + break; + } + setText( 0, i18n(s_itemAttributes[idx].label) ); + setPixmap( 0, *s_itemAttributes[idx].pixmap ); + if ( isDefault ) + setText( 0, text( 0 ) + i18n( " (Default)" ) ); + setText( 1, qualifier ); + // Set the pixmaps for which of the perms are set + updatePermPixmaps(); +} + +void KACLListViewItem::calcEffectiveRights() +{ + QString strEffective = QString( "---" ); + + // Do we need to worry about the mask entry? It applies to named users, + // owning group, and named groups + if ( m_pACLListView->hasMaskEntry() + && ( type == KACLListView::NamedUser + || type == KACLListView::Group + || type == KACLListView::NamedGroup ) + && !isDefault ) + { + + strEffective[0] = ( m_pACLListView->maskPermissions() & value & ACL_READ ) ? 'r' : '-'; + strEffective[1] = ( m_pACLListView->maskPermissions() & value & ACL_WRITE ) ? 'w' : '-'; + strEffective[2] = ( m_pACLListView->maskPermissions() & value & ACL_EXECUTE ) ? 'x' : '-'; +/* + // What about any partial perms? + if ( maskPerms & partialPerms & ACL_READ || // Partial perms on entry + maskPartialPerms & perms & ACL_READ || // Partial perms on mask + maskPartialPerms & partialPerms & ACL_READ ) // Partial perms on mask and entry + strEffective[0] = 'R'; + if ( maskPerms & partialPerms & ACL_WRITE || // Partial perms on entry + maskPartialPerms & perms & ACL_WRITE || // Partial perms on mask + maskPartialPerms & partialPerms & ACL_WRITE ) // Partial perms on mask and entry + strEffective[1] = 'W'; + if ( maskPerms & partialPerms & ACL_EXECUTE || // Partial perms on entry + maskPartialPerms & perms & ACL_EXECUTE || // Partial perms on mask + maskPartialPerms & partialPerms & ACL_EXECUTE ) // Partial perms on mask and entry + strEffective[2] = 'X'; +*/ + } + else + { + // No, the effective value are just the value in this entry + strEffective[0] = ( value & ACL_READ ) ? 'r' : '-'; + strEffective[1] = ( value & ACL_WRITE ) ? 'w' : '-'; + strEffective[2] = ( value & ACL_EXECUTE ) ? 'x' : '-'; + + /* + // What about any partial perms? + if ( partialPerms & ACL_READ ) + strEffective[0] = 'R'; + if ( partialPerms & ACL_WRITE ) + strEffective[1] = 'W'; + if ( partialPerms & ACL_EXECUTE ) + strEffective[2] = 'X'; + */ + } + setText( 5, strEffective ); +} + +bool KACLListViewItem::isDeletable() const +{ + bool isMaskAndDeletable = false; + if (type == KACLListView::Mask ) { + if ( !isDefault && m_pACLListView->maskCanBeDeleted() ) + isMaskAndDeletable = true; + else if ( isDefault && m_pACLListView->defaultMaskCanBeDeleted() ) + isMaskAndDeletable = true; + } + return type != KACLListView::User && + type != KACLListView::Group && + type != KACLListView::Others && + ( type != KACLListView::Mask || isMaskAndDeletable ); +} + +bool KACLListViewItem::isAllowedToChangeType() const +{ + return type != KACLListView::User && + type != KACLListView::Group && + type != KACLListView::Others && + type != KACLListView::Mask; +} + +void KACLListViewItem::togglePerm( acl_perm_t perm ) +{ + value ^= perm; // Toggle the perm + if ( type == KACLListView::Mask && !isDefault ) { + m_pACLListView->setMaskPermissions( value ); + } + calcEffectiveRights(); + updatePermPixmaps(); +/* + // If the perm is in the partial perms then remove it. i.e. Once + // a user changes a partial perm it then applies to all selected files. + if ( m_pEntry->m_partialPerms & perm ) + m_pEntry->m_partialPerms ^= perm; + + m_pEntry->setPartialEntry( false ); + // Make sure that all entries have their effective rights calculated if + // we are changing the ACL_MASK entry. + if ( type == Mask ) + { + m_pACLListView->setMaskPartialPermissions( m_pEntry->m_partialPerms ); + m_pACLListView->setMaskPermissions( value ); + m_pACLListView->calculateEffectiveRights(); + } +*/ +} + + + +EditACLEntryDialog::EditACLEntryDialog( KACLListView *listView, KACLListViewItem *item, + const QStringList &users, + const QStringList &groups, + const QStringList &defaultUsers, + const QStringList &defaultGroups, + int allowedTypes, int allowedDefaultTypes, + bool allowDefaults ) + : KDialogBase( listView, "edit_entry_dialog", true, + i18n( "Edit ACL Entry" ), KDialogBase::Ok|KDialogBase::Cancel, + KDialogBase::Ok, false ), + m_listView( listView ), m_item( item ), m_users( users ), m_groups( groups ), + m_defaultUsers( defaultUsers ), m_defaultGroups( defaultGroups ), + m_allowedTypes( allowedTypes ), m_allowedDefaultTypes( allowedDefaultTypes ), + m_defaultCB( 0 ) +{ + QWidget *page = new QWidget( this ); + setMainWidget( page ); + QVBoxLayout *mainLayout = new QVBoxLayout( page, 0, spacingHint(), "mainLayout" ); + m_buttonGroup = new QVButtonGroup( i18n("Entry Type"), page, "bg" ); + + if ( allowDefaults ) { + m_defaultCB = new QCheckBox( i18n("Default for new files in this folder"), page, "defaultCB" ); + mainLayout->addWidget( m_defaultCB ); + connect( m_defaultCB, SIGNAL( toggled( bool ) ), + this, SLOT( slotUpdateAllowedUsersAndGroups() ) ); + connect( m_defaultCB, SIGNAL( toggled( bool ) ), + this, SLOT( slotUpdateAllowedTypes() ) ); + + } + + mainLayout->addWidget( m_buttonGroup ); + + QRadioButton *ownerType = new QRadioButton( i18n("Owner"), m_buttonGroup, "ownerType" ); + m_buttonGroup->insert( ownerType, KACLListView::User ); + QRadioButton *owningGroupType = new QRadioButton( i18n("Owning Group"), m_buttonGroup, "owningGroupType" ); + m_buttonGroup->insert( owningGroupType, KACLListView::Group ); + QRadioButton *othersType = new QRadioButton( i18n("Others"), m_buttonGroup, "othersType" ); + m_buttonGroup->insert( othersType, KACLListView::Others ); + QRadioButton *maskType = new QRadioButton( i18n("Mask"), m_buttonGroup, "maskType" ); + m_buttonGroup->insert( maskType, KACLListView::Mask ); + QRadioButton *namedUserType = new QRadioButton( i18n("Named User"), m_buttonGroup, "namesUserType" ); + m_buttonGroup->insert( namedUserType, KACLListView::NamedUser ); + QRadioButton *namedGroupType = new QRadioButton( i18n("Named Group"), m_buttonGroup, "namedGroupType" ); + m_buttonGroup->insert( namedGroupType, KACLListView::NamedGroup ); + + connect( m_buttonGroup, SIGNAL( clicked( int ) ), + this, SLOT( slotSelectionChanged( int ) ) ); + + m_widgetStack = new QWidgetStack( page ); + mainLayout->addWidget( m_widgetStack ); + + QHBox *usersBox = new QHBox( m_widgetStack ); + m_widgetStack->addWidget( usersBox, KACLListView::NamedUser ); + + QHBox *groupsBox = new QHBox( m_widgetStack ); + m_widgetStack->addWidget( groupsBox, KACLListView::NamedGroup ); + + QLabel *usersLabel = new QLabel( i18n( "User: " ), usersBox ); + m_usersCombo = new QComboBox( false, usersBox, "users" ); + usersLabel->setBuddy( m_usersCombo ); + + QLabel *groupsLabel = new QLabel( i18n( "Group: " ), groupsBox ); + m_groupsCombo = new QComboBox( false, groupsBox, "groups" ); + groupsLabel->setBuddy( m_groupsCombo ); + + if ( m_item ) { + m_buttonGroup->setButton( m_item->type ); + if ( m_defaultCB ) + m_defaultCB->setChecked( m_item->isDefault ); + slotUpdateAllowedTypes(); + slotSelectionChanged( m_item->type ); + slotUpdateAllowedUsersAndGroups(); + if ( m_item->type == KACLListView::NamedUser ) { + m_usersCombo->setCurrentText( m_item->qualifier ); + } else if ( m_item->type == KACLListView::NamedGroup ) { + m_groupsCombo->setCurrentText( m_item->qualifier ); + } + } else { + // new entry, preselect "named user", arguably the most common one + m_buttonGroup->setButton( KACLListView::NamedUser ); + slotUpdateAllowedTypes(); + slotSelectionChanged( KACLListView::NamedUser ); + slotUpdateAllowedUsersAndGroups(); + } + incInitialSize( QSize( 100, 0 ) ); +} + +void EditACLEntryDialog::slotUpdateAllowedTypes() +{ + int allowedTypes = m_allowedTypes; + if ( m_defaultCB && m_defaultCB->isChecked() ) { + allowedTypes = m_allowedDefaultTypes; + } + for ( int i=1; i < KACLListView::AllTypes; i=i*2 ) { + if ( allowedTypes & i ) + m_buttonGroup->find( i )->show(); + else + m_buttonGroup->find( i )->hide(); + } +} + +void EditACLEntryDialog::slotUpdateAllowedUsersAndGroups() +{ + const QString oldUser = m_usersCombo->currentText(); + const QString oldGroup = m_groupsCombo->currentText(); + m_usersCombo->clear(); + m_groupsCombo->clear(); + if ( m_defaultCB && m_defaultCB->isChecked() ) { + m_usersCombo->insertStringList( m_defaultUsers ); + if ( m_defaultUsers.find( oldUser ) != m_defaultUsers.end() ) + m_usersCombo->setCurrentText( oldUser ); + m_groupsCombo->insertStringList( m_defaultGroups ); + if ( m_defaultGroups.find( oldGroup ) != m_defaultGroups.end() ) + m_groupsCombo->setCurrentText( oldGroup ); + } else { + m_usersCombo->insertStringList( m_users ); + if ( m_users.find( oldUser ) != m_users.end() ) + m_usersCombo->setCurrentText( oldUser ); + m_groupsCombo->insertStringList( m_groups ); + if ( m_groups.find( oldGroup ) != m_groups.end() ) + m_groupsCombo->setCurrentText( oldGroup ); + } +} +void EditACLEntryDialog::slotOk() +{ + KACLListView::EntryType type = static_cast<KACLListView::EntryType>( m_buttonGroup->selectedId() ); + + QString qualifier; + if ( type == KACLListView::NamedUser ) + qualifier = m_usersCombo->currentText(); + if ( type == KACLListView::NamedGroup ) + qualifier = m_groupsCombo->currentText(); + + if ( !m_item ) { + m_item = new KACLListViewItem( m_listView, type, ACL_READ | ACL_WRITE | ACL_EXECUTE, false, qualifier ); + } else { + m_item->type = type; + m_item->qualifier = qualifier; + } + if ( m_defaultCB ) + m_item->isDefault = m_defaultCB->isChecked(); + m_item->repaint(); + + KDialogBase::slotOk(); +} + +void EditACLEntryDialog::slotSelectionChanged( int id ) +{ + switch ( id ) { + case KACLListView::User: + case KACLListView::Group: + case KACLListView::Others: + case KACLListView::Mask: + m_widgetStack->setEnabled( false ); + break; + case KACLListView::NamedUser: + m_widgetStack->setEnabled( true ); + m_widgetStack->raiseWidget( KACLListView::NamedUser ); + break; + case KACLListView::NamedGroup: + m_widgetStack->setEnabled( true ); + m_widgetStack->raiseWidget( KACLListView::NamedGroup ); + break; + default: + break; + } +} + + +KACLListView::KACLListView( QWidget* parent, const char* name ) + : KListView( parent, name ), + m_hasMask( false ), m_allowDefaults( false ) +{ + // Add the columns + addColumn( i18n( "Type" ) ); + addColumn( i18n( "Name" ) ); + addColumn( i18n( "read permission", "r" ) ); + addColumn( i18n( "write permission", "w" ) ); + addColumn( i18n( "execute permission", "x" ) ); + addColumn( i18n( "Effective" ) ); + + header()->setClickEnabled( false ); + + // Load the avatars + for ( int i=0; i < LAST_IDX; ++i ) { + s_itemAttributes[i].pixmap = new QPixmap( qembed_findImage( s_itemAttributes[i].pixmapName ) ); + } + m_yesPixmap = new QPixmap( qembed_findImage( "yes" ) ); + m_yesPartialPixmap = new QPixmap( qembed_findImage( "yespartial" ) ); + + setSelectionMode( QListView::Extended ); + + // fill the lists of all legal users and groups + struct passwd *user = 0; + setpwent(); + while ( ( user = getpwent() ) != 0 ) { + m_allUsers << QString::fromLatin1( user->pw_name ); + } + endpwent(); + + struct group *gr = 0; + setgrent(); + while ( ( gr = getgrent() ) != 0 ) { + m_allGroups << QString::fromLatin1( gr->gr_name ); + } + endgrent(); + m_allUsers.sort(); + m_allGroups.sort(); +} + + +KACLListView::~KACLListView() +{ + for ( int i=0; i < LAST_IDX; ++i ) { + delete s_itemAttributes[i].pixmap; + } + delete m_yesPixmap; + delete m_yesPartialPixmap; +} + +QStringList KACLListView::allowedUsers( bool defaults, KACLListViewItem *allowedItem ) +{ + QStringList allowedUsers = m_allUsers; + QListViewItemIterator it( this ); + while ( it.current() ) { + const KACLListViewItem *item = static_cast<const KACLListViewItem*>( *it ); + ++it; + if ( !item->type == NamedUser || item->isDefault != defaults ) continue; + if ( allowedItem && item == allowedItem && allowedItem->isDefault == defaults ) continue; + allowedUsers.remove( item->qualifier ); + } + return allowedUsers; +} + +QStringList KACLListView::allowedGroups( bool defaults, KACLListViewItem *allowedItem ) +{ + QStringList allowedGroups = m_allGroups; + QListViewItemIterator it( this ); + while ( it.current() ) { + const KACLListViewItem *item = static_cast<const KACLListViewItem*>( *it ); + ++it; + if ( !item->type == NamedGroup || item->isDefault != defaults ) continue; + if ( allowedItem && item == allowedItem && allowedItem->isDefault == defaults ) continue; + allowedGroups.remove( item->qualifier ); + } + return allowedGroups; +} + +void KACLListView::fillItemsFromACL( const KACL &pACL, bool defaults ) +{ + // clear out old entries of that ilk + QListViewItemIterator it( this ); + while ( KACLListViewItem *item = static_cast<KACLListViewItem*>( it.current() ) ) { + ++it; + if ( item->isDefault == defaults ) + delete item; + } + KACLListViewItem *item = + new KACLListViewItem( this, User, pACL.ownerPermissions(), defaults ); + + item = new KACLListViewItem( this, Group, pACL.owningGroupPermissions(), defaults ); + + item = new KACLListViewItem( this, Others, pACL.othersPermissions(), defaults ); + + bool hasMask = false; + unsigned short mask = pACL.maskPermissions( hasMask ); + if ( hasMask ) { + item = new KACLListViewItem( this, Mask, mask, defaults ); + } + + // read all named user entries + const ACLUserPermissionsList &userList = pACL.allUserPermissions(); + ACLUserPermissionsConstIterator itu = userList.begin(); + while ( itu != userList.end() ) { + new KACLListViewItem( this, NamedUser, (*itu).second, defaults, (*itu).first ); + ++itu; + } + + // and now all named groups + const ACLUserPermissionsList &groupList = pACL.allGroupPermissions(); + ACLUserPermissionsConstIterator itg = groupList.begin(); + while ( itg != groupList.end() ) { + new KACLListViewItem( this, NamedGroup, (*itg).second, defaults, (*itg).first ); + ++itg; + } +} + +void KACLListView::setACL( const KACL &acl ) +{ + if ( !acl.isValid() ) return; + // Remove any entries left over from displaying a previous ACL + m_ACL = acl; + fillItemsFromACL( m_ACL ); + + m_mask = acl.maskPermissions( m_hasMask ); + calculateEffectiveRights(); +} + +void KACLListView::setDefaultACL( const KACL &acl ) +{ + if ( !acl.isValid() ) return; + m_defaultACL = acl; + fillItemsFromACL( m_defaultACL, true ); + calculateEffectiveRights(); +} + +KACL KACLListView::itemsToACL( bool defaults ) const +{ + KACL newACL( 0 ); + bool atLeastOneEntry = false; + ACLUserPermissionsList users; + ACLGroupPermissionsList groups; + QListViewItemIterator it( const_cast<KACLListView*>( this ) ); + while ( QListViewItem* qlvi = it.current() ) { + ++it; + const KACLListViewItem* item = static_cast<KACLListViewItem*>( qlvi ); + if ( item->isDefault != defaults ) continue; + atLeastOneEntry = true; + switch ( item->type ) { + case User: + newACL.setOwnerPermissions( item->value ); + break; + case Group: + newACL.setOwningGroupPermissions( item->value ); + break; + case Others: + newACL.setOthersPermissions( item->value ); + break; + case Mask: + newACL.setMaskPermissions( item->value ); + break; + case NamedUser: + users.append( qMakePair( item->text( 1 ), item->value ) ); + break; + case NamedGroup: + groups.append( qMakePair( item->text( 1 ), item->value ) ); + break; + default: + break; + } + } + if ( atLeastOneEntry ) { + newACL.setAllUserPermissions( users ); + newACL.setAllGroupPermissions( groups ); + if ( newACL.isValid() ) + return newACL; + } + return KACL(); +} + +KACL KACLListView::getACL() +{ + return itemsToACL( false ); +} + + +KACL KACLListView::getDefaultACL() +{ + return itemsToACL( true ); +} + +void KACLListView::contentsMousePressEvent( QMouseEvent * e ) +{ + QListViewItem *clickedItem = itemAt( contentsToViewport( e->pos() ) ); + if ( !clickedItem ) return; + // if the click is on an as yet unselected item, select it first + if ( !clickedItem->isSelected() ) + KListView::contentsMousePressEvent( e ); + + if ( !currentItem() ) return; + int column = header()->sectionAt( e->x() ); + acl_perm_t perm; + switch ( column ) + { + case 2: + perm = ACL_READ; + break; + case 3: + perm = ACL_WRITE; + break; + case 4: + perm = ACL_EXECUTE; + break; + default: + return KListView::contentsMousePressEvent( e ); + } + KACLListViewItem* referenceItem = static_cast<KACLListViewItem*>( clickedItem ); + unsigned short referenceHadItSet = referenceItem->value & perm; + QListViewItemIterator it( this ); + while ( KACLListViewItem* item = static_cast<KACLListViewItem*>( it.current() ) ) { + ++it; + if ( !item->isSelected() ) continue; + // toggle those with the same value as the clicked item, leave the others + if ( referenceHadItSet == ( item->value & perm ) ) + item->togglePerm( perm ); + } +} + +void KACLListView::entryClicked( QListViewItem* pItem, const QPoint& /*pt*/, int col ) +{ + if ( !pItem ) return; + + QListViewItemIterator it( this ); + while ( KACLListViewItem* item = static_cast<KACLListViewItem*>( it.current() ) ) { + ++it; + if ( !item->isSelected() ) continue; + switch ( col ) + { + case 2: + item->togglePerm( ACL_READ ); + break; + case 3: + item->togglePerm( ACL_WRITE ); + break; + case 4: + item->togglePerm( ACL_EXECUTE ); + break; + + default: + ; // Do nothing + } + } + /* + // Has the user changed one of the required entries in a default ACL? + if ( m_pACL->aclType() == ACL_TYPE_DEFAULT && + ( col == 2 || col == 3 || col == 4 ) && + ( pACLItem->entryType() == ACL_USER_OBJ || + pACLItem->entryType() == ACL_GROUP_OBJ || + pACLItem->entryType() == ACL_OTHER ) ) + { + // Mark the required entries as no longer being partial entries. + // That is, they will get applied to all selected directories. + KACLListViewItem* pUserObj = findACLEntryByType( this, ACL_USER_OBJ ); + pUserObj->entry()->setPartialEntry( false ); + + KACLListViewItem* pGroupObj = findACLEntryByType( this, ACL_GROUP_OBJ ); + pGroupObj->entry()->setPartialEntry( false ); + + KACLListViewItem* pOther = findACLEntryByType( this, ACL_OTHER ); + pOther->entry()->setPartialEntry( false ); + + update(); + } + */ +} + + +void KACLListView::calculateEffectiveRights() +{ + QListViewItemIterator it( this ); + KACLListViewItem* pItem; + while ( ( pItem = dynamic_cast<KACLListViewItem*>( it.current() ) ) != 0 ) + { + ++it; + pItem->calcEffectiveRights(); + } +} + + +unsigned short KACLListView::maskPermissions() const +{ + return m_mask; +} + + +void KACLListView::setMaskPermissions( unsigned short maskPerms ) +{ + m_mask = maskPerms; + calculateEffectiveRights(); +} + + +acl_perm_t KACLListView::maskPartialPermissions() const +{ + // return m_pMaskEntry->m_partialPerms; + return 0; +} + + +void KACLListView::setMaskPartialPermissions( acl_perm_t /*maskPartialPerms*/ ) +{ + //m_pMaskEntry->m_partialPerms = maskPartialPerms; + calculateEffectiveRights(); +} + +bool KACLListView::hasDefaultEntries() const +{ + QListViewItemIterator it( const_cast<KACLListView*>( this ) ); + while ( it.current() ) { + const KACLListViewItem *item = static_cast<const KACLListViewItem*>( it.current() ); + ++it; + if ( item->isDefault ) return true; + } + return false; +} + +const KACLListViewItem* KACLListView::findDefaultItemByType( EntryType type ) const +{ + return findItemByType( type, true ); +} + +const KACLListViewItem* KACLListView::findItemByType( EntryType type, bool defaults ) const +{ + QListViewItemIterator it( const_cast<KACLListView*>( this ) ); + while ( it.current() ) { + const KACLListViewItem *item = static_cast<const KACLListViewItem*>( it.current() ); + ++it; + if ( item->isDefault == defaults && item->type == type ) { + return item; + } + } + return 0; +} + + +unsigned short KACLListView::calculateMaskValue( bool defaults ) const +{ + // KACL auto-adds the relevant maks entries, so we can simply query + bool dummy; + return itemsToACL( defaults ).maskPermissions( dummy ); +} + +void KACLListView::slotAddEntry() +{ + int allowedTypes = NamedUser | NamedGroup; + if ( !m_hasMask ) + allowedTypes |= Mask; + int allowedDefaultTypes = NamedUser | NamedGroup; + if ( !findDefaultItemByType( Mask ) ) + allowedDefaultTypes |= Mask; + if ( !hasDefaultEntries() ) + allowedDefaultTypes |= User | Group; + EditACLEntryDialog dlg( this, 0, + allowedUsers( false ), allowedGroups( false ), + allowedUsers( true ), allowedGroups( true ), + allowedTypes, allowedDefaultTypes, m_allowDefaults ); + dlg.exec(); + KACLListViewItem *item = dlg.item(); + if ( !item ) return; // canceled + if ( item->type == Mask && !item->isDefault ) { + m_hasMask = true; + m_mask = item->value; + } + if ( item->isDefault && !hasDefaultEntries() ) { + // first default entry, fill in what is needed + if ( item->type != User ) { + unsigned short v = findDefaultItemByType( User )->value; + new KACLListViewItem( this, User, v, true ); + } + if ( item->type != Group ) { + unsigned short v = findDefaultItemByType( Group )->value; + new KACLListViewItem( this, Group, v, true ); + } + if ( item->type != Others ) { + unsigned short v = findDefaultItemByType( Others )->value; + new KACLListViewItem( this, Others, v, true ); + } + } + const KACLListViewItem *defaultMaskItem = findDefaultItemByType( Mask ); + if ( item->isDefault && !defaultMaskItem ) { + unsigned short v = calculateMaskValue( true ); + new KACLListViewItem( this, Mask, v, true ); + } + if ( !item->isDefault && !m_hasMask && + ( item->type == Group + || item->type == NamedUser + || item->type == NamedGroup ) ) { + // auto-add a mask entry + unsigned short v = calculateMaskValue( false ); + new KACLListViewItem( this, Mask, v, false ); + m_hasMask = true; + m_mask = v; + } + calculateEffectiveRights(); + sort(); + setCurrentItem( item ); + // QListView doesn't seem to emit, in this case, and we need to update + // the buttons... + if ( childCount() == 1 ) + emit currentChanged( item ); +} + +void KACLListView::slotEditEntry() +{ + QListViewItem * current = currentItem(); + if ( !current ) return; + KACLListViewItem *item = static_cast<KACLListViewItem*>( current ); + int allowedTypes = item->type | NamedUser | NamedGroup; + bool itemWasMask = item->type == Mask; + if ( !m_hasMask || itemWasMask ) + allowedTypes |= Mask; + int allowedDefaultTypes = item->type | NamedUser | NamedGroup; + if ( !findDefaultItemByType( Mask ) ) + allowedDefaultTypes |= Mask; + if ( !hasDefaultEntries() ) + allowedDefaultTypes |= User | Group; + + EditACLEntryDialog dlg( this, item, + allowedUsers( false, item ), allowedGroups( false, item ), + allowedUsers( true, item ), allowedGroups( true, item ), + allowedTypes, allowedDefaultTypes, m_allowDefaults ); + dlg.exec(); + if ( itemWasMask && item->type != Mask ) { + m_hasMask = false; + m_mask = 0; + } + if ( !itemWasMask && item->type == Mask ) { + m_mask = item->value; + m_hasMask = true; + } + calculateEffectiveRights(); + sort(); +} + +void KACLListView::slotRemoveEntry() +{ + QListViewItemIterator it( this, QListViewItemIterator::Selected ); + while ( it.current() ) { + KACLListViewItem *item = static_cast<KACLListViewItem*>( it.current() ); + ++it; + /* First check if it's a mask entry and if so, make sure that there is + * either no name user or group entry, which means the mask can be + * removed, or don't remove it, but reset it. That is allowed. */ + if ( item->type == Mask ) { + bool itemWasDefault = item->isDefault; + if ( !itemWasDefault && maskCanBeDeleted() ) { + m_hasMask= false; + m_mask = 0; + delete item; + } else if ( itemWasDefault && defaultMaskCanBeDeleted() ) { + delete item; + } else { + item->value = 0; + item->repaint(); + } + if ( !itemWasDefault ) + calculateEffectiveRights(); + } else { + // for the base permissions, disable them, which is what libacl does + if ( !item->isDefault && + ( item->type == User + || item->type == Group + || item->type == Others ) ) { + item->value = 0; + item->repaint(); + } else { + delete item; + } + } + } +} + +bool KACLListView::maskCanBeDeleted() const +{ + return !findItemByType( NamedUser ) && !findItemByType( NamedGroup ); +} + +bool KACLListView::defaultMaskCanBeDeleted() const +{ + return !findDefaultItemByType( NamedUser ) && !findDefaultItemByType( NamedGroup ); +} + +#include "kacleditwidget.moc" +#include "kacleditwidget_p.moc" +#endif +// vim:set ts=8 sw=4: diff --git a/kio/kfile/kacleditwidget.h b/kio/kfile/kacleditwidget.h new file mode 100644 index 000000000..4fec2d7c3 --- /dev/null +++ b/kio/kfile/kacleditwidget.h @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (C) 2005 by Sean Harmer <[email protected]> * + * Till Adam <[email protected]> * + * * + * This program 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 program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#ifndef KACLEDITWIDGET_H +#define KACLEDITWIDGET_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef USE_POSIX_ACL + +#include <klistview.h> +#include <kacl.h> +#include <kfileitem.h> + +class KACLListViewItem; +class KACLListView; +class QPushButton; + +class KACLEditWidget : QWidget +{ + Q_OBJECT +public: + KACLEditWidget( QWidget *parent = 0, const char *name = 0 ); + KACL getACL() const; + KACL getDefaultACL() const; + void setACL( const KACL & ); + void setDefaultACL( const KACL & ); + void setAllowDefaults( bool value ); + void setReadOnly( bool value ); +private slots: + void slotUpdateButtons(); + +private: + KACLListView* m_listView; + QPushButton *m_AddBtn; + QPushButton *m_EditBtn; + QPushButton *m_DelBtn; +}; + + +#endif +#endif diff --git a/kio/kfile/kacleditwidget_p.h b/kio/kfile/kacleditwidget_p.h new file mode 100644 index 000000000..6a5a7d074 --- /dev/null +++ b/kio/kfile/kacleditwidget_p.h @@ -0,0 +1,200 @@ +/*************************************************************************** + * Copyright (C) 2005 by Sean Harmer <[email protected]> * + * Till Adam <[email protected]> * + * * + * This program 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 program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#ifndef KACLEDITWIDGET_P_H +#define KACLEDITWIDGET_P_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef USE_POSIX_ACL +#include <klistview.h> +#include <sys/acl.h> +#include <kacl.h> +#include <kfileitem.h> +#include <kdialogbase.h> +#include <qpixmap.h> +#include <qcombobox.h> + +class KACLListViewItem; +class QPushButton; +class QVButtonGroup; +class KACLListView; +class QWidgetStack; +class QCheckBox; + +/** +@author Sean Harmer +*/ +class KACLListView : public KListView +{ +Q_OBJECT + friend class KACLListViewItem; +public: + enum Types + { + OWNER_IDX = 0, + GROUP_IDX, + OTHERS_IDX, + MASK_IDX, + NAMED_USER_IDX, + NAMED_GROUP_IDX, + LAST_IDX + }; + enum EntryType { User = 1, + Group = 2, + Others = 4, + Mask = 8, + NamedUser = 16, + NamedGroup = 32, + AllTypes = 63 }; + + KACLListView( QWidget* parent = 0, const char* name = 0 ); + ~KACLListView(); + + bool hasMaskEntry() const { return m_hasMask; } + bool hasDefaultEntries() const; + bool allowDefaults() const { return m_allowDefaults; } + void setAllowDefaults( bool v ) { m_allowDefaults = v; } + unsigned short maskPermissions() const; + void setMaskPermissions( unsigned short maskPerms ); + acl_perm_t maskPartialPermissions() const; + void setMaskPartialPermissions( acl_perm_t maskPerms ); + + bool maskCanBeDeleted() const; + bool defaultMaskCanBeDeleted() const; + + const KACLListViewItem* findDefaultItemByType( EntryType type ) const; + const KACLListViewItem* findItemByType( EntryType type, + bool defaults = false ) const; + unsigned short calculateMaskValue( bool defaults ) const; + void calculateEffectiveRights(); + + QStringList allowedUsers( bool defaults, KACLListViewItem *allowedItem = 0 ); + QStringList allowedGroups( bool defaults, KACLListViewItem *allowedItem = 0 ); + + const KACL getACL() const { return getACL(); } + KACL getACL(); + + const KACL getDefaultACL() const { return getDefaultACL(); } + KACL getDefaultACL(); + + QPixmap getYesPixmap() const { return *m_yesPixmap; } + QPixmap getYesPartialPixmap() const { return *m_yesPartialPixmap; } + +public slots: + void slotAddEntry(); + void slotEditEntry(); + void slotRemoveEntry(); + void setACL( const KACL &anACL ); + void setDefaultACL( const KACL &anACL ); + +protected slots: + void entryClicked( QListViewItem* pItem, const QPoint& pt, int col ); +protected: + void contentsMousePressEvent( QMouseEvent * e ); + +private: + void fillItemsFromACL( const KACL &pACL, bool defaults = false ); + KACL itemsToACL( bool defaults ) const; + + KACL m_ACL; + KACL m_defaultACL; + unsigned short m_mask; + bool m_hasMask; + bool m_allowDefaults; + QStringList m_allUsers; + QStringList m_allGroups; + QPixmap* m_yesPixmap; + QPixmap* m_yesPartialPixmap; +}; + +class EditACLEntryDialog : public KDialogBase +{ + Q_OBJECT +public: + EditACLEntryDialog( KACLListView *listView, KACLListViewItem *item, + const QStringList &users, + const QStringList &groups, + const QStringList &defaultUsers, + const QStringList &defaultGroups, + int allowedTypes = KACLListView::AllTypes, + int allowedDefaultTypes = KACLListView::AllTypes, + bool allowDefault = false ); + KACLListViewItem* item() const { return m_item; } +public slots: + void slotOk(); + void slotSelectionChanged( int id ); +private slots: + void slotUpdateAllowedUsersAndGroups(); + void slotUpdateAllowedTypes(); +private: + KACLListView *m_listView; + KACLListViewItem *m_item; + QStringList m_users; + QStringList m_groups; + QStringList m_defaultUsers; + QStringList m_defaultGroups; + int m_allowedTypes; + int m_allowedDefaultTypes; + QVButtonGroup *m_buttonGroup; + QComboBox *m_usersCombo; + QComboBox *m_groupsCombo; + QWidgetStack *m_widgetStack; + QCheckBox *m_defaultCB; +}; + + +class KACLListViewItem : public KListViewItem +{ +public: + KACLListViewItem( QListView* parent, KACLListView::EntryType type, + unsigned short value, + bool defaultEntry, + const QString& qualifier = QString::null ); + virtual ~KACLListViewItem(); + virtual QString key( int column, bool ascending ) const; + + void calcEffectiveRights(); + + bool isDeletable() const; + bool isAllowedToChangeType() const; + + void togglePerm( acl_perm_t perm ); + + virtual void paintCell( QPainter *p, const QColorGroup &cg, + int column, int width, int alignment ); + + void updatePermPixmaps(); + void repaint(); + + KACLListView::EntryType type; + unsigned short value; + bool isDefault; + QString qualifier; + bool isPartial; + +private: + KACLListView* m_pACLListView; +}; + + +#endif +#endif diff --git a/kio/kfile/kcombiview.cpp b/kio/kfile/kcombiview.cpp new file mode 100644 index 000000000..fc16054f1 --- /dev/null +++ b/kio/kfile/kcombiview.cpp @@ -0,0 +1,371 @@ +/* This file is part of the KDE libraries + Copyright (C) 1998 Stephan Kulow <[email protected]> + 1998 Daniel Grana <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// $Id$ + +#include <assert.h> + +#include "kfileitem.h" +#include "kcombiview.h" +#include "kfileiconview.h" +#include "kfiledetailview.h" +#include "config-kfile.h" + +#include <qevent.h> + +#include <qdir.h> + +#include <kapplication.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kglobal.h> + +#include <qvaluelist.h> + +KCombiView::KCombiView( QWidget *parent, const char *name) + : QSplitter( parent, name), + KFileView(), + right(0), + m_lastViewForNextItem(0), + m_lastViewForPrevItem(0) +{ + left = new KFileIconView( this, "left" ); + left->setAcceptDrops(false); + left->viewport()->setAcceptDrops(false); + left->setGridX( 160 ); + left->KFileView::setViewMode( Directories ); + left->setArrangement( QIconView::LeftToRight ); + left->setParentView( this ); + left->setAcceptDrops(false); + left->installEventFilter( this ); + + connect( sig, SIGNAL( sortingChanged( QDir::SortSpec ) ), + SLOT( slotSortingChanged( QDir::SortSpec ) )); +} + +KCombiView::~KCombiView() +{ + delete right; +} + +void KCombiView::setRight(KFileView *view) +{ + delete right; + right = view; + right->KFileView::setViewMode( Files ); + setViewName( right->viewName() ); + + QValueList<int> lst; + lst << left->gridX() + 2 * left->spacing(); + setSizes( lst ); + setResizeMode( left, QSplitter::KeepSize ); + + right->setParentView( this ); + right->widget()->setAcceptDrops(acceptDrops()); + right->setDropOptions(dropOptions()); + right->widget()->installEventFilter( this ); +} + + +void KCombiView::insertItem( KFileItem *item ) +{ + KFileView::insertItem( item ); + + if ( item->isDir() ) { + left->updateNumbers( item ); + left->insertItem( item ); + } + else { + right->updateNumbers( item ); + right->insertItem( item ); + } +} + +void KCombiView::setSorting( QDir::SortSpec sort ) +{ + if ( !right ) + kdFatal() << "You need to call setRight( someview ) before!" << endl; + right->setSorting( sort ); + left->setSorting( sort ); + + KFileView::setSorting( right->sorting() ); +} + +void KCombiView::clearView() +{ + left->clearView(); + if ( right ) + right->clearView(); +} + +void KCombiView::updateView( bool b ) +{ + left->updateView( b ); + if ( right ) + right->updateView( b ); +} + +void KCombiView::updateView( const KFileItem *i ) +{ + left->updateView( i ); + if ( right ) + right->updateView( i ); +} + +void KCombiView::removeItem( const KFileItem *i ) +{ + left->removeItem( i ); + if ( right ) + right->removeItem( i ); + KFileView::removeItem( i ); +} + +void KCombiView::listingCompleted() +{ + left->listingCompleted(); + if ( right ) + right->listingCompleted(); +} + +void KCombiView::clear() +{ + KFileView::clear(); + left->KFileView::clear(); + if ( right ) + right->clear(); +} + +void KCombiView::clearSelection() +{ + left->clearSelection(); + if ( right ) + right->clearSelection(); +} + +void KCombiView::selectAll() +{ + left->selectAll(); + if ( right ) + right->selectAll(); +} + +void KCombiView::invertSelection() +{ + left->invertSelection(); + if ( right ) + right->invertSelection(); +} + +bool KCombiView::isSelected( const KFileItem *item ) const +{ + assert( right ); // for performance reasons no if ( right ) check. + return (right->isSelected( item ) || left->isSelected( item )); +} + +void KCombiView::setSelectionMode( KFile::SelectionMode sm ) +{ + // I think the left view (directories should always be in + // Single-Mode, right? + // left->setSelectionMode( sm ); + if ( !right ) + kdFatal() << "You need to call setRight( someview ) before!" << endl; + right->setSelectionMode( sm ); +} + +void KCombiView::setSelected( const KFileItem *item, bool enable ) +{ + left->setSelected( item, enable ); + if ( right ) + right->setSelected( item, enable ); +} + +void KCombiView::setCurrentItem( const KFileItem *item ) +{ + left->setCurrentItem( item ); + if ( right ) + right->setCurrentItem( item ); +} + +KFileItem * KCombiView::currentFileItem() const +{ + // we can actually have two current items, one in each view. So we simply + // prefer the fileview's item over the directory's. + // Smarter: if the right view has focus, prefer that over the left. + if ( !right ) + return left->currentFileItem(); + + KFileView *preferredView = focusView( right ); + KFileItem *item = preferredView->currentFileItem(); + if ( !item && preferredView != left ) + item = left->currentFileItem(); + + return item; +} + +void KCombiView::ensureItemVisible(const KFileItem *item) +{ + left->ensureItemVisible( item ); + if ( right ) + right->ensureItemVisible( item ); +} + +KFileItem * KCombiView::firstFileItem() const +{ + if ( !right ) + return left->firstFileItem(); + + KFileView *preferredView = focusView( left ); + KFileView *otherView = (preferredView == left) ? right : left; + KFileItem *item = preferredView->firstFileItem(); + if ( !item ) + item = otherView->firstFileItem(); + + return item; +} + +KFileItem * KCombiView::nextItem( const KFileItem *fileItem ) const +{ + if ( !right ) + return left->nextItem( fileItem ); + + KFileView *preferredView = focusView( left ); + KFileView *otherView = (preferredView == left) ? right : left; + KFileItem *item = preferredView->nextItem( fileItem ); + + if ( item ) + m_lastViewForNextItem = preferredView; + else { // no item, check other view + // when changing from one to another view, we need to continue + // with the next view's first item! + if ( m_lastViewForNextItem != otherView ) { + m_lastViewForNextItem = otherView; + return otherView->firstFileItem(); + } + + item = otherView->nextItem( fileItem ); + m_lastViewForNextItem = otherView; + } + + return item; +} + +KFileItem * KCombiView::prevItem( const KFileItem *fileItem ) const +{ + if ( !right ) + return left->nextItem( fileItem ); + + KFileView *preferredView = focusView( left ); + KFileView *otherView = (preferredView == left) ? right : left; + KFileItem *item = preferredView->prevItem( fileItem ); + if ( item ) + m_lastViewForPrevItem = preferredView; + + else { // no item, check other view + // when changing from one to another view, we need to continue + // with the next view's last item! + if ( m_lastViewForPrevItem != otherView ) { + fileItem = otherView->firstFileItem(); + while ( otherView->nextItem( fileItem ) ) // find the last item + fileItem = otherView->nextItem( fileItem ); + } + + item = otherView->prevItem( fileItem ); + m_lastViewForPrevItem = otherView; + } + + return item; +} + +void KCombiView::slotSortingChanged( QDir::SortSpec sorting ) +{ + KFileView::setSorting( sorting ); +} + +KFileView *KCombiView::focusView( KFileView *preferred ) const +{ + QWidget *w = focusWidget(); + KFileView *other = (right == preferred) ? left : right; + return (preferred && w == preferred->widget()) ? preferred : other; +} + +void KCombiView::readConfig( KConfig *config, const QString& group ) +{ + left->readConfig( config, group ); + if ( right ) + right->readConfig( config, group ); +} + +void KCombiView::writeConfig( KConfig *config, const QString& group ) +{ + left->writeConfig( config, group ); + if ( right ) + right->writeConfig( config, group ); +} + +KActionCollection * KCombiView::actionCollection() const +{ + return focusView( right )->actionCollection(); +} + +void KCombiView::setAcceptDrops(bool b) +{ + left->setAcceptDrops(b); + if (right) + right->widget()->setAcceptDrops(b); + QSplitter::setAcceptDrops(b); +} + +void KCombiView::setDropOptions_impl(int options) +{ + KFileView::setDropOptions_impl(options); + left->setDropOptions(options); + if (right) + right->setDropOptions(options); +} + +void KCombiView::virtual_hook( int id, void* data ) +{ + switch(id) { + case VIRTUAL_SET_DROP_OPTIONS: + setDropOptions_impl(*(int *)data); + break; + default: + KFileView::virtual_hook( id, data ); + } +} + +bool KCombiView::eventFilter( QObject *o, QEvent *e ) +{ + int type = e->type(); + + // only the focused view may have a selection + if ( type == QEvent::FocusIn ) + { + if ( o == left ) + right->clearSelection(); + else if ( o == right->widget() ) + left->clearSelection(); + } + + return QSplitter::eventFilter( o, e ); +} + +#include "kcombiview.moc" + diff --git a/kio/kfile/kcombiview.h b/kio/kfile/kcombiview.h new file mode 100644 index 000000000..b38876d71 --- /dev/null +++ b/kio/kfile/kcombiview.h @@ -0,0 +1,133 @@ +/* -*- c++ -*- + This file is part of the KDE libraries + Copyright (C) 1998 Stephan Kulow <[email protected]> + 1998 Daniel Grana <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _KCOMBIVIEW_H +#define _KCOMBIVIEW_H + +#include <qsplitter.h> +#include <klocale.h> + +#include <kfile.h> +#include <kfileview.h> + +class KFileIconView; +class QEvent; +class QIconViewItem; + +/** + * This view is designed to combine two KFileViews into one widget, to show + * directories on the left side and files on the right side. + * + * Methods like selectedItems() to query status _only_ work on the right side, + * i.e. on the files. + * + * After creating the KCombiView, you need to supply the view shown in the + * right, (see setRight()). Available KFileView implementations are + * KFileIconView and KFileDetailView. + * + * Most of the below methods are just implementations of the baseclass + * KFileView, so look there for documentation. + * + * @see KFileView + * @see KFileIconView + * @see KFileDetailView + * @see KDirOperator + */ +class KIO_EXPORT KCombiView : public QSplitter, + public KFileView +{ + Q_OBJECT + +public: + KCombiView( QWidget *parent, const char *name); + virtual ~KCombiView(); + + virtual QWidget *widget() { return this; } + virtual void clearView(); + + virtual void updateView( bool ); + virtual void updateView(const KFileItem*); + virtual void removeItem( const KFileItem * ); + virtual void listingCompleted(); + + /** + * Sets the view to be shown in the right. You need to call this before + * doing anything else with this widget. + */ + void setRight(KFileView *view); + + virtual void setSelectionMode( KFile::SelectionMode sm ); + + virtual void setSelected(const KFileItem *, bool); + virtual bool isSelected( const KFileItem * ) const; + virtual void clearSelection(); + virtual void selectAll(); + virtual void invertSelection(); + + virtual void setCurrentItem( const KFileItem * ); + virtual KFileItem * currentFileItem() const; + virtual KFileItem * firstFileItem() const; + virtual KFileItem * nextItem( const KFileItem * ) const; + virtual KFileItem * prevItem( const KFileItem * ) const; + + virtual void insertItem( KFileItem *i ); + virtual void clear(); + + virtual void setSorting( QDir::SortSpec sort ); + + virtual void readConfig( KConfig *, const QString& group = QString::null ); + virtual void writeConfig( KConfig *, const QString& group = QString::null); + + void ensureItemVisible( const KFileItem * ); + + virtual KActionCollection * actionCollection() const; + + virtual void setAcceptDrops(bool b); + +protected: + KFileIconView *left; + KFileView *right; + +protected slots: + void slotSortingChanged( QDir::SortSpec ); + +private: + KFileView *focusView( KFileView *preferred ) const; + + // in nextItem() and prevItem(), we have to switch views, when the first + // view returns 0L. So we need to remember which view was used in the + // previous call to next/prevItem(). Yes, it's a hack, but it works for + // some cases at least. + mutable KFileView *m_lastViewForNextItem; + mutable KFileView *m_lastViewForPrevItem; + +protected: + virtual bool eventFilter( QObject *o, QEvent *e ); + void setDropOptions_impl(int options); + + virtual void virtual_hook( int id, void* data ); +private: + class KCombiViewPrivate; + KCombiViewPrivate *d; + +}; + +#endif diff --git a/kio/kfile/kcustommenueditor.cpp b/kio/kfile/kcustommenueditor.cpp new file mode 100644 index 000000000..edbca55e3 --- /dev/null +++ b/kio/kfile/kcustommenueditor.cpp @@ -0,0 +1,242 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002 Waldo Bastian ([email protected]) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; version 2 + of the License. + + 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 <qhbox.h> +#include <qregexp.h> +#include <qimage.h> +#include <qpushbutton.h> +#include <qdir.h> + +#include <kbuttonbox.h> +#include <klocale.h> +#include <kglobal.h> +#include <kiconloader.h> +#include <klistview.h> +#include <kservice.h> +#include <kstandarddirs.h> +#include <kconfigbase.h> +#include <kopenwith.h> + +#include "kcustommenueditor.h" + +class KCustomMenuEditor::Item : public QListViewItem +{ +public: + Item(QListView *parent, KService::Ptr service) + : QListViewItem(parent), + s(service) + { + init(); + } + + Item(QListViewItem *parent, KService::Ptr service) + : QListViewItem(parent), + s(service) + { + init(); + } + + void init() + { + QString serviceName = s->name(); + + // item names may contain ampersands. To avoid them being converted + // to accelators, replace them with two ampersands. + serviceName.replace("&", "&&"); + + QPixmap normal = KGlobal::instance()->iconLoader()->loadIcon(s->icon(), KIcon::Small, + 0, KIcon::DefaultState, 0L, true); + + // make sure they are not larger than 16x16 + if (normal.width() > 16 || normal.height() > 16) { + QImage tmp = normal.convertToImage(); + tmp = tmp.smoothScale(16, 16); + normal.convertFromImage(tmp); + } + setText(0, serviceName); + setPixmap(0, normal); + } + + KService::Ptr s; +}; + +class KCustomMenuEditor::KCustomMenuEditorPrivate +{ +public: + QPushButton * pbRemove; + QPushButton * pbMoveUp; + QPushButton * pbMoveDown; +}; + +KCustomMenuEditor::KCustomMenuEditor(QWidget *parent) + : KDialogBase(parent, "custommenueditor", true, i18n("Menu Editor"), Ok|Cancel, Ok, true), + m_listView(0) +{ + d = new KCustomMenuEditorPrivate; + QHBox *page = makeHBoxMainWidget(); + m_listView = new KListView(page); + m_listView->addColumn(i18n("Menu")); + m_listView->setFullWidth(true); + m_listView->setSorting(-1); + KButtonBox *buttonBox = new KButtonBox(page, Vertical); + buttonBox->addButton(i18n("New..."), this, SLOT(slotNewItem())); + d->pbRemove=buttonBox->addButton(i18n("Remove"), this, SLOT(slotRemoveItem())); + d->pbMoveUp=buttonBox->addButton(i18n("Move Up"), this, SLOT(slotMoveUp())); + d->pbMoveDown=buttonBox->addButton(i18n("Move Down"), this, SLOT(slotMoveDown())); + buttonBox->layout(); + connect( m_listView, SIGNAL( selectionChanged () ), this, SLOT( refreshButton() ) ); + refreshButton(); +} + +KCustomMenuEditor::~KCustomMenuEditor() +{ + delete d; + d=0; +} + +void KCustomMenuEditor::refreshButton() +{ + QListViewItem *item = m_listView->currentItem(); + d->pbRemove->setEnabled( item ); + d->pbMoveUp->setEnabled( item && item->itemAbove() ); + d->pbMoveDown->setEnabled( item && item->itemBelow() ); +} + +void +KCustomMenuEditor::load(KConfigBase *cfg) +{ + cfg->setGroup(QString::null); + int count = cfg->readNumEntry("NrOfItems"); + QListViewItem *last = 0; + for(int i = 0; i < count; i++) + { + QString entry = cfg->readPathEntry(QString("Item%1").arg(i+1)); + if (entry.isEmpty()) + continue; + + // Try KSycoca first. + KService::Ptr menuItem = KService::serviceByDesktopPath( entry ); + if (!menuItem) + menuItem = KService::serviceByDesktopName( entry ); + if (!menuItem) + menuItem = new KService( entry ); + + if (!menuItem->isValid()) + continue; + + QListViewItem *item = new Item(m_listView, menuItem); + item->moveItem(last); + last = item; + } +} + +void +KCustomMenuEditor::save(KConfigBase *cfg) +{ + // First clear the whole config file. + QStringList groups = cfg->groupList(); + for(QStringList::ConstIterator it = groups.begin(); + it != groups.end(); ++it) + { + cfg->deleteGroup(*it); + } + + cfg->setGroup(QString::null); + Item * item = (Item *) m_listView->firstChild(); + int i = 0; + while(item) + { + i++; + QString path = item->s->desktopEntryPath(); + if (QDir::isRelativePath(path) || QDir::isRelativePath(KGlobal::dirs()->relativeLocation("xdgdata-apps", path))) + path = item->s->desktopEntryName(); + cfg->writePathEntry(QString("Item%1").arg(i), path); + item = (Item *) item->nextSibling(); + } + cfg->writeEntry("NrOfItems", i); +} + +void +KCustomMenuEditor::slotNewItem() +{ + QListViewItem *item = m_listView->currentItem(); + + KOpenWithDlg dlg(this); + dlg.setSaveNewApplications(true); + + if (dlg.exec()) + { + KService::Ptr s = dlg.service(); + if (s && s->isValid()) + { + Item *newItem = new Item(m_listView, s); + newItem->moveItem(item); + } + refreshButton(); + } +} + +void +KCustomMenuEditor::slotRemoveItem() +{ + QListViewItem *item = m_listView->currentItem(); + if (!item) + return; + + delete item; + refreshButton(); +} + +void +KCustomMenuEditor::slotMoveUp() +{ + QListViewItem *item = m_listView->currentItem(); + if (!item) + return; + + QListViewItem *searchItem = m_listView->firstChild(); + while(searchItem) + { + QListViewItem *next = searchItem->nextSibling(); + if (next == item) + { + searchItem->moveItem(item); + break; + } + searchItem = next; + } + refreshButton(); +} + +void +KCustomMenuEditor::slotMoveDown() +{ + QListViewItem *item = m_listView->currentItem(); + if (!item) + return; + + QListViewItem *after = item->nextSibling(); + if (!after) + return; + + item->moveItem( after ); + refreshButton(); +} + +#include "kcustommenueditor.moc" diff --git a/kio/kfile/kcustommenueditor.h b/kio/kfile/kcustommenueditor.h new file mode 100644 index 000000000..88151f5d4 --- /dev/null +++ b/kio/kfile/kcustommenueditor.h @@ -0,0 +1,67 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002 Waldo Bastian ([email protected]) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; version 2 + of the License. + + 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 _KCUSTOMMENUEDITOR_H_ +#define _KCUSTOMMENUEDITOR_H_ + +#include <kdialogbase.h> + +class KListView; +class KConfigBase; + + /* + * Dialog for editing custom menus. + * + * @author Waldo Bastian ([email protected]) + * @since 3.1 + */ +class KIO_EXPORT KCustomMenuEditor : public KDialogBase +{ + Q_OBJECT +public: + /** + * Create a dialog for editing a custom menu + */ + KCustomMenuEditor(QWidget *parent); + ~KCustomMenuEditor(); + /** + * load the custom menu + */ + void load(KConfigBase *); + + /** + * save the custom menu + */ + void save(KConfigBase *); + +public slots: + void slotNewItem(); + void slotRemoveItem(); + void slotMoveUp(); + void slotMoveDown(); + void refreshButton(); + +protected: + class Item; + KListView *m_listView; + + class KCustomMenuEditorPrivate; + KCustomMenuEditorPrivate *d; +}; + +#endif diff --git a/kio/kfile/kdiroperator.cpp b/kio/kfile/kdiroperator.cpp new file mode 100644 index 000000000..b12e45153 --- /dev/null +++ b/kio/kfile/kdiroperator.cpp @@ -0,0 +1,1740 @@ +/* This file is part of the KDE libraries + Copyright (C) 1999,2000 Stephan Kulow <[email protected]> + 1999,2000,2001,2002,2003 Carsten Pfeiffer <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <unistd.h> + +#include <qdir.h> +#include <qapplication.h> +#include <qdialog.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qpushbutton.h> +#include <qpopupmenu.h> +#include <qregexp.h> +#include <qtimer.h> +#include <qvbox.h> + +#include <kaction.h> +#include <kapplication.h> +#include <kdebug.h> +#include <kdialog.h> +#include <kdialogbase.h> +#include <kdirlister.h> +#include <kinputdialog.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kpopupmenu.h> +#include <kprogress.h> +#include <kstdaction.h> +#include <kio/job.h> +#include <kio/jobclasses.h> +#include <kio/netaccess.h> +#include <kio/previewjob.h> +#include <kio/renamedlg.h> +#include <kpropertiesdialog.h> +#include <kservicetypefactory.h> +#include <kstdaccel.h> +#include <kde_file.h> + +#include "config-kfile.h" +#include "kcombiview.h" +#include "kdiroperator.h" +#include "kfiledetailview.h" +#include "kfileiconview.h" +#include "kfilepreview.h" +#include "kfileview.h" +#include "kfileitem.h" +#include "kfilemetapreview.h" + + +template class QPtrStack<KURL>; +template class QDict<KFileItem>; + + +class KDirOperator::KDirOperatorPrivate +{ +public: + KDirOperatorPrivate() { + onlyDoubleClickSelectsFiles = false; + progressDelayTimer = 0L; + dirHighlighting = false; + config = 0L; + dropOptions = 0; + } + + ~KDirOperatorPrivate() { + delete progressDelayTimer; + } + + bool dirHighlighting; + QString lastURL; // used for highlighting a directory on cdUp + bool onlyDoubleClickSelectsFiles; + QTimer *progressDelayTimer; + KActionSeparator *viewActionSeparator; + int dropOptions; + + KConfig *config; + QString configGroup; +}; + +KDirOperator::KDirOperator(const KURL& _url, + QWidget *parent, const char* _name) + : QWidget(parent, _name), + dir(0), + m_fileView(0), + progress(0) +{ + myPreview = 0L; + myMode = KFile::File; + m_viewKind = KFile::Simple; + mySorting = static_cast<QDir::SortSpec>(QDir::Name | QDir::DirsFirst); + d = new KDirOperatorPrivate; + + if (_url.isEmpty()) { // no dir specified -> current dir + QString strPath = QDir::currentDirPath(); + strPath.append('/'); + currUrl = KURL(); + currUrl.setProtocol(QString::fromLatin1("file")); + currUrl.setPath(strPath); + } + else { + currUrl = _url; + if ( currUrl.protocol().isEmpty() ) + currUrl.setProtocol(QString::fromLatin1("file")); + + currUrl.addPath("/"); // make sure we have a trailing slash! + } + + setDirLister( new KDirLister( true ) ); + + connect(&myCompletion, SIGNAL(match(const QString&)), + SLOT(slotCompletionMatch(const QString&))); + + progress = new KProgress(this, "progress"); + progress->adjustSize(); + progress->move(2, height() - progress->height() -2); + + d->progressDelayTimer = new QTimer( this, "progress delay timer" ); + connect( d->progressDelayTimer, SIGNAL( timeout() ), + SLOT( slotShowProgress() )); + + myCompleteListDirty = false; + + backStack.setAutoDelete( true ); + forwardStack.setAutoDelete( true ); + + // action stuff + setupActions(); + setupMenu(); + + setFocusPolicy(QWidget::WheelFocus); +} + +KDirOperator::~KDirOperator() +{ + resetCursor(); + if ( m_fileView ) + { + if ( d->config ) + m_fileView->writeConfig( d->config, d->configGroup ); + + delete m_fileView; + m_fileView = 0L; + } + + delete myPreview; + delete dir; + delete d; +} + + +void KDirOperator::setSorting( QDir::SortSpec spec ) +{ + if ( m_fileView ) + m_fileView->setSorting( spec ); + mySorting = spec; + updateSortActions(); +} + +void KDirOperator::resetCursor() +{ + QApplication::restoreOverrideCursor(); + progress->hide(); +} + +void KDirOperator::insertViewDependentActions() +{ + // If we have a new view actionCollection(), insert its actions + // into viewActionMenu. + + if( !m_fileView ) + return; + + if ( (viewActionMenu->popupMenu()->count() == 0) || // Not yet initialized or... + (viewActionCollection != m_fileView->actionCollection()) ) // ...changed since. + { + if (viewActionCollection) + { + disconnect( viewActionCollection, SIGNAL( inserted( KAction * )), + this, SLOT( slotViewActionAdded( KAction * ))); + disconnect( viewActionCollection, SIGNAL( removed( KAction * )), + this, SLOT( slotViewActionRemoved( KAction * ))); + } + + viewActionMenu->popupMenu()->clear(); +// viewActionMenu->insert( shortAction ); +// viewActionMenu->insert( detailedAction ); +// viewActionMenu->insert( actionSeparator ); + viewActionMenu->insert( myActionCollection->action( "short view" ) ); + viewActionMenu->insert( myActionCollection->action( "detailed view" ) ); + viewActionMenu->insert( actionSeparator ); + viewActionMenu->insert( showHiddenAction ); +// viewActionMenu->insert( myActionCollection->action( "single" )); + viewActionMenu->insert( separateDirsAction ); + // Warning: adjust slotViewActionAdded() and slotViewActionRemoved() + // when you add/remove actions here! + + viewActionCollection = m_fileView->actionCollection(); + if (!viewActionCollection) + return; + + if ( !viewActionCollection->isEmpty() ) + { + viewActionMenu->insert( d->viewActionSeparator ); + + // first insert the normal actions, then the grouped ones + QStringList groups = viewActionCollection->groups(); + groups.prepend( QString::null ); // actions without group + QStringList::ConstIterator git = groups.begin(); + KActionPtrList list; + KAction *sep = actionCollection()->action("separator"); + for ( ; git != groups.end(); ++git ) + { + if ( git != groups.begin() ) + viewActionMenu->insert( sep ); + + list = viewActionCollection->actions( *git ); + KActionPtrList::ConstIterator it = list.begin(); + for ( ; it != list.end(); ++it ) + viewActionMenu->insert( *it ); + } + } + + connect( viewActionCollection, SIGNAL( inserted( KAction * )), + SLOT( slotViewActionAdded( KAction * ))); + connect( viewActionCollection, SIGNAL( removed( KAction * )), + SLOT( slotViewActionRemoved( KAction * ))); + } +} + +void KDirOperator::activatedMenu( const KFileItem *, const QPoint& pos ) +{ + setupMenu(); + updateSelectionDependentActions(); + + actionMenu->popup( pos ); +} + +void KDirOperator::updateSelectionDependentActions() +{ + bool hasSelection = m_fileView && m_fileView->selectedItems() && + !m_fileView->selectedItems()->isEmpty(); + myActionCollection->action( "trash" )->setEnabled( hasSelection ); + myActionCollection->action( "delete" )->setEnabled( hasSelection ); + myActionCollection->action( "properties" )->setEnabled( hasSelection ); +} + +void KDirOperator::setPreviewWidget(const QWidget *w) +{ + if(w != 0L) + m_viewKind = (m_viewKind | KFile::PreviewContents); + else + m_viewKind = (m_viewKind & ~KFile::PreviewContents); + + delete myPreview; + myPreview = w; + + KToggleAction *preview = static_cast<KToggleAction*>(myActionCollection->action("preview")); + preview->setEnabled( w != 0L ); + preview->setChecked( w != 0L ); + setView( static_cast<KFile::FileView>(m_viewKind) ); +} + +int KDirOperator::numDirs() const +{ + return m_fileView ? m_fileView->numDirs() : 0; +} + +int KDirOperator::numFiles() const +{ + return m_fileView ? m_fileView->numFiles() : 0; +} + +void KDirOperator::slotDetailedView() +{ + KFile::FileView view = static_cast<KFile::FileView>( (m_viewKind & ~KFile::Simple) | KFile::Detail ); + setView( view ); +} + +void KDirOperator::slotSimpleView() +{ + KFile::FileView view = static_cast<KFile::FileView>( (m_viewKind & ~KFile::Detail) | KFile::Simple ); + setView( view ); +} + +void KDirOperator::slotToggleHidden( bool show ) +{ + dir->setShowingDotFiles( show ); + updateDir(); + if ( m_fileView ) + m_fileView->listingCompleted(); +} + +void KDirOperator::slotSeparateDirs() +{ + if (separateDirsAction->isChecked()) + { + KFile::FileView view = static_cast<KFile::FileView>( m_viewKind | KFile::SeparateDirs ); + setView( view ); + } + else + { + KFile::FileView view = static_cast<KFile::FileView>( m_viewKind & ~KFile::SeparateDirs ); + setView( view ); + } +} + +void KDirOperator::slotDefaultPreview() +{ + m_viewKind = m_viewKind | KFile::PreviewContents; + if ( !myPreview ) { + myPreview = new KFileMetaPreview( this ); + (static_cast<KToggleAction*>( myActionCollection->action("preview") ))->setChecked(true); + } + + setView( static_cast<KFile::FileView>(m_viewKind) ); +} + +void KDirOperator::slotSortByName() +{ + int sorting = (m_fileView->sorting()) & ~QDir::SortByMask; + m_fileView->setSorting( static_cast<QDir::SortSpec>( sorting | QDir::Name )); + mySorting = m_fileView->sorting(); + caseInsensitiveAction->setEnabled( true ); +} + +void KDirOperator::slotSortBySize() +{ + int sorting = (m_fileView->sorting()) & ~QDir::SortByMask; + m_fileView->setSorting( static_cast<QDir::SortSpec>( sorting | QDir::Size )); + mySorting = m_fileView->sorting(); + caseInsensitiveAction->setEnabled( false ); +} + +void KDirOperator::slotSortByDate() +{ + int sorting = (m_fileView->sorting()) & ~QDir::SortByMask; + m_fileView->setSorting( static_cast<QDir::SortSpec>( sorting | QDir::Time )); + mySorting = m_fileView->sorting(); + caseInsensitiveAction->setEnabled( false ); +} + +void KDirOperator::slotSortReversed() +{ + if ( m_fileView ) + m_fileView->sortReversed(); +} + +void KDirOperator::slotToggleDirsFirst() +{ + QDir::SortSpec sorting = m_fileView->sorting(); + if ( !KFile::isSortDirsFirst( sorting ) ) + m_fileView->setSorting( static_cast<QDir::SortSpec>( sorting | QDir::DirsFirst )); + else + m_fileView->setSorting( static_cast<QDir::SortSpec>( sorting & ~QDir::DirsFirst)); + mySorting = m_fileView->sorting(); +} + +void KDirOperator::slotToggleIgnoreCase() +{ + QDir::SortSpec sorting = m_fileView->sorting(); + if ( !KFile::isSortCaseInsensitive( sorting ) ) + m_fileView->setSorting( static_cast<QDir::SortSpec>( sorting | QDir::IgnoreCase )); + else + m_fileView->setSorting( static_cast<QDir::SortSpec>( sorting & ~QDir::IgnoreCase)); + mySorting = m_fileView->sorting(); +} + +void KDirOperator::mkdir() +{ + bool ok; + QString where = url().pathOrURL(); + QString name = i18n( "New Folder" ); + if ( url().isLocalFile() && QFileInfo( url().path(+1) + name ).exists() ) + name = KIO::RenameDlg::suggestName( url(), name ); + + QString dir = KInputDialog::getText( i18n( "New Folder" ), + i18n( "Create new folder in:\n%1" ).arg( where ), + name, &ok, this); + if (ok) + mkdir( KIO::encodeFileName( dir ), true ); +} + +bool KDirOperator::mkdir( const QString& directory, bool enterDirectory ) +{ + // Creates "directory", relative to the current directory (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( currUrl ); + + QStringList dirs = QStringList::split( QDir::separator(), directory ); + QStringList::ConstIterator it = dirs.begin(); + + for ( ; it != dirs.end(); ++it ) + { + url.addPath( *it ); + exists = KIO::NetAccess::exists( url, false, 0 ); + writeOk = !exists && KIO::NetAccess::mkdir( url, topLevelWidget() ); + } + + if ( exists ) // url was already existant + { + KMessageBox::sorry(viewWidget(), i18n("A file or folder named %1 already exists.").arg(url.pathOrURL())); + enterDirectory = false; + } + else if ( !writeOk ) { + KMessageBox::sorry(viewWidget(), i18n("You do not have permission to " + "create that folder." )); + } + else if ( enterDirectory ) { + setURL( url, true ); + } + + return writeOk; +} + +KIO::DeleteJob * KDirOperator::del( const KFileItemList& items, + bool ask, bool showProgress ) +{ + return del( items, this, ask, showProgress ); +} + +KIO::DeleteJob * KDirOperator::del( const KFileItemList& 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; + } + + KURL::List urls; + QStringList files; + KFileItemListIterator it( items ); + + for ( ; it.current(); ++it ) { + KURL url = (*it)->url(); + urls.append( url ); + if ( url.isLocalFile() ) + files.append( url.path() ); + else + files.append( url.prettyURL() ); + } + + bool doIt = !ask; + if ( ask ) { + int ret; + if ( items.count() == 1 ) { + ret = KMessageBox::warningContinueCancel( parent, + i18n( "<qt>Do you really want to delete\n <b>'%1'</b>?</qt>" ) + .arg( files.first() ), + i18n("Delete File"), + KStdGuiItem::del(), "AskForDelete" ); + } + else + ret = KMessageBox::warningContinueCancelList( parent, + i18n("Do you really want to delete this item?", "Do you really want to delete these %n items?", items.count() ), + files, + i18n("Delete Files"), + KStdGuiItem::del(), "AskForDelete" ); + doIt = (ret == KMessageBox::Continue); + } + + if ( doIt ) { + KIO::DeleteJob *job = KIO::del( urls, false, showProgress ); + job->setWindow (topLevelWidget()); + job->setAutoErrorHandlingEnabled( true, parent ); + return job; + } + + return 0L; +} + +void KDirOperator::deleteSelected() +{ + if ( !m_fileView ) + return; + + const KFileItemList *list = m_fileView->selectedItems(); + if ( list ) + del( *list ); +} + +KIO::CopyJob * KDirOperator::trash( const KFileItemList& 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; + KFileItemListIterator it( items ); + + for ( ; it.current(); ++it ) { + KURL url = (*it)->url(); + urls.append( url ); + if ( url.isLocalFile() ) + files.append( url.path() ); + else + files.append( url.prettyURL() ); + } + + bool doIt = !ask; + if ( ask ) { + int ret; + if ( items.count() == 1 ) { + ret = KMessageBox::warningContinueCancel( parent, + i18n( "<qt>Do you really want to trash\n <b>'%1'</b>?</qt>" ) + .arg( files.first() ), + i18n("Trash File"), + KGuiItem(i18n("to trash", "&Trash"),"edittrash"), "AskForTrash" ); + } + else + ret = KMessageBox::warningContinueCancelList( parent, + i18n("translators: not called for n == 1", "Do you really want to trash these %n items?", items.count() ), + files, + i18n("Trash Files"), + KGuiItem(i18n("to trash", "&Trash"),"edittrash"), "AskForTrash" ); + doIt = (ret == KMessageBox::Continue); + } + + if ( doIt ) { + KIO::CopyJob *job = KIO::trash( urls, showProgress ); + job->setWindow (topLevelWidget()); + job->setAutoErrorHandlingEnabled( true, parent ); + return job; + } + + return 0L; +} + +void KDirOperator::trashSelected(KAction::ActivationReason reason, Qt::ButtonState state) +{ + if ( !m_fileView ) + return; + + if ( reason == KAction::PopupMenuActivation && ( state & Qt::ShiftButton ) ) { + deleteSelected(); + return; + } + + const KFileItemList *list = m_fileView->selectedItems(); + if ( list ) + trash( *list, this ); +} + +void KDirOperator::close() +{ + resetCursor(); + pendingMimeTypes.clear(); + myCompletion.clear(); + myDirCompletion.clear(); + myCompleteListDirty = true; + dir->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.stripWhiteSpace(); + // 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, 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.findRev('/'); + ASSERT(position >= 0); // we already inserted the current dir in case + QString filename = text.mid(position + 1, text.length()); + if (filename != selection) + selection = QString::null; + } + + 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.path()); + 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 + kdDebug(kfile_area) << "TODO KDirOperator::checkPath()" << endl; +} + +void KDirOperator::setURL(const KURL& _newurl, bool clearforward) +{ + KURL newurl; + + if ( !_newurl.isValid() ) + newurl.setPath( QDir::homeDirPath() ); + else + newurl = _newurl; + + QString pathstr = newurl.path(+1); + newurl.setPath(pathstr); + + // already set + if ( newurl.equals( currUrl, true ) ) + return; + + if ( !isReadable( newurl ) ) { + // maybe newurl is a file? check its parent directory + newurl.cd(QString::fromLatin1("..")); + if ( !isReadable( newurl ) ) { + resetCursor(); + KMessageBox::error(viewWidget(), + i18n("The specified folder does not exist " + "or was not readable.")); + return; + } + } + + if (clearforward) { + // autodelete should remove this one + backStack.push(new KURL(currUrl)); + forwardStack.clear(); + } + + d->lastURL = currUrl.url(-1); + currUrl = newurl; + + pathChanged(); + emit urlEntered(newurl); + + // enable/disable actions + forwardAction->setEnabled( !forwardStack.isEmpty() ); + backAction->setEnabled( !backStack.isEmpty() ); + upAction->setEnabled( !isRoot() ); + + openURL( newurl ); +} + +void KDirOperator::updateDir() +{ + dir->emitChanges(); + if ( m_fileView ) + m_fileView->listingCompleted(); +} + +void KDirOperator::rereadDir() +{ + pathChanged(); + openURL( currUrl, false, true ); +} + + +bool KDirOperator::openURL( const KURL& url, bool keep, bool reload ) +{ + bool result = dir->openURL( url, keep, reload ); + if ( !result ) // in that case, neither completed() nor canceled() will be emitted by KDL + slotCanceled(); + + return result; +} + +// Protected +void KDirOperator::pathChanged() +{ + if (!m_fileView) + return; + + pendingMimeTypes.clear(); + m_fileView->clear(); + myCompletion.clear(); + myDirCompletion.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( waitCursor ); + + if ( !isReadable( currUrl )) { + KMessageBox::error(viewWidget(), + i18n("The specified folder does not exist " + "or was not readable.")); + if (backStack.isEmpty()) + home(); + else + back(); + } +} + +void KDirOperator::slotRedirected( const KURL& newURL ) +{ + currUrl = newURL; + pendingMimeTypes.clear(); + myCompletion.clear(); + myDirCompletion.clear(); + myCompleteListDirty = true; + emit urlEntered( newURL ); +} + +// Code pinched from kfm then hacked +void KDirOperator::back() +{ + if ( backStack.isEmpty() ) + return; + + forwardStack.push( new KURL(currUrl) ); + + KURL *s = backStack.pop(); + + setURL(*s, false); + delete s; +} + +// Code pinched from kfm then hacked +void KDirOperator::forward() +{ + if ( forwardStack.isEmpty() ) + return; + + backStack.push(new KURL(currUrl)); + + KURL *s = forwardStack.pop(); + setURL(*s, false); + delete s; +} + +KURL KDirOperator::url() const +{ + return currUrl; +} + +void KDirOperator::cdUp() +{ + KURL tmp(currUrl); + tmp.cd(QString::fromLatin1("..")); + setURL(tmp, true); +} + +void KDirOperator::home() +{ + KURL u; + u.setPath( QDir::homeDirPath() ); + setURL(u, true); +} + +void KDirOperator::clearFilter() +{ + dir->setNameFilter( QString::null ); + dir->clearMimeFilter(); + checkPreviewSupport(); +} + +void KDirOperator::setNameFilter(const QString& filter) +{ + dir->setNameFilter(filter); + checkPreviewSupport(); +} + +void KDirOperator::setMimeFilter( const QStringList& mimetypes ) +{ + dir->setMimeFilter( mimetypes ); + checkPreviewSupport(); +} + +bool KDirOperator::checkPreviewSupport() +{ + KToggleAction *previewAction = static_cast<KToggleAction*>( myActionCollection->action( "preview" )); + + bool hasPreviewSupport = false; + KConfig *kc = KGlobal::config(); + KConfigGroupSaver cs( kc, ConfigGroup ); + if ( kc->readBoolEntry( "Show Default Preview", true ) ) + hasPreviewSupport = checkPreviewInternal(); + + previewAction->setEnabled( hasPreviewSupport ); + return hasPreviewSupport; +} + +bool KDirOperator::checkPreviewInternal() const +{ + QStringList supported = KIO::PreviewJob::supportedMimeTypes(); + // no preview support for directories? + if ( dirOnlyMode() && supported.findIndex( "inode/directory" ) == -1 ) + return false; + + QStringList mimeTypes = dir->mimeFilters(); + QStringList nameFilter = QStringList::split( " ", dir->nameFilter() ); + + if ( mimeTypes.isEmpty() && nameFilter.isEmpty() && !supported.isEmpty() ) + return true; + else { + QRegExp r; + r.setWildcard( true ); // the "mimetype" can be "image/*" + + if ( !mimeTypes.isEmpty() ) { + QStringList::Iterator it = supported.begin(); + + for ( ; it != supported.end(); ++it ) { + r.setPattern( *it ); + + QStringList result = mimeTypes.grep( r ); + if ( !result.isEmpty() ) { // matches! -> we want previews + return true; + } + } + } + + if ( !nameFilter.isEmpty() ) { + // find the mimetypes of all the filter-patterns and + KServiceTypeFactory *fac = KServiceTypeFactory::self(); + QStringList::Iterator it1 = nameFilter.begin(); + for ( ; it1 != nameFilter.end(); ++it1 ) { + if ( (*it1) == "*" ) { + return true; + } + + KMimeType *mt = fac->findFromPattern( *it1 ); + if ( !mt ) + continue; + QString mime = mt->name(); + delete mt; + + // 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.search( mime ) != -1 ) { + return true; + } + } + } + } + } + + return false; +} + +KFileView* KDirOperator::createView( QWidget* parent, KFile::FileView view ) +{ + KFileView* new_view = 0L; + bool separateDirs = KFile::isSeparateDirs( view ); + bool preview = ( KFile::isPreviewInfo(view) || KFile::isPreviewContents( view ) ); + + if ( separateDirs || preview ) { + KCombiView *combi = 0L; + if (separateDirs) + { + combi = new KCombiView( parent, "combi view" ); + combi->setOnlyDoubleClickSelectsFiles(d->onlyDoubleClickSelectsFiles); + } + + KFileView* v = 0L; + if ( KFile::isSimpleView( view ) ) + v = createView( combi, KFile::Simple ); + else + v = createView( combi, KFile::Detail ); + + v->setOnlyDoubleClickSelectsFiles(d->onlyDoubleClickSelectsFiles); + + if (combi) + combi->setRight( v ); + + if (preview) + { + KFilePreview* pView = new KFilePreview( combi ? combi : v, parent, "preview" ); + pView->setOnlyDoubleClickSelectsFiles(d->onlyDoubleClickSelectsFiles); + new_view = pView; + } + else + new_view = combi; + } + else if ( KFile::isDetailView( view ) && !preview ) { + new_view = new KFileDetailView( parent, "detail view"); + new_view->setViewName( i18n("Detailed View") ); + } + else /* if ( KFile::isSimpleView( view ) && !preview ) */ { + KFileIconView *iconView = new KFileIconView( parent, "simple view"); + new_view = iconView; + new_view->setViewName( i18n("Short View") ); + } + + new_view->widget()->setAcceptDrops(acceptDrops()); + return new_view; +} + +void KDirOperator::setAcceptDrops(bool b) +{ + if (m_fileView) + m_fileView->widget()->setAcceptDrops(b); + QWidget::setAcceptDrops(b); +} + +void KDirOperator::setDropOptions(int options) +{ + d->dropOptions = options; + if (m_fileView) + m_fileView->setDropOptions(options); +} + +void KDirOperator::setView( KFile::FileView view ) +{ + bool separateDirs = KFile::isSeparateDirs( view ); + bool preview=( KFile::isPreviewInfo(view) || KFile::isPreviewContents( view ) ); + + if (view == KFile::Default) { + if ( KFile::isDetailView( (KFile::FileView) defaultView ) ) + view = KFile::Detail; + else + view = KFile::Simple; + + separateDirs = KFile::isSeparateDirs( static_cast<KFile::FileView>(defaultView) ); + preview = ( KFile::isPreviewInfo( static_cast<KFile::FileView>(defaultView) ) || + KFile::isPreviewContents( static_cast<KFile::FileView>(defaultView) ) ) + && myActionCollection->action("preview")->isEnabled(); + + if ( preview ) { // instantiates KFileMetaPreview and calls setView() + m_viewKind = defaultView; + slotDefaultPreview(); + return; + } + else if ( !separateDirs ) + separateDirsAction->setChecked(true); + } + + // if we don't have any files, we can't separate dirs from files :) + if ( (mode() & KFile::File) == 0 && + (mode() & KFile::Files) == 0 ) { + separateDirs = false; + separateDirsAction->setEnabled( false ); + } + + m_viewKind = static_cast<int>(view) | (separateDirs ? KFile::SeparateDirs : 0); + view = static_cast<KFile::FileView>(m_viewKind); + + KFileView *new_view = createView( this, view ); + if ( preview ) { + // we keep the preview-_widget_ around, but not the KFilePreview. + // KFilePreview::setPreviewWidget handles the reparenting for us + static_cast<KFilePreview*>(new_view)->setPreviewWidget(myPreview, url()); + } + + setView( new_view ); +} + + +void KDirOperator::connectView(KFileView *view) +{ + // TODO: do a real timer and restart it after that + pendingMimeTypes.clear(); + bool listDir = true; + + if ( dirOnlyMode() ) + view->setViewMode(KFileView::Directories); + else + view->setViewMode(KFileView::All); + + if ( myMode & KFile::Files ) + view->setSelectionMode( KFile::Extended ); + else + view->setSelectionMode( KFile::Single ); + + if (m_fileView) + { + if ( d->config ) // save and restore the views' configuration + { + m_fileView->writeConfig( d->config, d->configGroup ); + view->readConfig( d->config, d->configGroup ); + } + + // transfer the state from old view to new view + view->clear(); + view->addItemList( *m_fileView->items() ); + listDir = false; + + if ( m_fileView->widget()->hasFocus() ) + view->widget()->setFocus(); + + KFileItem *oldCurrentItem = m_fileView->currentFileItem(); + if ( oldCurrentItem ) { + view->setCurrentItem( oldCurrentItem ); + view->setSelected( oldCurrentItem, false ); + view->ensureItemVisible( oldCurrentItem ); + } + + const KFileItemList *oldSelected = m_fileView->selectedItems(); + if ( !oldSelected->isEmpty() ) { + KFileItemListIterator it( *oldSelected ); + for ( ; it.current(); ++it ) + view->setSelected( it.current(), true ); + } + + m_fileView->widget()->hide(); + delete m_fileView; + } + + else + { + if ( d->config ) + view->readConfig( d->config, d->configGroup ); + } + + m_fileView = view; + m_fileView->setDropOptions(d->dropOptions); + viewActionCollection = 0L; + KFileViewSignaler *sig = view->signaler(); + + connect(sig, SIGNAL( activatedMenu(const KFileItem *, const QPoint& ) ), + this, SLOT( activatedMenu(const KFileItem *, const QPoint& ))); + connect(sig, SIGNAL( dirActivated(const KFileItem *) ), + this, SLOT( selectDir(const KFileItem*) ) ); + connect(sig, SIGNAL( fileSelected(const KFileItem *) ), + this, SLOT( selectFile(const KFileItem*) ) ); + connect(sig, SIGNAL( fileHighlighted(const KFileItem *) ), + this, SLOT( highlightFile(const KFileItem*) )); + connect(sig, SIGNAL( sortingChanged( QDir::SortSpec ) ), + this, SLOT( slotViewSortingChanged( QDir::SortSpec ))); + connect(sig, SIGNAL( dropped(const KFileItem *, QDropEvent*, const KURL::List&) ), + this, SIGNAL( dropped(const KFileItem *, QDropEvent*, const KURL::List&)) ); + + if ( reverseAction->isChecked() != m_fileView->isReversed() ) + slotSortReversed(); + + updateViewActions(); + m_fileView->widget()->resize(size()); + m_fileView->widget()->show(); + + if ( listDir ) { + QApplication::setOverrideCursor( waitCursor ); + openURL( currUrl ); + } + else + view->listingCompleted(); +} + +KFile::Mode KDirOperator::mode() const +{ + return myMode; +} + +void KDirOperator::setMode(KFile::Mode m) +{ + if (myMode == m) + return; + + myMode = m; + + dir->setDirOnlyMode( dirOnlyMode() ); + + // reset the view with the different mode + setView( static_cast<KFile::FileView>(m_viewKind) ); +} + +void KDirOperator::setView(KFileView *view) +{ + if ( view == m_fileView ) { + return; + } + + setFocusProxy(view->widget()); + view->setSorting( mySorting ); + view->setOnlyDoubleClickSelectsFiles( d->onlyDoubleClickSelectsFiles ); + connectView(view); // also deletes the old view + + emit viewChanged( view ); +} + +void KDirOperator::setDirLister( KDirLister *lister ) +{ + if ( lister == dir ) // sanity check + return; + + delete dir; + dir = lister; + + dir->setAutoUpdate( true ); + + QWidget* mainWidget = topLevelWidget(); + dir->setMainWindow (mainWidget); + kdDebug (kfile_area) << "mainWidget=" << mainWidget << endl; + + connect( dir, SIGNAL( percent( int )), + SLOT( slotProgress( int ) )); + connect( dir, SIGNAL(started( const KURL& )), SLOT(slotStarted())); + connect( dir, SIGNAL(newItems(const KFileItemList &)), + SLOT(insertNewFiles(const KFileItemList &))); + connect( dir, SIGNAL(completed()), SLOT(slotIOFinished())); + connect( dir, SIGNAL(canceled()), SLOT(slotCanceled())); + connect( dir, SIGNAL(deleteItem(KFileItem *)), + SLOT(itemDeleted(KFileItem *))); + connect( dir, SIGNAL(redirection( const KURL& )), + SLOT( slotRedirected( const KURL& ))); + connect( dir, SIGNAL( clear() ), SLOT( slotClearView() )); + connect( dir, SIGNAL( refreshItems( const KFileItemList& ) ), + SLOT( slotRefreshItems( const KFileItemList& ) ) ); +} + +void KDirOperator::insertNewFiles(const KFileItemList &newone) +{ + if ( newone.isEmpty() || !m_fileView ) + return; + + myCompleteListDirty = true; + m_fileView->addItemList( newone ); + emit updateInformation(m_fileView->numDirs(), m_fileView->numFiles()); + + KFileItem *item; + KFileItemListIterator it( newone ); + + while ( (item = it.current()) ) { + // highlight the dir we come from, if possible + if ( d->dirHighlighting && item->isDir() && + item->url().url(-1) == d->lastURL ) { + m_fileView->setCurrentItem( item ); + m_fileView->ensureItemVisible( item ); + } + + ++it; + } + + QTimer::singleShot(200, this, SLOT(resetCursor())); +} + +void KDirOperator::selectDir(const KFileItem *item) +{ + setURL(item->url(), true); +} + +void KDirOperator::itemDeleted(KFileItem *item) +{ + pendingMimeTypes.removeRef( item ); + if ( m_fileView ) + { + m_fileView->removeItem( static_cast<KFileItem *>( item )); + emit updateInformation(m_fileView->numDirs(), m_fileView->numFiles()); + } +} + +void KDirOperator::selectFile(const KFileItem *item) +{ + QApplication::restoreOverrideCursor(); + + emit fileSelected( item ); +} + +void KDirOperator::setCurrentItem( const QString& filename ) +{ + if ( m_fileView ) { + const KFileItem *item = 0L; + + if ( !filename.isNull() ) + item = static_cast<KFileItem *>(dir->findByName( filename )); + + m_fileView->clearSelection(); + if ( item ) { + m_fileView->setCurrentItem( item ); + m_fileView->setSelected( item, true ); + m_fileView->ensureItemVisible( item ); + } + } +} + +QString KDirOperator::makeCompletion(const QString& string) +{ + if ( string.isEmpty() ) { + m_fileView->clearSelection(); + return QString::null; + } + + prepareCompletionObjects(); + return myCompletion.makeCompletion( string ); +} + +QString KDirOperator::makeDirCompletion(const QString& string) +{ + if ( string.isEmpty() ) { + m_fileView->clearSelection(); + return QString::null; + } + + prepareCompletionObjects(); + return myDirCompletion.makeCompletion( string ); +} + +void KDirOperator::prepareCompletionObjects() +{ + if ( !m_fileView ) + return; + + if ( myCompleteListDirty ) { // create the list of all possible completions + KFileItemListIterator it( *(m_fileView->items()) ); + for( ; it.current(); ++it ) { + KFileItem *item = it.current(); + + myCompletion.addItem( item->name() ); + if ( item->isDir() ) + myDirCompletion.addItem( item->name() ); + } + myCompleteListDirty = false; + } +} + +void KDirOperator::slotCompletionMatch(const QString& match) +{ + setCurrentItem( match ); + emit completion( match ); +} + +void KDirOperator::setupActions() +{ + myActionCollection = new KActionCollection( topLevelWidget(), this, "KDirOperator::myActionCollection" ); + + actionMenu = new KActionMenu( i18n("Menu"), myActionCollection, "popupMenu" ); + upAction = KStdAction::up( this, SLOT( cdUp() ), myActionCollection, "up" ); + upAction->setText( i18n("Parent Folder") ); + backAction = KStdAction::back( this, SLOT( back() ), myActionCollection, "back" ); + forwardAction = KStdAction::forward( this, SLOT(forward()), myActionCollection, "forward" ); + homeAction = KStdAction::home( this, SLOT( home() ), myActionCollection, "home" ); + homeAction->setText(i18n("Home Folder")); + reloadAction = KStdAction::redisplay( this, SLOT(rereadDir()), myActionCollection, "reload" ); + actionSeparator = new KActionSeparator( myActionCollection, "separator" ); + d->viewActionSeparator = new KActionSeparator( myActionCollection, + "viewActionSeparator" ); + mkdirAction = new KAction( i18n("New Folder..."), 0, + this, SLOT( mkdir() ), myActionCollection, "mkdir" ); + KAction* trash = new KAction( i18n( "Move to Trash" ), "edittrash", Key_Delete, myActionCollection, "trash" ); + connect( trash, SIGNAL( activated( KAction::ActivationReason, Qt::ButtonState ) ), + this, SLOT( trashSelected( KAction::ActivationReason, Qt::ButtonState ) ) ); + new KAction( i18n( "Delete" ), "editdelete", SHIFT+Key_Delete, this, + SLOT( deleteSelected() ), myActionCollection, "delete" ); + mkdirAction->setIcon( QString::fromLatin1("folder_new") ); + reloadAction->setText( i18n("Reload") ); + reloadAction->setShortcut( KStdAccel::shortcut( KStdAccel::Reload )); + + + // the sort menu actions + sortActionMenu = new KActionMenu( i18n("Sorting"), myActionCollection, "sorting menu"); + byNameAction = new KRadioAction( i18n("By Name"), 0, + this, SLOT( slotSortByName() ), + myActionCollection, "by name" ); + byDateAction = new KRadioAction( i18n("By Date"), 0, + this, SLOT( slotSortByDate() ), + myActionCollection, "by date" ); + bySizeAction = new KRadioAction( i18n("By Size"), 0, + this, SLOT( slotSortBySize() ), + myActionCollection, "by size" ); + reverseAction = new KToggleAction( i18n("Reverse"), 0, + this, SLOT( slotSortReversed() ), + myActionCollection, "reversed" ); + + QString sortGroup = QString::fromLatin1("sort"); + byNameAction->setExclusiveGroup( sortGroup ); + byDateAction->setExclusiveGroup( sortGroup ); + bySizeAction->setExclusiveGroup( sortGroup ); + + + dirsFirstAction = new KToggleAction( i18n("Folders First"), 0, + myActionCollection, "dirs first"); + caseInsensitiveAction = new KToggleAction(i18n("Case Insensitive"), 0, + myActionCollection, "case insensitive" ); + + connect( dirsFirstAction, SIGNAL( toggled( bool ) ), + SLOT( slotToggleDirsFirst() )); + connect( caseInsensitiveAction, SIGNAL( toggled( bool ) ), + SLOT( slotToggleIgnoreCase() )); + + + + // the view menu actions + viewActionMenu = new KActionMenu( i18n("&View"), myActionCollection, "view menu" ); + connect( viewActionMenu->popupMenu(), SIGNAL( aboutToShow() ), + SLOT( insertViewDependentActions() )); + + shortAction = new KRadioAction( i18n("Short View"), "view_multicolumn", + KShortcut(), myActionCollection, "short view" ); + detailedAction = new KRadioAction( i18n("Detailed View"), "view_detailed", + KShortcut(), myActionCollection, "detailed view" ); + + showHiddenAction = new KToggleAction( i18n("Show Hidden Files"), KShortcut(), + myActionCollection, "show hidden" ); +// showHiddenAction->setCheckedState( i18n("Hide Hidden Files") ); + separateDirsAction = new KToggleAction( i18n("Separate Folders"), KShortcut(), + this, + SLOT(slotSeparateDirs()), + myActionCollection, "separate dirs" ); + KToggleAction *previewAction = new KToggleAction(i18n("Show Preview"), + "thumbnail", KShortcut(), + myActionCollection, + "preview" ); + previewAction->setCheckedState(i18n("Hide Preview")); + connect( previewAction, SIGNAL( toggled( bool )), + SLOT( togglePreview( bool ))); + + + QString viewGroup = QString::fromLatin1("view"); + shortAction->setExclusiveGroup( viewGroup ); + detailedAction->setExclusiveGroup( viewGroup ); + + connect( shortAction, SIGNAL( activated() ), + SLOT( slotSimpleView() )); + connect( detailedAction, SIGNAL( activated() ), + SLOT( slotDetailedView() )); + connect( showHiddenAction, SIGNAL( toggled( bool ) ), + SLOT( slotToggleHidden( bool ) )); + + new KAction( i18n("Properties"), KShortcut(ALT+Key_Return), this, + SLOT(slotProperties()), myActionCollection, "properties" ); +} + +void KDirOperator::setupMenu() +{ + setupMenu(AllActions); +} + +void KDirOperator::setupMenu(int whichActions) +{ + // first fill the submenus (sort and view) + sortActionMenu->popupMenu()->clear(); + sortActionMenu->insert( byNameAction ); + sortActionMenu->insert( byDateAction ); + sortActionMenu->insert( bySizeAction ); + sortActionMenu->insert( actionSeparator ); + sortActionMenu->insert( reverseAction ); + sortActionMenu->insert( dirsFirstAction ); + sortActionMenu->insert( caseInsensitiveAction ); + + // now plug everything into the popupmenu + actionMenu->popupMenu()->clear(); + if (whichActions & NavActions) + { + actionMenu->insert( upAction ); + actionMenu->insert( backAction ); + actionMenu->insert( forwardAction ); + actionMenu->insert( homeAction ); + actionMenu->insert( actionSeparator ); + } + + if (whichActions & FileActions) + { + actionMenu->insert( mkdirAction ); + if (currUrl.isLocalFile() && !(KApplication::keyboardMouseState() & Qt::ShiftButton)) + actionMenu->insert( myActionCollection->action( "trash" ) ); + KConfig *globalconfig = KGlobal::config(); + KConfigGroupSaver cs( globalconfig, QString::fromLatin1("KDE") ); + if (!currUrl.isLocalFile() || (KApplication::keyboardMouseState() & Qt::ShiftButton) || + globalconfig->readBoolEntry("ShowDeleteCommand", false)) + actionMenu->insert( myActionCollection->action( "delete" ) ); + actionMenu->insert( actionSeparator ); + } + + if (whichActions & SortActions) + { + actionMenu->insert( sortActionMenu ); + actionMenu->insert( actionSeparator ); + } + + if (whichActions & ViewActions) + { + actionMenu->insert( viewActionMenu ); + actionMenu->insert( actionSeparator ); + } + + if (whichActions & FileActions) + { + actionMenu->insert( myActionCollection->action( "properties" ) ); + } +} + +void KDirOperator::updateSortActions() +{ + if ( KFile::isSortByName( mySorting ) ) + byNameAction->setChecked( true ); + else if ( KFile::isSortByDate( mySorting ) ) + byDateAction->setChecked( true ); + else if ( KFile::isSortBySize( mySorting ) ) + bySizeAction->setChecked( true ); + + dirsFirstAction->setChecked( KFile::isSortDirsFirst( mySorting ) ); + caseInsensitiveAction->setChecked( KFile::isSortCaseInsensitive(mySorting) ); + caseInsensitiveAction->setEnabled( KFile::isSortByName( mySorting ) ); + + if ( m_fileView ) + reverseAction->setChecked( m_fileView->isReversed() ); +} + +void KDirOperator::updateViewActions() +{ + KFile::FileView fv = static_cast<KFile::FileView>( m_viewKind ); + + separateDirsAction->setChecked( KFile::isSeparateDirs( fv ) && + separateDirsAction->isEnabled() ); + + shortAction->setChecked( KFile::isSimpleView( fv )); + detailedAction->setChecked( KFile::isDetailView( fv )); +} + +void KDirOperator::readConfig( KConfig *kc, const QString& group ) +{ + if ( !kc ) + return; + QString oldGroup = kc->group(); + if ( !group.isEmpty() ) + kc->setGroup( group ); + + defaultView = 0; + int sorting = 0; + + QString viewStyle = kc->readEntry( QString::fromLatin1("View Style"), + QString::fromLatin1("Simple") ); + if ( viewStyle == QString::fromLatin1("Detail") ) + defaultView |= KFile::Detail; + else + defaultView |= KFile::Simple; + if ( kc->readBoolEntry( QString::fromLatin1("Separate Directories"), + DefaultMixDirsAndFiles ) ) + defaultView |= KFile::SeparateDirs; + if ( kc->readBoolEntry(QString::fromLatin1("Show Preview"), false)) + defaultView |= KFile::PreviewContents; + + if ( kc->readBoolEntry( QString::fromLatin1("Sort case insensitively"), + DefaultCaseInsensitive ) ) + sorting |= QDir::IgnoreCase; + if ( kc->readBoolEntry( QString::fromLatin1("Sort directories first"), + DefaultDirsFirst ) ) + sorting |= QDir::DirsFirst; + + + QString name = QString::fromLatin1("Name"); + QString sortBy = kc->readEntry( QString::fromLatin1("Sort by"), name ); + if ( sortBy == name ) + sorting |= QDir::Name; + else if ( sortBy == QString::fromLatin1("Size") ) + sorting |= QDir::Size; + else if ( sortBy == QString::fromLatin1("Date") ) + sorting |= QDir::Time; + + mySorting = static_cast<QDir::SortSpec>( sorting ); + setSorting( mySorting ); + + + if ( kc->readBoolEntry( QString::fromLatin1("Show hidden files"), + DefaultShowHidden ) ) { + showHiddenAction->setChecked( true ); + dir->setShowingDotFiles( true ); + } + if ( kc->readBoolEntry( QString::fromLatin1("Sort reversed"), + DefaultSortReversed ) ) + reverseAction->setChecked( true ); + + kc->setGroup( oldGroup ); +} + +void KDirOperator::writeConfig( KConfig *kc, const QString& group ) +{ + if ( !kc ) + return; + + const QString oldGroup = kc->group(); + + if ( !group.isEmpty() ) + kc->setGroup( group ); + + QString sortBy = QString::fromLatin1("Name"); + if ( KFile::isSortBySize( mySorting ) ) + sortBy = QString::fromLatin1("Size"); + else if ( KFile::isSortByDate( mySorting ) ) + sortBy = QString::fromLatin1("Date"); + kc->writeEntry( QString::fromLatin1("Sort by"), sortBy ); + + kc->writeEntry( QString::fromLatin1("Sort reversed"), + reverseAction->isChecked() ); + kc->writeEntry( QString::fromLatin1("Sort case insensitively"), + caseInsensitiveAction->isChecked() ); + kc->writeEntry( QString::fromLatin1("Sort directories first"), + dirsFirstAction->isChecked() ); + + // don't save the separate dirs or preview when an application specific + // preview is in use. + bool appSpecificPreview = false; + if ( myPreview ) { + QWidget *preview = const_cast<QWidget*>( myPreview ); // grmbl + KFileMetaPreview *tmp = dynamic_cast<KFileMetaPreview*>( preview ); + appSpecificPreview = (tmp == 0L); + } + + if ( !appSpecificPreview ) { + if ( separateDirsAction->isEnabled() ) + kc->writeEntry( QString::fromLatin1("Separate Directories"), + separateDirsAction->isChecked() ); + + KToggleAction *previewAction = static_cast<KToggleAction*>(myActionCollection->action("preview")); + if ( previewAction->isEnabled() ) { + bool hasPreview = previewAction->isChecked(); + kc->writeEntry( QString::fromLatin1("Show Preview"), hasPreview ); + } + } + + kc->writeEntry( QString::fromLatin1("Show hidden files"), + showHiddenAction->isChecked() ); + + KFile::FileView fv = static_cast<KFile::FileView>( m_viewKind ); + QString style; + if ( KFile::isDetailView( fv ) ) + style = QString::fromLatin1("Detail"); + else if ( KFile::isSimpleView( fv ) ) + style = QString::fromLatin1("Simple"); + kc->writeEntry( QString::fromLatin1("View Style"), style ); + + kc->setGroup( oldGroup ); +} + + +void KDirOperator::resizeEvent( QResizeEvent * ) +{ + if (m_fileView) + m_fileView->widget()->resize( size() ); + + if ( progress->parent() == this ) // might be reparented into a statusbar + progress->move(2, height() - progress->height() -2); +} + +void KDirOperator::setOnlyDoubleClickSelectsFiles( bool enable ) +{ + d->onlyDoubleClickSelectsFiles = enable; + if ( m_fileView ) + m_fileView->setOnlyDoubleClickSelectsFiles( enable ); +} + +bool KDirOperator::onlyDoubleClickSelectsFiles() const +{ + return d->onlyDoubleClickSelectsFiles; +} + +void KDirOperator::slotStarted() +{ + progress->setProgress( 0 ); + // delay showing the progressbar for one second + d->progressDelayTimer->start( 1000, true ); +} + +void KDirOperator::slotShowProgress() +{ + progress->raise(); + progress->show(); + QApplication::flushX(); +} + +void KDirOperator::slotProgress( int percent ) +{ + progress->setProgress( percent ); + // we have to redraw this as fast as possible + if ( progress->isVisible() ) + QApplication::flushX(); +} + + +void KDirOperator::slotIOFinished() +{ + d->progressDelayTimer->stop(); + slotProgress( 100 ); + progress->hide(); + emit finishedLoading(); + resetCursor(); + + if ( m_fileView ) + m_fileView->listingCompleted(); +} + +void KDirOperator::slotCanceled() +{ + emit finishedLoading(); + resetCursor(); + + if ( m_fileView ) + m_fileView->listingCompleted(); +} + +KProgress * KDirOperator::progressBar() const +{ + return progress; +} + +void KDirOperator::clearHistory() +{ + backStack.clear(); + backAction->setEnabled( false ); + forwardStack.clear(); + forwardAction->setEnabled( false ); +} + +void KDirOperator::slotViewActionAdded( KAction *action ) +{ + if ( viewActionMenu->popupMenu()->count() == 5 ) // need to add a separator + viewActionMenu->insert( d->viewActionSeparator ); + + viewActionMenu->insert( action ); +} + +void KDirOperator::slotViewActionRemoved( KAction *action ) +{ + viewActionMenu->remove( action ); + + if ( viewActionMenu->popupMenu()->count() == 6 ) // remove the separator + viewActionMenu->remove( d->viewActionSeparator ); +} + +void KDirOperator::slotViewSortingChanged( QDir::SortSpec sort ) +{ + mySorting = sort; + updateSortActions(); +} + +void KDirOperator::setEnableDirHighlighting( bool enable ) +{ + d->dirHighlighting = enable; +} + +bool KDirOperator::dirHighlighting() const +{ + return d->dirHighlighting; +} + +void KDirOperator::slotProperties() +{ + if ( m_fileView ) { + const KFileItemList *list = m_fileView->selectedItems(); + if ( !list->isEmpty() ) + (void) new KPropertiesDialog( *list, this, "props dlg", true); + } +} + +void KDirOperator::slotClearView() +{ + if ( m_fileView ) + m_fileView->clearView(); +} + +// ### temporary code +#include <dirent.h> +bool KDirOperator::isReadable( const KURL& url ) +{ + if ( !url.isLocalFile() ) + return true; // what else can we say? + + KDE_struct_stat buf; + QString ts = url.path(+1); + 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; +} + +void KDirOperator::togglePreview( bool on ) +{ + if ( on ) + slotDefaultPreview(); + else + setView( (KFile::FileView) (m_viewKind & ~(KFile::PreviewContents|KFile::PreviewInfo)) ); +} + +void KDirOperator::slotRefreshItems( const KFileItemList& items ) +{ + if ( !m_fileView ) + return; + + KFileItemListIterator it( items ); + for ( ; it.current(); ++it ) + m_fileView->updateView( it.current() ); +} + +void KDirOperator::setViewConfig( KConfig *config, const QString& group ) +{ + d->config = config; + d->configGroup = group; +} + +KConfig * KDirOperator::viewConfig() +{ + return d->config; +} + +QString KDirOperator::viewConfigGroup() const +{ + return d->configGroup; +} + +void KDirOperator::virtual_hook( int, void* ) +{ /*BASE::virtual_hook( id, data );*/ } + +#include "kdiroperator.moc" diff --git a/kio/kfile/kdiroperator.h b/kio/kfile/kdiroperator.h new file mode 100644 index 000000000..a91c7ff1a --- /dev/null +++ b/kio/kfile/kdiroperator.h @@ -0,0 +1,950 @@ +// -*- c++ -*- +/* This file is part of the KDE libraries + Copyright (C) 1999 Stephan Kulow <[email protected]> + 2000,2001 Carsten Pfeiffer <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef KDIROPERATOR_H_ +#define KDIROPERATOR_H_ + +#include <qwidget.h> +#include <qptrstack.h> + +#include <kaction.h> +#include <kcompletion.h> +#include <kdirlister.h> +#include <kfileview.h> +#include <kfileitem.h> +#include <kfile.h> + +class QPopupMenu; +class QTimer; + +class KAction; +class KDirLister; +class KToggleAction; +class KActionSeparator; +class KActionMenu; +class QWidgetStack; +class KProgress; +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� <[email protected]> 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(finishedLoading()), + * SLOT(slotLoadingFinished())); + * + * op->readConfig( KGlobal::config(), "Your KDiroperator ConfigGroup" ); + * 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 <[email protected]>, Carsten Pfeiffer <[email protected]> + */ +class KIO_EXPORT KDirOperator : public QWidget +{ + Q_OBJECT + + public: + /** + * The various action types. These values can be or'd together + * @since 3.1 + */ + enum ActionTypes { 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. + */ + KDirOperator(const KURL& urlName = KURL(), + QWidget *parent = 0, const char* name = 0); + /** + * Destroys the KDirOperator. + */ + virtual ~KDirOperator(); + + /** + * Enables/disables showing hidden files. + */ + // ### KDE4: make virtual + void setShowHiddenFiles ( bool s ) { showHiddenAction->setChecked( s ); } + + /** + * @returns true when hidden files are shown or false otherwise. + */ + bool showHiddenFiles () const { return showHiddenAction->isChecked(); } + + /** + * Stops loading immediately. You don't need to call this, usually. + */ + void close(); + /// Reimplemented to avoid "hidden virtual" warnings + virtual bool close( bool alsoDelete ) { return QWidget::close( alsoDelete ); } + + /** + * Sets a filter like "*.cpp *.h *.o". Only files matching that filter + * will be shown. Call updateDir() to apply it. + * + * @see KDirLister::setNameFilter + * @see nameFilter + */ + void setNameFilter(const QString& filter); + + /** + * @returns the current namefilter. + * @see setNameFilter + */ + const QString& nameFilter() const { return dir->nameFilter(); } + + /** + * 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 { return dir->mimeFilters(); } + + /** + * 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 + */ + // ### KDE4: make 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 + */ + // ### KDE4: make virtual + void setView(KFileView *view); + + /** + * @returns the currently used view. + * @see setView + */ + KFileView * view() const { return m_fileView; } + + /** + * Returns the widget of the current view. 0L if there is no view/widget. + * (KFileView itself is not a widget.) + */ + QWidget * viewWidget() const { return m_fileView ? m_fileView->widget() : 0L; } + + /** + * Sets one of the predefined fileviews + * @see KFile::FileView + */ + // ### KDE4: make virtual + void setView(KFile::FileView view); + + /** + * Sets the way to sort files and directories. + */ + void setSorting( QDir::SortSpec ); + + /** + * @returns the current way of sorting files and directories + */ + QDir::SortSpec sorting() const { return mySorting; } + + /** + * @returns true if we are displaying the root directory of the current url + */ + bool isRoot() const { return url().path() == QChar('/'); } + + /** + * @returns the object listing the directory + */ + KDirLister *dirLister() const { return dir; } + + /** + * @returns the progress widget, that is shown during directory listing. + * You can for example reparent() it to put it into a statusbar. + */ + KProgress * 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. + */ + // ### KDE4: make virtual + void setMode( KFile::Mode m ); + /** + * @returns the listing/selection mode. + */ + KFile::Mode 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! + */ + // ### KDE4: make virtual + void setPreviewWidget(const QWidget *w); + + /** + * @returns a list of all currently selected items. If there is no view, + * then 0L is returned. + */ + const KFileItemList * selectedItems() const { + return ( m_fileView ? m_fileView->selectedItems() : 0L ); + } + + /** + * @returns true if @p item is currently selected, or false otherwise. + */ + inline bool isSelected( const KFileItem *item ) const { + return ( m_fileView ? m_fileView->isSelected( item ) : false ); + } + + /** + * @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 { + return const_cast<KCompletion *>( &myCompletion ); + } + + /** + * @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 { + return const_cast<KCompletion *>( &myDirCompletion ); + } + + /** + * 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 separator : a separator + * @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 date : sorts by date + * @li by size : sorts by size + * @li reversed : reverses the sort order + * @li dirs first : sorts directories before files + * @li case insensitive : sorts case insensitively + * @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 separate dirs : shows directories in a separate pane + * @li preview : shows a preview next to the fileview + * @li single : hides the separate view for directories or the preview + * @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 { return myActionCollection; } + + /** + * 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. + * + * @see viewConfig + * @see viewConfigGroup + * @since 3.1 + */ + // ### KDE4: make virtual + void setViewConfig( KConfig *config, const QString& group ); + + /** + * Returns the KConfig object used for saving and restoring view's + * configuration. + * @returns the KConfig object used for saving and restoring view's + * configuration. + * @since 3.1 + */ + KConfig *viewConfig(); + + /** + * Returns the group name used for saving and restoring view's + * configuration. + * @returns the group name used for saving and restoring view's + * configuration. + * @since 3.1 + */ + QString 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( KConfig *, const QString& group = QString::null ); + + /** + * Saves the current settings like sorting, simple or detailed view. + * + * @see readConfig + * @see setViewConfig + */ + virtual void writeConfig( KConfig *, const QString& group = QString::null ); + + + /** + * 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. + */ + // ### KDE4: make virtual and turn QString into KURL + 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 ask specifies whether a confirmation dialog should be shown + * @param showProgress passed to the DeleteJob to show a progress dialog + */ + // ### KDE4: make virtual + KIO::DeleteJob * del( const KFileItemList& items, + bool ask = true, bool showProgress = 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 + * @since 3.1 + */ + // ### KDE4: make virtual + KIO::DeleteJob * del( const KFileItemList& items, QWidget *parent, + 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, because this behavior introduces bug #136630. + * Don't enable until this bug is fixed. + */ + // ### KDE4: make 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 { return dirOnlyMode( myMode ); } + + static bool dirOnlyMode( uint mode ) { + return ( (mode & KFile::Directory) && + (mode & (KFile::File | KFile::Files)) == 0 ); + } + + /** + * 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 + * @since 3.2 + */ + // ### KDE4: make 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 + * @since 3.4 + */ + // ### KDE4: make virtual + KIO::CopyJob * trash( const KFileItemList& 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 view is the predefined view to be set, note: this can be several ones OR:ed together. + * @returns the created KFileView + * @see KFileView + * @see KCombiView + * @see KFileDetailView + * @see KFileIconView + * @see KFilePreview + * @see KFile::FileView + * @see setView + */ + virtual KFileView* createView( QWidget* parent, KFile::FileView view ); + /** + * Sets a custom KDirLister to list directories. + */ + // ### KDE4: make virtual + void setDirLister( KDirLister *lister ); + + virtual void resizeEvent( QResizeEvent * ); + + /** + * 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. + * @since 3.1 + */ + 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(); + +public slots: + /** + * Goes one step back in the history and opens that url. + */ + // ### KDE4: make virtual + void back(); + + /** + * Goes one step forward in the history and opens that url. + */ + // ### KDE4: make virtual + void forward(); + + /** + * Enters the home directory. + */ + // ### KDE4: make virtual + void home(); + + /** + * Goes one directory up from the current url. + */ + // ### KDE4: make virtual + void cdUp(); + + /** + * to update the view after changing the settings + */ + void updateDir(); + + /** + * Re-reads the current url. + */ + // ### KDE4: make virtual + void rereadDir(); + + /** + * Opens a dialog to create a new directory. + */ + // ### KDE4: make virtual + void mkdir(); + + /** + * Deletes the currently selected files/directories. + */ + // ### KDE4: make 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. + * @since 3.4 + */ + // ### KDE4: make virtual + void trashSelected(KAction::ActivationReason, Qt::ButtonState); + +protected 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(); + + /** + * Adds a new list of KFileItems to the view + * (coming from KDirLister) + */ + void insertNewFiles(const KFileItemList &newone); + + /** + * Removes the given KFileItem item from the view (usually called from + * KDirLister). + */ + void itemDeleted(KFileItem *); + + /** + * Enters the directory specified by the given @p item. + */ + // ### KDE4: make virtual + void selectDir(const KFileItem *item ); + + /** + * Emits fileSelected( item ) + */ + void selectFile(const KFileItem *item); + + /** + * Emits fileHighlighted( i ) + */ + void highlightFile(const KFileItem* i) { emit fileHighlighted( i ); } + + /** + * Called upon right-click to activate the popupmenu. + */ + virtual void activatedMenu( const KFileItem *, const QPoint& pos ); + + /** + * Changes sorting to sort by name + */ + void sortByName() { byNameAction->setChecked( true ); } + + /** + * Changes sorting to sort by size + */ + void sortBySize() { bySizeAction->setChecked( true ); } + + /** + * Changes sorting to sort by date + */ + void sortByDate() { byDateAction->setChecked( true ); } + + /** + * Changes sorting to reverse sorting + */ + void sortReversed() { reverseAction->setChecked( !reverseAction->isChecked() ); } + + /** + * Toggles showing directories first / having them sorted like files. + */ + void toggleDirsFirst() { dirsFirstAction->setChecked( !dirsFirstAction->isChecked() ); } + + /** + * Toggles case sensitive / case insensitive sorting + */ + void toggleIgnoreCase() { caseInsensitiveAction->setChecked( !caseInsensitiveAction->isChecked() ); } + + /** + * Tries to make the given @p match as current item in the view and emits + * completion( match ) + */ + void slotCompletionMatch(const QString& match); + +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( KFileView * 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 ); + /** + * 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. + * @since 3.2 + */ + void dropped(const KFileItem *item, QDropEvent*event, const KURL::List&urls); +private: + /** + * Contains all URLs you can reach with the back button. + */ + QPtrStack<KURL> backStack; + + /** + * Contains all URLs you can reach with the forward button. + */ + QPtrStack<KURL> forwardStack; + + KDirLister *dir; + KURL currUrl; + + KCompletion myCompletion; + KCompletion myDirCompletion; + bool myCompleteListDirty; + QDir::SortSpec mySorting; + + /** + * 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); + + void connectView(KFileView *); + + bool openURL( const KURL& url, bool keep = false, bool reload = false ); + + KFileView *m_fileView; + KFileItemList pendingMimeTypes; + + // the enum KFile::FileView as an int + int m_viewKind; + int defaultView; + + KFile::Mode myMode; + KProgress *progress; + + const QWidget *myPreview; // temporary pointer for the preview widget + + // actions for the popupmenus + // ### clean up all those -- we have them all in the actionMenu! + KActionMenu *actionMenu; + + KAction *backAction; + KAction *forwardAction; + KAction *homeAction; + KAction *upAction; + KAction *reloadAction; + KActionSeparator *actionSeparator; + KAction *mkdirAction; + + KActionMenu *sortActionMenu; + KRadioAction *byNameAction; + KRadioAction *byDateAction; + KRadioAction *bySizeAction; + KToggleAction *reverseAction; + KToggleAction *dirsFirstAction; + KToggleAction *caseInsensitiveAction; + + KActionMenu *viewActionMenu; + KRadioAction *shortAction; + KRadioAction *detailedAction; + KToggleAction *showHiddenAction; + KToggleAction *separateDirsAction; + + KActionCollection *myActionCollection; + KActionCollection *viewActionCollection; + +private slots: + /** + * @internal + */ + void slotDetailedView(); + void slotSimpleView(); + void slotToggleHidden( bool ); + + void slotSeparateDirs(); + void slotDefaultPreview(); + void togglePreview( bool ); + + void slotSortByName(); + void slotSortBySize(); + void slotSortByDate(); + 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 slotViewSortingChanged( QDir::SortSpec ); + + void slotClearView(); + void slotRefreshItems( const KFileItemList& items ); + + void slotProperties(); + + void insertViewDependentActions(); + +private: + static bool isReadable( const KURL& url ); + +protected: + virtual void virtual_hook( int id, void* data ); +private: + class KDirOperatorPrivate; + KDirOperatorPrivate *d; +}; + +#endif diff --git a/kio/kfile/kdirselectdialog.cpp b/kio/kfile/kdirselectdialog.cpp new file mode 100644 index 000000000..b8ea409e9 --- /dev/null +++ b/kio/kfile/kdirselectdialog.cpp @@ -0,0 +1,481 @@ +/* + Copyright (C) 2001,2002 Carsten Pfeiffer <[email protected]> + Copyright (C) 2001 Michael Jarrett <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qdir.h> +#include <qlayout.h> +#include <qpopupmenu.h> +#include <qstringlist.h> +#include <qvaluestack.h> + +#include <kactionclasses.h> +#include <kapplication.h> +#include <kcombobox.h> +#include <kconfig.h> +#include <kfiledialog.h> +#include <kfilespeedbar.h> +#include <kglobalsettings.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kprotocolinfo.h> +#include <krecentdirs.h> +#include <kshell.h> +#include <kurl.h> +#include <kurlcompletion.h> +#include <kurlpixmapprovider.h> +#include <kinputdialog.h> +#include <kio/netaccess.h> +#include <kio/renamedlg.h> +#include <kmessagebox.h> + +#include "kfiletreeview.h" +#include "kdirselectdialog.h" + +// ### add mutator for treeview! + +class KDirSelectDialog::KDirSelectDialogPrivate +{ +public: + KDirSelectDialogPrivate() + { + urlCombo = 0L; + branch = 0L; + comboLocked = false; + } + + KFileSpeedBar *speedBar; + KHistoryCombo *urlCombo; + KFileTreeBranch *branch; + QString recentDirClass; + KURL startURL; + QValueStack<KURL> dirsToList; + + bool comboLocked : 1; +}; + +static KURL rootUrl(const KURL &url) +{ + KURL root = url; + root.setPath( "/" ); + + if (!kapp->authorizeURLAction("list", KURL(), root)) + { + root = KURL::fromPathOrURL( QDir::homeDirPath() ); + if (!kapp->authorizeURLAction("list", KURL(), root)) + { + root = url; + } + } + return root; +} + +KDirSelectDialog::KDirSelectDialog(const QString &startDir, bool localOnly, + QWidget *parent, const char *name, + bool modal) + : KDialogBase( parent, name, modal, i18n("Select Folder"), + Ok|Cancel|User1, Ok, false, + KGuiItem( i18n("New Folder..."), "folder_new" ) ), + m_localOnly( localOnly ) +{ + d = new KDirSelectDialogPrivate; + d->branch = 0L; + + QFrame *page = makeMainWidget(); + QHBoxLayout *hlay = new QHBoxLayout( page, 0, spacingHint() ); + m_mainLayout = new QVBoxLayout(); + d->speedBar = new KFileSpeedBar( page, "speedbar" ); + connect( d->speedBar, SIGNAL( activated( const KURL& )), + SLOT( setCurrentURL( const KURL& )) ); + hlay->addWidget( d->speedBar, 0 ); + hlay->addLayout( m_mainLayout, 1 ); + + // Create dir list + m_treeView = new KFileTreeView( page ); + m_treeView->addColumn( i18n("Folders") ); + m_treeView->setColumnWidthMode( 0, QListView::Maximum ); + m_treeView->setResizeMode( QListView::AllColumns ); + + d->urlCombo = new KHistoryCombo( page, "url combo" ); + d->urlCombo->setTrapReturnKey( true ); + d->urlCombo->setPixmapProvider( new KURLPixmapProvider() ); + KURLCompletion *comp = new KURLCompletion(); + comp->setMode( KURLCompletion::DirCompletion ); + d->urlCombo->setCompletionObject( comp, true ); + d->urlCombo->setAutoDeleteCompletionObject( true ); + d->urlCombo->setDuplicatesEnabled( false ); + connect( d->urlCombo, SIGNAL( textChanged( const QString& ) ), + SLOT( slotComboTextChanged( const QString& ) )); + + m_contextMenu = new QPopupMenu( this ); + KAction* newFolder = new KAction( i18n("New Folder..."), "folder_new", 0, this, SLOT( slotMkdir() ), this); + newFolder->plug(m_contextMenu); + m_contextMenu->insertSeparator(); + m_showHiddenFolders = new KToggleAction ( i18n( "Show Hidden Folders" ), 0, this, + SLOT( slotShowHiddenFoldersToggled() ), this); + m_showHiddenFolders->plug(m_contextMenu); + + d->startURL = KFileDialog::getStartURL( startDir, d->recentDirClass ); + if ( localOnly && !d->startURL.isLocalFile() ) + { + d->startURL = KURL(); + QString docPath = KGlobalSettings::documentPath(); + if (QDir(docPath).exists()) + d->startURL.setPath( docPath ); + else + d->startURL.setPath( QDir::homeDirPath() ); + } + + KURL root = rootUrl(d->startURL); + + m_startDir = d->startURL.url(); + + d->branch = createBranch( root ); + + readConfig( KGlobal::config(), "DirSelect Dialog" ); + + m_mainLayout->addWidget( m_treeView, 1 ); + m_mainLayout->addWidget( d->urlCombo, 0 ); + + connect( m_treeView, SIGNAL( currentChanged( QListViewItem * )), + SLOT( slotCurrentChanged() )); + connect( m_treeView, SIGNAL( contextMenu( KListView *, QListViewItem *, const QPoint & )), + SLOT( slotContextMenu( KListView *, QListViewItem *, const QPoint & ))); + + connect( d->urlCombo, SIGNAL( activated( const QString& )), + SLOT( slotURLActivated( const QString& ))); + connect( d->urlCombo, SIGNAL( returnPressed( const QString& )), + SLOT( slotURLActivated( const QString& ))); + + setCurrentURL( d->startURL ); +} + + +KDirSelectDialog::~KDirSelectDialog() +{ + delete d; +} + +void KDirSelectDialog::setCurrentURL( const KURL& url ) +{ + if ( !url.isValid() ) + return; + + KURL root = rootUrl(url); + + d->startURL = url; + if ( !d->branch || + url.protocol() != d->branch->url().protocol() || + url.host() != d->branch->url().host() ) + { + if ( d->branch ) + { + // removing the root-item causes the currentChanged() signal to be + // emitted, but we don't want to update the location-combo yet. + d->comboLocked = true; + view()->removeBranch( d->branch ); + d->comboLocked = false; + } + + d->branch = createBranch( root ); + } + + d->branch->disconnect( SIGNAL( populateFinished( KFileTreeViewItem * )), + this, SLOT( slotNextDirToList( KFileTreeViewItem *))); + connect( d->branch, SIGNAL( populateFinished( KFileTreeViewItem * )), + SLOT( slotNextDirToList( KFileTreeViewItem * ) )); + + KURL dirToList = root; + d->dirsToList.clear(); + QString path = url.path(+1); + int pos = path.length(); + + if ( path.isEmpty() ) // e.g. ftp://host.com/ -> just list the root dir + d->dirsToList.push( root ); + + else + { + while ( pos > 0 ) + { + pos = path.findRev( '/', pos -1 ); + if ( pos >= 0 ) + { + dirToList.setPath( path.left( pos +1 ) ); + d->dirsToList.push( dirToList ); +// qDebug( "List: %s", dirToList.url().latin1()); + } + } + } + + if ( !d->dirsToList.isEmpty() ) + openNextDir( d->branch->root() ); +} + +void KDirSelectDialog::openNextDir( KFileTreeViewItem * /*parent*/ ) +{ + if ( !d->branch ) + return; + + KURL url = d->dirsToList.pop(); + + KFileTreeViewItem *item = view()->findItem( d->branch, url.path().mid(1)); + if ( item ) + { + if ( !item->isOpen() ) + item->setOpen( true ); + else // already open -> go to next one + slotNextDirToList( item ); + } +// else +// qDebug("###### openNextDir: item not found!"); +} + +void KDirSelectDialog::slotNextDirToList( KFileTreeViewItem *item ) +{ + // scroll to make item the topmost item + view()->ensureItemVisible( item ); + QRect r = view()->itemRect( item ); + if ( r.isValid() ) + { + int x, y; + view()->viewportToContents( view()->contentsX(), r.y(), x, y ); + view()->setContentsPos( x, y ); + } + + if ( !d->dirsToList.isEmpty() ) + openNextDir( item ); + else + { + d->branch->disconnect( SIGNAL( populateFinished( KFileTreeViewItem * )), + this, SLOT( slotNextDirToList( KFileTreeViewItem *))); + view()->setCurrentItem( item ); + item->setSelected( true ); + } +} + +void KDirSelectDialog::readConfig( KConfig *config, const QString& group ) +{ + d->urlCombo->clear(); + + KConfigGroup conf( config, group ); + d->urlCombo->setHistoryItems( conf.readPathListEntry( "History Items" )); + + QSize defaultSize( 400, 450 ); + resize( conf.readSizeEntry( "DirSelectDialog Size", &defaultSize )); +} + +void KDirSelectDialog::saveConfig( KConfig *config, const QString& group ) +{ + KConfigGroup conf( config, group ); + conf.writePathEntry( "History Items", d->urlCombo->historyItems(), ',', + true, true); + conf.writeEntry( "DirSelectDialog Size", size(), true, true ); + + d->speedBar->save( config ); + + config->sync(); +} + +void KDirSelectDialog::slotUser1() +{ + slotMkdir(); +} + +void KDirSelectDialog::accept() +{ + KFileTreeViewItem *item = m_treeView->currentKFileTreeViewItem(); + if ( !item ) + return; + + if ( !d->recentDirClass.isEmpty() ) + { + KURL dir = item->url(); + if ( !item->isDir() ) + dir = dir.upURL(); + + KRecentDirs::add(d->recentDirClass, dir.url()); + } + + d->urlCombo->addToHistory( item->url().prettyURL() ); + KFileDialog::setStartDir( url() ); + + KDialogBase::accept(); + saveConfig( KGlobal::config(), "DirSelect Dialog" ); +} + + +KURL KDirSelectDialog::url() const +{ + return m_treeView->currentURL(); +} + +void KDirSelectDialog::slotCurrentChanged() +{ + if ( d->comboLocked ) + return; + + KFileTreeViewItem *current = view()->currentKFileTreeViewItem(); + KURL u = current ? current->url() : (d->branch ? d->branch->rootUrl() : KURL()); + + if ( u.isValid() ) + { + if ( u.isLocalFile() ) + d->urlCombo->setEditText( u.path() ); + + else // remote url + d->urlCombo->setEditText( u.prettyURL() ); + } + else + d->urlCombo->setEditText( QString::null ); +} + +void KDirSelectDialog::slotURLActivated( const QString& text ) +{ + if ( text.isEmpty() ) + return; + + KURL url = KURL::fromPathOrURL( text ); + d->urlCombo->addToHistory( url.prettyURL() ); + + if ( localOnly() && !url.isLocalFile() ) + return; // ### messagebox + + KURL oldURL = m_treeView->currentURL(); + if ( oldURL.isEmpty() ) + oldURL = KURL::fromPathOrURL( m_startDir ); + + setCurrentURL( url ); +} + +KFileTreeBranch * KDirSelectDialog::createBranch( const KURL& url ) +{ + QString title = url.isLocalFile() ? url.path() : url.prettyURL(); + KFileTreeBranch *branch = view()->addBranch( url, title, m_showHiddenFolders->isChecked() ); + branch->setChildRecurse( false ); + view()->setDirOnlyMode( branch, true ); + + return branch; +} + +void KDirSelectDialog::slotComboTextChanged( const QString& text ) +{ + if ( d->branch ) + { + KURL url = KURL::fromPathOrURL( KShell::tildeExpand( text ) ); + KFileTreeViewItem *item = d->branch->findTVIByURL( url ); + if ( item ) + { + view()->setCurrentItem( item ); + view()->setSelected( item, true ); + view()->ensureItemVisible( item ); + return; + } + } + + QListViewItem *item = view()->currentItem(); + if ( item ) + { + item->setSelected( false ); + // 2002/12/27, deselected item is not repainted, so force it + item->repaint(); + } +} + +void KDirSelectDialog::slotContextMenu( KListView *, QListViewItem *, const QPoint& pos ) +{ + m_contextMenu->popup( pos ); +} + +void KDirSelectDialog::slotMkdir() +{ + bool ok; + QString where = url().pathOrURL(); + QString name = i18n( "New Folder" ); + if ( url().isLocalFile() && QFileInfo( url().path(+1) + name ).exists() ) + name = KIO::RenameDlg::suggestName( url(), name ); + + QString directory = KIO::encodeFileName( KInputDialog::getText( i18n( "New Folder" ), + i18n( "Create new folder in:\n%1" ).arg( where ), + name, &ok, this)); + if (!ok) + return; + + bool selectDirectory = true; + bool writeOk = false; + bool exists = false; + KURL folderurl( url() ); + + QStringList dirs = QStringList::split( QDir::separator(), directory ); + QStringList::ConstIterator it = dirs.begin(); + + for ( ; it != dirs.end(); ++it ) + { + folderurl.addPath( *it ); + exists = KIO::NetAccess::exists( folderurl, false, 0 ); + writeOk = !exists && KIO::NetAccess::mkdir( folderurl, topLevelWidget() ); + } + + if ( exists ) // url was already existant + { + QString which = folderurl.isLocalFile() ? folderurl.path() : folderurl.prettyURL(); + KMessageBox::sorry(this, i18n("A file or folder named %1 already exists.").arg(which)); + selectDirectory = false; + } + else if ( !writeOk ) { + KMessageBox::sorry(this, i18n("You do not have permission to create that folder." )); + } + else if ( selectDirectory ) { + setCurrentURL( folderurl ); + } +} + +void KDirSelectDialog::slotShowHiddenFoldersToggled() +{ + KURL currentURL = url(); + + d->comboLocked = true; + view()->removeBranch( d->branch ); + d->comboLocked = false; + + KURL root = rootUrl(d->startURL); + d->branch = createBranch( root ); + + setCurrentURL( currentURL ); +} + +// static +KURL KDirSelectDialog::selectDirectory( const QString& startDir, + bool localOnly, + QWidget *parent, + const QString& caption) +{ + KDirSelectDialog myDialog( startDir, localOnly, parent, + "kdirselect dialog", true ); + + if ( !caption.isNull() ) + myDialog.setCaption( caption ); + + if ( myDialog.exec() == QDialog::Accepted ) + return KIO::NetAccess::mostLocalURL(myDialog.url(),parent); + else + return KURL(); +} + +void KDirSelectDialog::virtual_hook( int id, void* data ) +{ KDialogBase::virtual_hook( id, data ); } + +#include "kdirselectdialog.moc" diff --git a/kio/kfile/kdirselectdialog.h b/kio/kfile/kdirselectdialog.h new file mode 100644 index 000000000..7f2bb13c5 --- /dev/null +++ b/kio/kfile/kdirselectdialog.h @@ -0,0 +1,131 @@ +/* + Copyright (C) 2001 Michael Jarrett <[email protected]> + Copyright (C) 2001 Carsten Pfeiffer <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KDIRSELECTDIALOG_H +#define KDIRSELECTDIALOG_H + +#include <kdialogbase.h> +#include <kurl.h> + +class QPopupMenu; +class QVBoxLayout; +class KConfig; +class KFileTreeBranch; +class KFileTreeView; +class KFileTreeViewItem; +class KToggleAction; + +/** + * A pretty dialog for a KDirSelect control for selecting directories. + * @author Michael Jarrett <[email protected]> + * @see KFileDialog + */ +class KIO_EXPORT KDirSelectDialog : public KDialogBase +{ + Q_OBJECT + +public: + /** + * The constructor. Creates a dialog to select a directory (url). + * @internal use the static selectDirectory function + * @param startDir the directory, initially shown + * @param localOnly unused. You can only select paths below the startDir + * @param parent the parent for the dialog, usually 0L + * @param name the QObject::name + * @param modal if the dialog is modal or not + */ + KDirSelectDialog(const QString& startDir = QString::null, + bool localOnly = false, + QWidget *parent = 0L, + const char *name = 0, bool modal = false); + + /** + */ + ~KDirSelectDialog(); + + /** + * Returns the currently-selected URL, or a blank URL if none is selected. + * @return The currently-selected URL, if one was selected. + */ + KURL url() const; + + KFileTreeView * view() const { return m_treeView; } + + bool localOnly() const { return m_localOnly; } + + /** + * Creates a KDirSelectDialog, and returns the result. + * @param startDir the directory, initially shown + * The tree will display this directory and subdirectories of it. + * @param localOnly unused. You can only select paths below the startDir + * @param parent the parent widget to use for the dialog, or NULL to create a parent-less dialog + * @param caption the caption to use for the dialog, or QString::null for the default caption + * @return The URL selected, or an empty URL if the user canceled + * or no URL was selected. + */ + static KURL selectDirectory( const QString& startDir = QString::null, + bool localOnly = false, QWidget *parent = 0L, + const QString& caption = QString::null); + + /** + * @return The path for the root node + */ + QString startDir() const { return m_startDir; } + +public slots: + void setCurrentURL( const KURL& url ); + +protected slots: + virtual void slotUser1(); + +protected: + virtual void accept(); + + // Layouts protected so that subclassing is easy + QVBoxLayout *m_mainLayout; + QString m_startDir; + +private slots: + void slotCurrentChanged(); + void slotURLActivated( const QString& ); + void slotNextDirToList( KFileTreeViewItem *dirItem ); + void slotComboTextChanged( const QString& text ); + void slotContextMenu( KListView *, QListViewItem *, const QPoint & ); + void slotShowHiddenFoldersToggled(); + void slotMkdir(); + +private: + void readConfig( KConfig *config, const QString& group ); + void saveConfig( KConfig *config, const QString& group ); + void openNextDir( KFileTreeViewItem *parent ); + KFileTreeBranch * createBranch( const KURL& url ); + + KFileTreeView *m_treeView; + QPopupMenu *m_contextMenu; + KToggleAction *m_showHiddenFolders; + bool m_localOnly; + +protected: + virtual void virtual_hook( int id, void* data ); +private: + class KDirSelectDialogPrivate; + KDirSelectDialogPrivate *d; +}; + +#endif diff --git a/kio/kfile/kdirsize.cpp b/kio/kfile/kdirsize.cpp new file mode 100644 index 000000000..cebe42cbc --- /dev/null +++ b/kio/kfile/kdirsize.cpp @@ -0,0 +1,166 @@ +/* This file is part of the KDE libraries + Copyright (C) 2000 David Faure <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kdirsize.h" +#include <kdebug.h> +#include <kglobal.h> +#include <qapplication.h> +#include <qtimer.h> +#include <config-kfile.h> + +using namespace KIO; + +KDirSize::KDirSize( const KURL & directory ) + : KIO::Job(false /*No GUI*/), m_bAsync(true), m_totalSize(0L), m_totalFiles(0L), m_totalSubdirs(0L) +{ + startNextJob( directory ); +} + +KDirSize::KDirSize( const KFileItemList & lstItems ) + : KIO::Job(false /*No GUI*/), m_bAsync(true), m_totalSize(0L), m_totalFiles(0L), m_totalSubdirs(0L), m_lstItems(lstItems) +{ + QTimer::singleShot( 0, this, SLOT(processList()) ); +} + +void KDirSize::processList() +{ + while (!m_lstItems.isEmpty()) + { + KFileItem * item = m_lstItems.first(); + m_lstItems.removeFirst(); + if ( !item->isLink() ) + { + if ( item->isDir() ) + { + kdDebug(kfile_area) << "KDirSize::processList dir -> listing" << endl; + KURL url = item->url(); + startNextJob( url ); + return; // we'll come back later, when this one's finished + } + else + { + m_totalSize += item->size(); +// no long long with kdDebug() +// kdDebug(kfile_area) << "KDirSize::processList file -> " << m_totalSize << endl; + } + } + } + kdDebug(kfile_area) << "KDirSize::processList finished" << endl; + if ( !m_bAsync ) + qApp->exit_loop(); + emitResult(); +} + +void KDirSize::startNextJob( const KURL & url ) +{ + KIO::ListJob * listJob = KIO::listRecursive( url, false /* no GUI */ ); + connect( listJob, SIGNAL(entries( KIO::Job *, + const KIO::UDSEntryList& )), + SLOT( slotEntries( KIO::Job*, + const KIO::UDSEntryList& ))); + addSubjob( listJob ); +} + +void KDirSize::slotEntries( KIO::Job*, const KIO::UDSEntryList & list ) +{ + static const QString& dot = KGlobal::staticQString( "." ); + static const QString& dotdot = KGlobal::staticQString( ".." ); + KIO::UDSEntryListConstIterator it = list.begin(); + KIO::UDSEntryListConstIterator end = list.end(); + for (; it != end; ++it) { + KIO::UDSEntry::ConstIterator it2 = (*it).begin(); + KIO::filesize_t size = 0; + bool isLink = false; + bool isDir = false; + QString name; + for( ; it2 != (*it).end(); it2++ ) { + switch( (*it2).m_uds ) { + case KIO::UDS_NAME: + name = (*it2).m_str; + break; + case KIO::UDS_LINK_DEST: + isLink = !(*it2).m_str.isEmpty(); + break; + case KIO::UDS_SIZE: + size = ((*it2).m_long); + break; + case KIO::UDS_FILE_TYPE: + isDir = S_ISDIR((*it2).m_long); + break; + default: + break; + } + } + if ( name == dot ) + m_totalSize += size; + else if ( name != dotdot ) + { + if (!isLink) + m_totalSize += size; + if (!isDir) + m_totalFiles++; + else + m_totalSubdirs++; + //kdDebug(kfile_area) << name << ":" << size << endl; + } + } +} + +//static +KDirSize * KDirSize::dirSizeJob( const KURL & directory ) +{ + return new KDirSize( directory ); // useless - but consistent with other jobs +} + +//static +KDirSize * KDirSize::dirSizeJob( const KFileItemList & lstItems ) +{ + return new KDirSize( lstItems ); +} + +//static +KIO::filesize_t KDirSize::dirSize( const KURL & directory ) +{ + KDirSize * dirSize = dirSizeJob( directory ); + dirSize->setSync(); + qApp->enter_loop(); + return dirSize->totalSize(); +} + + +void KDirSize::slotResult( KIO::Job * job ) +{ + kdDebug(kfile_area) << " KDirSize::slotResult( KIO::Job * job ) m_lstItems:" << m_lstItems.count() << endl; + if ( !m_lstItems.isEmpty() ) + { + subjobs.remove(job); // Remove job, but don't kill this job. + processList(); + } + else + { + if ( !m_bAsync ) + qApp->exit_loop(); + KIO::Job::slotResult( job ); + } +} + +void KDirSize::virtual_hook( int id, void* data ) +{ KIO::Job::virtual_hook( id, data ); } + +#include "kdirsize.moc" diff --git a/kio/kfile/kdirsize.h b/kio/kfile/kdirsize.h new file mode 100644 index 000000000..64ae80765 --- /dev/null +++ b/kio/kfile/kdirsize.h @@ -0,0 +1,106 @@ +/* This file is part of the KDE libraries + Copyright (C) 2000 David Faure <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KDIRSIZE_H +#define __KDIRSIZE_H + +#include <kio/job.h> +#include <kfileitem.h> + +/** + * Computes a directory size (similar to "du", but doesn't give the same results + * since we simply sum up the dir and file sizes, whereas du speaks disk blocks) + */ +class KIO_EXPORT KDirSize : public KIO::Job +{ + Q_OBJECT +protected: + KDirSize( const KURL & directory ); + KDirSize( const KFileItemList & lstItems ); + ~KDirSize() {} + +public: + /** + * @return the size we found + */ + KIO::filesize_t totalSize() const { return m_totalSize; } + + /** + * @return the total number of files (counting symlinks to files, sockets + * and character devices as files) in this directory and all sub-directories + * @since 3.3 + */ + KIO::filesize_t totalFiles() const { return m_totalFiles; } + + /** + * @return the total number of sub-directories found (not including the + * directory the search started from and treating symlinks to directories + * as directories) + * @since 3.3 + */ + KIO::filesize_t totalSubdirs() const { return m_totalSubdirs; } + + /** + * Asynchronous method. Connect to the result signal. + * This one lists a single directory. + */ + static KDirSize * dirSizeJob( const KURL & directory ); + + /** + * Asynchronous method. Connect to the result signal. + * This one lists the items from @p lstItems. + * The reason we asks for items instead of just urls, is so that + * we directly know if the item is a file or a directory, + * and in case of a file, we already have its size. + */ + static KDirSize * dirSizeJob( const KFileItemList & lstItems ); + + /** + * Synchronous method - you get the result as soon as + * the call returns. + */ + static KIO::filesize_t dirSize( const KURL & directory ); + +protected: + /** + * @internal + */ + void setSync() { m_bAsync = false; } + + void startNextJob( const KURL & url ); + +protected slots: + + virtual void slotResult( KIO::Job *job ); + void slotEntries( KIO::Job * , const KIO::UDSEntryList &); + void processList(); + +private: + bool m_bAsync; + KIO::filesize_t m_totalSize; + KIO::filesize_t m_totalFiles; + KIO::filesize_t m_totalSubdirs; + KFileItemList m_lstItems; +protected: + virtual void virtual_hook( int id, void* data ); +private: + class KDirSize* d; +}; + +#endif diff --git a/kio/kfile/kdiskfreesp.cpp b/kio/kfile/kdiskfreesp.cpp new file mode 100644 index 000000000..f748b2a66 --- /dev/null +++ b/kio/kfile/kdiskfreesp.cpp @@ -0,0 +1,169 @@ +/* + * kdiskfreesp.cpp + * + * Copyright (c) 1999 Michael Kropfberger <[email protected]> + * + * Requires the Qt widget libraries, available at no cost at + * http://www.troll.no/ + * + * + * 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 "kdiskfreesp.h" +#include <qfile.h> +#include <qtextstream.h> + +#include <kdebug.h> +#include <kprocess.h> +#include <kio/global.h> +#include <config-kfile.h> + +#include "kdiskfreesp.moc" + +#define DF_COMMAND "df" +#define DF_ARGS "-k" +#define NO_FS_TYPE true + +#define BLANK ' ' +#define FULL_PERCENT 95.0 + +/*************************************************************************** + * constructor +**/ +KDiskFreeSp::KDiskFreeSp(QObject *parent, const char *name) + : QObject(parent,name) +{ + dfProc = new KProcess(); Q_CHECK_PTR(dfProc); + dfProc->setEnvironment("LANGUAGE", "C"); + connect( dfProc, SIGNAL(receivedStdout(KProcess *, char *, int) ), + this, SLOT (receivedDFStdErrOut(KProcess *, char *, int)) ); + connect(dfProc,SIGNAL(processExited(KProcess *) ), + this, SLOT(dfDone() ) ); + + readingDFStdErrOut=false; +} + + +/*************************************************************************** + * destructor +**/ +KDiskFreeSp::~KDiskFreeSp() +{ + delete dfProc; +} + +/*************************************************************************** + * is called, when the df-command writes on StdOut +**/ +void KDiskFreeSp::receivedDFStdErrOut(KProcess *, char *data, int len) +{ + QCString tmp(data,len+1); // adds a zero-byte + dfStringErrOut.append(tmp); +} + +/*************************************************************************** + * reads the df-commands results +**/ +int KDiskFreeSp::readDF( const QString & mountPoint ) +{ + if (readingDFStdErrOut || dfProc->isRunning()) + return -1; + m_mountPoint = mountPoint; + dfStringErrOut=""; // yet no data received + dfProc->clearArguments(); + (*dfProc) << QString::fromLocal8Bit(DF_COMMAND) << QString::fromLocal8Bit(DF_ARGS); + if (!dfProc->start( KProcess::NotifyOnExit, KProcess::AllOutput )) + kdError() << "could not execute ["<< DF_COMMAND << "]" << endl; + return 1; +} + + +/*************************************************************************** + * is called, when the df-command has finished +**/ +void KDiskFreeSp::dfDone() +{ + readingDFStdErrOut=true; + + QTextStream t (dfStringErrOut, IO_ReadOnly); + QString s=t.readLine(); + if ( (s.isEmpty()) || ( s.left(10) != QString::fromLatin1("Filesystem") ) ) + kdError() << "Error running df command... got [" << s << "]" << endl; + while ( !t.eof() ) { + QString u,v; + s=t.readLine(); + s=s.simplifyWhiteSpace(); + if ( !s.isEmpty() ) { + //kdDebug(kfile_area) << "GOT: [" << s << "]" << endl; + + if (s.find(BLANK)<0) // devicename was too long, rest in next line + if ( !t.eof() ) { // just appends the next line + v=t.readLine(); + s=s.append(v); + s=s.simplifyWhiteSpace(); + //kdDebug(kfile_area) << "SPECIAL GOT: [" << s << "]" << endl; + }//if silly linefeed + + //kdDebug(kfile_area) << "[" << s << "]" << endl; + + //QString deviceName = s.left(s.find(BLANK)); + s=s.remove(0,s.find(BLANK)+1 ); + //kdDebug(kfile_area) << " DeviceName: [" << deviceName << "]" << endl; + + if (!NO_FS_TYPE) + s=s.remove(0,s.find(BLANK)+1 ); // eat fs type + + u=s.left(s.find(BLANK)); + unsigned long kBSize = u.toULong(); + s=s.remove(0,s.find(BLANK)+1 ); + //kdDebug(kfile_area) << " Size: [" << kBSize << "]" << endl; + + u=s.left(s.find(BLANK)); + unsigned long kBUsed = u.toULong(); + s=s.remove(0,s.find(BLANK)+1 ); + //kdDebug(kfile_area) << " Used: [" << kBUsed << "]" << endl; + + u=s.left(s.find(BLANK)); + unsigned long kBAvail = u.toULong(); + s=s.remove(0,s.find(BLANK)+1 ); + //kdDebug(kfile_area) << " Avail: [" << kBAvail << "]" << endl; + + + s=s.remove(0,s.find(BLANK)+1 ); // delete the capacity 94% + QString mountPoint = s.stripWhiteSpace(); + //kdDebug(kfile_area) << " MountPoint: [" << mountPoint << "]" << endl; + + if ( mountPoint == m_mountPoint ) + { + //kdDebug(kfile_area) << "Found mount point. Emitting" << endl; + emit foundMountPoint( mountPoint, kBSize, kBUsed, kBAvail ); + emit foundMountPoint( kBSize, kBUsed, kBAvail, mountPoint ); // sic! + } + }//if not header + }//while further lines available + + readingDFStdErrOut=false; + emit done(); + delete this; +} + +KDiskFreeSp * KDiskFreeSp::findUsageInfo( const QString & path ) +{ + KDiskFreeSp * job = new KDiskFreeSp; + QString mountPoint = KIO::findPathMountPoint( path ); + job->readDF( mountPoint ); + return job; +} diff --git a/kio/kfile/kdiskfreesp.h b/kio/kfile/kdiskfreesp.h new file mode 100644 index 000000000..53b83ba40 --- /dev/null +++ b/kio/kfile/kdiskfreesp.h @@ -0,0 +1,89 @@ +/* + * kdiskfreesp.h + * + * Copyright (c) 1999 Michael Kropfberger <[email protected]> + * + * Requires the Qt widget libraries, available at no cost at + * http://www.troll.no/ + * + * + * 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 __KDISKFREESP_H__ +#define __KDISKFREESP_H__ + +#include <qobject.h> +#include <qstring.h> + +#include <kdelibs_export.h> + +class KProcess; + +/** + * This class parses the output of "df" to find the disk usage + * information for a given partition (mount point). + */ +class KIO_EXPORT KDiskFreeSp : public QObject +{ Q_OBJECT +public: + KDiskFreeSp( QObject *parent=0, const char *name=0 ); + /** + * Destructor - this object autodeletes itself when it's done + */ + ~KDiskFreeSp(); + /** + * Call this to fire a search on the disk usage information + * for @p mountPoint. foundMountPoint will be emitted + * if this mount point is found, with the info requested. + * done is emitted in any case. + */ + int readDF( const QString & mountPoint ); + + /** + * Call this to fire a search on the disk usage information + * for the mount point containing @p path. + * foundMountPoint will be emitted + * if this mount point is found, with the info requested. + * done is emitted in any case. + */ + static KDiskFreeSp * findUsageInfo( const QString & path ); + +signals: + void foundMountPoint( const QString & mountPoint, unsigned long kBSize, unsigned long kBUsed, unsigned long kBAvail ); + + // This one is a hack around a weird (compiler?) bug. In the former signal, + // the slot in KPropsDlg would get 0L, 0L as the last two parameters. + // When using const ulong& instead, all is ok. + void foundMountPoint( const unsigned long&, const unsigned long&, const unsigned long&, const QString& ); + void done(); + +private slots: + void receivedDFStdErrOut(KProcess *, char *data, int len); + void dfDone(); + +private: + KProcess *dfProc; + QCString dfStringErrOut; + QString m_mountPoint; + bool readingDFStdErrOut; + class KDiskFreeSpPrivate; + KDiskFreeSpPrivate * d; +}; +/***************************************************************************/ + + +#endif diff --git a/kio/kfile/kencodingfiledialog.cpp b/kio/kfile/kencodingfiledialog.cpp new file mode 100644 index 000000000..63ce8a5dc --- /dev/null +++ b/kio/kfile/kencodingfiledialog.cpp @@ -0,0 +1,223 @@ +// -*- c++ -*- +/* This file is part of the KDE libraries + Copyright (C) 2003 Joseph Wenninger <[email protected]> + 2003 Andras Mantia <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config-kfile.h" + +#include "kencodingfiledialog.h" +#include <kcombobox.h> +#include <ktoolbar.h> +#include <kglobal.h> +#include <klocale.h> +#include <kcharsets.h> +#include <qtextcodec.h> +#include <kdiroperator.h> +#include <krecentdocument.h> + +struct KEncodingFileDialogPrivate +{ + KComboBox *encoding; +}; + +KEncodingFileDialog::KEncodingFileDialog(const QString& startDir, const QString& encoding , const QString& filter, + const QString& caption, KFileDialog::OperationMode type, QWidget *parent, const char* name, bool modal) + : KFileDialog(startDir,filter,parent,name,modal), d(new KEncodingFileDialogPrivate) +{ + setCaption(caption); + + setOperationMode( type ); + + KToolBar *tb = toolBar(); + tb->insertSeparator(); + int index = tb->insertCombo(QStringList(), -1 /*id*/, false /*writable*/, 0 /*signal*/, 0 /*receiver*/, 0 /*slot*/ ); + d->encoding = tb->getCombo( tb->idAt( index ) ); + if ( !d->encoding ) + return; + + d->encoding->clear (); + QString sEncoding = encoding; + if (sEncoding.isEmpty()) + sEncoding = QString::fromLatin1(KGlobal::locale()->encoding()); + + QStringList encodings (KGlobal::charsets()->availableEncodingNames()); + int insert = 0; + for (uint i=0; i < encodings.count(); i++) + { + bool found = false; + QTextCodec *codecForEnc = KGlobal::charsets()->codecForName(encodings[i], found); + + if (found) + { + d->encoding->insertItem (encodings[i]); + if ( (codecForEnc->name() == sEncoding) || (encodings[i] == sEncoding) ) + { + d->encoding->setCurrentItem(insert); + } + + insert++; + } + } + + +} + +KEncodingFileDialog::~KEncodingFileDialog() +{ + delete d; +} + + +QString KEncodingFileDialog::selectedEncoding() const +{ + if (d->encoding) + return d->encoding->currentText(); + else + return QString::null; +} + + +KEncodingFileDialog::Result KEncodingFileDialog::getOpenFileNameAndEncoding(const QString& encoding, + const QString& startDir, + const QString& filter, + QWidget *parent, const QString& caption) +{ + KEncodingFileDialog dlg(startDir, encoding,filter,caption.isNull() ? i18n("Open") : caption,Opening,parent, + "filedialog", true); + + dlg.setMode( KFile::File | KFile::LocalOnly ); + dlg.ops->clearHistory(); + dlg.exec(); + + Result res; + res.fileNames<<dlg.selectedFile(); + res.encoding=dlg.selectedEncoding(); + return res; +} + +KEncodingFileDialog::Result KEncodingFileDialog::getOpenFileNamesAndEncoding(const QString& encoding, + const QString& startDir, + const QString& filter, + QWidget *parent, + const QString& caption) +{ + KEncodingFileDialog dlg(startDir, encoding,filter,caption.isNull() ? i18n("Open") : caption,Opening,parent, + "filedialog", true); + dlg.setMode(KFile::Files | KFile::LocalOnly); + dlg.ops->clearHistory(); + dlg.exec(); + + Result res; + res.fileNames=dlg.selectedFiles(); + res.encoding=dlg.selectedEncoding(); + return res; +} + +KEncodingFileDialog::Result KEncodingFileDialog::getOpenURLAndEncoding(const QString& encoding, const QString& startDir, + const QString& filter, QWidget *parent, const QString& caption) +{ + KEncodingFileDialog dlg(startDir, encoding,filter,caption.isNull() ? i18n("Open") : caption,Opening,parent, + "filedialog", true); + + dlg.setMode( KFile::File ); + dlg.ops->clearHistory(); + dlg.exec(); + + Result res; + res.URLs<<dlg.selectedURL(); + res.encoding=dlg.selectedEncoding(); + return res; +} + +KEncodingFileDialog::Result KEncodingFileDialog::getOpenURLsAndEncoding(const QString& encoding, const QString& startDir, + const QString& filter, + QWidget *parent, + const QString& caption) +{ + KEncodingFileDialog dlg(startDir, encoding,filter,caption.isNull() ? i18n("Open") : caption,Opening,parent, + "filedialog", true); + + dlg.setMode(KFile::Files); + dlg.ops->clearHistory(); + dlg.exec(); + + Result res; + res.URLs=dlg.selectedURLs(); + res.encoding=dlg.selectedEncoding(); + return res; +} + + +KEncodingFileDialog::Result KEncodingFileDialog::getSaveFileNameAndEncoding(const QString& encoding, + const QString& dir, + const QString& filter, + QWidget *parent, + const QString& caption) +{ + bool specialDir = dir.at(0) == ':'; + KEncodingFileDialog dlg(specialDir?dir:QString::null, encoding,filter,caption.isNull() ? i18n("Save As") : caption, + Saving,parent, "filedialog", true); + + if ( !specialDir ) + dlg.setSelection( dir ); // may also be a filename + dlg.exec(); + + QString filename = dlg.selectedFile(); + if (!filename.isEmpty()) + KRecentDocument::add(filename); + + Result res; + res.fileNames<<filename; + res.encoding=dlg.selectedEncoding(); + return res; +} + + +KEncodingFileDialog::Result KEncodingFileDialog::getSaveURLAndEncoding(const QString& encoding, + const QString& dir, const QString& filter, + QWidget *parent, const QString& caption) +{ + bool specialDir = dir.at(0) == ':'; + KEncodingFileDialog dlg(specialDir?dir:QString::null, encoding,filter,caption.isNull() ? i18n("Save As") : + caption, Saving,parent, "filedialog", true); + + if ( !specialDir ) + dlg.setSelection( dir ); // may also be a filename + + dlg.exec(); + + KURL url = dlg.selectedURL(); + if (url.isValid()) + KRecentDocument::add( url ); + + Result res; + res.URLs<<url; + res.encoding=dlg.selectedEncoding(); + return res; +} + + + +void KEncodingFileDialog::virtual_hook( int id, void* data ) +{ + KFileDialog::virtual_hook( id, data ); +} + + +#include "kencodingfiledialog.moc" diff --git a/kio/kfile/kencodingfiledialog.h b/kio/kfile/kencodingfiledialog.h new file mode 100644 index 000000000..fbf3b34e6 --- /dev/null +++ b/kio/kfile/kencodingfiledialog.h @@ -0,0 +1,313 @@ +// -*- c++ -*- +/* This file is part of the KDE libraries + Copyright (C) 2003 Joseph Wenninger <[email protected]> + 2003 Andras Mantia <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KENCODINGFILEDIALOG_H__ +#define __KENCODINGFILEDIALOG_H__ + +#include <kfiledialog.h> + +struct KEncodingFileDialogPrivate; + +/** + * Provides a user (and developer) friendly way to + * select files with support for choosing encoding + * + * + * The dialog has been designed to allow applications to customise it + * by subclassing. It uses geometry management to ensure that subclasses + * can easily add children that will be incorporated into the layout. + */ + +class KIO_EXPORT KEncodingFileDialog : public KFileDialog +{ + Q_OBJECT + +public: + class Result { + public: + QStringList fileNames; + KURL::List URLs; + QString encoding; + }; + + /** + * Constructs a file dialog for text files with encoding selection possibility. + * + * @param startDir This can either be + * @li The URL of the directory to start in. + * @li QString::null to start in the current working + * directory, or the last directory where a file has been + * selected. + * @li ':<keyword>' to start in the directory last used + * by a filedialog in the same application that specified + * the same keyword. + * @li '::<keyword>' to start in the directory last used + * by a filedialog in any application that specified the + * same keyword. + * + * @param encoding The encoding shown in the encoding combo. If it's + * QString::null, the global default encoding will be shown. + * + * @param filter This is a space separated list of shell globs. + * You can set the text to be displayed for the glob, and + * provide multiple globs. See setFilter() for details on + * how to do this... + * + * @param caption The caption of the dialog + * + * @param type This can either be + * @li Opening (open dialog, the default setting) + * @li Saving + * @param parent The parent widget of this dialog + * @param name The name of this object + * @param modal Whether to create a modal dialog or not + * + * @since 3.2 + */ + KEncodingFileDialog (const QString& startDir = QString::null, + const QString& encoding = QString::null, + const QString& filter = QString::null, + const QString& caption = QString::null, KFileDialog::OperationMode type = KFileDialog::Opening, + QWidget *parent= 0, const char *name="", bool modal = true); + /** + * Destructs the file dialog. + */ + ~KEncodingFileDialog(); + + + /** + * @returns The selected encoding if the constructor with the encoding parameter was used, otherwise QString::null. + */ + QString selectedEncoding() const; + + + /** + * Creates a modal file dialog and return the selected + * filename or an empty string if none was chosen additionally a chosen + * encoding value is returned. + * + * Note that with + * this method the user must select an existing filename. + * + * @param startDir This can either be + * @li The URL of the directory to start in. + * @li QString::null to start in the current working + * directory, or the last directory where a file has been + * selected. + * @li ':<keyword>' to start in the directory last used + * by a filedialog in the same application that specified + * the same keyword. + * @li '::<keyword>' to start in the directory last used + * by a filedialog in any application that specified the + * same keyword. + * @param encoding The encoding shown in the encoding combo. + * @param filter This is a space separated list of shell globs. + * You can set the text to be displayed for the glob, and + * provide multiple globs. See setFilter() for details on + * how to do this... + * @param parent The widget the dialog will be centered on initially. + * @param caption The name of the dialog widget. + */ + static Result getOpenFileNameAndEncoding(const QString& encoding=QString::null, + const QString& startDir= QString::null, + const QString& filter= QString::null, + QWidget *parent= 0, + const QString& caption = QString::null); + + /** + * Creates a modal file dialog and returns the selected encoding and the selected + * filenames or an empty list if none was chosen. + * + * Note that with + * this method the user must select an existing filename. + * + * @param startDir This can either be + * @li The URL of the directory to start in. + * @li QString::null to start in the current working + * directory, or the last directory where a file has been + * selected. + * @li ':<keyword>' to start in the directory last used + * by a filedialog in the same application that specified + * the same keyword. + * @li '::<keyword>' to start in the directory last used + * by a filedialog in any application that specified the + * same keyword. + * @param encoding The encoding shown in the encoding combo. + * @param filter This is a space separated list of shell globs. + * You can set the text to be displayed for the glob, and + * provide multiple globs. See setFilter() for details on + * how to do this. + * @param parent The widget the dialog will be centered on initially. + * @param caption The name of the dialog widget. + */ + static Result getOpenFileNamesAndEncoding(const QString& encoding=QString::null, + const QString& startDir= QString::null, + const QString& filter= QString::null, + QWidget *parent = 0, + const QString& caption= QString::null); + + /** + * Creates a modal file dialog and returns the selected encoding and + * URL or an empty string if none was chosen. + * + * Note that with + * this method the user must select an existing URL. + * + * @param startDir This can either be + * @li The URL of the directory to start in. + * @li QString::null to start in the current working + * directory, or the last directory where a file has been + * selected. + * @li ':<keyword>' to start in the directory last used + * by a filedialog in the same application that specified + * the same keyword. + * @li '::<keyword>' to start in the directory last used + * by a filedialog in any application that specified the + * same keyword. + * @param encoding The encoding shown in the encoding combo. + * @param filter This is a space separated list of shell globs. + * You can set the text to be displayed for the glob, and + * provide multiple globs. See setFilter() for details on + * how to do this... + * @param parent The widget the dialog will be centered on initially. + * @param caption The name of the dialog widget. + */ + static Result getOpenURLAndEncoding(const QString& encoding=QString::null, + const QString& startDir = QString::null, + const QString& filter= QString::null, + QWidget *parent= 0, + const QString& caption = QString::null); + + + + + /** + * Creates a modal file dialog and returns the selected encoding + * URLs or an empty list if none was chosen. + * + * Note that with + * this method the user must select an existing filename. + * + * @param startDir This can either be + * @li The URL of the directory to start in. + * @li QString::null to start in the current working + * directory, or the last directory where a file has been + * selected. + * @li ':<keyword>' to start in the directory last used + * by a filedialog in the same application that specified + * the same keyword. + * @li '::<keyword>' to start in the directory last used + * by a filedialog in any application that specified the + * same keyword. + * @param encoding The encoding shown in the encoding combo. + * @param filter This is a space separated list of shell globs. + * You can set the text to be displayed for the glob, and + * provide multiple globs. See setFilter() for details on + * how to do this... + * @param parent The widget the dialog will be centered on initially. + * @param caption The name of the dialog widget. + */ + static Result getOpenURLsAndEncoding(const QString& encoding=QString::null, + const QString& startDir= QString::null, + const QString& filter= QString::null, + QWidget *parent = 0, + const QString& caption= QString::null); + + + + /** + * Creates a modal file dialog and returns the selected encoding and + * filename or an empty string if none was chosen. + * + * Note that with this + * method the user need not select an existing filename. + * + * @param startDir This can either be + * @li The URL of the directory to start in. + * @li a relative path or a filename determining the + * directory to start in and the file to be selected. + * @li QString::null to start in the current working + * directory, or the last directory where a file has been + * selected. + * @li ':<keyword>' to start in the directory last used + * by a filedialog in the same application that specified + * the same keyword. + * @li '::<keyword>' to start in the directory last used + * by a filedialog in any application that specified the + * same keyword. + * @param encoding The encoding shown in the encoding combo. + * @param filter This is a space separated list of shell globs. + * You can set the text to be displayed for the glob, and + * provide multiple globs. See setFilter() for details on + * how to do this... + * @param parent The widget the dialog will be centered on initially. + * @param caption The name of the dialog widget. + */ + static Result getSaveFileNameAndEncoding(const QString& encoding=QString::null, + const QString& startDir=QString::null, + const QString& filter= QString::null, + QWidget *parent= 0, + const QString& caption = QString::null); + + + /** + * Creates a modal file dialog and returns the selected encoding and + * filename or an empty string if none was chosen. + * + * Note that with this + * method the user need not select an existing filename. + * + * @param startDir This can either be + * @li The URL of the directory to start in. + * @li a relative path or a filename determining the + * directory to start in and the file to be selected. + * @li QString::null to start in the current working + * directory, or the last directory where a file has been + * selected. + * @li ':<keyword>' to start in the directory last used + * by a filedialog in the same application that specified + * the same keyword. + * @li '::<keyword>' to start in the directory last used + * by a filedialog in any application that specified the + * same keyword. + * @param encoding The encoding shown in the encoding combo. + * @param filter This is a space separated list of shell globs. + * You can set the text to be displayed for the glob, and + * provide multiple globs. See setFilter() for details on + * how to do this... + * @param parent The widget the dialog will be centered on initially. + * @param caption The name of the dialog widget. + */ + static Result getSaveURLAndEncoding(const QString& encoding=QString::null, + const QString& startDir= QString::null, + const QString& filter= QString::null, + QWidget *parent= 0, + const QString& caption = QString::null); + + + +protected: + virtual void virtual_hook( int id, void* data ); +private: + KEncodingFileDialogPrivate *d; +}; + +#endif diff --git a/kio/kfile/kfile.h b/kio/kfile/kfile.h new file mode 100644 index 000000000..70253f9d8 --- /dev/null +++ b/kio/kfile/kfile.h @@ -0,0 +1,129 @@ +/* This file is part of the KDE libraries + Copyright (C) 2000 Carsten Pfeiffer <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + version 2, License 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_H +#define KFILE_H + +#include <qdir.h> + +#include "kdelibs_export.h" + +/** + * KFile is a class which provides a namespace for some enumerated + * values associated with the kfile library. You will never need to + * construct a KFile object itself. + */ + +class KIO_EXPORT KFile +{ +public: + /** + * Modes of operation for the dialog. + * @li @p File - Get a single file name from the user. + * @li @p Directory - Get a directory name from the user. + * @li @p Files - Get multiple file names from the user. + * @li @p ExistingOnly - Never return a filename which does not exist yet + * @li @p LocalOnly - Don't return remote filenames + */ + enum Mode { + File = 1, + Directory = 2, + Files = 4, + ExistingOnly = 8, + LocalOnly = 16, + ModeMax = 65536 + }; + + enum FileView { + Default = 0, + Simple = 1, + Detail = 2, + SeparateDirs = 4, + PreviewContents = 8, + PreviewInfo = 16, + FileViewMax = 65536 + }; + + enum SelectionMode { + Single = 1, + Multi = 2, + Extended = 4, + NoSelection = 8 + }; + + + // + // some bittests + // + + + // sorting specific + + // grr, who had the idea to set QDir::Name to 0x0? + static bool isSortByName( const QDir::SortSpec& sort ) { + return (sort & QDir::Time) != QDir::Time && + (sort & QDir::Size) != QDir::Size; + } + + static bool isSortBySize( const QDir::SortSpec& sort ) { + return (sort & QDir::Size) == QDir::Size; + } + + static bool isSortByDate( const QDir::SortSpec& sort ) { + return (sort & QDir::Time) == QDir::Time; + } + + static bool isSortDirsFirst( const QDir::SortSpec& sort ) { + return (sort & QDir::DirsFirst) == QDir::DirsFirst; + } + + static bool isSortCaseInsensitive( const QDir::SortSpec& sort ) { + return (sort & QDir::IgnoreCase) == QDir::IgnoreCase; + } + + + // view specific + static bool isDefaultView( const FileView& view ) { + return (view & Default) == Default; + } + + static bool isSimpleView( const FileView& view ) { + return (view & Simple) == Simple; + } + + static bool isDetailView( const FileView& view ) { + return (view & Detail) == Detail; + } + + static bool isSeparateDirs( const FileView& view ) { + return (view & SeparateDirs) == SeparateDirs; + } + + static bool isPreviewContents( const FileView& view ) { + return (view & PreviewContents) == PreviewContents; + } + + /** + * @since 3.1 + */ + static bool isPreviewInfo( const FileView& view ) { + return (view & PreviewInfo) == PreviewInfo; + } + +}; + +#endif // KFILE_H diff --git a/kio/kfile/kfilebookmarkhandler.cpp b/kio/kfile/kfilebookmarkhandler.cpp new file mode 100644 index 000000000..f69ae07a2 --- /dev/null +++ b/kio/kfile/kfilebookmarkhandler.cpp @@ -0,0 +1,81 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002 Carsten Pfeiffer <[email protected]> + + 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, version 2. + + 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 <stdio.h> +#include <stdlib.h> + +#include <kbookmarkimporter.h> +#include <kbookmarkdombuilder.h> +#include <kpopupmenu.h> +#include <kstandarddirs.h> + +#include "kfiledialog.h" +#include "kfilebookmarkhandler.h" + +KFileBookmarkHandler::KFileBookmarkHandler( KFileDialog *dialog ) + : QObject( dialog, "KFileBookmarkHandler" ), + KBookmarkOwner(), + m_dialog( dialog ) +{ + m_menu = new KPopupMenu( dialog, "bookmark menu" ); + + QString file = locate( "data", "kfile/bookmarks.xml" ); + if ( file.isEmpty() ) + file = locateLocal( "data", "kfile/bookmarks.xml" ); + + KBookmarkManager *manager = KBookmarkManager::managerForFile( file, false); + + // import old bookmarks + if ( !KStandardDirs::exists( file ) ) { + QString oldFile = locate( "data", "kfile/bookmarks.html" ); + if ( !oldFile.isEmpty() ) + importOldBookmarks( oldFile, manager ); + } + + manager->setUpdate( true ); + manager->setShowNSBookmarks( false ); + + m_bookmarkMenu = new KBookmarkMenu( manager, this, m_menu, + dialog->actionCollection(), true ); +} + +KFileBookmarkHandler::~KFileBookmarkHandler() +{ + delete m_bookmarkMenu; +} + +QString KFileBookmarkHandler::currentURL() const +{ + return m_dialog->baseURL().url(); +} + +void KFileBookmarkHandler::importOldBookmarks( const QString& path, + KBookmarkManager *manager ) +{ + KBookmarkDomBuilder *builder = new KBookmarkDomBuilder( manager->root(), manager ); + KNSBookmarkImporter importer( path ); + builder->connectImporter( &importer ); + importer.parseNSBookmarks(); + delete builder; + manager->save(); +} + +void KFileBookmarkHandler::virtual_hook( int id, void* data ) +{ KBookmarkOwner::virtual_hook( id, data ); } + +#include "kfilebookmarkhandler.moc" diff --git a/kio/kfile/kfilebookmarkhandler.h b/kio/kfile/kfilebookmarkhandler.h new file mode 100644 index 000000000..a6ad4fa2a --- /dev/null +++ b/kio/kfile/kfilebookmarkhandler.h @@ -0,0 +1,63 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002 Carsten Pfeiffer <[email protected]> + + 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, version 2. + + 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 KFILEBOOKMARKHANDLER_H +#define KFILEBOOKMARKHANDLER_H + +#include <kbookmarkmanager.h> +#include <kbookmarkmenu.h> + +class QTextStream; +class KPopupMenu; + + +class KIO_EXPORT KFileBookmarkHandler : public QObject, public KBookmarkOwner +{ + Q_OBJECT + +public: + KFileBookmarkHandler( KFileDialog *dialog ); + ~KFileBookmarkHandler(); + + QPopupMenu * popupMenu(); + + // KBookmarkOwner interface: + virtual void openBookmarkURL( const QString& url ) { emit openURL( url ); } + virtual QString currentURL() const; + + KPopupMenu *menu() const { return m_menu; } + +signals: + void openURL( const QString& url ); + +private: + void importOldBookmarks( const QString& path, KBookmarkManager *manager ); + + KFileDialog *m_dialog; + KPopupMenu *m_menu; + KBookmarkMenu *m_bookmarkMenu; + +protected: + virtual void virtual_hook( int id, void* data ); +private: + class KFileBookmarkHandlerPrivate; + KFileBookmarkHandlerPrivate *d; +}; + + +#endif // KFILEBOOKMARKHANDLER_H diff --git a/kio/kfile/kfiledetailview.cpp b/kio/kfile/kfiledetailview.cpp new file mode 100644 index 000000000..78aef5fb0 --- /dev/null +++ b/kio/kfile/kfiledetailview.cpp @@ -0,0 +1,686 @@ +// -*- c++ -*- +/* This file is part of the KDE libraries + Copyright (C) 1997 Stephan Kulow <[email protected]> + 2000, 2001 Carsten Pfeiffer <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qevent.h> +#include <qkeycode.h> +#include <qheader.h> +#include <qpainter.h> +#include <qpixmap.h> + +#include <kapplication.h> +#include <kfileitem.h> +#include <kglobal.h> +#include <kglobalsettings.h> +#include <kiconloader.h> +#include <kicontheme.h> +#include <klocale.h> +#include <kdebug.h> +#include <kurldrag.h> + +#include "kfiledetailview.h" +#include "config-kfile.h" + +#define COL_NAME 0 +#define COL_SIZE 1 +#define COL_DATE 2 +#define COL_PERM 3 +#define COL_OWNER 4 +#define COL_GROUP 5 + +class KFileDetailView::KFileDetailViewPrivate +{ +public: + KFileDetailViewPrivate() : dropItem(0) + { } + + KFileListViewItem *dropItem; + QTimer autoOpenTimer; +}; + +KFileDetailView::KFileDetailView(QWidget *parent, const char *name) + : KListView(parent, name), KFileView(), d(new KFileDetailViewPrivate()) +{ + // this is always the static section, not the index depending on column order + m_sortingCol = COL_NAME; + m_blockSortingSignal = false; + setViewName( i18n("Detailed View") ); + + addColumn( i18n( "Name" ) ); + addColumn( i18n( "Size" ) ); + addColumn( i18n( "Date" ) ); + addColumn( i18n( "Permissions" ) ); + addColumn( i18n( "Owner" ) ); + addColumn( i18n( "Group" ) ); + setShowSortIndicator( true ); + setAllColumnsShowFocus( true ); + setDragEnabled(true); + + connect( header(), SIGNAL( clicked(int)), + SLOT(slotSortingChanged(int) )); + + + connect( this, SIGNAL( returnPressed(QListViewItem *) ), + SLOT( slotActivate( QListViewItem *) ) ); + + connect( this, SIGNAL( clicked(QListViewItem *, const QPoint&, int)), + SLOT( selected( QListViewItem *) ) ); + connect( this, SIGNAL( doubleClicked(QListViewItem *, const QPoint&, int)), + SLOT( slotActivate( QListViewItem *) ) ); + + connect( this, SIGNAL(contextMenuRequested( QListViewItem *, + const QPoint &, int )), + this, SLOT( slotActivateMenu( QListViewItem *, const QPoint& ))); + + KFile::SelectionMode sm = KFileView::selectionMode(); + switch ( sm ) { + case KFile::Multi: + QListView::setSelectionMode( QListView::Multi ); + break; + case KFile::Extended: + QListView::setSelectionMode( QListView::Extended ); + break; + case KFile::NoSelection: + QListView::setSelectionMode( QListView::NoSelection ); + break; + default: // fall through + case KFile::Single: + QListView::setSelectionMode( QListView::Single ); + break; + } + + // for highlighting + if ( sm == KFile::Multi || sm == KFile::Extended ) + connect( this, SIGNAL( selectionChanged() ), + SLOT( slotSelectionChanged() )); + else + connect( this, SIGNAL( selectionChanged( QListViewItem * ) ), + SLOT( highlighted( QListViewItem * ) )); + + // DND + connect( &(d->autoOpenTimer), SIGNAL( timeout() ), + this, SLOT( slotAutoOpen() )); + + setSorting( sorting() ); + + m_resolver = + new KMimeTypeResolver<KFileListViewItem,KFileDetailView>( this ); +} + +KFileDetailView::~KFileDetailView() +{ + delete m_resolver; + delete d; +} + +void KFileDetailView::readConfig( KConfig *config, const QString& group ) +{ + restoreLayout( config, group ); +} + +void KFileDetailView::writeConfig( KConfig *config, const QString& group ) +{ + saveLayout( config, group ); +} + +void KFileDetailView::setSelected( const KFileItem *info, bool enable ) +{ + if ( !info ) + return; + + // we can only hope that this casts works + KFileListViewItem *item = (KFileListViewItem*)info->extraData( this ); + + if ( item ) + KListView::setSelected( item, enable ); +} + +void KFileDetailView::setCurrentItem( const KFileItem *item ) +{ + if ( !item ) + return; + KFileListViewItem *it = (KFileListViewItem*) item->extraData( this ); + if ( it ) + KListView::setCurrentItem( it ); +} + +KFileItem * KFileDetailView::currentFileItem() const +{ + KFileListViewItem *current = static_cast<KFileListViewItem*>( currentItem() ); + if ( current ) + return current->fileInfo(); + + return 0L; +} + +void KFileDetailView::clearSelection() +{ + KListView::clearSelection(); +} + +void KFileDetailView::selectAll() +{ + if (KFileView::selectionMode() == KFile::NoSelection || + KFileView::selectionMode() == KFile::Single) + return; + + KListView::selectAll( true ); +} + +void KFileDetailView::invertSelection() +{ + KListView::invertSelection(); +} + +void KFileDetailView::slotActivateMenu (QListViewItem *item,const QPoint& pos ) +{ + if ( !item ) { + sig->activateMenu( 0, pos ); + return; + } + KFileListViewItem *i = (KFileListViewItem*) item; + sig->activateMenu( i->fileInfo(), pos ); +} + +void KFileDetailView::clearView() +{ + m_resolver->m_lstPendingMimeIconItems.clear(); + KListView::clear(); +} + +void KFileDetailView::insertItem( KFileItem *i ) +{ + KFileView::insertItem( i ); + + KFileListViewItem *item = new KFileListViewItem( (QListView*) this, i ); + + setSortingKey( item, i ); + + i->setExtraData( this, item ); + + if ( !i->isMimeTypeKnown() ) + m_resolver->m_lstPendingMimeIconItems.append( item ); +} + +void KFileDetailView::slotActivate( QListViewItem *item ) +{ + if ( !item ) + return; + + const KFileItem *fi = ( (KFileListViewItem*)item )->fileInfo(); + if ( fi ) + sig->activate( fi ); +} + +void KFileDetailView::selected( QListViewItem *item ) +{ + if ( !item ) + return; + + if ( KGlobalSettings::singleClick() ) { + const KFileItem *fi = ( (KFileListViewItem*)item )->fileInfo(); + if ( fi && (fi->isDir() || !onlyDoubleClickSelectsFiles()) ) + sig->activate( fi ); + } +} + +void KFileDetailView::highlighted( QListViewItem *item ) +{ + if ( !item ) + return; + + const KFileItem *fi = ( (KFileListViewItem*)item )->fileInfo(); + if ( fi ) + sig->highlightFile( fi ); +} + + +void KFileDetailView::setSelectionMode( KFile::SelectionMode sm ) +{ + disconnect( this, SIGNAL( selectionChanged() )); + disconnect( this, SIGNAL( selectionChanged( QListViewItem * ) )); + + KFileView::setSelectionMode( sm ); + + switch ( KFileView::selectionMode() ) { + case KFile::Multi: + QListView::setSelectionMode( QListView::Multi ); + break; + case KFile::Extended: + QListView::setSelectionMode( QListView::Extended ); + break; + case KFile::NoSelection: + QListView::setSelectionMode( QListView::NoSelection ); + break; + default: // fall through + case KFile::Single: + QListView::setSelectionMode( QListView::Single ); + break; + } + + if ( sm == KFile::Multi || sm == KFile::Extended ) + connect( this, SIGNAL( selectionChanged() ), + SLOT( slotSelectionChanged() )); + else + connect( this, SIGNAL( selectionChanged( QListViewItem * )), + SLOT( highlighted( QListViewItem * ))); +} + +bool KFileDetailView::isSelected( const KFileItem *i ) const +{ + if ( !i ) + return false; + + KFileListViewItem *item = (KFileListViewItem*) i->extraData( this ); + return (item && item->isSelected()); +} + + +void KFileDetailView::updateView( bool b ) +{ + if ( !b ) + return; + + QListViewItemIterator it( (QListView*)this ); + for ( ; it.current(); ++it ) { + KFileListViewItem *item=static_cast<KFileListViewItem *>(it.current()); + item->setPixmap( 0, item->fileInfo()->pixmap(KIcon::SizeSmall) ); + } +} + +void KFileDetailView::updateView( const KFileItem *i ) +{ + if ( !i ) + return; + + KFileListViewItem *item = (KFileListViewItem*) i->extraData( this ); + if ( !item ) + return; + + item->init(); + setSortingKey( item, i ); + + //item->repaint(); // only repaints if visible +} + +void KFileDetailView::setSortingKey( KFileListViewItem *item, + const KFileItem *i ) +{ + // see also setSorting() + QDir::SortSpec spec = KFileView::sorting(); + + if ( spec & QDir::Time ) + item->setKey( sortingKey( i->time( KIO::UDS_MODIFICATION_TIME ), + i->isDir(), spec )); + else if ( spec & QDir::Size ) + item->setKey( sortingKey( i->size(), i->isDir(), spec )); + + else // Name or Unsorted + item->setKey( sortingKey( i->text(), i->isDir(), spec )); +} + + +void KFileDetailView::removeItem( const KFileItem *i ) +{ + if ( !i ) + return; + + KFileListViewItem *item = (KFileListViewItem*) i->extraData( this ); + m_resolver->m_lstPendingMimeIconItems.remove( item ); + delete item; + + KFileView::removeItem( i ); +} + +void KFileDetailView::slotSortingChanged( int col ) +{ + // col is the section here, not the index! + + QDir::SortSpec sort = sorting(); + int sortSpec = -1; + bool reversed = (col == m_sortingCol) && (sort & QDir::Reversed) == 0; + m_sortingCol = col; + + switch( col ) { + case COL_NAME: + sortSpec = (sort & ~QDir::SortByMask | QDir::Name); + break; + case COL_SIZE: + sortSpec = (sort & ~QDir::SortByMask | QDir::Size); + break; + case COL_DATE: + sortSpec = (sort & ~QDir::SortByMask | QDir::Time); + break; + + // the following columns have no equivalent in QDir, so we set it + // to QDir::Unsorted and remember the column (m_sortingCol) + case COL_OWNER: + case COL_GROUP: + case COL_PERM: + // grmbl, QDir::Unsorted == SortByMask. + sortSpec = (sort & ~QDir::SortByMask);// | QDir::Unsorted; + break; + default: + break; + } + + if ( reversed ) + sortSpec |= QDir::Reversed; + else + sortSpec &= ~QDir::Reversed; + + if ( sort & QDir::IgnoreCase ) + sortSpec |= QDir::IgnoreCase; + else + sortSpec &= ~QDir::IgnoreCase; + + + KFileView::setSorting( static_cast<QDir::SortSpec>( sortSpec ) ); + + KFileItem *item; + KFileItemListIterator it( *items() ); + + if ( sortSpec & QDir::Time ) { + for ( ; (item = it.current()); ++it ) + viewItem(item)->setKey( sortingKey( item->time( KIO::UDS_MODIFICATION_TIME ), item->isDir(), sortSpec )); + } + + else if ( sortSpec & QDir::Size ) { + for ( ; (item = it.current()); ++it ) + viewItem(item)->setKey( sortingKey( item->size(), item->isDir(), + sortSpec )); + } + else { // Name or Unsorted -> use column text + for ( ; (item = it.current()); ++it ) { + KFileListViewItem *i = viewItem( item ); + i->setKey( sortingKey( i->text(m_sortingCol), item->isDir(), + sortSpec )); + } + } + + KListView::setSorting( m_sortingCol, !reversed ); + KListView::sort(); + + if ( !m_blockSortingSignal ) + sig->changeSorting( static_cast<QDir::SortSpec>( sortSpec ) ); +} + + +void KFileDetailView::setSorting( QDir::SortSpec spec ) +{ + int col = 0; + if ( spec & QDir::Time ) + col = COL_DATE; + else if ( spec & QDir::Size ) + col = COL_SIZE; + else if ( spec & QDir::Unsorted ) + col = m_sortingCol; + else + col = COL_NAME; + + // inversed, because slotSortingChanged will reverse it + if ( spec & QDir::Reversed ) + spec = (QDir::SortSpec) (spec & ~QDir::Reversed); + else + spec = (QDir::SortSpec) (spec | QDir::Reversed); + + m_sortingCol = col; + KFileView::setSorting( (QDir::SortSpec) spec ); + + + // don't emit sortingChanged() when called via setSorting() + m_blockSortingSignal = true; // can't use blockSignals() + slotSortingChanged( col ); + m_blockSortingSignal = false; +} + +void KFileDetailView::ensureItemVisible( const KFileItem *i ) +{ + if ( !i ) + return; + + KFileListViewItem *item = (KFileListViewItem*) i->extraData( this ); + + if ( item ) + KListView::ensureItemVisible( item ); +} + +// we're in multiselection mode +void KFileDetailView::slotSelectionChanged() +{ + sig->highlightFile( 0L ); +} + +KFileItem * KFileDetailView::firstFileItem() const +{ + KFileListViewItem *item = static_cast<KFileListViewItem*>( firstChild() ); + if ( item ) + return item->fileInfo(); + return 0L; +} + +KFileItem * KFileDetailView::nextItem( const KFileItem *fileItem ) const +{ + if ( fileItem ) { + KFileListViewItem *item = viewItem( fileItem ); + if ( item && item->itemBelow() ) + return ((KFileListViewItem*) item->itemBelow())->fileInfo(); + else + return 0L; + } + else + return firstFileItem(); +} + +KFileItem * KFileDetailView::prevItem( const KFileItem *fileItem ) const +{ + if ( fileItem ) { + KFileListViewItem *item = viewItem( fileItem ); + if ( item && item->itemAbove() ) + return ((KFileListViewItem*) item->itemAbove())->fileInfo(); + else + return 0L; + } + else + return firstFileItem(); +} + +void KFileDetailView::keyPressEvent( QKeyEvent *e ) +{ + KListView::keyPressEvent( e ); + + if ( e->key() == Key_Return || e->key() == Key_Enter ) { + if ( e->state() & ControlButton ) + e->ignore(); + else + e->accept(); + } +} + +// +// mimetype determination on demand +// +void KFileDetailView::mimeTypeDeterminationFinished() +{ + // anything to do? +} + +void KFileDetailView::determineIcon( KFileListViewItem *item ) +{ + (void) item->fileInfo()->determineMimeType(); + updateView( item->fileInfo() ); +} + +void KFileDetailView::listingCompleted() +{ + m_resolver->start(); +} + +QDragObject *KFileDetailView::dragObject() +{ + // create a list of the URL:s that we want to drag + KURL::List urls; + KFileItemListIterator it( * KFileView::selectedItems() ); + for ( ; it.current(); ++it ){ + urls.append( (*it)->url() ); + } + QPixmap pixmap; + if( urls.count() > 1 ) + pixmap = DesktopIcon( "kmultiple", KIcon::SizeSmall ); + if( pixmap.isNull() ) + pixmap = currentFileItem()->pixmap( KIcon::SizeSmall ); + + QPoint hotspot; + hotspot.setX( pixmap.width() / 2 ); + hotspot.setY( pixmap.height() / 2 ); + QDragObject* myDragObject = new KURLDrag( urls, widget() ); + myDragObject->setPixmap( pixmap, hotspot ); + return myDragObject; +} + +void KFileDetailView::slotAutoOpen() +{ + d->autoOpenTimer.stop(); + if( !d->dropItem ) + return; + + KFileItem *fileItem = d->dropItem->fileInfo(); + if (!fileItem) + return; + + if( fileItem->isFile() ) + return; + + if ( fileItem->isDir() || fileItem->isLink()) + sig->activate( fileItem ); +} + +bool KFileDetailView::acceptDrag(QDropEvent* e) const +{ + return KURLDrag::canDecode( e ) && + (e->source()!= const_cast<KFileDetailView*>(this)) && + ( e->action() == QDropEvent::Copy + || e->action() == QDropEvent::Move + || e->action() == QDropEvent::Link ); +} + +void KFileDetailView::contentsDragEnterEvent( QDragEnterEvent *e ) +{ + if ( ! acceptDrag( e ) ) { // can we decode this ? + e->ignore(); // No + return; + } + e->acceptAction(); // Yes + + if ((dropOptions() & AutoOpenDirs) == 0) + return; + + KFileListViewItem *item = dynamic_cast<KFileListViewItem*>(itemAt( contentsToViewport( e->pos() ) )); + if ( item ) { // are we over an item ? + d->dropItem = item; + d->autoOpenTimer.start( autoOpenDelay() ); // restart timer + } + else + { + d->dropItem = 0; + d->autoOpenTimer.stop(); + } +} + +void KFileDetailView::contentsDragMoveEvent( QDragMoveEvent *e ) +{ + if ( ! acceptDrag( e ) ) { // can we decode this ? + e->ignore(); // No + return; + } + e->acceptAction(); // Yes + + if ((dropOptions() & AutoOpenDirs) == 0) + return; + + KFileListViewItem *item = dynamic_cast<KFileListViewItem*>(itemAt( contentsToViewport( e->pos() ) )); + if ( item ) { // are we over an item ? + if (d->dropItem != item) + { + d->dropItem = item; + d->autoOpenTimer.start( autoOpenDelay() ); // restart timer + } + } + else + { + d->dropItem = 0; + d->autoOpenTimer.stop(); + } +} + +void KFileDetailView::contentsDragLeaveEvent( QDragLeaveEvent * ) +{ + d->dropItem = 0; + d->autoOpenTimer.stop(); +} + +void KFileDetailView::contentsDropEvent( QDropEvent *e ) +{ + d->dropItem = 0; + d->autoOpenTimer.stop(); + + if ( ! acceptDrag( e ) ) { // can we decode this ? + e->ignore(); // No + return; + } + e->acceptAction(); // Yes + + KFileListViewItem *item = dynamic_cast<KFileListViewItem*>(itemAt( contentsToViewport( e->pos() ) )); + KFileItem * fileItem = 0; + if (item) + fileItem = item->fileInfo(); + + emit dropped(e, fileItem); + + KURL::List urls; + if (KURLDrag::decode( e, urls ) && !urls.isEmpty()) + { + emit dropped(e, urls, fileItem ? fileItem->url() : KURL()); + sig->dropURLs(fileItem, e, urls); + } +} + + +///////////////////////////////////////////////////////////////// + + +void KFileListViewItem::init() +{ + KFileListViewItem::setPixmap( COL_NAME, inf->pixmap(KIcon::SizeSmall)); + + setText( COL_NAME, inf->text() ); + setText( COL_SIZE, KGlobal::locale()->formatNumber( inf->size(), 0)); + setText( COL_DATE, inf->timeString() ); + setText( COL_PERM, inf->permissionsString() ); + setText( COL_OWNER, inf->user() ); + setText( COL_GROUP, inf->group() ); +} + + +void KFileDetailView::virtual_hook( int id, void* data ) +{ KListView::virtual_hook( id, data ); + KFileView::virtual_hook( id, data ); } + +#include "kfiledetailview.moc" diff --git a/kio/kfile/kfiledetailview.h b/kio/kfile/kfiledetailview.h new file mode 100644 index 000000000..174a2483d --- /dev/null +++ b/kio/kfile/kfiledetailview.h @@ -0,0 +1,219 @@ +// -*- c++ -*- +/* This file is part of the KDE libraries + Copyright (C) 1997 Stephan Kulow <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KFILEDETAILVIEW_H +#define KFILEDETAILVIEW_H + +class KFileItem; +class QWidget; +class QKeyEvent; + +#include <klistview.h> +#include <kmimetyperesolver.h> + +#include "kfileview.h" + +/** + * An item for the listiew, that has a reference to its corresponding + * KFileItem. + */ +class KIO_EXPORT KFileListViewItem : public KListViewItem +{ +public: + KFileListViewItem( QListView *parent, const QString &text, + const QPixmap &icon, KFileItem *fi ) + : KListViewItem( parent, text ), inf( fi ) { + setPixmap( 0, icon ); + setText( 0, text ); + } + + /** + * @since 3.1 + */ + KFileListViewItem( QListView *parent, KFileItem *fi ) + : KListViewItem( parent ), inf( fi ) { + init(); + } + + KFileListViewItem( QListView *parent, const QString &text, + const QPixmap &icon, KFileItem *fi, + QListViewItem *after) + : KListViewItem( parent, after ), inf( fi ) { + setPixmap( 0, icon ); + setText( 0, text ); + } + ~KFileListViewItem() { + inf->removeExtraData( listView() ); + } + + /** + * @returns the corresponding KFileItem + */ + KFileItem *fileInfo() const { + return inf; + } + + virtual QString key( int /*column*/, bool /*ascending*/ ) const { + return m_key; + } + + void setKey( const QString& key ) { m_key = key; } + + QRect rect() const + { + QRect r = listView()->itemRect(this); + return QRect( listView()->viewportToContents( r.topLeft() ), + QSize( r.width(), r.height() ) ); + } + + /** + * @since 3.1 + */ + void init(); + +private: + KFileItem *inf; + QString m_key; + +private: + class KFileListViewItemPrivate; + KFileListViewItemPrivate *d; + +}; + +/** + * A list-view capable of showing KFileItem'. Used in the filedialog + * for example. Most of the documentation is in KFileView class. + * + * @see KDirOperator + * @see KCombiView + * @see KFileIconView + */ +class KIO_EXPORT KFileDetailView : public KListView, public KFileView +{ + Q_OBJECT + +public: + KFileDetailView(QWidget *parent, const char *name); + virtual ~KFileDetailView(); + + virtual QWidget *widget() { return this; } + virtual void clearView(); + virtual void setAutoUpdate( bool ) {} // ### unused. remove in KDE4 + + virtual void setSelectionMode( KFile::SelectionMode sm ); + + virtual void updateView( bool ); + virtual void updateView(const KFileItem*); + virtual void removeItem( const KFileItem *); + virtual void listingCompleted(); + + virtual void setSelected(const KFileItem *, bool); + virtual bool isSelected(const KFileItem *i) const; + virtual void clearSelection(); + virtual void selectAll(); + virtual void invertSelection(); + + virtual void setCurrentItem( const KFileItem * ); + virtual KFileItem * currentFileItem() const; + virtual KFileItem * firstFileItem() const; + virtual KFileItem * nextItem( const KFileItem * ) const; + virtual KFileItem * prevItem( const KFileItem * ) const; + + virtual void insertItem( KFileItem *i ); + + // implemented to get noticed about sorting changes (for sortingIndicator) + virtual void setSorting( QDir::SortSpec ); + + void ensureItemVisible( const KFileItem * ); + + // for KMimeTypeResolver + void mimeTypeDeterminationFinished(); + void determineIcon( KFileListViewItem *item ); + QScrollView *scrollWidget() const { return (QScrollView*) this; } + + virtual void readConfig( KConfig *, const QString& group = QString::null ); + virtual void writeConfig( KConfig *, const QString& group = QString::null); + +signals: + /** + * The user dropped something. + * @p fileItem points to the item dropped on or can be 0 if the + * user dropped on empty space. + * @since 3.2 + */ + void dropped(QDropEvent *event, KFileItem *fileItem); + /** + * The user dropped the URLs @p urls. + * @p url points to the item dropped on or can be empty if the + * user dropped on empty space. + * @since 3.2 + */ + void dropped(QDropEvent *event, const KURL::List &urls, const KURL &url); + +protected: + virtual void keyPressEvent( QKeyEvent * ); + + // DND support + virtual QDragObject *dragObject(); + virtual void contentsDragEnterEvent( QDragEnterEvent *e ); + virtual void contentsDragMoveEvent( QDragMoveEvent *e ); + virtual void contentsDragLeaveEvent( QDragLeaveEvent *e ); + virtual void contentsDropEvent( QDropEvent *ev ); + virtual bool acceptDrag(QDropEvent* e ) const; + + int m_sortingCol; + +protected slots: + void slotSelectionChanged(); + +private slots: + void slotSortingChanged( int ); + void selected( QListViewItem *item ); + void slotActivate( QListViewItem *item ); + void highlighted( QListViewItem *item ); + void slotActivateMenu ( QListViewItem *item, const QPoint& pos ); + void slotAutoOpen(); + +private: + virtual void insertItem(QListViewItem *i) { KListView::insertItem(i); } + virtual void setSorting(int i, bool b) { KListView::setSorting(i, b); } + virtual void setSelected(QListViewItem *i, bool b) { KListView::setSelected(i, b); } + + inline KFileListViewItem * viewItem( const KFileItem *item ) const { + if ( item ) + return (KFileListViewItem *) item->extraData( this ); + return 0L; + } + + void setSortingKey( KFileListViewItem *item, const KFileItem *i ); + + + bool m_blockSortingSignal; + KMimeTypeResolver<KFileListViewItem,KFileDetailView> *m_resolver; + +protected: + virtual void virtual_hook( int id, void* data ); +private: + class KFileDetailViewPrivate; + KFileDetailViewPrivate *d; +}; + +#endif // KFILEDETAILVIEW_H diff --git a/kio/kfile/kfiledialog.cpp b/kio/kfile/kfiledialog.cpp new file mode 100644 index 000000000..5668ec616 --- /dev/null +++ b/kio/kfile/kfiledialog.cpp @@ -0,0 +1,2372 @@ +// -*- c++ -*- +/* This file is part of the KDE libraries + Copyright (C) 1997, 1998 Richard Moore <[email protected]> + 1998 Stephan Kulow <[email protected]> + 1998 Daniel Grana <[email protected]> + 1999,2000,2001,2002,2003 Carsten Pfeiffer <[email protected]> + 2003 Clarence Dang <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kfiledialog.h" + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + +#include <qptrcollection.h> +#include <qcheckbox.h> +#include <qcombobox.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qlineedit.h> +#include <qptrlist.h> +#include <qpixmap.h> +#include <qtextcodec.h> +#include <qtooltip.h> +#include <qtimer.h> +#include <qwhatsthis.h> +#include <qfiledialog.h> + +#include <kaccel.h> +#include <kaction.h> +#include <kapplication.h> +#include <kcharsets.h> +#include <kcmdlineargs.h> +#include <kcompletionbox.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kglobal.h> +#include <kglobalsettings.h> +#include <kiconloader.h> +#include <kimageio.h> +#include <kio/job.h> +#include <kio/netaccess.h> +#include <kio/scheduler.h> +#include <kio/kservicetypefactory.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kmimetype.h> +#include <kpopupmenu.h> +#include <kprotocolinfo.h> +#include <kpushbutton.h> +#include <krecentdirs.h> +#include <kshell.h> +#include <kstandarddirs.h> +#include <kstdguiitem.h> +#include <kstaticdeleter.h> +#include <ktoolbar.h> +#include <ktoolbarbutton.h> +#include <kurl.h> +#include <kurlcombobox.h> +#include <kurlcompletion.h> +#include <kuser.h> + +#include "config-kfile.h" +#include "kpreviewwidgetbase.h" + +#include <kdirselectdialog.h> +#include <kfileview.h> +#include <krecentdocument.h> +#include <kfilefiltercombo.h> +#include <kdiroperator.h> +#include <kimagefilepreview.h> + +#include <kfilespeedbar.h> +#include <kfilebookmarkhandler.h> + +#ifdef Q_WS_X11 +#include <X11/Xlib.h> +#include <fixx11h.h> +#endif + +enum Buttons { HOTLIST_BUTTON, + PATH_COMBO, CONFIGURE_BUTTON }; + +template class QPtrList<KIO::StatJob>; + +namespace { + static void silenceQToolBar(QtMsgType, const char *) + { + } +} + +struct KFileDialogPrivate +{ + // 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; + QWidget *mainWidget; + + QLabel *locationLabel; + + // @deprecated remove in KDE4 + QLabel *filterLabel; + KURLComboBox *pathCombo; + KPushButton *okButton, *cancelButton; + KFileSpeedBar *urlBar; + QHBoxLayout *urlBarLayout; + QWidget *customWidget; + + // 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 + + QPtrList<KIO::StatJob> 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 :1; + + // the KDirOperators view is set in KFileDialog::show(), so to avoid + // setting it again and again, we have this nice little boolean :) + bool hasView :1; + + bool hasDefaultFilter :1; // necessary for the operationMode + KFileDialog::OperationMode operationMode; + + // The file class used for KRecentDirs + QString fileClass; + + KFileBookmarkHandler *bookmarkHandler; + + // the ID of the path drop down so subclasses can place their custom widgets properly + int m_pathComboIndex; +}; + +KURL *KFileDialog::lastDirectory; // to set the start path + +static KStaticDeleter<KURL> ldd; + +KFileDialog::KFileDialog(const QString& startDir, const QString& filter, + QWidget *parent, const char* name, bool modal) + : KDialogBase( parent, name, modal, QString::null, 0 ) +{ + init( startDir, filter, 0 ); +} + +KFileDialog::KFileDialog(const QString& startDir, const QString& filter, + QWidget *parent, const char* name, bool modal, QWidget* widget) + : KDialogBase( parent, name, modal, QString::null, 0 ) +{ + init( startDir, filter, widget ); +} + + +KFileDialog::~KFileDialog() +{ + hide(); + + KConfig *config = KGlobal::config(); + + if (d->urlBar) + d->urlBar->save( config ); + + config->sync(); + + delete d->bookmarkHandler; // Should be deleted before ops! + delete ops; + delete d; +} + +void KFileDialog::setLocationLabel(const QString& text) +{ + d->locationLabel->setText(text); +} + +void KFileDialog::setFilter(const QString& filter) +{ + int pos = filter.find('/'); + + // Check for an un-escaped '/', if found + // interpret as a MIME filter. + + if (pos > 0 && filter[pos - 1] != '\\') { + QStringList filters = QStringList::split( " ", filter ); + setMimeFilter( filters ); + return; + } + + // Strip the escape characters from + // escaped '/' characters. + + QString copy (filter); + for (pos = 0; (pos = copy.find("\\/", pos)) != -1; ++pos) + copy.remove(pos, 1); + + ops->clearFilter(); + filterWidget->setFilter(copy); + ops->setNameFilter(filterWidget->currentFilter()); + d->hasDefaultFilter = false; + filterWidget->setEditable( true ); + + updateAutoSelectExtension (); +} + +QString KFileDialog::currentFilter() const +{ + return filterWidget->currentFilter(); +} + +// deprecated +void KFileDialog::setFilterMimeType(const QString &label, + const KMimeType::List &types, + const KMimeType::Ptr &defaultType) +{ + d->mimetypes.clear(); + d->filterLabel->setText(label); + + KMimeType::List::ConstIterator it; + for( it = types.begin(); it != types.end(); ++it) + d->mimetypes.append( (*it)->name() ); + + setMimeFilter( d->mimetypes, defaultType->name() ); +} + +void KFileDialog::setMimeFilter( const QStringList& mimeTypes, + const QString& defaultType ) +{ + d->mimetypes = mimeTypes; + filterWidget->setMimeFilter( mimeTypes, defaultType ); + + QStringList types = QStringList::split(" ", filterWidget->currentFilter()); + types.append( QString::fromLatin1( "inode/directory" )); + ops->clearFilter(); + ops->setMimeFilter( types ); + d->hasDefaultFilter = !defaultType.isEmpty(); + filterWidget->setEditable( !d->hasDefaultFilter || + d->operationMode != Saving ); + + updateAutoSelectExtension (); +} + +void KFileDialog::clearFilter() +{ + d->mimetypes.clear(); + filterWidget->setFilter( QString::null ); + ops->clearFilter(); + d->hasDefaultFilter = false; + filterWidget->setEditable( true ); + + updateAutoSelectExtension (); +} + +QString KFileDialog::currentMimeFilter() const +{ + int i = filterWidget->currentItem(); + if (filterWidget->showsAllTypes()) + i--; + + if ((i >= 0) && (i < (int) d->mimetypes.count())) + return d->mimetypes[i]; + return QString::null; // The "all types" item has no mimetype +} + +KMimeType::Ptr KFileDialog::currentFilterMimeType() +{ + return KMimeType::mimeType( currentMimeFilter() ); +} + +void KFileDialog::setPreviewWidget(const QWidget *w) { + ops->setPreviewWidget(w); + ops->clearHistory(); + d->hasView = true; +} + +void KFileDialog::setPreviewWidget(const KPreviewWidgetBase *w) { + ops->setPreviewWidget(w); + ops->clearHistory(); + d->hasView = true; +} + +KURL KFileDialog::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 = ops->url(); + u.addPath( url ); // works for filenames and relative paths + u.cleanPath(); // fix "dir/.." + } + } + else // complete URL + u = url; + + return u; +} + +// FIXME: check for "existing" flag here? +void KFileDialog::slotOk() +{ + kdDebug(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 KFileItemList *items = ops->selectedItems(); + + if ( (mode() & KFile::Directory) != KFile::Directory ) { + if ( locationEdit->currentText().stripWhiteSpace().isEmpty() ) { + if ( !items || 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; + KFileItemListIterator it( *items ); + QString endQuote = QString::fromLatin1("\" "); + QString name, files; + while ( it.current() ) { + name = (*it)->name(); + if ( multi ) { + name.prepend( '"' ); + name.append( endQuote ); + } + + files.append( name ); + ++it; + } + setLocationText( files ); + return; + } + } + } + + bool dirOnly = ops->dirOnlyMode(); + + // we can use our kfileitems, no need to parse anything + if ( items && !locationEdit->lineEdit()->edited() && + !(items->isEmpty() && !dirOnly) ) { + + d->urlList.clear(); + d->filenames = QString::null; + + if ( dirOnly ) { + d->url = ops->url(); + } + else { + if ( !(mode() & KFile::Files) ) {// single selection + d->url = items->getFirst()->url(); + } + + else { // multi (dirs and/or files) + d->url = ops->url(); + KFileItemListIterator it( *items ); + while ( it.current() ) { + d->urlList.append( (*it)->url() ); + ++it; + } + } + } + + 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( d->mainWidget, + i18n("You can only select local files."), + i18n("Remote Files Not Accepted") ); + return; + } + + d->url = url; + accept(); + return; + } + + + KURL selectedURL; + + if ( (mode() & KFile::Files) == KFile::Files ) {// multiselection mode + QString locationText = locationEdit->currentText(); + if ( locationText.contains( '/' )) { + // relative path? -> prepend the current directory + KURL u( ops->url(), KShell::tildeExpand(locationText)); + if ( u.isValid() ) + selectedURL = u; + else + selectedURL = ops->url(); + } + else // simple filename -> just use the current URL + selectedURL = ops->url(); + } + + else { + selectedURL = getCompleteURL(locationEdit->currentText()); + + // appendExtension() may change selectedURL + appendExtension (selectedURL); + } + + if ( !selectedURL.isValid() ) { + KMessageBox::sorry( d->mainWidget, i18n("%1\ndoes not appear to be a valid URL.\n").arg(d->url.url()), i18n("Invalid URL") ); + return; + } + + KURL url = KIO::NetAccess::mostLocalURL(selectedURL,topLevelWidget()); + if ( (mode() & KFile::LocalOnly) == KFile::LocalOnly && + !url.isLocalFile() ) { + KMessageBox::sorry( d->mainWidget, + 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 ) { + kdDebug(kfile_area) << "Directory" << endl; + bool done = true; + if ( d->url.isLocalFile() ) { + if ( locationEdit->currentText().stripWhiteSpace().isEmpty() ) { + QFileInfo info( d->url.path() ); + if ( info.isDir() ) { + d->filenames = QString::null; + d->urlList.clear(); + d->urlList.append( d->url ); + accept(); + } + else if (!info.exists() && (mode() & KFile::File) != KFile::File) { + // directory doesn't exist, create and enter it + if ( ops->mkdir( d->url.url(), true )) + return; + else + accept(); + } + 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 ( ops->dirOnlyMode() ) + { + KURL fullURL(d->url, locationEdit->currentText()); + if ( QFile::exists( fullURL.path() ) ) + { + d->url = fullURL; + d->filenames = QString::null; + d->urlList.clear(); + accept(); + return; + } + else // doesn't exist -> reject + return; + } + } + + d->filenames = locationEdit->currentText(); + accept(); // what can we do? + } + + } + else { // FIXME: remote directory, should we allow that? +// qDebug( "**** Selected remote directory: %s", d->url.url().latin1()); + d->filenames = QString::null; + d->urlList.clear(); + d->urlList.append( d->url ); + + if ( mode() & KFile::ExistingOnly ) + done = false; + else + accept(); + } + + if ( done ) + return; + } + + if (!kapp->authorizeURLAction("open", KURL(), d->url)) + { + QString msg = KIO::buildErrorString(KIO::ERR_ACCESS_DENIED, d->url.prettyURL()); + KMessageBox::error( d->mainWidget, msg); + return; + } + + KIO::StatJob *job = 0L; + d->statJobs.clear(); + d->filenames = KShell::tildeExpand(locationEdit->currentText()); + + if ( (mode() & KFile::Files) == KFile::Files && + !locationEdit->currentText().contains( '/' )) { + kdDebug(kfile_area) << "Files\n"; + KURL::List list = parseSelectedURLs(); + for ( KURL::List::ConstIterator it = list.begin(); + it != list.end(); ++it ) + { + if (!kapp->authorizeURLAction("open", KURL(), *it)) + { + QString msg = KIO::buildErrorString(KIO::ERR_ACCESS_DENIED, (*it).prettyURL()); + KMessageBox::error( d->mainWidget, msg); + return; + } + } + for ( KURL::List::ConstIterator it = list.begin(); + it != list.end(); ++it ) + { + job = KIO::stat( *it, !(*it).isLocalFile() ); + job->setWindow (topLevelWidget()); + KIO::Scheduler::scheduleJob( job ); + d->statJobs.append( job ); + connect( job, SIGNAL( result(KIO::Job *) ), + SLOT( slotStatResult( KIO::Job *) )); + } + return; + } + + job = KIO::stat(d->url,!d->url.isLocalFile()); + job->setWindow (topLevelWidget()); + d->statJobs.append( job ); + connect(job, SIGNAL(result(KIO::Job*)), SLOT(slotStatResult(KIO::Job*))); +} + + +static bool isDirectory (const KIO::UDSEntry &t) +{ + bool isDir = false; + + for (KIO::UDSEntry::ConstIterator it = t.begin(); + it != t.end(); + it++) + { + if ((*it).m_uds == KIO::UDS_FILE_TYPE) + { + isDir = S_ISDIR ((mode_t) ((*it).m_long)); + break; + } + } + + return isDir; +} + +// 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 KFileDialog::slotStatResult(KIO::Job* job) +{ + kdDebug(kfile_area) << "slotStatResult" << endl; + KIO::StatJob *sJob = static_cast<KIO::StatJob *>( job ); + + if ( !d->statJobs.removeRef( 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 && !ops->dirOnlyMode()) + { + accept(); + return; + } + + KIO::UDSEntry t = sJob->statResult(); + if (isDirectory (t)) + { + if ( ops->dirOnlyMode() ) + { + d->filenames = QString::null; + d->urlList.clear(); + accept(); + } + else // in File[s] mode, directory means error -> cd into it + { + if ( count == 0 ) { + locationEdit->clearEdit(); + locationEdit->lineEdit()->setEdited( false ); + setURL( sJob->url() ); + } + } + d->statJobs.clear(); + return; + } + else if ( ops->dirOnlyMode() ) + { + return; // ### error message? + } + + kdDebug(kfile_area) << "filename " << sJob->url().url() << endl; + + if ( count == 0 ) + accept(); +} + +void KFileDialog::accept() +{ + setResult( QDialog::Accepted ); // parseSelectedURLs() checks that + + *lastDirectory = ops->url(); + if (!d->fileClass.isEmpty()) + KRecentDirs::add(d->fileClass, ops->url().url()); + + // clear the topmost item, we insert it as full path later on as item 1 + locationEdit->changeItem( QString::null, 0 ); + + KURL::List list = selectedURLs(); + QValueListConstIterator<KURL> 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(-1) : url.prettyURL(-1); + + // remove dupes + for ( int i = 1; i < locationEdit->count(); i++ ) { + if ( locationEdit->text( i ) == file ) { + locationEdit->removeItem( i-- ); + break; + } + } + locationEdit->insertItem( file, 1 ); + } + + KConfig *config = KGlobal::config(); + config->setForceGlobal( true ); + writeConfig( config, ConfigGroup ); + config->setForceGlobal( false ); + + saveRecentFiles( config ); + config->sync(); + + KDialogBase::accept(); + + addToRecentDocuments(); + + if ( (mode() & KFile::Files) != KFile::Files ) // single selection + emit fileSelected(d->url.url()); + + ops->close(); + emit okClicked(); +} + + +void KFileDialog::fileHighlighted(const KFileItem *i) +{ + if (i && i->isDir()) + return; + + + if ( (ops->mode() & KFile::Files) != KFile::Files ) { + if ( !i ) + return; + + d->url = i->url(); + + if ( !locationEdit->hasFocus() ) { // don't disturb while editing + setLocationText( i->name() ); + } + emit fileHighlighted(d->url.url()); + } + + else { + multiSelectionChanged(); + emit selectionChanged(); + } +} + +void KFileDialog::fileSelected(const KFileItem *i) +{ + if (i && i->isDir()) + return; + + if ( (ops->mode() & KFile::Files) != KFile::Files ) { + if ( !i ) + return; + + d->url = i->url(); + setLocationText( i->name() ); + } + else { + multiSelectionChanged(); + emit selectionChanged(); + } + slotOk(); +} + + +// I know it's slow to always iterate thru the whole filelist +// (ops->selectedItems()), but what can we do? +void KFileDialog::multiSelectionChanged() +{ + if ( locationEdit->hasFocus() ) // don't disturb + return; + + locationEdit->lineEdit()->setEdited( false ); + KFileItem *item; + const KFileItemList *list = ops->selectedItems(); + if ( !list ) { + locationEdit->clearEdit(); + return; + } + + static const QString &begin = KGlobal::staticQString(" \""); + KFileItemListIterator it ( *list ); + QString text; + while ( (item = it.current()) ) { + text.append( begin ).append( item->name() ).append( '\"' ); + ++it; + } + + setLocationText( text.stripWhiteSpace() ); +} + +void KFileDialog::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 + disconnect( locationEdit, SIGNAL( textChanged( const QString& ) ), + this, SLOT( slotLocationChanged( const QString& ) ) ); + locationEdit->setCurrentItem( 0 ); + connect( locationEdit, SIGNAL( textChanged( const QString& ) ), + SLOT( slotLocationChanged( const QString& )) ); + locationEdit->setEditText( text ); + + // don't change selection when user has clicked on an item + if ( d->operationMode == Saving && !locationEdit->isVisible()) + setNonExtSelection(); +} + +static const char autocompletionWhatsThisText[] = I18N_NOOP("<p>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 <b>Text Completion</b> menu.") "</qt>"; +void KFileDialog::updateLocationWhatsThis (void) +{ + QString whatsThisText; + if (d->operationMode == KFileDialog::Saving) + { + whatsThisText = "<qt>" + i18n("This is the name to save the file as.") + + i18n (autocompletionWhatsThisText); + } + else if (ops->mode() & KFile::Files) + { + whatsThisText = "<qt>" + 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 = "<qt>" + i18n("This is the name of the file to open.") + + i18n (autocompletionWhatsThisText); + } + + QWhatsThis::add(d->locationLabel, whatsThisText); + QWhatsThis::add(locationEdit, whatsThisText); +} + +void KFileDialog::init(const QString& startDir, const QString& filter, QWidget* widget) +{ + initStatic(); + d = new KFileDialogPrivate(); + + d->boxLayout = 0; + d->keepLocation = false; + d->operationMode = Opening; + d->bookmarkHandler = 0; + d->hasDefaultFilter = false; + d->hasView = false; + d->mainWidget = new QWidget( this, "KFileDialog::mainWidget"); + setMainWidget( d->mainWidget ); + d->okButton = new KPushButton( KStdGuiItem::ok(), d->mainWidget ); + d->okButton->setDefault( true ); + d->cancelButton = new KPushButton(KStdGuiItem::cancel(), d->mainWidget); + connect( d->okButton, SIGNAL( clicked() ), SLOT( slotOk() )); + connect( d->cancelButton, SIGNAL( clicked() ), SLOT( slotCancel() )); + d->customWidget = widget; + d->autoSelectExtCheckBox = 0; // delayed loading + d->autoSelectExtChecked = false; + d->urlBar = 0; // delayed loading + + QtMsgHandler oldHandler = qInstallMsgHandler( silenceQToolBar ); + toolbar = new KToolBar( d->mainWidget, "KFileDialog::toolbar", true); + toolbar->setFlat(true); + qInstallMsgHandler( oldHandler ); + + d->pathCombo = new KURLComboBox( KURLComboBox::Directories, true, + toolbar, "path combo" ); + QToolTip::add( d->pathCombo, i18n("Current location") ); + QWhatsThis::add( d->pathCombo, "<qt>" + i18n("This is the currently listed location. " + "The drop-down list also lists commonly used locations. " + "This includes standard locations, such as your home folder, as well as " + "locations that have been visited recently.") + i18n (autocompletionWhatsThisText)); + + KURL u; + u.setPath( QDir::rootDirPath() ); + QString text = i18n("Root Folder: %1").arg( u.path() ); + d->pathCombo->addDefaultURL( u, + KMimeType::pixmapForURL( u, 0, KIcon::Small ), + text ); + + u.setPath( QDir::homeDirPath() ); + text = i18n("Home Folder: %1").arg( u.path( +1 ) ); + d->pathCombo->addDefaultURL( u, KMimeType::pixmapForURL( u, 0, KIcon::Small ), + text ); + + KURL docPath; + docPath.setPath( KGlobalSettings::documentPath() ); + if ( (u.path(+1) != docPath.path(+1)) && + QDir(docPath.path(+1)).exists() ) + { + text = i18n("Documents: %1").arg( docPath.path( +1 ) ); + d->pathCombo->addDefaultURL( docPath, + KMimeType::pixmapForURL( docPath, 0, KIcon::Small ), + text ); + } + + u.setPath( KGlobalSettings::desktopPath() ); + text = i18n("Desktop: %1").arg( u.path( +1 ) ); + d->pathCombo->addDefaultURL( u, + KMimeType::pixmapForURL( u, 0, KIcon::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.path() ) ) + { + d->url = d->url.upURL(); + QDir dir( d->url.path() ); + while ( !dir.exists() ) + { + d->url = d->url.upURL(); + dir.setPath( d->url.path() ); + } + } + } + + ops = new KDirOperator(d->url, d->mainWidget, "KFileDialog::ops"); + ops->setOnlyDoubleClickSelectsFiles( true ); + connect(ops, SIGNAL(urlEntered(const KURL&)), + SLOT(urlEntered(const KURL&))); + connect(ops, SIGNAL(fileHighlighted(const KFileItem *)), + SLOT(fileHighlighted(const KFileItem *))); + connect(ops, SIGNAL(fileSelected(const KFileItem *)), + SLOT(fileSelected(const KFileItem *))); + connect(ops, SIGNAL(finishedLoading()), + SLOT(slotLoadingFinished())); + + ops->setupMenu(KDirOperator::SortActions | + KDirOperator::FileActions | + KDirOperator::ViewActions); + KActionCollection *coll = ops->actionCollection(); + + // plug nav items into the toolbar + coll->action( "up" )->plug( toolbar ); + coll->action( "up" )->setWhatsThis(i18n("<qt>Click this button to enter the parent folder.<p>" + "For instance, if the current location is file:/home/%1 clicking this " + "button will take you to file:/home.</qt>").arg( KUser().loginName() )); + coll->action( "back" )->plug( toolbar ); + coll->action( "back" )->setWhatsThis(i18n("Click this button to move backwards one step in the browsing history.")); + coll->action( "forward" )->plug( toolbar ); + coll->action( "forward" )->setWhatsThis(i18n("Click this button to move forward one step in the browsing history.")); + coll->action( "reload" )->plug( toolbar ); + coll->action( "reload" )->setWhatsThis(i18n("Click this button to reload the contents of the current location.")); + coll->action( "mkdir" )->setShortcut(Key_F10); + coll->action( "mkdir" )->plug( toolbar ); + coll->action( "mkdir" )->setWhatsThis(i18n("Click this button to create a new folder.")); + + KToggleAction *showSidebarAction = + new KToggleAction(i18n("Show Quick Access Navigation Panel"), Key_F9, coll,"toggleSpeedbar"); + showSidebarAction->setCheckedState(i18n("Hide Quick Access Navigation Panel")); + connect( showSidebarAction, SIGNAL( toggled( bool ) ), + SLOT( toggleSpeedbar( bool )) ); + + KToggleAction *showBookmarksAction = + new KToggleAction(i18n("Show Bookmarks"), 0, coll, "toggleBookmarks"); + showBookmarksAction->setCheckedState(i18n("Hide Bookmarks")); + connect( showBookmarksAction, SIGNAL( toggled( bool ) ), + SLOT( toggleBookmarks( bool )) ); + + KActionMenu *menu = new KActionMenu( i18n("Configure"), "configure", this, "extra menu" ); + menu->setWhatsThis(i18n("<qt>This is the configuration menu for the file dialog. " + "Various options can be accessed from this menu including: <ul>" + "<li>how files are sorted in the list</li>" + "<li>types of view, including icon and list</li>" + "<li>showing of hidden files</li>" + "<li>the Quick Access navigation panel</li>" + "<li>file previews</li>" + "<li>separating folders from files</li></ul></qt>")); + menu->insert( coll->action( "sorting menu" )); + menu->insert( coll->action( "separator" )); + coll->action( "short view" )->setShortcut(Key_F6); + menu->insert( coll->action( "short view" )); + coll->action( "detailed view" )->setShortcut(Key_F7); + menu->insert( coll->action( "detailed view" )); + menu->insert( coll->action( "separator" )); + coll->action( "show hidden" )->setShortcut(Key_F8); + menu->insert( coll->action( "show hidden" )); + menu->insert( showSidebarAction ); + menu->insert( showBookmarksAction ); + coll->action( "preview" )->setShortcut(Key_F11); + menu->insert( coll->action( "preview" )); + coll->action( "separate dirs" )->setShortcut(Key_F12); + menu->insert( coll->action( "separate dirs" )); + + menu->setDelayed( false ); + connect( menu->popupMenu(), SIGNAL( aboutToShow() ), + ops, SLOT( updateSelectionDependentActions() )); + menu->plug( toolbar ); + + //Insert a separator. + KToolBarSeparator* spacerWidget = new KToolBarSeparator(Horizontal, false /*no line*/, + toolbar); + d->m_pathComboIndex = toolbar->insertWidget(-1, -1, spacerWidget); + toolbar->insertWidget(PATH_COMBO, 0, d->pathCombo); + + + toolbar->setItemAutoSized (PATH_COMBO); + toolbar->setIconText(KToolBar::IconOnly); + toolbar->setBarPos(KToolBar::Top); + toolbar->setMovingEnabled(false); + toolbar->adjustSize(); + + KURLCompletion *pathCompletionObj = new KURLCompletion( KURLCompletion::DirCompletion ); + d->pathCombo->setCompletionObject( pathCompletionObj ); + d->pathCombo->setAutoDeleteCompletionObject( true ); + + connect( d->pathCombo, SIGNAL( urlActivated( const KURL& )), + this, SLOT( enterURL( const KURL& ) )); + connect( d->pathCombo, SIGNAL( returnPressed( const QString& )), + this, SLOT( enterURL( const QString& ) )); + + QString whatsThisText; + + // the Location label/edit + d->locationLabel = new QLabel(i18n("&Location:"), d->mainWidget); + locationEdit = new KURLComboBox(KURLComboBox::Files, true, + d->mainWidget, "LocationEdit"); + connect( locationEdit, SIGNAL( textChanged( const QString& ) ), + SLOT( slotLocationChanged( const QString& )) ); + + updateLocationWhatsThis (); + d->locationLabel->setBuddy(locationEdit); + + locationEdit->setFocus(); + KURLCompletion *fileCompletionObj = new KURLCompletion( KURLCompletion::FileCompletion ); + QString dir = d->url.url(+1); + pathCompletionObj->setDir( dir ); + fileCompletionObj->setDir( dir ); + locationEdit->setCompletionObject( fileCompletionObj ); + locationEdit->setAutoDeleteCompletionObject( true ); + connect( fileCompletionObj, SIGNAL( match( const QString& ) ), + SLOT( fileCompletion( const QString& )) ); + + connect( locationEdit, SIGNAL( returnPressed() ), + this, SLOT( slotOk())); + connect(locationEdit, SIGNAL( activated( const QString& )), + this, SLOT( locationActivated( const QString& ) )); + + // the Filter label/edit + whatsThisText = i18n("<qt>This is the filter to apply to the file list. " + "File names that do not match the filter will not be shown.<p>" + "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.<p>" + "Wildcards such as * and ? are allowed.</qt>"); + d->filterLabel = new QLabel(i18n("&Filter:"), d->mainWidget); + QWhatsThis::add(d->filterLabel, whatsThisText); + filterWidget = new KFileFilterCombo(d->mainWidget, + "KFileDialog::filterwidget"); + QWhatsThis::add(filterWidget, whatsThisText); + setFilter(filter); + d->filterLabel->setBuddy(filterWidget); + connect(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 (d->mainWidget); + connect(d->autoSelectExtCheckBox, SIGNAL(clicked()), SLOT(slotAutoSelectExtClicked())); + + initGUI(); // activate GM + + KConfig* config = KGlobal::config(); + readRecentFiles( config ); + + adjustSize(); + + ops->setViewConfig( config, ConfigGroup ); + readConfig( config, ConfigGroup ); + setSelection(d->selection); +} + +void KFileDialog::initSpeedbar() +{ + d->urlBar = new KFileSpeedBar( d->mainWidget, "url bar" ); + connect( d->urlBar, SIGNAL( activated( const KURL& )), + 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. + d->urlBar->setCurrentItem( d->url ); + + d->urlBarLayout->insertWidget( 0, d->urlBar ); +} + +void KFileDialog::initGUI() +{ + delete d->boxLayout; // deletes all sub layouts + + d->boxLayout = new QVBoxLayout( d->mainWidget, 0, KDialog::spacingHint()); + d->boxLayout->addWidget(toolbar, AlignTop); + + d->urlBarLayout = new QHBoxLayout( d->boxLayout ); // needed for the urlBar that may appear + QVBoxLayout *vbox = new QVBoxLayout( d->urlBarLayout ); + + vbox->addWidget(ops, 4); + vbox->addSpacing(3); + + QGridLayout* lafBox= new QGridLayout(2, 3, KDialog::spacingHint()); + + lafBox->addWidget(d->locationLabel, 0, 0, AlignVCenter); + lafBox->addWidget(locationEdit, 0, 1, AlignVCenter); + lafBox->addWidget(d->okButton, 0, 2, AlignVCenter); + + lafBox->addWidget(d->filterLabel, 1, 0, AlignVCenter); + lafBox->addWidget(filterWidget, 1, 1, AlignVCenter); + lafBox->addWidget(d->cancelButton, 1, 2, AlignVCenter); + + lafBox->setColStretch(1, 4); + + vbox->addLayout(lafBox, 0); + vbox->addSpacing(3); + + // add the Automatically Select Extension checkbox + vbox->addWidget (d->autoSelectExtCheckBox); + vbox->addSpacing (3); + + setTabOrder(ops, d->autoSelectExtCheckBox); + setTabOrder (d->autoSelectExtCheckBox, locationEdit); + setTabOrder(locationEdit, filterWidget); + setTabOrder(filterWidget, d->okButton); + setTabOrder(d->okButton, d->cancelButton); + setTabOrder(d->cancelButton, d->pathCombo); + setTabOrder(d->pathCombo, ops); + + // If a custom widget was specified... + if ( d->customWidget != 0 ) + { + // ...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->customWidget->reparent( d->mainWidget, QPoint() ); + + vbox->addWidget( d->customWidget ); + vbox->addSpacing(3); + + // 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->customWidget); + setTabOrder(d->customWidget, d->pathCombo); + } + else + { + setTabOrder(d->cancelButton, d->pathCombo); + } + + setTabOrder(d->pathCombo, ops); +} + +void KFileDialog::slotFilterChanged() +{ + QString filter = filterWidget->currentFilter(); + ops->clearFilter(); + + if ( filter.find( '/' ) > -1 ) { + QStringList types = QStringList::split( " ", filter ); + types.prepend( "inode/directory" ); + ops->setMimeFilter( types ); + } + else + ops->setNameFilter( filter ); + + ops->updateDir(); + + updateAutoSelectExtension (); + + emit filterChanged( filter ); +} + + +void KFileDialog::setURL(const KURL& url, bool clearforward) +{ + d->selection = QString::null; + ops->setURL( url, clearforward); +} + +// Protected +void KFileDialog::urlEntered(const KURL& url) +{ + QString filename = locationEdit->currentText(); + d->selection = QString::null; + + if ( d->pathCombo->count() != 0 ) { // little hack + d->pathCombo->setURL( url ); + } + + locationEdit->blockSignals( true ); + locationEdit->setCurrentItem( 0 ); + if ( d->keepLocation ) + locationEdit->setEditText( filename ); + + locationEdit->blockSignals( false ); + + QString dir = url.url(+1); + static_cast<KURLCompletion*>( d->pathCombo->completionObject() )->setDir( dir ); + static_cast<KURLCompletion*>( locationEdit->completionObject() )->setDir( dir ); + + if ( d->urlBar ) + d->urlBar->setCurrentItem( url ); +} + +void KFileDialog::locationActivated( const QString& url ) +{ + // This guard prevents any URL _typed_ by the user from being interpreted + // twice (by returnPressed/slotOk and here, activated/locationActivated) + // after the user presses Enter. Without this, _both_ setSelection and + // slotOk would "u.addPath( url )" ...so instead we leave it up to just + // slotOk.... + if (!locationEdit->lineEdit()->edited()) + setSelection( url ); +} + +void KFileDialog::enterURL( const KURL& url) +{ + setURL( url ); +} + +void KFileDialog::enterURL( const QString& url ) +{ + setURL( KURL::fromPathOrURL( KURLCompletion::replacedPath( url, true, true )) ); +} + +void KFileDialog::toolbarCallback(int) // SLOT +{ + /* + * yes, nothing uses this anymore. + * it used to be used to show the configure dialog + */ +} + + +void KFileDialog::setSelection(const QString& url) +{ + kdDebug(kfile_area) << "setSelection " << url << endl; + + if (url.isEmpty()) { + d->selection = QString::null; + return; + } + + KURL u = getCompleteURL(url); + if (!u.isValid()) { // if it still is + kdWarning() << url << " is not a correct argument for setSelection!" << endl; + return; + } + + if (!KProtocolInfo::supportsListing(u)) { + locationEdit->lineEdit()->setEdited( true ); + 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()); + 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.findRev('/'); + if (sep >= 0) { // there is a / in it + if ( KProtocolInfo::supportsListing( u )) { + KURL dir(u); + dir.setQuery( QString::null ); + dir.setFileName( QString::null ); + setURL(dir, true ); + } + + // filename must be decoded, or "name with space" would become + // "name%20with%20space", so we use KURL::fileName() + filename = u.fileName(); + kdDebug(kfile_area) << "filename " << filename << endl; + d->selection = filename; + 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. OOOPS. + locationEdit->lineEdit()->setEdited( true ); + } + + d->url = ops->url(); + d->url.addPath(filename); + } +} + +void KFileDialog::slotLoadingFinished() +{ + if ( !d->selection.isNull() ) + ops->setCurrentItem( d->selection ); +} + +// ### remove in KDE4 +void KFileDialog::pathComboChanged( const QString& ) +{ +} +void KFileDialog::dirCompletion( const QString& ) // SLOT +{ +} +void KFileDialog::fileCompletion( const QString& match ) +{ + if ( match.isEmpty() && ops->view() ) + ops->view()->clearSelection(); + else + ops->setCurrentItem( match ); +} + +void KFileDialog::slotLocationChanged( const QString& text ) +{ + if ( text.isEmpty() && ops->view() ) + ops->view()->clearSelection(); + + updateFilter(); +} + +void KFileDialog::updateStatusLine(int /* dirs */, int /* files */) +{ + kdWarning() << "KFileDialog::updateStatusLine is deprecated! The status line no longer exists. Do not try and use it!" << endl; +} + +QString KFileDialog::getOpenFileName(const QString& startDir, + const QString& filter, + QWidget *parent, const QString& caption) +{ + KFileDialog dlg(startDir, filter, parent, "filedialog", true); + dlg.setOperationMode( Opening ); + + dlg.setMode( KFile::File | KFile::LocalOnly ); + dlg.setCaption(caption.isNull() ? i18n("Open") : caption); + + dlg.ops->clearHistory(); + dlg.exec(); + + return dlg.selectedFile(); +} + +QString KFileDialog::getOpenFileNameWId(const QString& startDir, + const QString& filter, + WId parent_id, const QString& caption) +{ + QWidget* parent = QWidget::find( parent_id ); + KFileDialog dlg(startDir, filter, parent, "filedialog", true); +#ifdef Q_WS_X11 + if( parent == NULL && parent_id != 0 ) + XSetTransientForHint( qt_xdisplay(), dlg.winId(), parent_id ); +#else + // TODO +#endif + + dlg.setOperationMode( KFileDialog::Opening ); + + dlg.setMode( KFile::File | KFile::LocalOnly ); + dlg.setCaption(caption.isNull() ? i18n("Open") : caption); + + dlg.ops->clearHistory(); + dlg.exec(); + + return dlg.selectedFile(); +} + +QStringList KFileDialog::getOpenFileNames(const QString& startDir, + const QString& filter, + QWidget *parent, + const QString& caption) +{ + KFileDialog dlg(startDir, filter, parent, "filedialog", true); + dlg.setOperationMode( Opening ); + + dlg.setCaption(caption.isNull() ? i18n("Open") : caption); + dlg.setMode(KFile::Files | KFile::LocalOnly); + dlg.ops->clearHistory(); + dlg.exec(); + + return dlg.selectedFiles(); +} + +KURL KFileDialog::getOpenURL(const QString& startDir, const QString& filter, + QWidget *parent, const QString& caption) +{ + KFileDialog dlg(startDir, filter, parent, "filedialog", true); + dlg.setOperationMode( Opening ); + + dlg.setCaption(caption.isNull() ? i18n("Open") : caption); + dlg.setMode( KFile::File ); + dlg.ops->clearHistory(); + dlg.exec(); + + return dlg.selectedURL(); +} + +KURL::List KFileDialog::getOpenURLs(const QString& startDir, + const QString& filter, + QWidget *parent, + const QString& caption) +{ + KFileDialog dlg(startDir, filter, parent, "filedialog", true); + dlg.setOperationMode( Opening ); + + dlg.setCaption(caption.isNull() ? i18n("Open") : caption); + dlg.setMode(KFile::Files); + dlg.ops->clearHistory(); + dlg.exec(); + + return dlg.selectedURLs(); +} + +KURL KFileDialog::getExistingURL(const QString& startDir, + QWidget *parent, + const QString& caption) +{ + return KDirSelectDialog::selectDirectory(startDir, false, parent, caption); +} + +QString KFileDialog::getExistingDirectory(const QString& startDir, + QWidget *parent, + const QString& caption) +{ +#ifdef Q_WS_WIN + return QFileDialog::getExistingDirectory(startDir, parent, "getExistingDirectory", + caption, true, true); +#else + KURL url = KDirSelectDialog::selectDirectory(startDir, true, parent, + caption); + if ( url.isValid() ) + return url.path(); + + return QString::null; +#endif +} + +KURL KFileDialog::getImageOpenURL( const QString& startDir, QWidget *parent, + const QString& caption) +{ + QStringList mimetypes = KImageIO::mimeTypes( KImageIO::Reading ); + KFileDialog dlg(startDir, + mimetypes.join(" "), + parent, "filedialog", true); + dlg.setOperationMode( Opening ); + dlg.setCaption( caption.isNull() ? i18n("Open") : caption ); + dlg.setMode( KFile::File ); + + KImageFilePreview *ip = new KImageFilePreview( &dlg ); + dlg.setPreviewWidget( ip ); + dlg.exec(); + + return dlg.selectedURL(); +} + +KURL KFileDialog::selectedURL() const +{ + if ( result() == QDialog::Accepted ) + return d->url; + else + return KURL(); +} + +KURL::List KFileDialog::selectedURLs() const +{ + KURL::List list; + if ( result() == QDialog::Accepted ) { + if ( (ops->mode() & KFile::Files) == KFile::Files ) + list = parseSelectedURLs(); + else + list.append( d->url ); + } + return list; +} + + +KURL::List& KFileDialog::parseSelectedURLs() const +{ + if ( d->filenames.isEmpty() ) { + return d->urlList; + } + + d->urlList.clear(); + if ( d->filenames.contains( '/' )) { // assume _one_ absolute filename + static const QString &prot = KGlobal::staticQString(":/"); + KURL u; + if ( d->filenames.find( prot ) != -1 ) + u = d->filenames; + else + u.setPath( d->filenames ); + + if ( u.isValid() ) + d->urlList.append( u ); + else + KMessageBox::error( d->mainWidget, + i18n("The chosen filenames do not\n" + "appear to be valid."), + i18n("Invalid Filenames") ); + } + + else + d->urlList = tokenize( d->filenames ); + + d->filenames = QString::null; // indicate that we parsed that one + + return d->urlList; +} + + +// FIXME: current implementation drawback: a filename can't contain quotes +KURL::List KFileDialog::tokenize( const QString& line ) const +{ + KURL::List urls; + KURL u( ops->url() ); + QString name; + + int count = line.contains( '"' ); + 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 + QWidget *that = const_cast<KFileDialog *>(this); + KMessageBox::sorry(that, i18n("The requested filenames\n" + "%1\n" + "do not appear to be valid;\n" + "make sure every filename is enclosed in double quotes.").arg(line), + i18n("Filename Error")); + return urls; + } + + int start = 0; + int index1 = -1, index2 = -1; + while ( true ) { + index1 = line.find( '"', start ); + index2 = line.find( '"', 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 KFileDialog::selectedFile() const +{ + if ( result() == QDialog::Accepted ) + { + KURL url = KIO::NetAccess::mostLocalURL(d->url,topLevelWidget()); + if (url.isLocalFile()) + return url.path(); + else { + KMessageBox::sorry( d->mainWidget, + i18n("You can only select local files."), + i18n("Remote Files Not Accepted") ); + } + } + return QString::null; +} + +QStringList KFileDialog::selectedFiles() const +{ + QStringList list; + KURL url; + + if ( result() == QDialog::Accepted ) { + if ( (ops->mode() & KFile::Files) == KFile::Files ) { + KURL::List urls = parseSelectedURLs(); + QValueListConstIterator<KURL> it = urls.begin(); + while ( it != urls.end() ) { + 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 KFileDialog::baseURL() const +{ + return ops->url(); +} + +QString KFileDialog::getSaveFileName(const QString& dir, const QString& filter, + QWidget *parent, + const QString& caption) +{ + bool specialDir = dir.at(0) == ':'; + KFileDialog dlg( specialDir ? dir : QString::null, filter, parent, "filedialog", true); + if ( !specialDir ) + dlg.setSelection( dir ); // may also be a filename + + dlg.setOperationMode( Saving ); + dlg.setCaption(caption.isNull() ? i18n("Save As") : caption); + + dlg.exec(); + + QString filename = dlg.selectedFile(); + if (!filename.isEmpty()) + KRecentDocument::add(filename); + + return filename; +} + +QString KFileDialog::getSaveFileNameWId(const QString& dir, const QString& filter, + WId parent_id, + const QString& caption) +{ + bool specialDir = dir.at(0) == ':'; + QWidget* parent = QWidget::find( parent_id ); + KFileDialog dlg( specialDir ? dir : QString::null, filter, parent, "filedialog", true); +#ifdef Q_WS_X11 + if( parent == NULL && parent_id != 0 ) + XSetTransientForHint(qt_xdisplay(), dlg.winId(), parent_id); +#else + // TODO +#endif + + if ( !specialDir ) + dlg.setSelection( dir ); // may also be a filename + + dlg.setOperationMode( KFileDialog::Saving); + dlg.setCaption(caption.isNull() ? i18n("Save As") : caption); + + dlg.exec(); + + QString filename = dlg.selectedFile(); + if (!filename.isEmpty()) + KRecentDocument::add(filename); + + return filename; +} + +KURL KFileDialog::getSaveURL(const QString& dir, const QString& filter, + QWidget *parent, const QString& caption) +{ + bool specialDir = dir.at(0) == ':'; + KFileDialog dlg(specialDir ? dir : QString::null, filter, parent, "filedialog", true); + if ( !specialDir ) + dlg.setSelection( dir ); // may also be a filename + + dlg.setCaption(caption.isNull() ? i18n("Save As") : caption); + dlg.setOperationMode( Saving ); + + dlg.exec(); + + KURL url = dlg.selectedURL(); + if (url.isValid()) + KRecentDocument::add( url ); + + return url; +} + +void KFileDialog::show() +{ + if ( !d->hasView ) { // delayed view-creation + ops->setView(KFile::Default); + ops->clearHistory(); + d->hasView = true; + } + + KDialogBase::show(); +} + +void KFileDialog::setMode( KFile::Mode m ) +{ + ops->setMode(m); + if ( ops->dirOnlyMode() ) { + filterWidget->setDefaultFilter( i18n("*|All Folders") ); + } + else { + filterWidget->setDefaultFilter( i18n("*|All Files") ); + } + + updateAutoSelectExtension (); +} + +void KFileDialog::setMode( unsigned int m ) +{ + setMode(static_cast<KFile::Mode>( m )); +} + +KFile::Mode KFileDialog::mode() const +{ + return ops->mode(); +} + + +void KFileDialog::readConfig( KConfig *kc, const QString& group ) +{ + if ( !kc ) + return; + + QString oldGroup = kc->group(); + if ( !group.isEmpty() ) + kc->setGroup( group ); + + ops->readConfig( kc, group ); + + KURLComboBox *combo = d->pathCombo; + combo->setURLs( kc->readPathListEntry( RecentURLs ), KURLComboBox::RemoveTop ); + combo->setMaxItems( kc->readNumEntry( RecentURLsNumber, + DefaultRecentURLsNumber ) ); + combo->setURL( ops->url() ); + autoDirectoryFollowing = kc->readBoolEntry( AutoDirectoryFollowing, + DefaultDirectoryFollowing ); + + KGlobalSettings::Completion cm = (KGlobalSettings::Completion) + kc->readNumEntry( PathComboCompletionMode, + KGlobalSettings::completionMode() ); + if ( cm != KGlobalSettings::completionMode() ) + combo->setCompletionMode( cm ); + + cm = (KGlobalSettings::Completion) + kc->readNumEntry( LocationComboCompletionMode, + KGlobalSettings::completionMode() ); + if ( cm != KGlobalSettings::completionMode() ) + locationEdit->setCompletionMode( cm ); + + // show or don't show the speedbar + toggleSpeedbar( kc->readBoolEntry(ShowSpeedbar, true) ); + + // show or don't show the bookmarks + toggleBookmarks( kc->readBoolEntry(ShowBookmarks, false) ); + + // does the user want Automatically Select Extension? + d->autoSelectExtChecked = kc->readBoolEntry (AutoSelectExtChecked, DefaultAutoSelectExtChecked); + updateAutoSelectExtension (); + + int w1 = minimumSize().width(); + int w2 = toolbar->sizeHint().width() + 10; + if (w1 < w2) + setMinimumWidth(w2); + + QSize size = configDialogSize( group ); + resize( size ); + kc->setGroup( oldGroup ); +} + +void KFileDialog::writeConfig( KConfig *kc, const QString& group ) +{ + if ( !kc ) + return; + + QString oldGroup = kc->group(); + if ( !group.isEmpty() ) + kc->setGroup( group ); + + kc->writePathEntry( RecentURLs, d->pathCombo->urls() ); + saveDialogSize( group, true ); + kc->writeEntry( PathComboCompletionMode, static_cast<int>(d->pathCombo->completionMode()) ); + kc->writeEntry( LocationComboCompletionMode, static_cast<int>(locationEdit->completionMode()) ); + kc->writeEntry( ShowSpeedbar, d->urlBar && !d->urlBar->isHidden() ); + kc->writeEntry( ShowBookmarks, d->bookmarkHandler != 0 ); + kc->writeEntry( AutoSelectExtChecked, d->autoSelectExtChecked ); + + ops->writeConfig( kc, group ); + kc->setGroup( oldGroup ); +} + + +void KFileDialog::readRecentFiles( KConfig *kc ) +{ + QString oldGroup = kc->group(); + kc->setGroup( ConfigGroup ); + + locationEdit->setMaxItems( kc->readNumEntry( RecentFilesNumber, + DefaultRecentURLsNumber ) ); + locationEdit->setURLs( kc->readPathListEntry( RecentFiles ), + KURLComboBox::RemoveBottom ); + locationEdit->insertItem( QString::null, 0 ); // dummy item without pixmap + locationEdit->setCurrentItem( 0 ); + + kc->setGroup( oldGroup ); +} + +void KFileDialog::saveRecentFiles( KConfig *kc ) +{ + QString oldGroup = kc->group(); + kc->setGroup( ConfigGroup ); + + kc->writePathEntry( RecentFiles, locationEdit->urls() ); + + kc->setGroup( oldGroup ); +} + +KPushButton * KFileDialog::okButton() const +{ + return d->okButton; +} + +KPushButton * KFileDialog::cancelButton() const +{ + return d->cancelButton; +} + +KURLBar * KFileDialog::speedBar() +{ + return d->urlBar; +} + +void KFileDialog::slotCancel() +{ + ops->close(); + KDialogBase::slotCancel(); + + KConfig *config = KGlobal::config(); + config->setForceGlobal( true ); + writeConfig( config, ConfigGroup ); + config->setForceGlobal( false ); +} + +void KFileDialog::setKeepLocation( bool keep ) +{ + d->keepLocation = keep; +} + +bool KFileDialog::keepsLocation() const +{ + return d->keepLocation; +} + +void KFileDialog::setOperationMode( OperationMode mode ) +{ + d->operationMode = mode; + d->keepLocation = (mode == Saving); + filterWidget->setEditable( !d->hasDefaultFilter || mode != Saving ); + if ( mode == Opening ) + d->okButton->setGuiItem( KGuiItem( i18n("&Open"), "fileopen") ); + else if ( mode == Saving ) { + d->okButton->setGuiItem( KStdGuiItem::save() ); + setNonExtSelection(); + } + else + d->okButton->setGuiItem( KStdGuiItem::ok() ); + updateLocationWhatsThis (); + updateAutoSelectExtension (); +} + +KFileDialog::OperationMode KFileDialog::operationMode() const +{ + return d->operationMode; +} + +void KFileDialog::slotAutoSelectExtClicked() +{ + kdDebug (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 + updateLocationEditExtension (d->extension /* extension hasn't changed */); +} + +static QString getExtensionFromPatternList (const QStringList &patternList) +{ + QString ret; + kdDebug (kfile_area) << "\tgetExtension " << patternList << endl; + + QStringList::ConstIterator patternListEnd = patternList.end (); + for (QStringList::ConstIterator it = patternList.begin (); + it != patternListEnd; + it++) + { + kdDebug (kfile_area) << "\t\ttry: \'" << (*it) << "\'" << endl; + + // is this pattern like "*.BMP" rather than useless things like: + // + // README + // *. + // *.* + // *.JP*G + // *.JP? + if ((*it).startsWith ("*.") && + (*it).length () > 2 && + (*it).find ('*', 2) < 0 && (*it).find ('?', 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 KFileDialog::currentFilterExtension (void) +{ + return d->extension; +} + +void KFileDialog::updateAutoSelectExtension (void) +{ + if (!d->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 ...) + // + + kdDebug (kfile_area) << "Figure out an extension: " << endl; + QString lastExtension = d->extension; + d->extension = QString::null; + + // Automatically Select Extension is only valid if the user is _saving_ a _file_ + if ((operationMode () == Saving) && (mode () & KFile::File)) + { + // + // Get an extension from the filter + // + + QString filter = currentFilter (); + if (!filter.isEmpty ()) + { + // e.g. "*.cpp" + if (filter.find ('/') < 0) + { + d->extension = getExtensionFromPatternList (QStringList::split (" ", filter)).lower (); + kdDebug (kfile_area) << "\tsetFilter-style: pattern ext=\'" + << d->extension << "\'" << endl; + } + // e.g. "text/html" + else + { + KMimeType::Ptr mime = KMimeType::mimeType (filter); + + // first try X-KDE-NativeExtension + QString nativeExtension = mime->property ("X-KDE-NativeExtension").toString (); + if (nativeExtension.at (0) == '.') + { + d->extension = nativeExtension.lower (); + kdDebug (kfile_area) << "\tsetMimeFilter-style: native ext=\'" + << d->extension << "\'" << endl; + } + + // no X-KDE-NativeExtension + if (d->extension.isEmpty ()) + { + d->extension = getExtensionFromPatternList (mime->patterns ()).lower (); + kdDebug (kfile_area) << "\tsetMimeFilter-style: pattern ext=\'" + << d->extension << "\'" << endl; + } + } + } + + + // + // GUI: checkbox + // + + QString whatsThisExtension; + if (!d->extension.isEmpty ()) + { + // remember: sync any changes to the string with below + d->autoSelectExtCheckBox->setText (i18n ("Automatically select filename e&xtension (%1)").arg (d->extension)); + whatsThisExtension = i18n ("the extension <b>%1</b>").arg (d->extension); + + d->autoSelectExtCheckBox->setEnabled (true); + d->autoSelectExtCheckBox->setChecked (d->autoSelectExtChecked); + } + else + { + // remember: sync any changes to the string with above + d->autoSelectExtCheckBox->setText (i18n ("Automatically select filename e&xtension")); + whatsThisExtension = i18n ("a suitable extension"); + + d->autoSelectExtCheckBox->setChecked (false); + d->autoSelectExtCheckBox->setEnabled (false); + } + + const QString locationLabelText = stripUndisplayable (d->locationLabel->text ()); + const QString filterLabelText = stripUndisplayable (d->filterLabel->text ()); + QWhatsThis::add (d->autoSelectExtCheckBox, + "<qt>" + + i18n ( + "This option enables some convenient features for " + "saving files with extensions:<br>" + "<ol>" + "<li>Any extension specified in the <b>%1</b> text " + "area will be updated if you change the file type " + "to save in.<br>" + "<br></li>" + "<li>If no extension is specified in the <b>%2</b> " + "text area when you click " + "<b>Save</b>, %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.<br>" + "<br>" + "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)." + "</li>" + "</ol>" + "If unsure, keep this option enabled as it makes your " + "files more manageable." + ) + .arg (locationLabelText) + .arg (locationLabelText) + .arg (whatsThisExtension) + + "</qt>" + ); + + d->autoSelectExtCheckBox->show (); + + + // update the current filename's extension + updateLocationEditExtension (lastExtension); + } + // Automatically Select Extension not valid + else + { + d->autoSelectExtCheckBox->setChecked (false); + d->autoSelectExtCheckBox->hide (); + } +} + +// Updates the extension of the filename specified in locationEdit if the +// Automatically Select Extension feature is enabled. +// (this prevents you from accidently saving "file.kwd" as RTF, for example) +void KFileDialog::updateLocationEditExtension (const QString &lastExtension) +{ + if (!d->autoSelectExtCheckBox->isChecked () || d->extension.isEmpty ()) + return; + + QString urlStr = locationEdit->currentText (); + if (urlStr.isEmpty ()) + return; + + KURL url = getCompleteURL (urlStr); + kdDebug (kfile_area) << "updateLocationEditExtension (" << url << ")" << endl; + + const int fileNameOffset = urlStr.findRev ('/') + 1; + QString fileName = urlStr.mid (fileNameOffset); + + const int dot = fileName.findRev ('.'); + 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, topLevelWidget())) + { + kdDebug (kfile_area) << "\tfile exists" << endl; + + if (isDirectory (t)) + { + kdDebug (kfile_area) << "\tisDir - won't alter extension" << endl; + 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 + d->extension; + if ( newText != locationEdit->currentText() ) + { + locationEdit->setCurrentText (urlStr.left (fileNameOffset) + fileName + d->extension); + locationEdit->lineEdit()->setEdited (true); + } + } +} + +// Updates the filter if the extension of the filename specified in locationEdit is changed +// (this prevents you from accidently saving "file.kwd" as RTF, for example) +void KFileDialog::updateFilter () +{ + if ((operationMode() == Saving) && (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.findIndex(mime->name()) != -1) { + filterWidget->setCurrentFilter(mime->name()); + } + } + } +} + +// applies only to a file that doesn't already exist +void KFileDialog::appendExtension (KURL &url) +{ + if (!d->autoSelectExtCheckBox->isChecked () || d->extension.isEmpty ()) + return; + + QString fileName = url.fileName (); + if (fileName.isEmpty ()) + return; + + kdDebug (kfile_area) << "appendExtension(" << url << ")" << endl; + + const int len = fileName.length (); + const int dot = fileName.findRev ('.'); + + 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, topLevelWidget())) + { + kdDebug (kfile_area) << "\tfile exists - won't append extension" << endl; + 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.") + // + kdDebug (kfile_area) << "\tstrip trailing dot" << endl; + url.setFileName (fileName.left (len - 1)); + } + // evilmatically append extension :) if the user hasn't specified one + else if (unspecifiedExtension) + { + kdDebug (kfile_area) << "\tappending extension \'" << d->extension << "\'..." << endl; + url.setFileName (fileName + d->extension); + kdDebug (kfile_area) << "\tsaving as \'" << url << "\'" << endl; + } +} + + +// adds the selected files/urls to 'recent documents' +void KFileDialog::addToRecentDocuments() +{ + int m = 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 ); + } + } +} + +KActionCollection * KFileDialog::actionCollection() const +{ + return ops->actionCollection(); +} + +void KFileDialog::keyPressEvent( QKeyEvent *e ) +{ + if ( e->key() == Key_Escape ) + { + e->accept(); + d->cancelButton->animateClick(); + } + else + KDialogBase::keyPressEvent( e ); +} + +void KFileDialog::toggleSpeedbar( bool show ) +{ + if ( show ) + { + if ( !d->urlBar ) + initSpeedbar(); + + d->urlBar->show(); + + // check to see if they have a home item defined, if not show the home button + KURLBarItem *urlItem = static_cast<KURLBarItem*>( d->urlBar->listBox()->firstItem() ); + KURL homeURL; + homeURL.setPath( QDir::homeDirPath() ); + while ( urlItem ) + { + if ( homeURL.equals( urlItem->url(), true ) ) + { + ops->actionCollection()->action( "home" )->unplug( toolbar ); + break; + } + + urlItem = static_cast<KURLBarItem*>( urlItem->next() ); + } + } + else + { + if (d->urlBar) + d->urlBar->hide(); + + if ( !ops->actionCollection()->action( "home" )->isPlugged( toolbar ) ) + ops->actionCollection()->action( "home" )->plug( toolbar, 3 ); + } + + static_cast<KToggleAction *>(actionCollection()->action("toggleSpeedbar"))->setChecked( show ); +} + +void KFileDialog::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& ))); + + toolbar->insertButton(QString::fromLatin1("bookmark"), + (int)HOTLIST_BUTTON, true, + i18n("Bookmarks"), 5); + toolbar->getButton(HOTLIST_BUTTON)->setPopup(d->bookmarkHandler->menu(), + true); + QWhatsThis::add(toolbar->getButton(HOTLIST_BUTTON), + i18n("<qt>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.<p>" + "These bookmarks are specific to the file dialog, but otherwise operate " + "like bookmarks elsewhere in KDE.</qt>")); + } + else if (d->bookmarkHandler) + { + delete d->bookmarkHandler; + d->bookmarkHandler = 0; + toolbar->removeItem(HOTLIST_BUTTON); + } + + static_cast<KToggleAction *>(actionCollection()->action("toggleBookmarks"))->setChecked( show ); +} + +int KFileDialog::pathComboIndex() +{ + return d->m_pathComboIndex; +} + +// static +void KFileDialog::initStatic() +{ + if ( lastDirectory ) + return; + + lastDirectory = ldd.setObject(lastDirectory, new KURL()); +} + +// static +KURL KFileDialog::getStartURL( const QString& startDir, + QString& recentDirClass ) +{ + initStatic(); + + recentDirClass = QString::null; + KURL ret; + + bool useDefaultStartDir = startDir.isEmpty(); + if ( !useDefaultStartDir ) + { + if (startDir[0] == ':') + { + recentDirClass = startDir; + ret = KURL::fromPathOrURL( KRecentDirs::dir(recentDirClass) ); + } + else + { + ret = KCmdLineArgs::makeURL( QFile::encodeName(startDir) ); + // If we won't be able to list it (e.g. http), then use default + if ( !KProtocolInfo::supportsListing( ret ) ) + useDefaultStartDir = true; + } + } + + if ( useDefaultStartDir ) + { + if (lastDirectory->isEmpty()) { + lastDirectory->setPath(KGlobalSettings::documentPath()); + KURL home; + home.setPath( QDir::homeDirPath() ); + // 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(+1) == home.path(+1) || + QDir::currentDirPath() != QDir::homeDirPath() || + !QDir(lastDirectory->path(+1)).exists() ) + lastDirectory->setPath(QDir::currentDirPath()); + } + ret = *lastDirectory; + } + + return ret; +} + +void KFileDialog::setStartDir( const KURL& directory ) +{ + initStatic(); + if ( directory.isValid() ) + *lastDirectory = directory; +} + +void KFileDialog::setNonExtSelection() +{ + // Enhanced rename: Don't highlight the file extension. + QString pattern, filename = locationEdit->currentText().stripWhiteSpace(); + KServiceTypeFactory::self()->findFromPattern( filename, &pattern ); + + if ( !pattern.isEmpty() && pattern.at( 0 ) == '*' && pattern.find( '*' , 1 ) == -1 ) + locationEdit->lineEdit()->setSelection( 0, filename.length() - pattern.stripWhiteSpace().length()+1 ); + else + { + int lastDot = filename.findRev( '.' ); + if ( lastDot > 0 ) + locationEdit->lineEdit()->setSelection( 0, lastDot ); + } +} + +void KFileDialog::virtual_hook( int id, void* data ) +{ KDialogBase::virtual_hook( id, data ); } + + +#include "kfiledialog.moc" diff --git a/kio/kfile/kfiledialog.h b/kio/kfile/kfiledialog.h new file mode 100644 index 000000000..e90338608 --- /dev/null +++ b/kio/kfile/kfiledialog.h @@ -0,0 +1,989 @@ +// -*- c++ -*- +/* This file is part of the KDE libraries + Copyright (C) 1997, 1998 Richard Moore <[email protected]> + 1998 Stephan Kulow <[email protected]> + 1998 Daniel Grana <[email protected]> + 2000,2001 Carsten Pfeiffer <[email protected]> + 2001 Frerich Raabe <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KFILEDIALOG_H__ +#define __KFILEDIALOG_H__ + +#include <qstring.h> + +#include <kdialogbase.h> +#include <kfile.h> +#include <kurl.h> +#include <kmimetype.h> +#include <kio/jobclasses.h> + +class QCheckBox; +class QHBoxLayout; +class QGridLayout; +class QLabel; +class QPopupMenu; +class QVBoxLayout; + +class KActionCollection; +class KDirOperator; +class KURLBar; +class KURLComboBox; +class KFileFilterCombo; +class KFileView; +class KFileItem; +class KPushButton; +class KToolBar; +class KPreviewWidgetBase; + +struct KFileDialogPrivate; + +/** + * Provides a user (and developer) friendly way to + * select files and directories. + * + * The widget can be used as a drop in replacement for the + * QFileDialog widget, but has greater functionality and a nicer GUI. + * + * You will usually want to use one of the static methods + * getOpenFileName(), getSaveFileName(), getOpenURL() + * or for multiple files getOpenFileNames() or getOpenURLs(). + * + * The dialog has been designed to allow applications to customise it + * by subclassing. It uses geometry management to ensure that subclasses + * can easily add children that will be incorporated into the layout. + * + * \image html kfiledialog.png "KDE File Dialog" + * + * @short A file selection dialog. + * + * @author Richard J. Moore <[email protected]>, Carsten Pfeiffer <[email protected]> + */ +class KIO_EXPORT KFileDialog : public KDialogBase +{ + Q_OBJECT + +public: + + /** + * Defines some default behavior of the filedialog. + * E.g. in mode @p Opening and @p Saving, the selected files/urls will + * be added to the "recent documents" list. The Saving mode also implies + * setKeepLocation() being set. + * + * @p Other means that no default actions are performed. + * + * @see setOperationMode + * @see operationMode + */ + enum OperationMode { Other = 0, Opening, Saving }; + + /** + * Constructs a file dialog. + * + * @param startDir This can either be + * @li The URL of the directory to start in. + * @li QString::null to start in the current working + * directory, or the last directory where a file has been + * selected. + * @li ':<keyword>' to start in the directory last used + * by a filedialog in the same application that specified + * the same keyword. + * @li '::<keyword>' 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. + * @param parent The parent widget of this dialog + * @param name The name of this object + * @param modal Whether to create a modal dialog or not + * See setFilter() for details on how to use this argument. + * + */ + KFileDialog(const QString& startDir, const QString& filter, + QWidget *parent, const char *name, + bool modal); + + /** + * Constructs a file dialog. + * + * The parameters here are identical to the first constructor except + * for the addition of a QWidget parameter. + * + * Historical note: The original version of KFileDialog did not have this extra + * parameter. It was added later, and, in order to maintain binary compatibility, + * it was placed in a new constructor instead of added to the original one. + * + * @param startDir This can either be + * @li The URL of the directory to start in. + * @li QString::null to start in the current working + * directory, or the last directory where a file has been + * selected. + * @li ':<keyword>' to start in the directory last used + * by a filedialog in the same application that specified + * the same keyword. + * @li '::<keyword>' 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() for details on how to use this argument. + * + * @param widget A widget, or a widget of widgets, for displaying custom + * data in the dialog. 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 KFileDialog. + * @param parent The parent widget of this dialog + * @param name The name of this object + * @param modal Whether to create a modal dialog or not + * @since 3.1 + */ + KFileDialog(const QString& startDir, const QString& filter, + QWidget *parent, const char *name, + bool modal, QWidget* widget); + + + /** + * Destructs the file dialog. + */ + ~KFileDialog(); + + /** + * @returns The selected fully qualified filename. + */ + KURL selectedURL() const; + + /** + * @returns The list of selected URLs. + */ + KURL::List selectedURLs() const; + + /** + * @returns the currently shown directory. + */ + KURL baseURL() const; + + /** + * Returns the full path of the selected file in the local filesystem. + * (Local files only) + */ + QString selectedFile() const; + + /** + * Returns a list of all selected local files. + */ + QStringList selectedFiles() const; + + /** + * Sets the directory to view. + * + * @param url URL to show. + * @param clearforward Indicates whether the forward queue + * should be cleared. + */ + 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. + */ + 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( KFileDialog::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 KFileDialog. + * + * @see operationMode + * @see KFileDialog::OperationMode + */ + void setOperationMode( KFileDialog::OperationMode ); + + /** + * @returns the current operation mode, Opening, Saving or Other. Default + * is Other. + * + * @see operationMode + * @see KFileDialog::OperationMode + */ + 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 KFileDialog::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. + */ + void setKeepLocation( bool keep ); + + /** + * @returns whether the contents of the location edit are kept when + * changing directories. + */ + 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 + */ + 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() + */ + QString currentFilter() const; + + /** + * Sets the filter up to specify the output type. + * + * @param label the label to use instead of "Filter:" + * @param types a list of mimetypes that can be used as output format + * @param defaultType the default mimetype to use as output format. + * + * Do not use in conjunction with setFilter() + * @deprecated + */ + void setFilterMimeType(const QString &label, const KMimeType::List &types, const KMimeType::Ptr &defaultType) KDE_DEPRECATED; + + /** + * Returns the mimetype for the desired output format. + * + * This is only valid if setFilterMimeType() has been called + * previously. + * + * @see setFilterMimeType() + */ + 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() + */ + void setMimeFilter( const QStringList& types, + const QString& defaultType = QString::null ); + + /** + * The mimetype for the desired output format. + * + * This is only valid if setMimeFilter() has been called + * previously. + * + * @see setMimeFilter() + */ + QString currentMimeFilter() const; + + /** + * Clears any mime- or namefilter. Does not reload the directory. + */ + void clearFilter(); + + /** + * @deprecated + * Add a preview widget and enter the preview mode. + * + * In this mode + * the dialog is split and the right part contains your widget. + * This widget has to inherit QWidget and it has to implement + * a slot showPreview(const KURL &); which is called + * every time the file changes. You may want to look at + * koffice/lib/kofficecore/koFilterManager.cc for some hints :) + * + * Ownership is transferred to KFileDialog. You need to create the + * preview-widget with "new", i.e. on the heap. + */ + void setPreviewWidget(const QWidget *w) KDE_DEPRECATED; + + /** + * 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 KFileDialog. You need to create the + * preview-widget with "new", i.e. on the heap. + * + * @param w The widget to be used for the preview. + */ + void setPreviewWidget(const KPreviewWidgetBase *w); + + /** + * Creates a modal file dialog and return the selected + * filename or an empty string if none was chosen. + * + * Note that with + * this method the user must select an existing filename. + * + * @param startDir This can either be + * @li The URL of the directory to start in. + * @li QString::null to start in the current working + * directory, or the last directory where a file has been + * selected. + * @li ':<keyword>' to start in the directory last used + * by a filedialog in the same application that specified + * the same keyword. + * @li '::<keyword>' to start in the directory last used + * by a filedialog in any application that specified the + * same keyword. + * @param filter This is a space separated list of shell globs. + * You can set the text to be displayed for the glob, and + * provide multiple globs. See setFilter() for details on + * how to do this... + * @param parent The widget the dialog will be centered on initially. + * @param caption The name of the dialog widget. + */ + static QString getOpenFileName(const QString& startDir= QString::null, + const QString& filter= QString::null, + QWidget *parent= 0, + const QString& caption = QString::null); + + + /** + * Use this version only if you have no QWidget available as + * parent widget. This can be the case if the parent widget is + * a widget in another process or if the parent widget is a + * non-Qt widget. For example, in a GTK program. + * + * @since 3.4 + */ + static QString getOpenFileNameWId(const QString& startDir, + const QString& filter, + WId parent_id, const QString& caption); + + /** + * Creates a modal file dialog and returns the selected + * filenames or an empty list if none was chosen. + * + * Note that with + * this method the user must select an existing filename. + * + * @param startDir This can either be + * @li The URL of the directory to start in. + * @li QString::null to start in the current working + * directory, or the last directory where a file has been + * selected. + * @li ':<keyword>' to start in the directory last used + * by a filedialog in the same application that specified + * the same keyword. + * @li '::<keyword>' to start in the directory last used + * by a filedialog in any application that specified the + * same keyword. + * @param filter This is a space separated list of shell globs. + * You can set the text to be displayed for the glob, and + * provide multiple globs. See setFilter() for details on + * how to do this... + * @param parent The widget the dialog will be centered on initially. + * @param caption The name of the dialog widget. + */ + static QStringList getOpenFileNames(const QString& startDir= QString::null, + const QString& filter= QString::null, + QWidget *parent = 0, + const QString& caption= QString::null); + + + + /** + * Creates a modal file dialog and returns the selected + * URL or an empty string if none was chosen. + * + * Note that with + * this method the user must select an existing URL. + * + * @param startDir This can either be + * @li The URL of the directory to start in. + * @li QString::null to start in the current working + * directory, or the last directory where a file has been + * selected. + * @li ':<keyword>' to start in the directory last used + * by a filedialog in the same application that specified + * the same keyword. + * @li '::<keyword>' to start in the directory last used + * by a filedialog in any application that specified the + * same keyword. + * @param filter This is a space separated list of shell globs. + * You can set the text to be displayed for the glob, and + * provide multiple globs. See setFilter() for details on + * how to do this... + * @param parent The widget the dialog will be centered on initially. + * @param caption The name of the dialog widget. + */ + static KURL getOpenURL(const QString& startDir = QString::null, + const QString& filter= QString::null, + QWidget *parent= 0, + const QString& caption = QString::null); + + + + /** + * Creates a modal file dialog and returns the selected + * URLs or an empty list if none was chosen. + * + * Note that with + * this method the user must select an existing filename. + * + * @param startDir This can either be + * @li The URL of the directory to start in. + * @li QString::null to start in the current working + * directory, or the last directory where a file has been + * selected. + * @li ':<keyword>' to start in the directory last used + * by a filedialog in the same application that specified + * the same keyword. + * @li '::<keyword>' to start in the directory last used + * by a filedialog in any application that specified the + * same keyword. + * @param filter This is a space separated list of shell globs. + * You can set the text to be displayed for the glob, and + * provide multiple globs. See setFilter() for details on + * how to do this... + * @param parent The widget the dialog will be centered on initially. + * @param caption The name of the dialog widget. + */ + static KURL::List getOpenURLs(const QString& startDir= QString::null, + const QString& filter= QString::null, + QWidget *parent = 0, + const QString& caption= QString::null); + + + + /** + * Creates a modal file dialog and returns the selected + * filename or an empty string if none was chosen. + * + * Note that with this + * method the user need not select an existing filename. + * + * @param startDir This can either be + * @li The URL of the directory to start in. + * @li a relative path or a filename determining the + * directory to start in and the file to be selected. + * @li QString::null to start in the current working + * directory, or the last directory where a file has been + * selected. + * @li ':<keyword>' to start in the directory last used + * by a filedialog in the same application that specified + * the same keyword. + * @li '::<keyword>' to start in the directory last used + * by a filedialog in any application that specified the + * same keyword. + * @param filter This is a space separated list of shell globs. + * You can set the text to be displayed for the glob, and + * provide multiple globs. See setFilter() for details on + * how to do this... + * @param parent The widget the dialog will be centered on initially. + * @param caption The name of the dialog widget. + */ + static QString getSaveFileName(const QString& startDir= QString::null, + const QString& filter= QString::null, + QWidget *parent= 0, + const QString& caption = QString::null); + + + /** + * This function accepts the window id of the parent window, instead + * of QWidget*. It should be used only when necessary. + * @since 3.4 + */ + static QString getSaveFileNameWId(const QString& dir, const QString& filter, + WId parent_id, + const QString& caption); + + /** + * Creates a modal file dialog and returns the selected + * filename or an empty string if none was chosen. + * + * Note that with this + * method the user need not select an existing filename. + * + * @param startDir This can either be + * @li The URL of the directory to start in. + * @li a relative path or a filename determining the + * directory to start in and the file to be selected. + * @li QString::null to start in the current working + * directory, or the last directory where a file has been + * selected. + * @li ':<keyword>' to start in the directory last used + * by a filedialog in the same application that specified + * the same keyword. + * @li '::<keyword>' to start in the directory last used + * by a filedialog in any application that specified the + * same keyword. + * @param filter This is a space separated list of shell globs. + * You can set the text to be displayed for the glob, and + * provide multiple globs. See setFilter() for details on + * how to do this... + * @param parent The widget the dialog will be centered on initially. + * @param caption The name of the dialog widget. + */ + static KURL getSaveURL(const QString& startDir= QString::null, + const QString& filter= QString::null, + QWidget *parent= 0, + const QString& caption = QString::null); + + + /** + * Creates a modal file dialog and returns the selected + * directory or an empty string if none was chosen. + * + * @param startDir This can either be + * @li The URL of the directory to start in. + * @li QString::null to start in the current working + * directory, or the last directory where a file has been + * selected. + * @li ':<keyword>' to start in the directory last used + * by a filedialog in the same application that specified + * the same keyword. + * @li '::<keyword>' to start in the directory last used + * by a filedialog in any application that specified the + * same keyword. + * @param parent The widget the dialog will be centered on initially. + * @param caption The name of the dialog widget. + */ + static QString getExistingDirectory(const QString & startDir = QString::null, + QWidget * parent = 0, + const QString& caption= QString::null); + + /** + * Creates a modal file dialog and returns the selected + * directory or an empty string if none was chosen. + * + * Contrary to getExistingDirectory(), this method allows the + * selection of a remote directory. + * + * @param startDir This can either be + * @li The URL of the directory to start in. + * @li QString::null to start in the current working + * directory, or the last directory where a file has been + * selected. + * @li ':<keyword>' to start in the directory last used + * by a filedialog in the same application that specified + * the same keyword. + * @li '::<keyword>' to start in the directory last used + * by a filedialog in any application that specified the + * same keyword. + * @param parent The widget the dialog will be centered on initially. + * @param caption The name of the dialog widget. + * @since 3.1 + */ + static KURL getExistingURL(const QString & startDir = QString::null, + QWidget * parent = 0, + const QString& caption= QString::null); + /** + * Creates a modal file dialog with an image previewer and returns the + * selected url or an empty string if none was chosen. + * + * @param startDir This can either be + * @li The URL of the directory to start in. + * @li QString::null to start in the current working + * directory, or the last directory where a file has been + * selected. + * @li ':<keyword>' to start in the directory last used + * by a filedialog in the same application that specified + * the same keyword. + * @li '::<keyword>' to start in the directory last used + * by a filedialog in any application that specified the + * same keyword. + * @param parent The widget the dialog will be centered on initially. + * @param caption The name of the dialog widget. + */ + static KURL getImageOpenURL( const QString& startDir = QString::null, + QWidget *parent = 0, + const QString& caption = QString::null ); + virtual void show(); + + /** + * Convenient overload of the other setMode(unsigned int) method. + */ + void setMode( KFile::Mode m ); + + /** + * 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::Mode mode = static_cast<KFile::Mode>( KFile::Files | + * KFile::ExistingOnly | + * KFile::LocalOnly ); + * setMode( mode ); + * \endcode + */ + void setMode( unsigned int m ); + + /** + * Returns the mode of the filedialog. + * @see setMode() + */ + KFile::Mode 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. + */ + 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 { return toolbar; } + + /** + * @returns a pointer to the OK-Button in the filedialog. You may use it + * e.g. to set a custom text to it. + */ + KPushButton *okButton() const; + + /** + * @returns a pointer to the Cancel-Button in the filedialog. You may use + * it e.g. to set a custom text to it. + */ + KPushButton *cancelButton() const; + + /** + * @returns the KURLBar object used as the "speed bar". You can add custom + * entries to it like that: + * \code + * KURLBar *urlBar = fileDialog->speedBar(); + * if ( urlBar ) + * urlBar->insertDynamicItem( someURL, i18n("The URL's description") ); + * \endcode + * + * Note that this method may return a null-pointer if the user configured + * to not use the speed-bar. + * @see KURLBar + * @see KURLBar::insertDynamicItem + * @since 3.2 + */ + KURLBar *speedBar(); + + /** + * @returns a pointer to the action collection, holding all the used + * KActions. + */ + KActionCollection *actionCollection() const; + + /** + * @returns the index of the path combobox so when inserting widgets into + * the dialog (e.g. subclasses) they can do so without hardcoding in an index + */ + int pathComboIndex(); + + /** + * This method implements the logic to determine the user's default directory + * to be listed. E.g. the documents direcory, home directory or a recently + * used directory. + * @param startDir A url/directory, to be used. May use the ':' and '::' syntax + * as documented in the KFileDialog() constructor. + * @param recentDirClass If the ':' or '::' 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). + * @since 3.1 + */ + static KURL getStartURL( const QString& startDir, QString& recentDirClass ); + + /** + * @internal + * Used by KDirSelectDialog to share the dialog's start directory. + */ + static void setStartDir( const KURL& directory ); + +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 KDialogBase + * 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 ); + +protected: + KToolBar *toolbar; + + static KURL *lastDirectory; + + KURLComboBox *locationEdit; + + KFileFilterCombo *filterWidget; + + /** + * Reimplemented to animate the cancel button. + */ + virtual void keyPressEvent( QKeyEvent *e ); + + /** + * Perform basic initialization tasks. Called by constructors. + * @since 3.1 + */ + void init(const QString& startDir, const QString& filter, QWidget* widget); + + /** + * rebuild geometry management. + * + */ + virtual void initGUI(); + + /** + * called when an item is highlighted/selected in multiselection mode. + * handles setting the locationEdit. + */ + void multiSelectionChanged(); + + /** + * Reads configuration and applies it (size, recent directories, ...) + */ + virtual void readConfig( KConfig *, const QString& group = QString::null ); + + /** + * Saves the current configuration + */ + virtual void writeConfig( KConfig *, const QString& group = QString::null ); + + /** + * Reads the recent used files and inserts them into the location combobox + */ + virtual void readRecentFiles( KConfig * ); + + /** + * Saves the entries from the location combobox. + */ + virtual void saveRecentFiles( KConfig * ); + + /** + * 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; + + /** + * Returns the absolute version of the URL specified in locationEdit. + * @since 3.2 + */ + KURL getCompleteURL(const QString&); + + /** + * Returns the filename extension associated with the currentFilter(). + * QString::null is returned if an extension is not available or if + * operationMode() != Saving. + * @since 3.2 + */ + QString currentFilterExtension(); + + /** + * Updates the currentFilterExtension and the availability of the + * Automatically Select Extension Checkbox (visible if operationMode() + * == Saving and enabled if an extension _will_ be associated with the + * currentFilter(), _after_ this call). You should call this after + * filterWidget->setCurrentItem(). + * @since 3.2 + */ + void updateAutoSelectExtension(); + + +protected slots: + void urlEntered( const KURL& ); + void enterURL( const KURL& url ); + void enterURL( const QString& url ); + void locationActivated( const QString& url ); + + /** + * @deprecated, + */ + // ### remove in KDE4 + void toolbarCallback(int); + /** + * @deprecated + */ + // ### remove in KDE4 + void pathComboChanged( const QString& ); + /** + * @deprecated + */ + // ### remove in KDE4 + void dirCompletion( const QString& ); + + void slotFilterChanged(); + void fileHighlighted(const KFileItem *i); + void fileSelected(const KFileItem *i); + void slotStatResult(KIO::Job* job); + void slotLoadingFinished(); + + void fileCompletion( const QString& ); + /** + * @since 3.1 + */ + void toggleSpeedbar( bool ); + + /** + * @since 3.4 + */ + void toggleBookmarks(bool show); + + /** + * @deprecated + */ + virtual void updateStatusLine(int dirs, int files); + + virtual void slotOk(); + virtual void accept(); + virtual void slotCancel(); + + void slotAutoSelectExtClicked(); + void addToRecentDocuments(); + void initSpeedbar(); + +private slots: + void slotLocationChanged( const QString& text ); + +private: + KFileDialog(const KFileDialog&); + KFileDialog operator=(const KFileDialog&); + + void setLocationText( const QString& text ); + void updateLocationWhatsThis(); + + void appendExtension(KURL &url); + void updateLocationEditExtension(const QString &); + void updateFilter(); + + static void initStatic(); + + void setNonExtSelection(); + +protected: + KDirOperator *ops; + bool autoDirectoryFollowing; + + KURL::List& parseSelectedURLs() const; + +protected: + virtual void virtual_hook( int id, void* data ); +private: + KFileDialogPrivate *d; +}; + +#endif diff --git a/kio/kfile/kfilefiltercombo.cpp b/kio/kfile/kfilefiltercombo.cpp new file mode 100644 index 000000000..747a738fe --- /dev/null +++ b/kio/kfile/kfilefiltercombo.cpp @@ -0,0 +1,203 @@ +/* This file is part of the KDE libraries + Copyright (C) Stephan Kulow <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <klocale.h> +#include <kdebug.h> +#include <kstaticdeleter.h> +#include <config-kfile.h> + +#include "kfilefiltercombo.h" + +class KFileFilterCombo::KFileFilterComboPrivate +{ +public: + KFileFilterComboPrivate() { + hasAllSupportedFiles = false; + defaultFilter = i18n("*|All Files"); + isMimeFilter = false; + } + + // when we have more than 3 mimefilters and no default-filter, + // we don't show the comments of all mimefilters in one line, + // instead we show "All supported files". We have to translate + // that back to the list of mimefilters in currentFilter() tho. + bool hasAllSupportedFiles; + // true when setMimeFilter was called + bool isMimeFilter; + QString lastFilter; + QString defaultFilter; +}; + +KFileFilterCombo::KFileFilterCombo( QWidget *parent, const char *name) + : KComboBox(true, parent, name), d( new KFileFilterComboPrivate ) +{ + setTrapReturnKey( true ); + setInsertionPolicy(NoInsertion); + connect( this, SIGNAL( activated( int )), this, SIGNAL( filterChanged() )); + connect( this, SIGNAL( returnPressed() ), this, SIGNAL( filterChanged() )); + connect( this, SIGNAL( filterChanged() ), SLOT( slotFilterChanged() )); + m_allTypes = false; +} + +KFileFilterCombo::~KFileFilterCombo() +{ + delete d; +} + +void KFileFilterCombo::setFilter(const QString& filter) +{ + clear(); + filters.clear(); + d->hasAllSupportedFiles = false; + + if (!filter.isEmpty()) { + QString tmp = filter; + int index = tmp.find('\n'); + while (index > 0) { + filters.append(tmp.left(index)); + tmp = tmp.mid(index + 1); + index = tmp.find('\n'); + } + filters.append(tmp); + } + else + filters.append( d->defaultFilter ); + + QStringList::ConstIterator it; + QStringList::ConstIterator end(filters.end()); + for (it = filters.begin(); it != end; ++it) { + int tab = (*it).find('|'); + insertItem((tab < 0) ? *it : + (*it).mid(tab + 1)); + } + + d->lastFilter = currentText(); + d->isMimeFilter = false; +} + +QString KFileFilterCombo::currentFilter() const +{ + QString f = currentText(); + if (f == text(currentItem())) { // user didn't edit the text + f = *filters.at(currentItem()); + if ( d->isMimeFilter || (currentItem() == 0 && d->hasAllSupportedFiles) ) { + return f; // we have a mimetype as filter + } + } + + int tab = f.find('|'); + if (tab < 0) + return f; + else + return f.left(tab); +} + +void KFileFilterCombo::setCurrentFilter( const QString& filter ) +{ + int pos = 0; + for( QStringList::ConstIterator it = filters.begin(); + it != filters.end(); + ++it, ++pos ) { + if( *it == filter ) { + setCurrentItem( pos ); + filterChanged(); + return; + } + } + setCurrentText( filter ); + filterChanged(); +} + +void KFileFilterCombo::setMimeFilter( const QStringList& types, + const QString& defaultType ) +{ + clear(); + filters.clear(); + QString delim = QString::fromLatin1(", "); + d->hasAllSupportedFiles = false; + + m_allTypes = defaultType.isEmpty() && (types.count() > 1); + + QString allComments, allTypes; + int i = 0; + for(QStringList::ConstIterator it = types.begin(); it != types.end(); ++it, ++i) + { + if ( m_allTypes && it != types.begin() ) { + allComments += delim; + allTypes += ' '; + } + + kdDebug(kfile_area) << *it << endl; + KMimeType::Ptr type = KMimeType::mimeType( *it ); + filters.append( type->name() ); + if ( m_allTypes ) + { + allTypes += type->name(); + allComments += type->comment(); + } + insertItem( type->comment() ); + if ( type->name() == defaultType ) + setCurrentItem( i ); + } + + if ( m_allTypes ) + { + if ( i < 3 ) // show the mime-comments of at max 3 types + insertItem( allComments, 0 ); + else { + insertItem( i18n("All Supported Files"), 0 ); + d->hasAllSupportedFiles = true; + } + + filters.prepend( allTypes ); + } + + d->lastFilter = currentText(); + d->isMimeFilter = true; +} + +void KFileFilterCombo::slotFilterChanged() +{ + d->lastFilter = currentText(); +} + +bool KFileFilterCombo::eventFilter( QObject *o, QEvent *e ) +{ + if ( o == lineEdit() && e->type() == QEvent::FocusOut ) { + if ( currentText() != d->lastFilter ) + emit filterChanged(); + } + + return KComboBox::eventFilter( o, e ); +} + +void KFileFilterCombo::setDefaultFilter( const QString& filter ) +{ + d->defaultFilter = filter; +} + +QString KFileFilterCombo::defaultFilter() const +{ + return d->defaultFilter; +} + +void KFileFilterCombo::virtual_hook( int id, void* data ) +{ KComboBox::virtual_hook( id, data ); } + +#include "kfilefiltercombo.moc" diff --git a/kio/kfile/kfilefiltercombo.h b/kio/kfile/kfilefiltercombo.h new file mode 100644 index 000000000..9b7c103bf --- /dev/null +++ b/kio/kfile/kfilefiltercombo.h @@ -0,0 +1,104 @@ +/* This file is part of the KDE libraries + Copyright (C) Stephan Kulow <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KFILEFILTERCOMBO_H +#define KFILEFILTERCOMBO_H + +#include <qstringlist.h> +#include <qptrdict.h> + +#include <kcombobox.h> +#include <kmimetype.h> + +class KFileFilterComboPrivate; + +class KIO_EXPORT KFileFilterCombo : public KComboBox +{ + Q_OBJECT + + public: + KFileFilterCombo(QWidget *parent= 0, const char *name= 0); + ~KFileFilterCombo(); + + void setFilter(const QString& filter); + + /** + * @returns the current filter, either something like "*.cpp *.h" + * or the current mimetype, like "text/html", or a list of those, like + " "text/html text/plain image/png", all separated with one space. + */ + QString currentFilter() const; + + /** + * Sets the current filter. Filter must match one of the filter items + * passed before to this widget. + * @since 3.4 + */ + void setCurrentFilter( const QString& filter ); + + /** + * Sets a list of mimetypes. + * If @p defaultType is set, it will be set as the current item. + * Otherwise, a first item showing all the mimetypes will be created. + */ + void setMimeFilter( const QStringList& types, const QString& defaultType ); + + /** + * @return true if the filter's first item is the list of all mimetypes + */ + bool showsAllTypes() const { return m_allTypes; } + + /** + * This method allows you to set a default-filter, that is used when an + * empty filter is set. Make sure you call this before calling + * setFilter(). + * + * By default, this is set to i18n("*|All Files") + * @see defaultFilter + */ + void setDefaultFilter( const QString& filter ); + + /** + * @return the default filter, used when an empty filter is set. + * @see setDefaultFilter + */ + QString defaultFilter() const; + + protected: + virtual bool eventFilter( QObject *o, QEvent *e ); + +// KDE4: those variables are private. filters() was added + QStringList filters; + bool m_allTypes; + + signals: + void filterChanged(); + +private slots: + void slotFilterChanged(); + +protected: + virtual void virtual_hook( int id, void* data ); +private: + friend class KFileDialog; // gone in KDE4 + class KFileFilterComboPrivate; + KFileFilterComboPrivate *d; +}; + +#endif diff --git a/kio/kfile/kfileiconview.cpp b/kio/kfile/kfileiconview.cpp new file mode 100644 index 000000000..0668e91e2 --- /dev/null +++ b/kio/kfile/kfileiconview.cpp @@ -0,0 +1,942 @@ +// -*- c++ -*- +/* This file is part of the KDE libraries + Copyright (C) 1997 Stephan Kulow <[email protected]> + 2000,2001,2002 Carsten Pfeiffer <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qfontmetrics.h> +#include <qkeycode.h> +#include <qlabel.h> +#include <qpainter.h> +#include <qpixmap.h> +#include <qregexp.h> +#include <qtimer.h> +#include <qtooltip.h> + +#include <kaction.h> +#include <kapplication.h> +#include <klocale.h> +#include <kfileitem.h> +#include <kiconeffect.h> +#include <kglobalsettings.h> +#include <kurldrag.h> +#include <kio/previewjob.h> + +#include "kfileiconview.h" +#include "config-kfile.h" + +#define DEFAULT_PREVIEW_SIZE 60 +#define DEFAULT_SHOW_PREVIEWS false +#define DEFAULT_VIEW_MODE "SmallColumns" + +KFileIconViewItem::~KFileIconViewItem() +{ + fileInfo()->removeExtraData( iconView() ); +} + +class KFileIconView::KFileIconViewPrivate +{ +public: + KFileIconViewPrivate( KFileIconView *parent ) { + previewIconSize = 60; + job = 0; + dropItem = 0; + + noArrangement = false; + ignoreMaximumSize = false; + smallColumns = new KRadioAction( i18n("Small Icons"), 0, parent, + SLOT( slotSmallColumns() ), + parent->actionCollection(), + "small columns" ); + + largeRows = new KRadioAction( i18n("Large Icons"), 0, parent, + SLOT( slotLargeRows() ), + parent->actionCollection(), + "large rows" ); + + smallColumns->setExclusiveGroup(QString::fromLatin1("IconView mode")); + largeRows->setExclusiveGroup(QString::fromLatin1("IconView mode")); + + previews = new KToggleAction( i18n("Thumbnail Previews"), 0, + parent->actionCollection(), + "show previews" ); + zoomIn = KStdAction::zoomIn( parent, SLOT( zoomIn() ), + parent->actionCollection(), "zoomIn" ); + zoomOut = KStdAction::zoomOut( parent, SLOT( zoomOut() ), + parent->actionCollection(), "zoomOut" ); + + previews->setGroup("previews"); + zoomIn->setGroup("previews"); + zoomOut->setGroup("previews"); + + connect( previews, SIGNAL( toggled( bool )), + parent, SLOT( slotPreviewsToggled( bool ))); + + connect( &previewTimer, SIGNAL( timeout() ), + parent, SLOT( showPreviews() )); + connect( &autoOpenTimer, SIGNAL( timeout() ), + parent, SLOT( slotAutoOpen() )); + } + + ~KFileIconViewPrivate() { + if ( job ) + job->kill(); + } + + KRadioAction *smallColumns, *largeRows; + KAction *zoomIn, *zoomOut; + KToggleAction *previews; + KIO::PreviewJob *job; + KFileIconViewItem *dropItem; + QTimer previewTimer; + QTimer autoOpenTimer; + QStringList previewMimeTypes; + int previewIconSize; + bool noArrangement :1; + bool ignoreMaximumSize :1; +}; + +KFileIconView::KFileIconView(QWidget *parent, const char *name) + : KIconView(parent, name), KFileView() +{ + d = new KFileIconViewPrivate( this ); + + setViewName( i18n("Icon View") ); + + toolTip = 0; + setResizeMode( Adjust ); + setMaxItemWidth( 300 ); + setWordWrapIconText( false ); + setArrangement( TopToBottom ); + setAutoArrange( true ); + setItemsMovable( false ); + setMode( KIconView::Select ); + KIconView::setSorting( true ); + // as long as QIconView only shows tooltips when the cursor is over the + // icon (and not the text), we have to create our own tooltips + setShowToolTips( false ); + slotSmallColumns(); + d->smallColumns->setChecked( true ); + + connect( this, SIGNAL( returnPressed(QIconViewItem *) ), + SLOT( slotActivate( QIconViewItem *) ) ); + + // we want single click _and_ double click (as convenience) + connect( this, SIGNAL( clicked(QIconViewItem *, const QPoint&) ), + SLOT( selected( QIconViewItem *) ) ); + connect( this, SIGNAL( doubleClicked(QIconViewItem *, const QPoint&) ), + SLOT( slotActivate( QIconViewItem *) ) ); + + connect( this, SIGNAL( onItem( QIconViewItem * ) ), + SLOT( showToolTip( QIconViewItem * ) ) ); + connect( this, SIGNAL( onViewport() ), + SLOT( removeToolTip() ) ); + connect( this, SIGNAL( contextMenuRequested(QIconViewItem*,const QPoint&)), + SLOT( slotActivateMenu( QIconViewItem*, const QPoint& ) ) ); + + KFile::SelectionMode sm = KFileView::selectionMode(); + switch ( sm ) { + case KFile::Multi: + QIconView::setSelectionMode( QIconView::Multi ); + break; + case KFile::Extended: + QIconView::setSelectionMode( QIconView::Extended ); + break; + case KFile::NoSelection: + QIconView::setSelectionMode( QIconView::NoSelection ); + break; + default: // fall through + case KFile::Single: + QIconView::setSelectionMode( QIconView::Single ); + break; + } + + if ( sm == KFile::Multi || sm == KFile::Extended ) + connect( this, SIGNAL( selectionChanged() ), + SLOT( slotSelectionChanged() )); + else + connect( this, SIGNAL( selectionChanged( QIconViewItem * )), + SLOT( highlighted( QIconViewItem * ))); + + viewport()->installEventFilter( this ); + + // for mimetype resolving + m_resolver = new KMimeTypeResolver<KFileIconViewItem,KFileIconView>(this); +} + +KFileIconView::~KFileIconView() +{ + delete m_resolver; + removeToolTip(); + delete d; +} + +void KFileIconView::readConfig( KConfig *kc, const QString& group ) +{ + QString gr = group.isEmpty() ? QString("KFileIconView") : group; + KConfigGroupSaver cs( kc, gr ); + QString small = QString::fromLatin1("SmallColumns"); + d->previewIconSize = kc->readNumEntry( "Preview Size", DEFAULT_PREVIEW_SIZE ); + d->previews->setChecked( kc->readBoolEntry( "ShowPreviews", DEFAULT_SHOW_PREVIEWS ) ); + + if ( kc->readEntry("ViewMode", DEFAULT_VIEW_MODE ) == small ) { + d->smallColumns->setChecked( true ); + slotSmallColumns(); + } + else { + d->largeRows->setChecked( true ); + slotLargeRows(); + } + + if ( d->previews->isChecked() ) + showPreviews(); +} + +void KFileIconView::writeConfig( KConfig *kc, const QString& group ) +{ + QString gr = group.isEmpty() ? QString("KFileIconView") : group; + KConfigGroupSaver cs( kc, gr ); + + QString viewMode = d->smallColumns->isChecked() ? + QString::fromLatin1("SmallColumns") : + QString::fromLatin1("LargeRows"); + if(!kc->hasDefault( "ViewMode" ) && viewMode == DEFAULT_VIEW_MODE ) + kc->revertToDefault( "ViewMode" ); + else + kc->writeEntry( "ViewMode", viewMode ); + + int previewsIconSize = d->previewIconSize; + if(!kc->hasDefault( "Preview Size" ) && previewsIconSize == DEFAULT_PREVIEW_SIZE ) + kc->revertToDefault( "Preview Size" ); + else + kc->writeEntry( "Preview Size", previewsIconSize ); + + bool showPreviews = d->previews->isChecked(); + if(!kc->hasDefault( "ShowPreviews" ) && showPreviews == DEFAULT_SHOW_PREVIEWS ) + kc->revertToDefault( "ShowPreviews" ); + else + kc->writeEntry( "ShowPreviews", showPreviews ); +} + +void KFileIconView::removeToolTip() +{ + delete toolTip; + toolTip = 0; +} + +void KFileIconView::showToolTip( QIconViewItem *item ) +{ + delete toolTip; + toolTip = 0; + + if ( !item ) + return; + + int w = maxItemWidth() - ( itemTextPos() == QIconView::Bottom ? 0 : + item->pixmapRect().width() ) - 4; + if ( fontMetrics().width( item->text() ) >= w ) { + toolTip = new QLabel( QString::fromLatin1(" %1 ").arg(item->text()), 0, + "myToolTip", + WStyle_StaysOnTop | WStyle_Customize | WStyle_NoBorder | WStyle_Tool | WX11BypassWM ); + toolTip->setFrameStyle( QFrame::Plain | QFrame::Box ); + toolTip->setLineWidth( 1 ); + toolTip->setAlignment( AlignLeft | AlignTop ); + toolTip->move( QCursor::pos() + QPoint( 14, 14 ) ); + toolTip->adjustSize(); + QRect screen = QApplication::desktop()->screenGeometry( + QApplication::desktop()->screenNumber(QCursor::pos())); + if (toolTip->x()+toolTip->width() > screen.right()) { + toolTip->move(toolTip->x()+screen.right()-toolTip->x()-toolTip->width(), toolTip->y()); + } + if (toolTip->y()+toolTip->height() > screen.bottom()) { + toolTip->move(toolTip->x(), screen.bottom()-toolTip->y()-toolTip->height()+toolTip->y()); + } + toolTip->setFont( QToolTip::font() ); + toolTip->setPalette( QToolTip::palette(), true ); + toolTip->show(); + } +} + +void KFileIconView::slotActivateMenu( QIconViewItem* item, const QPoint& pos ) +{ + if ( !item ) { + sig->activateMenu( 0, pos ); + return; + } + KFileIconViewItem *i = (KFileIconViewItem*) item; + sig->activateMenu( i->fileInfo(), pos ); +} + +void KFileIconView::hideEvent( QHideEvent *e ) +{ + removeToolTip(); + KIconView::hideEvent( e ); +} + +void KFileIconView::keyPressEvent( QKeyEvent *e ) +{ + KIconView::keyPressEvent( e ); + + // ignore Ctrl-Return so that the dialog can catch it. + if ( (e->state() & ControlButton) && + (e->key() == Key_Return || e->key() == Key_Enter) ) + e->ignore(); +} + +void KFileIconView::setSelected( const KFileItem *info, bool enable ) +{ + KFileIconViewItem *item = viewItem( info ); + if ( item ) + KIconView::setSelected( item, enable, true ); +} + +void KFileIconView::selectAll() +{ + if (KFileView::selectionMode() == KFile::NoSelection || + KFileView::selectionMode() == KFile::Single) + return; + + KIconView::selectAll( true ); +} + +void KFileIconView::clearSelection() +{ + KIconView::clearSelection(); +} + +void KFileIconView::invertSelection() +{ + KIconView::invertSelection(); +} + +void KFileIconView::clearView() +{ + m_resolver->m_lstPendingMimeIconItems.clear(); + + KIconView::clear(); + stopPreview(); +} + +void KFileIconView::insertItem( KFileItem *i ) +{ + KFileView::insertItem( i ); + + QIconView* qview = static_cast<QIconView*>( this ); + // Since creating and initializing an item leads to a repaint, + // we disable updates on the IconView for a while. + qview->setUpdatesEnabled( false ); + KFileIconViewItem *item = new KFileIconViewItem( qview, i ); + initItem( item, i, true ); + qview->setUpdatesEnabled( true ); + + if ( !i->isMimeTypeKnown() ) + m_resolver->m_lstPendingMimeIconItems.append( item ); + + i->setExtraData( this, item ); +} + +void KFileIconView::slotActivate( QIconViewItem *item ) +{ + if ( !item ) + return; + const KFileItem *fi = ( (KFileIconViewItem*)item )->fileInfo(); + if ( fi ) + sig->activate( fi ); +} + +void KFileIconView::selected( QIconViewItem *item ) +{ + if ( !item || (KApplication::keyboardMouseState() & (ShiftButton | ControlButton)) != 0 ) + return; + + if ( KGlobalSettings::singleClick() ) { + const KFileItem *fi = ( (KFileIconViewItem*)item )->fileInfo(); + if ( fi && (fi->isDir() || !onlyDoubleClickSelectsFiles()) ) + sig->activate( fi ); + } +} + +void KFileIconView::setCurrentItem( const KFileItem *item ) +{ + KFileIconViewItem *it = viewItem( item ); + if ( it ) + KIconView::setCurrentItem( it ); +} + +KFileItem * KFileIconView::currentFileItem() const +{ + KFileIconViewItem *current = static_cast<KFileIconViewItem*>( currentItem() ); + if ( current ) + return current->fileInfo(); + + return 0L; +} + +void KFileIconView::highlighted( QIconViewItem *item ) +{ + if ( !item ) + return; + const KFileItem *fi = ( (KFileIconViewItem*)item )->fileInfo(); + if ( fi ) + sig->highlightFile( fi ); +} + +void KFileIconView::setSelectionMode( KFile::SelectionMode sm ) +{ + disconnect( SIGNAL( selectionChanged() ), this ); + disconnect( SIGNAL( selectionChanged( QIconViewItem * )), this ); + + KFileView::setSelectionMode( sm ); + switch ( KFileView::selectionMode() ) { + case KFile::Multi: + QIconView::setSelectionMode( QIconView::Multi ); + break; + case KFile::Extended: + QIconView::setSelectionMode( QIconView::Extended ); + break; + case KFile::NoSelection: + QIconView::setSelectionMode( QIconView::NoSelection ); + break; + default: // fall through + case KFile::Single: + QIconView::setSelectionMode( QIconView::Single ); + break; + } + + if ( sm == KFile::Multi || sm == KFile::Extended ) + connect( this, SIGNAL( selectionChanged() ), + SLOT( slotSelectionChanged() )); + else + connect( this, SIGNAL( selectionChanged( QIconViewItem * )), + SLOT( highlighted( QIconViewItem * ))); +} + +bool KFileIconView::isSelected( const KFileItem *i ) const +{ + KFileIconViewItem *item = viewItem( i ); + return (item && item->isSelected()); +} + +void KFileIconView::updateView( bool b ) +{ + if ( !b ) + return; // eh? + + KFileIconViewItem *item = static_cast<KFileIconViewItem*>(QIconView::firstItem()); + if ( item ) { + do { + if ( d->previews->isChecked() ) { + if ( canPreview( item->fileInfo() ) ) + item->setPixmapSize( QSize( d->previewIconSize, d->previewIconSize ) ); + } + else { + // unset pixmap size (used for previews) + if ( !item->pixmapSize().isNull() ) + item->setPixmapSize( QSize( 0, 0 ) ); + } + // recalculate item parameters but avoid an in-place repaint + item->setPixmap( (item->fileInfo())->pixmap( myIconSize ), true, false ); + item = static_cast<KFileIconViewItem *>(item->nextItem()); + } while ( item != 0L ); + } +} + +void KFileIconView::updateView( const KFileItem *i ) +{ + KFileIconViewItem *item = viewItem( i ); + if ( item ) + initItem( item, i, true ); +} + +void KFileIconView::removeItem( const KFileItem *i ) +{ + if ( !i ) + return; + + if ( d->job ) + d->job->removeItem( i ); + + KFileIconViewItem *item = viewItem( i ); + m_resolver->m_lstPendingMimeIconItems.remove( item ); + delete item; + + KFileView::removeItem( i ); +} + +void KFileIconView::setIconSize( int size ) +{ + myIconSize = size; + updateIcons(); +} + +void KFileIconView::setPreviewSize( int size ) +{ + if ( size < 30 ) + size = 30; // minimum + + d->previewIconSize = size; + if ( d->previews->isChecked() ) + showPreviews(); +} + +void KFileIconView::setIgnoreMaximumSize(bool ignoreSize) +{ + d->ignoreMaximumSize = ignoreSize; +} + +void KFileIconView::updateIcons() +{ + updateView( true ); + arrangeItemsInGrid(); +} + +void KFileIconView::ensureItemVisible( const KFileItem *i ) +{ + KFileIconViewItem *item = viewItem( i ); + if ( item ) + KIconView::ensureItemVisible( item ); +} + +void KFileIconView::slotSelectionChanged() +{ + sig->highlightFile( 0L ); +} + +void KFileIconView::slotSmallColumns() +{ + // setItemTextPos(), setArrangement(), setWordWrapIconText() and + // setIconSize() all call arrangeItemsInGrid() :( Prevent this. + d->noArrangement = true; // stop arrangeItemsInGrid()! + + // Make sure to uncheck previews if selected + if ( d->previews->isChecked() ) + { + stopPreview(); + d->previews->setChecked( false ); + } + setGridX( -1 ); + setMaxItemWidth( 300 ); + setItemTextPos( Right ); + setArrangement( TopToBottom ); + setWordWrapIconText( false ); + setSpacing( 0 ); + + d->noArrangement = false; // now we can arrange + setIconSize( KIcon::SizeSmall ); +} + +void KFileIconView::slotLargeRows() +{ + // setItemTextPos(), setArrangement(), setWordWrapIconText() and + // setIconSize() all call arrangeItemsInGrid() :( Prevent this. + d->noArrangement = true; // stop arrangeItemsInGrid()! + + setGridX( KGlobal::iconLoader()->currentSize( KIcon::Desktop ) + 50 ); + setItemTextPos( Bottom ); + setArrangement( LeftToRight ); + setWordWrapIconText( true ); + setSpacing( 5 ); // default in QIconView + + d->noArrangement = false; // now we can arrange + setIconSize( KIcon::SizeMedium ); +} + +void KFileIconView::stopPreview() +{ + if ( d->job ) { + d->job->kill(); + d->job = 0L; + } +} + +void KFileIconView::slotPreviewsToggled( bool on ) +{ + if ( on ) + showPreviews(); + else { + stopPreview(); + slotLargeRows(); + } +} + +void KFileIconView::showPreviews() +{ + if ( d->previewMimeTypes.isEmpty() ) + d->previewMimeTypes = KIO::PreviewJob::supportedMimeTypes(); + + stopPreview(); + d->previews->setChecked( true ); + + if ( !d->largeRows->isChecked() ) { + d->largeRows->setChecked( true ); + slotLargeRows(); // also sets the icon size and updates the grid + } + else { + updateIcons(); + } + + d->job = KIO::filePreview(*items(), d->previewIconSize,d->previewIconSize); + d->job->setIgnoreMaximumSize(d->ignoreMaximumSize); + + connect( d->job, SIGNAL( result( KIO::Job * )), + this, SLOT( slotPreviewResult( KIO::Job * ))); + connect( d->job, SIGNAL( gotPreview( const KFileItem*, const QPixmap& )), + SLOT( gotPreview( const KFileItem*, const QPixmap& ) )); +// connect( d->job, SIGNAL( failed( const KFileItem* )), +// this, SLOT( slotFailed( const KFileItem* ) )); +} + +void KFileIconView::slotPreviewResult( KIO::Job *job ) +{ + if ( job == d->job ) + d->job = 0L; +} + +void KFileIconView::gotPreview( const KFileItem *item, const QPixmap& pix ) +{ + KFileIconViewItem *it = viewItem( item ); + if ( it ) + if( item->overlays() & KIcon::HiddenOverlay ) + { + QPixmap p( pix ); + + KIconEffect::semiTransparent( p ); + it->setPixmap( p ); + } + else + it->setPixmap( pix ); +} + +bool KFileIconView::canPreview( const KFileItem *item ) const +{ + QStringList::Iterator it = d->previewMimeTypes.begin(); + QRegExp r; + r.setWildcard( true ); + + for ( ; it != d->previewMimeTypes.end(); ++it ) { + QString type = *it; + // the "mimetype" can be "image/*" + if ( type.at( type.length() - 1 ) == '*' ) { + r.setPattern( type ); + if ( r.search( item->mimetype() ) != -1 ) + return true; + } + else + if ( item->mimetype() == type ) + return true; + } + + return false; +} + +KFileItem * KFileIconView::firstFileItem() const +{ + KFileIconViewItem *item = static_cast<KFileIconViewItem*>( firstItem() ); + if ( item ) + return item->fileInfo(); + return 0L; +} + +KFileItem * KFileIconView::nextItem( const KFileItem *fileItem ) const +{ + if ( fileItem ) { + KFileIconViewItem *item = viewItem( fileItem ); + if ( item && item->nextItem() ) + return ((KFileIconViewItem*) item->nextItem())->fileInfo(); + } + return 0L; +} + +KFileItem * KFileIconView::prevItem( const KFileItem *fileItem ) const +{ + if ( fileItem ) { + KFileIconViewItem *item = viewItem( fileItem ); + if ( item && item->prevItem() ) + return ((KFileIconViewItem*) item->prevItem())->fileInfo(); + } + return 0L; +} + +void KFileIconView::setSorting( QDir::SortSpec spec ) +{ + KFileView::setSorting( spec ); + KFileItemListIterator it( *items() ); + + KFileItem *item; + + if ( spec & QDir::Time ) { + for ( ; (item = it.current()); ++it ) + // warning, time_t is often signed -> cast it + viewItem(item)->setKey( sortingKey( (unsigned long)item->time( KIO::UDS_MODIFICATION_TIME ), item->isDir(), spec )); + } + + else if ( spec & QDir::Size ) { + for ( ; (item = it.current()); ++it ) + viewItem(item)->setKey( sortingKey( item->size(), item->isDir(), + spec )); + } + else { // Name or Unsorted + for ( ; (item = it.current()); ++it ) + viewItem(item)->setKey( sortingKey( item->text(), item->isDir(), + spec )); + } + + KIconView::setSorting( true, !isReversed() ); + sort( !isReversed() ); +} + +// +// mimetype determination on demand +// +void KFileIconView::mimeTypeDeterminationFinished() +{ + // anything to do? +} + +void KFileIconView::determineIcon( KFileIconViewItem *item ) +{ + (void) item->fileInfo()->determineMimeType(); + updateView( item->fileInfo() ); +} + +void KFileIconView::listingCompleted() +{ + arrangeItemsInGrid(); + + // QIconView doesn't set the current item automatically, so we have to do + // that. We don't want to emit selectionChanged() tho. + if ( !currentItem() ) { + bool block = signalsBlocked(); + blockSignals( true ); + QIconViewItem *item = viewItem( firstFileItem() ); + KIconView::setCurrentItem( item ); + KIconView::setSelected( item, false ); + blockSignals( block ); + } + + m_resolver->start( d->previews->isChecked() ? 0 : 10 ); +} + +// need to remove our tooltip, eventually +bool KFileIconView::eventFilter( QObject *o, QEvent *e ) +{ + if ( o == viewport() || o == this ) { + int type = e->type(); + if ( type == QEvent::Leave || + type == QEvent::FocusOut ) + removeToolTip(); + } + + return KIconView::eventFilter( o, e ); +} + +///////////////////////////////////////////////////////////////// + +// ### workaround for Qt3 Bug +void KFileIconView::showEvent( QShowEvent *e ) +{ + KIconView::showEvent( e ); +} + + +void KFileIconView::initItem( KFileIconViewItem *item, const KFileItem *i, + bool updateTextAndPixmap ) +{ + if ( d->previews->isChecked() && canPreview( i ) ) + item->setPixmapSize( QSize( d->previewIconSize, d->previewIconSize ) ); + + if ( updateTextAndPixmap ) + { + // this causes a repaint of the item, which we want to avoid during + // directory listing, when all items are created. We want to paint all + // items at once, not every single item in that case. + item->setText( i->text() , false, false ); + item->setPixmap( i->pixmap( myIconSize ) ); + } + + // see also setSorting() + QDir::SortSpec spec = KFileView::sorting(); + + if ( spec & QDir::Time ) + // warning, time_t is often signed -> cast it + item->setKey( sortingKey( (unsigned long) i->time( KIO::UDS_MODIFICATION_TIME ), + i->isDir(), spec )); + else if ( spec & QDir::Size ) + item->setKey( sortingKey( i->size(), i->isDir(), spec )); + + else // Name or Unsorted + item->setKey( sortingKey( i->text(), i->isDir(), spec )); + + //qDebug("** key for: %s: %s", i->text().latin1(), item->key().latin1()); + + if ( d->previews->isChecked() ) + d->previewTimer.start( 10, true ); +} + +void KFileIconView::arrangeItemsInGrid( bool update ) +{ + if ( d->noArrangement ) + return; + + KIconView::arrangeItemsInGrid( update ); +} + +void KFileIconView::zoomIn() +{ + setPreviewSize( d->previewIconSize + 30 ); +} + +void KFileIconView::zoomOut() +{ + setPreviewSize( d->previewIconSize - 30 ); +} + +QDragObject *KFileIconView::dragObject() +{ + // create a list of the URL:s that we want to drag + KURL::List urls; + KFileItemListIterator it( * KFileView::selectedItems() ); + for ( ; it.current(); ++it ){ + urls.append( (*it)->url() ); + } + QPixmap pixmap; + if( urls.count() > 1 ) + pixmap = DesktopIcon( "kmultiple", iconSize() ); + if( pixmap.isNull() ) + pixmap = currentFileItem()->pixmap( iconSize() ); + + QPoint hotspot; + hotspot.setX( pixmap.width() / 2 ); + hotspot.setY( pixmap.height() / 2 ); + QDragObject* myDragObject = new KURLDrag( urls, widget() ); + myDragObject->setPixmap( pixmap, hotspot ); + return myDragObject; +} + +void KFileIconView::slotAutoOpen() +{ + d->autoOpenTimer.stop(); + if( !d->dropItem ) + return; + + KFileItem *fileItem = d->dropItem->fileInfo(); + if (!fileItem) + return; + + if( fileItem->isFile() ) + return; + + if ( fileItem->isDir() || fileItem->isLink()) + sig->activate( fileItem ); +} + +bool KFileIconView::acceptDrag(QDropEvent* e) const +{ + return KURLDrag::canDecode( e ) && + (e->source()!=const_cast<KFileIconView*>(this)) && + ( e->action() == QDropEvent::Copy + || e->action() == QDropEvent::Move + || e->action() == QDropEvent::Link ); +} + +void KFileIconView::contentsDragEnterEvent( QDragEnterEvent *e ) +{ + if ( ! acceptDrag( e ) ) { // can we decode this ? + e->ignore(); // No + return; + } + e->acceptAction(); // Yes + + if ((dropOptions() & AutoOpenDirs) == 0) + return; + + KFileIconViewItem *item = dynamic_cast<KFileIconViewItem*>(findItem( contentsToViewport( e->pos() ) )); + if ( item ) { // are we over an item ? + d->dropItem = item; + d->autoOpenTimer.start( autoOpenDelay() ); // restart timer + } + else + { + d->dropItem = 0; + d->autoOpenTimer.stop(); + } +} + +void KFileIconView::contentsDragMoveEvent( QDragMoveEvent *e ) +{ + if ( ! acceptDrag( e ) ) { // can we decode this ? + e->ignore(); // No + return; + } + e->acceptAction(); // Yes + + if ((dropOptions() & AutoOpenDirs) == 0) + return; + + KFileIconViewItem *item = dynamic_cast<KFileIconViewItem*>(findItem( contentsToViewport( e->pos() ) )); + if ( item ) { // are we over an item ? + if (d->dropItem != item) + { + d->dropItem = item; + d->autoOpenTimer.start( autoOpenDelay() ); // restart timer + } + } + else + { + d->dropItem = 0; + d->autoOpenTimer.stop(); + } +} + +void KFileIconView::contentsDragLeaveEvent( QDragLeaveEvent * ) +{ + d->dropItem = 0; + d->autoOpenTimer.stop(); +} + +void KFileIconView::contentsDropEvent( QDropEvent *e ) +{ + d->dropItem = 0; + d->autoOpenTimer.stop(); + + if ( ! acceptDrag( e ) ) { // can we decode this ? + e->ignore(); // No + return; + } + e->acceptAction(); // Yes + + KFileIconViewItem *item = dynamic_cast<KFileIconViewItem*>(findItem( contentsToViewport( e->pos() ) )); + KFileItem * fileItem = 0; + if (item) + fileItem = item->fileInfo(); + + emit dropped(e, fileItem); + + KURL::List urls; + if (KURLDrag::decode( e, urls ) && !urls.isEmpty()) + { + emit dropped(e, urls, fileItem ? fileItem->url() : KURL()); + sig->dropURLs(fileItem, e, urls); + } +} + +void KFileIconView::virtual_hook( int id, void* data ) +{ KIconView::virtual_hook( id, data ); + KFileView::virtual_hook( id, data ); } + +#include "kfileiconview.moc" diff --git a/kio/kfile/kfileiconview.h b/kio/kfile/kfileiconview.h new file mode 100644 index 000000000..63a841a8e --- /dev/null +++ b/kio/kfile/kfileiconview.h @@ -0,0 +1,265 @@ +// -*- c++ -*- +/* This file is part of the KDE libraries + Copyright (C) 1997 Stephan Kulow <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KFILEICONVIEW_H +#define KFILEICONVIEW_H + +class KFileItem; +class QWidget; +class QLabel; + +#include <kiconview.h> +#include <kiconloader.h> +#include <kfileview.h> +#include <kmimetyperesolver.h> +#include <kfile.h> + +/** + * An item for the iconview, that has a reference to its corresponding + * KFileItem. + */ +class KIO_EXPORT KFileIconViewItem : public KIconViewItem +{ +public: + KFileIconViewItem( QIconView *parent, const QString &text, + const QPixmap &pixmap, + KFileItem *fi ) + : KIconViewItem( parent, text, pixmap ), inf( fi ) {} + /** + * @since 3.1 + */ + KFileIconViewItem( QIconView *parent, KFileItem *fi ) + : KIconViewItem( parent ), inf( fi ) {} + + virtual ~KFileIconViewItem(); + + /** + * @returns the corresponding KFileItem + */ + KFileItem *fileInfo() const { + return inf; + } + +private: + KFileItem *inf; + +private: + class KFileIconViewItemPrivate; + KFileIconViewItemPrivate *d; + +}; + +namespace KIO { + class Job; +} + +/** + * An icon-view capable of showing KFileItem's. Used in the filedialog + * for example. Most of the documentation is in KFileView class. + * + * @see KDirOperator + * @see KCombiView + * @see KFileDetailView + */ +class KIO_EXPORT KFileIconView : public KIconView, public KFileView +{ + Q_OBJECT + +public: + KFileIconView(QWidget *parent, const char *name); + virtual ~KFileIconView(); + + virtual QWidget *widget() { return this; } + virtual void clearView(); + virtual void setAutoUpdate( bool ) {} // ### unused. remove in KDE4 + + virtual void updateView( bool ); + virtual void updateView(const KFileItem*); + virtual void removeItem(const KFileItem*); + + virtual void listingCompleted(); + + virtual void insertItem( KFileItem *i ); + virtual void setSelectionMode( KFile::SelectionMode sm ); + + virtual void setSelected(const KFileItem *, bool); + virtual bool isSelected(const KFileItem *i) const; + virtual void clearSelection(); + virtual void selectAll(); + virtual void invertSelection(); + + virtual void setCurrentItem( const KFileItem * ); + virtual KFileItem * currentFileItem() const; + virtual KFileItem * firstFileItem() const; + virtual KFileItem * nextItem( const KFileItem * ) const; + virtual KFileItem * prevItem( const KFileItem * ) const; + + /** + * Sets the size of the icons to show. Defaults to KIcon::SizeSmall. + */ + void setIconSize( int size ); + + /** + * Sets the size of the previews. Defaults to KIcon::SizeLarge. + */ + void setPreviewSize( int size ); + + /** + * Disables the "Maximum file size" configuration option for previews + * + * Set this before calling showPreviews() + * + * @since 3.4 + **/ + void setIgnoreMaximumSize(bool ignoreSize=true); + + /** + * @returns the current size used for icons. + */ + int iconSize() const { return myIconSize; } + + void ensureItemVisible( const KFileItem * ); + + virtual void setSorting(QDir::SortSpec sort); + + virtual void readConfig( KConfig *, const QString& group = QString::null ); + virtual void writeConfig( KConfig *, const QString& group = QString::null); + + // for KMimeTypeResolver + void mimeTypeDeterminationFinished(); + void determineIcon( KFileIconViewItem *item ); + QScrollView *scrollWidget() const { return (QScrollView*) this; } + void setAcceptDrops(bool b) + { + KIconView::setAcceptDrops(b); + viewport()->setAcceptDrops(b); + } + +public slots: + /** + * Starts loading previews for all files shown and shows them. Switches + * into 'large rows' mode, if that isn't the current mode yet. + * + * @sa setIgnoreMaximumSize + */ + void showPreviews(); + + void zoomIn(); + + void zoomOut(); + + /** + * Reimplemented for performance reasons. + * @since 3.1 + */ + virtual void arrangeItemsInGrid( bool updated = true ); + +protected: + /** + * Reimplemented to not let QIconView eat return-key events + */ + virtual void keyPressEvent( QKeyEvent * ); + + /** + * Reimplemented to remove an eventual tooltip + */ + virtual void hideEvent( QHideEvent * ); + + // ### workaround for Qt3 bug (see #35080) + virtual void showEvent( QShowEvent * ); + + virtual bool eventFilter( QObject *o, QEvent *e ); + + // DND support + virtual QDragObject *dragObject(); + virtual void contentsDragEnterEvent( QDragEnterEvent *e ); + virtual void contentsDragMoveEvent( QDragMoveEvent *e ); + virtual void contentsDragLeaveEvent( QDragLeaveEvent *e ); + virtual void contentsDropEvent( QDropEvent *ev ); + + // KDE4: Make virtual + bool acceptDrag(QDropEvent* e ) const; + +private slots: + void selected( QIconViewItem *item ); + void slotActivate( QIconViewItem * ); + void highlighted( QIconViewItem *item ); + void showToolTip( QIconViewItem *item ); + void removeToolTip(); + void slotActivateMenu( QIconViewItem *, const QPoint& ); + void slotSelectionChanged(); + + void slotSmallColumns(); + void slotLargeRows(); + void slotPreviewsToggled( bool ); + + void slotPreviewResult( KIO::Job * ); + void gotPreview( const KFileItem *item, const QPixmap& pix ); + void slotAutoOpen(); + +signals: + /** + * The user dropped something. + * @p fileItem points to the item dropped on or can be 0 if the + * user dropped on empty space. + * @since 3.2 + */ + void dropped(QDropEvent *event, KFileItem *fileItem); + /** + * The user dropped the URLs @p urls. + * @p url points to the item dropped on or can be empty if the + * user dropped on empty space. + * @since 3.2 + */ + void dropped(QDropEvent *event, const KURL::List &urls, const KURL &url); + +private: + KMimeTypeResolver<KFileIconViewItem,KFileIconView> *m_resolver; + + QLabel *toolTip; + int th; + int myIconSize; + + virtual void insertItem(QIconViewItem *a, QIconViewItem *b) { KIconView::insertItem(a, b); } + virtual void setSelectionMode(QIconView::SelectionMode m) { KIconView::setSelectionMode(m); } + virtual void setSelected(QIconViewItem *i, bool a, bool b) { KIconView::setSelected(i, a, b); } + + bool canPreview( const KFileItem * ) const; + void stopPreview(); + + void updateIcons(); + + inline KFileIconViewItem * viewItem( const KFileItem *item ) const { + if ( item ) + return (KFileIconViewItem *) item->extraData( this ); + return 0L; + } + + void initItem(KFileIconViewItem *item, const KFileItem *i, + bool updateTextAndPixmap ); + +protected: + virtual void virtual_hook( int id, void* data ); +private: + class KFileIconViewPrivate; + KFileIconViewPrivate *d; +}; + +#endif // KFILESIMPLEVIEW_H diff --git a/kio/kfile/kfilemetainfowidget.cpp b/kio/kfile/kfilemetainfowidget.cpp new file mode 100644 index 000000000..ee76c82e2 --- /dev/null +++ b/kio/kfile/kfilemetainfowidget.cpp @@ -0,0 +1,375 @@ +/* This file is part of the KDE libraries + Copyright (C) 2001,2002 Rolf Magnus <[email protected]> + + 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. + + $Id$ + */ + +#include "kfilemetainfowidget.h" + +#include <keditcl.h> +#include <klocale.h> +#include <knuminput.h> +#include <kcombobox.h> +#include <klineedit.h> +#include <kstringvalidator.h> +#include <kdebug.h> + +#include <qlabel.h> +#include <qcheckbox.h> +#include <qspinbox.h> +#include <qdatetimeedit.h> +#include <qpixmap.h> +#include <qimage.h> +#include <qlayout.h> +#include <qvalidator.h> + +/* + Widgets used for different types: + + bool : QCheckBox + int : QSpinBox + QString : KComboBox if the validator is a KStringListValidator, else lineedit + QDateTime : QDateTimeEdit + +*/ + +KFileMetaInfoWidget::KFileMetaInfoWidget(KFileMetaInfoItem item, + QValidator* val, + QWidget* parent, const char* name) + : QWidget(parent, name), + m_value(item.value()), + m_item(item), + m_validator(val) +{ + init(item, ReadWrite); +} + +KFileMetaInfoWidget::KFileMetaInfoWidget(KFileMetaInfoItem item, + Mode mode, + QValidator* val, + QWidget* parent, const char* name) + : QWidget(parent, name), + m_value(item.value()), + m_item(item), + m_validator(val) +{ + init(item, mode); +} + +void KFileMetaInfoWidget::init(KFileMetaInfoItem item, Mode mode) +{ + kdDebug(7033) << "*** item " << m_item.key() + << " is a " << value().typeName() << endl; + + if (m_item.isEditable() && !(mode & ReadOnly)) + m_widget = makeWidget(); + else + switch (m_value.type()) + { + case QVariant::Image : + m_widget = new QLabel(this, "info image"); + static_cast<QLabel*>(m_widget)->setPixmap(QPixmap(m_value.toImage())); + break; + case QVariant::Pixmap : + m_widget = new QLabel(this, "info pixmap"); + static_cast<QLabel*>(m_widget)->setPixmap(m_value.toPixmap()); + break; + default: + m_widget = new QLabel(item.string(true), this, "info label"); + } + + (new QHBoxLayout(this))->addWidget(m_widget); +} + +KFileMetaInfoWidget::~KFileMetaInfoWidget() +{ +} + +QWidget* KFileMetaInfoWidget::makeWidget() +{ + QString valClass; + QWidget* w; + + switch (m_value.type()) + { + case QVariant::Invalid: // no type + // just make a label + w = new QLabel(i18n("<Error>"), this, "label"); + break; + + case QVariant::Int: // an int + case QVariant::UInt: // an unsigned int + w = makeIntWidget(); + break; + + case QVariant::Bool: // a bool + w = makeBoolWidget(); + break; + + case QVariant::Double: // a double + w = makeDoubleWidget(); + break; + + + case QVariant::Date: // a QDate + w = makeDateWidget(); + break; + + case QVariant::Time: // a QTime + w = makeTimeWidget(); + break; + + case QVariant::DateTime: // a QDateTime + w = makeDateTimeWidget(); + break; + +#if 0 + case QVariant::Size: // a QSize + case QVariant::String: // a QString + case QVariant::List: // a QValueList + case QVariant::Map: // a QMap + case QVariant::StringList: // a QStringList + case QVariant::Font: // a QFont + case QVariant::Pixmap: // a QPixmap + case QVariant::Brush: // a QBrush + case QVariant::Rect: // a QRect + case QVariant::Color: // a QColor + case QVariant::Palette: // a QPalette + case QVariant::ColorGroup: // a QColorGroup + case QVariant::IconSet: // a QIconSet + case QVariant::Point: // a QPoint + case QVariant::Image: // a QImage + case QVariant::CString: // a QCString + case QVariant::PointArray: // a QPointArray + case QVariant::Region: // a QRegion + case QVariant::Bitmap: // a QBitmap + case QVariant::Cursor: // a QCursor + case QVariant::ByteArray: // a QByteArray + case QVariant::BitArray: // a QBitArray + case QVariant::SizePolicy: // a QSizePolicy + case QVariant::KeySequence: // a QKeySequence +#endif + default: + w = makeStringWidget(); + } + + kdDebug(7033) << "*** item " << m_item.key() + << "is a " << m_item.value().typeName() << endl; + if (m_validator) + kdDebug(7033) << " and validator is a " << m_validator->className() << endl; + + kdDebug(7033) << "*** created a " << w->className() << " for it\n"; + + return w; +} + +// **************************************************************** +// now the different methods to make the widgets for specific types +// **************************************************************** + +QWidget* KFileMetaInfoWidget::makeBoolWidget() +{ + QCheckBox* cb = new QCheckBox(this, "metainfo bool widget"); + cb->setChecked(m_item.value().toBool()); + connect(cb, SIGNAL(toggled(bool)), this, SLOT(slotChanged(bool))); + return cb; +} + +QWidget* KFileMetaInfoWidget::makeIntWidget() +{ + QSpinBox* sb = new QSpinBox(this, "metainfo integer widget"); + sb->setValue(m_item.value().toInt()); + + if (m_validator) + { + if (m_validator->inherits("QIntValidator")) + { + sb->setMinValue(static_cast<QIntValidator*>(m_validator)->bottom()); + sb->setMaxValue(static_cast<QIntValidator*>(m_validator)->top()); + } + reparentValidator(sb, m_validator); + sb->setValidator(m_validator); + } + + // make sure that an uint cannot be set to a value < 0 + if (m_item.type() == QVariant::UInt) + sb->setMinValue(QMAX(sb->minValue(), 0)); + + connect(sb, SIGNAL(valueChanged(int)), this, SLOT(slotChanged(int))); + return sb; +} + +QWidget* KFileMetaInfoWidget::makeDoubleWidget() +{ + KDoubleNumInput* dni = new KDoubleNumInput(m_item.value().toDouble(), + this, "metainfo double widget"); + + + if (m_validator) + { + if (m_validator->inherits("QDoubleValidator")) + { + dni->setMinValue(static_cast<QDoubleValidator*>(m_validator)->bottom()); + dni->setMaxValue(static_cast<QDoubleValidator*>(m_validator)->top()); + } + reparentValidator(dni, m_validator); + } + + connect(dni, SIGNAL(valueChanged(double)), this, SLOT(slotChanged(double))); + return dni; +} + +QWidget* KFileMetaInfoWidget::makeStringWidget() +{ + if (m_validator && m_validator->inherits("KStringListValidator")) + { + KComboBox* b = new KComboBox(true, this, "metainfo combobox"); + KStringListValidator* val = static_cast<KStringListValidator*> + (m_validator); + b->insertStringList(val->stringList()); + b->setCurrentText(m_item.value().toString()); + connect(b, SIGNAL(activated(const QString &)), this, SLOT(slotComboChanged(const QString &))); + b->setValidator(val); + reparentValidator(b, val); + return b; + } + + if ( m_item.attributes() & KFileMimeTypeInfo::MultiLine ) { + KEdit *edit = new KEdit( this ); + edit->setText( m_item.value().toString() ); + connect( edit, SIGNAL( textChanged() ), + this, SLOT( slotMultiLineEditChanged() )); + // can't use a validator with a QTextEdit, but we may need to delete it + if ( m_validator ) + reparentValidator( edit, m_validator ); + return edit; + } + + KLineEdit* e = new KLineEdit(m_item.value().toString(), this); + if (m_validator) + { + e->setValidator(m_validator); + reparentValidator(e, m_validator); + } + connect(e, SIGNAL(textChanged(const QString&)), + this, SLOT(slotLineEditChanged(const QString&))); + return e; +} + +QWidget* KFileMetaInfoWidget::makeDateWidget() +{ + QWidget *e = new QDateEdit(m_item.value().toDate(), this); + connect(e, SIGNAL(valueChanged(const QDate&)), + this, SLOT(slotDateChanged(const QDate&))); + return e; +} + +QWidget* KFileMetaInfoWidget::makeTimeWidget() +{ + return new QTimeEdit(m_item.value().toTime(), this); +} + +QWidget* KFileMetaInfoWidget::makeDateTimeWidget() +{ + return new QDateTimeEdit(m_item.value().toDateTime(), this); +} + +void KFileMetaInfoWidget::reparentValidator( QWidget *widget, + QValidator *validator ) +{ + if ( !validator->parent() ) + widget->insertChild( validator ); +} + +// **************************************************************** +// now the slots that let us get notified if the value changed in the child +// **************************************************************** + +void KFileMetaInfoWidget::slotChanged(bool value) +{ + Q_ASSERT(m_widget->inherits("QComboBox")); + m_value = QVariant(value); + emit valueChanged(m_value); + m_dirty = true; +} + +void KFileMetaInfoWidget::slotChanged(int value) +{ + Q_ASSERT(m_widget->inherits("QSpinBox")); + m_value = QVariant(value); + emit valueChanged(m_value); + m_dirty = true; +} + +void KFileMetaInfoWidget::slotChanged(double value) +{ + Q_ASSERT(m_widget->inherits("KDoubleNumInput")); + m_value = QVariant(value); + emit valueChanged(m_value); + m_dirty = true; +} + +void KFileMetaInfoWidget::slotComboChanged(const QString &value) +{ + Q_ASSERT(m_widget->inherits("KComboBox")); + m_value = QVariant(value); + emit valueChanged(m_value); + m_dirty = true; +} + +void KFileMetaInfoWidget::slotLineEditChanged(const QString& value) +{ + Q_ASSERT(m_widget->inherits("KLineEdit")); + m_value = QVariant(value); + emit valueChanged(m_value); + m_dirty = true; +} + +// that may be a little expensive for long texts, but what can we do? +void KFileMetaInfoWidget::slotMultiLineEditChanged() +{ + Q_ASSERT(m_widget->inherits("QTextEdit")); + m_value = QVariant( static_cast<const QTextEdit*>( sender() )->text() ); + emit valueChanged(m_value); + m_dirty = true; +} + +void KFileMetaInfoWidget::slotDateChanged(const QDate& value) +{ + Q_ASSERT(m_widget->inherits("QDateEdit")); + m_value = QVariant(value); + emit valueChanged(m_value); + m_dirty = true; +} + +void KFileMetaInfoWidget::slotTimeChanged(const QTime& value) +{ + Q_ASSERT(m_widget->inherits("QTimeEdit")); + m_value = QVariant(value); + emit valueChanged(m_value); + m_dirty = true; +} + +void KFileMetaInfoWidget::slotDateTimeChanged(const QDateTime& value) +{ + Q_ASSERT(m_widget->inherits("QDateTimeEdit")); + m_value = QVariant(value); + emit valueChanged(m_value); + m_dirty = true; +} + +#include "kfilemetainfowidget.moc" diff --git a/kio/kfile/kfilemetainfowidget.h b/kio/kfile/kfilemetainfowidget.h new file mode 100644 index 000000000..4d0070949 --- /dev/null +++ b/kio/kfile/kfilemetainfowidget.h @@ -0,0 +1,95 @@ +/* This file is part of the KDE libraries + Copyright (C) 2001,2002 Rolf Magnus <[email protected]> + + 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 __KFILEMETAINFOWIDGET_H__ +#define __KFILEMETAINFOWIDGET_H__ + +#include <qwidget.h> +#include <qvariant.h> +#include <kfilemetainfo.h> + +/*! + * A widget to display file meta infos (like id3 for mp3) + */ +class KIO_EXPORT KFileMetaInfoWidget: public QWidget +{ + Q_OBJECT +public: + enum Mode + { + ReadOnly = 1, ///Only display the meta infos, and do not permit the user to edit them + ReadWrite = 0, ///Permits user to edit the displayed meta-info + Reserve = 0xff + }; + + KFileMetaInfoWidget(KFileMetaInfoItem item, QValidator* val = 0, + QWidget* parent = 0, const char* name = 0); + + KFileMetaInfoWidget(KFileMetaInfoItem item, Mode mode, QValidator* val = 0, + QWidget* parent = 0, const char* name = 0); + + virtual ~KFileMetaInfoWidget(); + + bool apply() + { + return m_item.isEditable() && m_item.setValue(m_value); + } + + void setValue(const QVariant& value) { m_value = value; } + QVariant value()const { return m_value; } + QValidator* validator() const { return m_validator; } + KFileMetaInfoItem item()const { return m_item; } + +signals: + void valueChanged(const QVariant& value); + +protected: + void reparentValidator(QWidget *widget, QValidator *validator); + virtual QWidget* makeWidget(); + + QWidget* makeBoolWidget(); + QWidget* makeIntWidget(); + QWidget* makeDoubleWidget(); + QWidget* makeStringWidget(); + QWidget* makeDateWidget(); + QWidget* makeTimeWidget(); + QWidget* makeDateTimeWidget(); + +private slots: + void slotChanged(bool value); + void slotChanged(int value); + void slotChanged(double value); + void slotComboChanged(const QString &value); + void slotLineEditChanged(const QString& value); + void slotMultiLineEditChanged(); + void slotDateChanged(const QDate& value); + void slotTimeChanged(const QTime& value); + void slotDateTimeChanged(const QDateTime& value); + +private: + void init(KFileMetaInfoItem item, Mode mode); + + QVariant m_value; // the value will be saved here until apply() is called + KFileMetaInfoItem m_item; + QWidget* m_widget; + QValidator* m_validator; + bool m_dirty : 1; +}; + +#endif diff --git a/kio/kfile/kfilemetapreview.cpp b/kio/kfile/kfilemetapreview.cpp new file mode 100644 index 000000000..4d5ec339b --- /dev/null +++ b/kio/kfile/kfilemetapreview.cpp @@ -0,0 +1,196 @@ +/* + * This file is part of the KDE project. + * Copyright (C) 2003 Carsten Pfeiffer <[email protected]> + * + * You can Freely distribute this program under the GNU Library General Public + * License. See the file "COPYING" for the exact licensing terms. + */ + +#include "kfilemetapreview.h" + +#include <qlayout.h> + +#include <kio/previewjob.h> +#include <klibloader.h> +#include <kimagefilepreview.h> +#include <kmimetype.h> + +bool KFileMetaPreview::s_tryAudioPreview = true; + +KFileMetaPreview::KFileMetaPreview( QWidget *parent, const char *name ) + : KPreviewWidgetBase( parent, name ), + haveAudioPreview( false ) +{ + QHBoxLayout *layout = new QHBoxLayout( this, 0, 0 ); + m_stack = new QWidgetStack( this ); + layout->addWidget( m_stack ); + + // ### +// m_previewProviders.setAutoDelete( true ); + initPreviewProviders(); +} + +KFileMetaPreview::~KFileMetaPreview() +{ +} + +void KFileMetaPreview::initPreviewProviders() +{ + m_previewProviders.clear(); + // hardcoded so far + + // image previews + KImageFilePreview *imagePreview = new KImageFilePreview( m_stack ); + (void) m_stack->addWidget( imagePreview ); + m_stack->raiseWidget( imagePreview ); + resize( imagePreview->sizeHint() ); + + QStringList mimeTypes = imagePreview->supportedMimeTypes(); + QStringList::ConstIterator it = mimeTypes.begin(); + for ( ; it != mimeTypes.end(); ++it ) + { +// qDebug(".... %s", (*it).latin1()); + m_previewProviders.insert( *it, imagePreview ); + } +} + +KPreviewWidgetBase * KFileMetaPreview::previewProviderFor( const QString& mimeType ) +{ +// qDebug("### looking for: %s", mimeType.latin1()); + // often the first highlighted item, where we can be sure, there is no plugin + // (this "folders reflect icons" is a konq-specific thing, right?) + if ( mimeType == "inode/directory" ) + return 0L; + + KPreviewWidgetBase *provider = m_previewProviders.find( mimeType ); + if ( provider ) + return provider; + +//qDebug("#### didn't find anything for: %s", mimeType.latin1()); + + if ( s_tryAudioPreview && + !mimeType.startsWith("text/") && !mimeType.startsWith("image/") ) + { + if ( !haveAudioPreview ) + { + KPreviewWidgetBase *audioPreview = createAudioPreview( m_stack ); + if ( audioPreview ) + { + haveAudioPreview = true; + (void) m_stack->addWidget( audioPreview ); + QStringList mimeTypes = audioPreview->supportedMimeTypes(); + QStringList::ConstIterator it = mimeTypes.begin(); + for ( ; it != mimeTypes.end(); ++it ) + m_previewProviders.insert( *it, audioPreview ); + } + } + } + + // with the new mimetypes from the audio-preview, try again + provider = m_previewProviders.find( mimeType ); + if ( provider ) + return provider; + + // ### mimetype may be image/* for example, try that + int index = mimeType.find( '/' ); + if ( index > 0 ) + { + provider = m_previewProviders.find( mimeType.left( index + 1 ) + "*" ); + if ( provider ) + return provider; + } + + KMimeType::Ptr mimeInfo = KMimeType::mimeType( mimeType ); + if ( mimeInfo ) + { + // check mime type inheritance + QString parentMimeType = mimeInfo->parentMimeType(); + while ( !parentMimeType.isEmpty() ) + { + provider = m_previewProviders.find( parentMimeType ); + if ( provider ) + return provider; + + KMimeType::Ptr parentMimeInfo = KMimeType::mimeType( parentMimeType ); + if ( !parentMimeInfo ) break; + + parentMimeType = parentMimeInfo->parentMimeType(); + } + + // check X-KDE-Text property + QVariant textProperty = mimeInfo->property( "X-KDE-text" ); + if ( textProperty.isValid() && textProperty.type() == QVariant::Bool ) + { + if ( textProperty.toBool() ) + { + provider = m_previewProviders.find( "text/plain" ); + if ( provider ) + return provider; + + provider = m_previewProviders.find( "text/*" ); + if ( provider ) + return provider; + } + } + } + + return 0L; +} + +void KFileMetaPreview::showPreview(const KURL &url) +{ + KMimeType::Ptr mt = KMimeType::findByURL( url ); + KPreviewWidgetBase *provider = previewProviderFor( mt->name() ); + if ( provider ) + { + if ( provider != m_stack->visibleWidget() ) // stop the previous preview + clearPreview(); + + m_stack->setEnabled( true ); + m_stack->raiseWidget( provider ); + provider->showPreview( url ); + } + else + { + clearPreview(); + m_stack->setEnabled( false ); + } +} + +void KFileMetaPreview::clearPreview() +{ + if ( m_stack->visibleWidget() ) + static_cast<KPreviewWidgetBase*>( m_stack->visibleWidget() )->clearPreview(); +} + +void KFileMetaPreview::addPreviewProvider( const QString& mimeType, + KPreviewWidgetBase *provider ) +{ + m_previewProviders.insert( mimeType, provider ); +} + +void KFileMetaPreview::clearPreviewProviders() +{ + QDictIterator<KPreviewWidgetBase> it( m_previewProviders ); + for ( ; it.current(); ++it ) + m_stack->removeWidget( it.current() ); + + m_previewProviders.clear(); +} + +// static +KPreviewWidgetBase * KFileMetaPreview::createAudioPreview( QWidget *parent ) +{ + KLibFactory *factory = KLibLoader::self()->factory( "kfileaudiopreview" ); + if ( !factory ) + { + s_tryAudioPreview = false; + return 0L; + } + + return dynamic_cast<KPreviewWidgetBase*>( factory->create( parent, "kfileaudiopreview" )); +} + +void KFileMetaPreview::virtual_hook( int, void* ) {} + +#include "kfilemetapreview.moc" diff --git a/kio/kfile/kfilemetapreview.h b/kio/kfile/kfilemetapreview.h new file mode 100644 index 000000000..ae0650ea1 --- /dev/null +++ b/kio/kfile/kfilemetapreview.h @@ -0,0 +1,56 @@ +/* + * This file is part of the KDE project. + * Copyright (C) 2003 Carsten Pfeiffer <[email protected]> + * + * You can Freely distribute this program under the GNU Library General Public + * License. See the file "COPYING" for the exact licensing terms. + */ + +#ifndef KFILEMETAPREVIEW_H +#define KFILEMETAPREVIEW_H + +#include <qdict.h> +#include <qwidgetstack.h> + +#include <kpreviewwidgetbase.h> +#include <kurl.h> + +class KIO_EXPORT KFileMetaPreview : public KPreviewWidgetBase +{ + Q_OBJECT + +public: + KFileMetaPreview(QWidget *parent, const char *name = 0); + ~KFileMetaPreview(); + + virtual void addPreviewProvider( const QString& mimeType, + KPreviewWidgetBase *provider ); + virtual void clearPreviewProviders(); + +public slots: + virtual void showPreview(const KURL &url); + virtual void clearPreview(); + +protected: + virtual KPreviewWidgetBase *previewProviderFor( const QString& mimeType ); + +protected: + virtual void virtual_hook( int id, void* data ); + +private: + void initPreviewProviders(); + + QWidgetStack *m_stack; + QDict<KPreviewWidgetBase> m_previewProviders; + bool haveAudioPreview; + + // may return 0L + static KPreviewWidgetBase * createAudioPreview( QWidget *parent ); + static bool s_tryAudioPreview; + +private: + class KFileMetaPreviewPrivate; + KFileMetaPreviewPrivate *d; +}; + +#endif // KFILEMETAPREVIEW_H diff --git a/kio/kfile/kfilepreview.cpp b/kio/kfile/kfilepreview.cpp new file mode 100644 index 000000000..14065c3cb --- /dev/null +++ b/kio/kfile/kfilepreview.cpp @@ -0,0 +1,279 @@ +/* This file is part of the KDE libraries + Copyright (C) 1998 Stephan Kulow <[email protected]> + 1998 Daniel Grana <[email protected]> + 2000 Werner Trobin <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <kaction.h> +#include <kfilepreview.h> +#include <kfilepreview.moc> +#include <klocale.h> + +#include <qlabel.h> + +#include "config-kfile.h" + +KFilePreview::KFilePreview(KFileView *view, QWidget *parent, const char *name) + : QSplitter(parent, name), KFileView() +{ + if ( view ) + init( view ); + else + init( new KFileIconView( (QSplitter*) this, "left" )); +} + + +KFilePreview::KFilePreview(QWidget *parent, const char *name) : + QSplitter(parent, name), KFileView() +{ + init( new KFileIconView((QSplitter*)this, "left") ); +} + +KFilePreview::~KFilePreview() +{ + // Why copy the actions in the first place? --ellis, 13 Jan 02. + //// don't delete the view's actions (inserted into our collection)! + //for ( uint i = 0; i < left->actionCollection()->count(); i++ ) + // actionCollection()->take( left->actionCollection()->action( i )); + + // don't delete the preview, we can reuse it + // (it will get deleted by ~KDirOperator) + if ( preview && preview->parentWidget() == this ) { + preview->reparent(0L, 0, QPoint(0, 0), false); + } +} + +void KFilePreview::init( KFileView *view ) +{ + setViewName( i18n("Preview") ); + + left = 0L; + setFileView( view ); + + preview = new QWidget((QSplitter*)this, "preview"); + QString tmp = i18n("No preview available."); + QLabel *l = new QLabel(tmp, preview); + l->setMinimumSize(l->sizeHint()); + l->move(10, 5); + preview->setMinimumWidth(l->sizeHint().width()+20); + setResizeMode(preview, QSplitter::KeepSize); + + // Why copy the actions? --ellis, 13 Jan 02. + //for ( uint i = 0; i < view->actionCollection()->count(); i++ ) + // actionCollection()->insert( view->actionCollection()->action( i )); +} + +void KFilePreview::setFileView( KFileView *view ) +{ + Q_ASSERT( view ); + + // Why copy the actions? --ellis, 13 Jan 02. + //if ( left ) { // remove any previous actions + // for ( uint i = 0; i < left->actionCollection()->count(); i++ ) + // actionCollection()->take( left->actionCollection()->action( i )); + //} + + delete left; + view->widget()->reparent( this, QPoint(0,0) ); + view->KFileView::setViewMode(All); + view->setParentView(this); + view->setSorting( sorting() ); + left = view; + + connect( left->signaler(), SIGNAL( fileHighlighted(const KFileItem*) ), + SLOT( slotHighlighted( const KFileItem * ))); + + // Why copy the actions? --ellis, 13 Jan 02. + //for ( uint i = 0; i < view->actionCollection()->count(); i++ ) + // actionCollection()->insert( view->actionCollection()->action( i )); +} + +// this url parameter is useless... it's the url of the current directory. +// what for? +void KFilePreview::setPreviewWidget(const QWidget *w, const KURL &) +{ + left->setOnlyDoubleClickSelectsFiles( onlyDoubleClickSelectsFiles() ); + + if (w) { + connect(this, SIGNAL( showPreview(const KURL &) ), + w, SLOT( showPreview(const KURL &) )); + connect( this, SIGNAL( clearPreview() ), + w, SLOT( clearPreview() )); + } + else { + preview->hide(); + return; + } + + delete preview; + preview = const_cast<QWidget*>(w); + preview->reparent((QSplitter*)this, 0, QPoint(0, 0), true); + preview->resize(preview->sizeHint()); + preview->show(); +} + +void KFilePreview::insertItem(KFileItem *item) +{ + KFileView::insertItem( item ); + left->insertItem(item); +} + +void KFilePreview::setSorting( QDir::SortSpec sort ) +{ + left->setSorting( sort ); + KFileView::setSorting( left->sorting() ); +} + +void KFilePreview::clearView() +{ + left->clearView(); + emit clearPreview(); +} + +void KFilePreview::updateView(bool b) +{ + left->updateView(b); + if(preview) + preview->repaint(b); +} + +void KFilePreview::updateView(const KFileItem *i) +{ + left->updateView(i); +} + +void KFilePreview::removeItem(const KFileItem *i) +{ + if ( left->isSelected( i ) ) + emit clearPreview(); + + left->removeItem(i); + KFileView::removeItem( i ); +} + +void KFilePreview::listingCompleted() +{ + left->listingCompleted(); +} + +void KFilePreview::clear() +{ + KFileView::clear(); + left->KFileView::clear(); +} + +void KFilePreview::clearSelection() +{ + left->clearSelection(); + emit clearPreview(); +} + +void KFilePreview::selectAll() +{ + left->selectAll(); +} + +void KFilePreview::invertSelection() +{ + left->invertSelection(); +} + +bool KFilePreview::isSelected( const KFileItem *i ) const +{ + return left->isSelected( i ); +} + +void KFilePreview::setSelectionMode(KFile::SelectionMode sm) { + left->setSelectionMode( sm ); +} + +void KFilePreview::setSelected(const KFileItem *item, bool enable) { + left->setSelected( item, enable ); +} + +void KFilePreview::setCurrentItem( const KFileItem *item ) +{ + left->setCurrentItem( item ); +} + +KFileItem * KFilePreview::currentFileItem() const +{ + return left->currentFileItem(); +} + +void KFilePreview::slotHighlighted(const KFileItem* item) +{ + if ( item ) + emit showPreview( item->url() ); + + else { // item = 0 -> multiselection mode + const KFileItemList *items = selectedItems(); + if ( items->count() == 1 ) + emit showPreview( items->getFirst()->url() ); + else + emit clearPreview(); + } + + // the preview widget appears and takes some space of the left view, + // so we may have to scroll to make the current item visible + left->ensureItemVisible(item); + } + +void KFilePreview::ensureItemVisible(const KFileItem *item) +{ + left->ensureItemVisible(item); +} + +KFileItem * KFilePreview::firstFileItem() const +{ + return left->firstFileItem(); +} + +KFileItem * KFilePreview::nextItem( const KFileItem *item ) const +{ + return left->nextItem( item ); +} + +KFileItem * KFilePreview::prevItem( const KFileItem *item ) const +{ + return left->prevItem( item ); +} + +KActionCollection * KFilePreview::actionCollection() const +{ + if ( left ) + return left->actionCollection(); + else { + kdWarning() << "KFilePreview::actionCollection(): called before setFileView()." << endl; //ellis + return KFileView::actionCollection(); + } +} + +void KFilePreview::readConfig( KConfig *config, const QString& group ) +{ + left->readConfig( config, group ); +} + +void KFilePreview::writeConfig( KConfig *config, const QString& group ) +{ + left->writeConfig( config, group ); +} + +void KFilePreview::virtual_hook( int id, void* data ) +{ KFileView::virtual_hook( id, data ); } + diff --git a/kio/kfile/kfilepreview.h b/kio/kfile/kfilepreview.h new file mode 100644 index 000000000..d6ee487f7 --- /dev/null +++ b/kio/kfile/kfilepreview.h @@ -0,0 +1,122 @@ +/* -*- c++ -*- + This file is part of the KDE libraries + Copyright (C) 1998 Stephan Kulow <[email protected]> + 1998 Daniel Grana <[email protected]> + 2000 Werner Trobin <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _KFILEPREVIEW_H +#define _KFILEPREVIEW_H + +#include <qsplitter.h> +#include <qwidget.h> +#include <qstring.h> + +#include <kurl.h> +#include <kfileitem.h> +#include <kfileiconview.h> +#include <kfiledetailview.h> +#include <kfile.h> + +/*! + * This KFileView is an empbedded preview for some file types. + */ +class KIO_EXPORT KFilePreview : public QSplitter, public KFileView +{ + Q_OBJECT + +public: + KFilePreview(QWidget *parent, const char *name); + KFilePreview(KFileView *view, QWidget *parent, const char *name); + virtual ~KFilePreview(); + + virtual QWidget *widget() { return this; } + virtual void clearView(); + + /** + * Delets the current view and sets the view to the given @p view. + * The view is reparented to have this as parent, if necessary. + */ + void setFileView(KFileView *view); + + /** + * @returns the current fileview + */ + KFileView* fileView() const { return left; } + + virtual void updateView( bool ); + virtual void updateView(const KFileItem*); + virtual void removeItem(const KFileItem*); + virtual void listingCompleted(); + + virtual void setSelectionMode( KFile::SelectionMode sm ); + + virtual void setSelected(const KFileItem *, bool); + virtual bool isSelected( const KFileItem * ) const; + virtual void clearSelection(); + virtual void selectAll(); + virtual void invertSelection(); + + virtual void insertItem(KFileItem *); + virtual void clear(); + + virtual void setCurrentItem( const KFileItem * ); + virtual KFileItem * currentFileItem() const; + virtual KFileItem * firstFileItem() const; + virtual KFileItem * nextItem( const KFileItem * ) const; + virtual KFileItem * prevItem( const KFileItem * ) const; + + virtual void setSorting( QDir::SortSpec sort ); + + virtual void readConfig( KConfig *, const QString& group = QString::null ); + virtual void writeConfig( KConfig *, const QString& group = QString::null); + + /** + * This overrides KFileView::actionCollection() by returning + * the actionCollection() of the KFileView (member left) it contains. + * This means that KFilePreview will never create a KActionCollection + * object of its own. + */ + virtual KActionCollection * actionCollection() const; + + void ensureItemVisible(const KFileItem *); + + void setPreviewWidget(const QWidget *w, const KURL &u); + +protected slots: + virtual void slotHighlighted( const KFileItem * ); + +signals: + void showPreview(const KURL &); + void clearPreview(); + +private: + void init( KFileView *view ); + + KFileView *left; + QWidget *preview; + QString viewname; + +protected: + /** \internal */ + virtual void virtual_hook( int id, void* data ); +private: + class KFilePreviewPrivate; + KFilePreviewPrivate *d; +}; +#endif diff --git a/kio/kfile/kfilesharedlg.cpp b/kio/kfile/kfilesharedlg.cpp new file mode 100644 index 000000000..9be183728 --- /dev/null +++ b/kio/kfile/kfilesharedlg.cpp @@ -0,0 +1,286 @@ +/* This file is part of the KDE project + Copyright (c) 2001 David Faure <[email protected]> + Copyright (c) 2001 Laurent Montel <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kfilesharedlg.h" +#include <qvbox.h> +#include <qlabel.h> +#include <qdir.h> +#include <qradiobutton.h> +#include <qbuttongroup.h> +#include <qlayout.h> +#include <kprocess.h> +#include <kprocio.h> +#include <klocale.h> +#include <kglobalsettings.h> +#include <kstandarddirs.h> +#include <kdebug.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <kio/kfileshare.h> +#include <kseparator.h> +#include <qpushbutton.h> +#include <kapplication.h> +#include <ksimpleconfig.h> +#include <kmessagebox.h> + +class KFileSharePropsPlugin::Private +{ +public: + QVBox *m_vBox; + KProcess *m_configProc; + bool m_bAllShared; + bool m_bAllUnshared; +}; + +KFileSharePropsPlugin::KFileSharePropsPlugin( KPropertiesDialog *_props ) + : KPropsDlgPlugin( _props ) +{ + d = new Private; + d->m_vBox = _props->addVBoxPage( i18n("&Share") ); + d->m_configProc = 0; + properties->setFileSharingPage(d->m_vBox); + 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) { + kdDebug() << "KFileSharePropsPlugin::supports: false because sharemode is advanced" << endl; + return false; + } + + KFileItemListIterator it( items ); + for ( ; it.current(); ++it ) + { + bool isLocal = ( *it )->isLocalFile(); + // We only support local dirs + if ( !(*it)->isDir() || !isLocal ) + return false; + // And sharing the trash doesn't make sense + if ( isLocal && (*it)->url().path( 1 ) == KGlobalSettings::trashPath() ) + 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 m_widget; + m_rbShare = 0L; + m_rbUnShare = 0L; + m_widget = new QWidget( d->m_vBox ); + QVBoxLayout * vbox = new QVBoxLayout( m_widget ); + + switch ( KFileShare::authorization() ) { + case KFileShare::Authorized: + { + // Check if all selected dirs are in $HOME + QString home = QDir::homeDirPath(); + 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; + KFileItemListIterator it( items ); + for ( ; it.current() && ok; ++it ) { + QString path = (*it)->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."), + m_widget ), 0 ); + } + else + { + // Everything ok, show the share/unshare GUI + vbox->setSpacing( KDialog::spacingHint() ); + vbox->setMargin( KDialog::marginHint() ); + + QButtonGroup *rbGroup = new QButtonGroup( m_widget ); + rbGroup->hide(); + m_rbUnShare = new QRadioButton( i18n("Not shared"), m_widget ); + connect( m_rbUnShare, SIGNAL( toggled(bool) ), SIGNAL( changed() ) ); + vbox->addWidget( m_rbUnShare, 0 ); + rbGroup->insert( m_rbUnShare ); + + m_rbShare = new QRadioButton( i18n("Shared"), m_widget ); + connect( m_rbShare, SIGNAL( toggled(bool) ), SIGNAL( changed() ) ); + vbox->addWidget( m_rbShare, 0 ); + rbGroup->insert( m_rbShare ); + + // Activate depending on status + if ( d->m_bAllShared ) + m_rbShare->setChecked(true); + if ( d->m_bAllUnshared ) + 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).") , m_widget ); + label->setAlignment( Qt::AlignAuto | Qt::AlignVCenter | Qt::WordBreak ); + vbox->addWidget( label, 0 ); + + KSeparator* sep=new KSeparator(m_widget); + vbox->addWidget( sep, 0 ); + label = new QLabel( i18n("You can also reconfigure file sharing authorization.") , m_widget ); + label->setAlignment( Qt::AlignAuto | Qt::AlignVCenter | Qt::WordBreak ); + vbox->addWidget( label, 0 ); + m_pbConfig = new QPushButton( i18n("Configure File Sharing..."), m_widget ); + connect( m_pbConfig, SIGNAL( clicked() ), SLOT( slotConfigureFileSharing() ) ); + vbox->addWidget( 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."), + 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."), + m_widget ), 0 ); + } else { + vbox->addWidget( new QLabel( i18n("File sharing is disabled."), + m_widget ), 0 ); + } + QHBoxLayout* hBox = new QHBoxLayout( (QWidget *)0L ); + vbox->addLayout( hBox, 0 ); + m_pbConfig = new QPushButton( i18n("Configure File Sharing..."), m_widget ); + connect( m_pbConfig, SIGNAL( clicked() ), SLOT( slotConfigureFileSharing() ) ); + hBox->addWidget( m_pbConfig, 0, Qt::AlignHCenter ); + vbox->addStretch( 10 ); // align items on top + break; + } + case KFileShare::NotInitialized: + kdWarning() << "KFileShare Authorization still NotInitialized after calling authorization() - impossible" << endl; + break; + } + m_widget->show(); // In case the dialog was shown already. +} + +void KFileSharePropsPlugin::slotConfigureFileSharing() +{ + if (d->m_configProc) return; + + d->m_configProc = new KProcess(this); + (*d->m_configProc) << KStandardDirs::findExe("kdesu") << locate("exe", "kcmshell") << "fileshare"; + if (!d->m_configProc->start( KProcess::NotifyOnExit )) + { + delete d->m_configProc; + d->m_configProc = 0; + return; + } + connect(d->m_configProc, SIGNAL(processExited(KProcess *)), + this, SLOT(slotConfigureFileSharingDone())); + m_pbConfig->setEnabled(false); +} + +void KFileSharePropsPlugin::slotConfigureFileSharingDone() +{ + delete d->m_configProc; + d->m_configProc = 0; + KFileShare::readConfig(); + KFileShare::readShareList(); + init(); +} + +void KFileSharePropsPlugin::applyChanges() +{ + kdDebug() << "KFileSharePropsPlugin::applyChanges" << endl; + if ( m_rbShare && m_rbUnShare ) + { + bool share = m_rbShare->isChecked(); + + if (share && d->m_bAllShared) + return; // Nothing to do + if (!share && d->m_bAllUnshared) + return; // Nothing to do + + KFileItemList items = properties->items(); + KFileItemListIterator it( items ); + bool ok = true; + for ( ; it.current() && ok; ++it ) { + QString path = (*it)->url().path(); + ok = setShared( path, share ); + if (!ok) { + if (share) + KMessageBox::detailedError(properties, + i18n("Sharing folder '%1' failed.").arg(path), + i18n("An error occurred while trying to share folder '%1'. " + "Make sure that the Perl script 'fileshareset' is set suid root.") + .arg(path)); + else + KMessageBox::error(properties, + i18n("Unsharing folder '%1' failed.").arg(path), + i18n("An error occurred while trying to unshare folder '%1'. " + "Make sure that the Perl script 'fileshareset' is set suid root.") + .arg(path)); + + properties->abortApplying(); + break; + } + } + + // Get the change back into our cached info + KFileShare::readShareList(); + } +} + +bool KFileSharePropsPlugin::setShared( const QString& path, bool shared ) +{ + kdDebug() << "KFileSharePropsPlugin::setShared " << path << "," << shared << endl; + return KFileShare::setShared( path, shared ); +} + +QWidget* KFileSharePropsPlugin::page() const +{ + return d->m_vBox; +} + +#include "kfilesharedlg.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/kfilesharedlg.h b/kio/kfile/kfilesharedlg.h new file mode 100644 index 000000000..c95de779b --- /dev/null +++ b/kio/kfile/kfilesharedlg.h @@ -0,0 +1,67 @@ +/* This file is part of the KDE project + Copyright (c) 2001 David Faure <[email protected]> + Copyright (c) 2001 Laurent Montel <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef kfilesharedlg_h +#define kfilesharedlg_h + +#include <kpropertiesdialog.h> +class QVBoxLayout; +class QRadioButton; +class QPushButton; + +/** + * This plugin provides a page to KPropsDlg, showing the "file sharing" options + * @author David Faure <[email protected]> + * @since 3.1 + */ +class KIO_EXPORT KFileSharePropsPlugin : public KPropsDlgPlugin +{ + Q_OBJECT +public: + KFileSharePropsPlugin( KPropertiesDialog *_props ); + virtual ~KFileSharePropsPlugin(); + + /** + * Apply all changes to the file. + * This function is called when the user presses 'Ok'. The last plugin inserted + * is called first. + */ + virtual void applyChanges(); + + static bool supports( const KFileItemList& items ); + + QWidget* page() const; + +protected slots: + void slotConfigureFileSharing(); + void slotConfigureFileSharingDone(); + +private: + void init(); + bool setShared( const QString&path, bool shared ); + + QWidget *m_widget; + QRadioButton *m_rbShare; + QRadioButton *m_rbUnShare; + QPushButton *m_pbConfig; + class Private; + Private *d; +}; + +#endif diff --git a/kio/kfile/kfilespeedbar.cpp b/kio/kfile/kfilespeedbar.cpp new file mode 100644 index 000000000..86ee85f52 --- /dev/null +++ b/kio/kfile/kfilespeedbar.cpp @@ -0,0 +1,99 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002 Carsten Pfeiffer <[email protected]> + + 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, version 2. + + 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 "kfilespeedbar.h" +#include "config-kfile.h" + +#include <qdir.h> + +#include <kconfig.h> +#include <kglobal.h> +#include <kglobalsettings.h> +#include <klocale.h> +#include <kprotocolinfo.h> +#include <kstandarddirs.h> +#include <kurl.h> + +KFileSpeedBar::KFileSpeedBar( QWidget *parent, const char *name ) + : KURLBar( true, parent, name ) +{ + KConfig *config = KGlobal::config(); + KConfigGroupSaver cs( config, ConfigGroup ); + m_initializeSpeedbar = config->readBoolEntry( "Set speedbar defaults", + true ); + setIconSize(KIcon::SizeSmallMedium); + readConfig( KGlobal::config(), "KFileDialog Speedbar" ); + + if ( m_initializeSpeedbar ) + { + KURL u; + u.setPath( KGlobalSettings::desktopPath() ); + insertItem( u, i18n("Desktop"), false ); + +//TODO: win32 + if ((KGlobalSettings::documentPath() != (QDir::homeDirPath()+"/")) && + QDir(KGlobalSettings::documentPath()).exists()) + { + u.setPath( KGlobalSettings::documentPath() ); + insertItem( u, i18n("Documents"), false, "document" ); + } + + u.setPath( QDir::homeDirPath() ); + insertItem( u, i18n("Home Folder"), false, + "folder_home" ); + + u = "media:/"; + if ( KProtocolInfo::isKnownProtocol( u ) ) + insertItem( u, i18n("Storage Media"), false, + KProtocolInfo::icon( "media" ) ); + + u = "remote:/"; + if ( KProtocolInfo::isKnownProtocol( u ) ) + insertItem( u, i18n("Network Folders"), false, + KProtocolInfo::icon( "remote" ) ); + } +} + +KFileSpeedBar::~KFileSpeedBar() +{ +} + +void KFileSpeedBar::save( KConfig *config ) +{ + if ( m_initializeSpeedbar && isModified() ) + { + KConfigGroup conf( config, ConfigGroup ); + // write to kdeglobals + conf.writeEntry( "Set speedbar defaults", false, true, true ); + } + + writeConfig( config, "KFileDialog Speedbar" ); +} + +QSize KFileSpeedBar::sizeHint() const +{ + QSize sizeHint = KURLBar::sizeHint(); + int ems = fontMetrics().width("mmmmmmmmmmmm"); + if (sizeHint.width() < ems) + { + sizeHint.setWidth(ems); + } + return sizeHint; +} + +#include "kfilespeedbar.moc" diff --git a/kio/kfile/kfilespeedbar.h b/kio/kfile/kfilespeedbar.h new file mode 100644 index 000000000..c941a2398 --- /dev/null +++ b/kio/kfile/kfilespeedbar.h @@ -0,0 +1,41 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002 Carsten Pfeiffer <[email protected]> + + 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, version 2. + + 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 KFILESPEEDBAR_H +#define KFILESPEEDBAR_H + +#include <kurlbar.h> + +class KConfig; + +class KIO_EXPORT KFileSpeedBar : public KURLBar +{ + Q_OBJECT +public: + KFileSpeedBar( QWidget *parent = 0, const char *name = 0 ); + ~KFileSpeedBar(); + + virtual void save( KConfig *config ); + virtual QSize sizeHint() const; + +private: + bool m_initializeSpeedbar : 1; + +}; + +#endif // KFILESPEEDBAR_H diff --git a/kio/kfile/kfiletreebranch.cpp b/kio/kfile/kfiletreebranch.cpp new file mode 100644 index 000000000..b37662f89 --- /dev/null +++ b/kio/kfile/kfiletreebranch.cpp @@ -0,0 +1,528 @@ +/* This file is part of the KDEproject + Copyright (C) 2000 David Faure <[email protected]> + 2000 Carsten Pfeiffer <[email protected]> + 2002 Klaas Freitag <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qfile.h> + +#include <kfileitem.h> +#include <kdebug.h> +#include <kde_file.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "kfiletreeviewitem.h" +#include "kfiletreebranch.h" + + +/* --- KFileTreeViewToplevelItem --- */ +KFileTreeBranch::KFileTreeBranch( KFileTreeView *parent, const KURL& url, + const QString& name, + const QPixmap& pix, bool showHidden, + KFileTreeViewItem *branchRoot ) + + : KDirLister( false ), + m_root( branchRoot ), + m_startURL( url ), + m_name ( name ), + m_rootIcon( pix ), + m_openRootIcon( pix ), + m_recurseChildren(true), + m_showExtensions(true) +{ + kdDebug( 250) << "Creating branch for url " << url.prettyURL() << endl; + + /* if non exists, create one */ + if( ! branchRoot ) + { + m_root = new KFileTreeViewItem( parent, + new 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( 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( 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 ); +} + +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. */ + kdDebug( 250) << "Starting to list " << url.prettyURL() << endl; +} + + +KFileTreeViewItem *KFileTreeBranch::parentKFTVItem( KFileItem *item ) +{ + KFileTreeViewItem *parent = 0; + + if( ! item ) 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(); + // kdDebug(250) << "Item's url is " << url.prettyURL() << endl; + KURL dirUrl( url ); + dirUrl.setFileName( QString::null ); + // kdDebug(250) << "Directory url is " << dirUrl.prettyURL() << endl; + + parent = findTVIByURL( dirUrl ); + // kdDebug(250) << "Returning as parent item <" << parent << ">" << endl; + return( parent ); +} + + +void KFileTreeBranch::slotRefreshItems( const KFileItemList& list ) +{ + KFileItemListIterator it( list ); + kdDebug(250) << "Refreshing " << list.count() << " items !" << endl; + KFileItem *currItem; + KFileTreeViewItem *item = 0; + + while ( (currItem = it.current()) != 0 ) + { + item = findTVIByURL(currItem->url()); + if (item) { + item->setPixmap(0, item->fileItem()->pixmap( KIcon::SizeSmall )); + item->setText( 0, item->fileItem()->text()); + } + ++it; + } +} + +void KFileTreeBranch::addItems( const KFileItemList& list ) +{ + KFileItemListIterator it( list ); + kdDebug(250) << "Adding " << list.count() << " items !" << endl; + KFileItem *currItem; + KFileTreeViewItemList treeViewItList; + KFileTreeViewItem *parentItem = 0; + + while ( (currItem = it.current()) != 0 ) + { + parentItem = parentKFTVItem( currItem ); + + + /* Only create a new KFileTreeViewItem if it does not yet exist */ + KFileTreeViewItem *newKFTVI = + static_cast<KFileTreeViewItem *>(currItem->extraData( this )); + + if( ! newKFTVI ) + { + newKFTVI = createTreeViewItem( parentItem, currItem ); + if (!newKFTVI) + { + // TODO: Don't fail if parentItem == 0 + ++it; + continue; + } + 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 */ + { + QString name = currItem->text(); + int mPoint = name.findRev( '.' ); + 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() ) + { + KURL url = currItem->url(); + QString filename = url.directory( false, true ) + 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 */ + kdDebug(250) << "Doing stat on " << filename << endl; + KDE_struct_stat statBuf; + if( KDE_stat( QFile::encodeName( filename ), &statBuf ) == 0 ) + { + int hardLinks = statBuf.st_nlink; /* Count of dirs */ + kdDebug(250) << "stat succeeded, hardlinks: " << hardLinks << endl; + // 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 + { + kdDebug(250) << "Emitting for " << url.prettyURL() << endl; + emit( directoryChildCount( newKFTVI, hardLinks-2)); // parentItem, hardLinks-1 )); + } + } + else + { + kdDebug(250) << "stat of " << filename << " failed !" << endl; + } + } + ++it; + + treeViewItList.append( newKFTVI ); + } + + emit newTreeViewItems( this, treeViewItList ); +} + +KFileTreeViewItem* KFileTreeBranch::createTreeViewItem( KFileTreeViewItem *parent, + KFileItem *fileItem ) +{ + KFileTreeViewItem *tvi = 0; + if( parent && fileItem ) + { + tvi = new KFileTreeViewItem( parent, + fileItem, + this ); + } + else + { + kdDebug(250) << "createTreeViewItem: Have no parent" << endl; + } + return( tvi ); +} + +void KFileTreeBranch::setChildRecurse( bool t ) +{ + m_recurseChildren = t; + if( t == false ) + m_openChildrenURLs.clear(); +} + + +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 ) +{ + if( !it ) return; + kdDebug(250) << "Slot Delete Item hitted for " << it->url().prettyURL() << endl; + + KFileTreeViewItem *kfti = static_cast<KFileTreeViewItem*>(it->extraData(this)); + + if( kfti ) + { + kdDebug( 250 ) << "Child count: " << kfti->childCount() << endl; + if( kfti->childCount() > 0 ) + { + KFileTreeViewItem *child = static_cast<KFileTreeViewItem*>(kfti->firstChild()); + + while( child ) + { + kdDebug(250) << "Calling child to be deleted !" << endl; + KFileTreeViewItem *nextChild = static_cast<KFileTreeViewItem*>(child->nextSibling()); + slotDeleteItem( child->fileItem()); + child = nextChild; + } + } + + kdDebug(250) << "Found corresponding KFileTreeViewItem" << endl; + if( m_lastFoundURL.equals(it->url(), true )) + { + m_lastFoundURL = KURL(); + m_lastFoundItem = 0L; + } + delete( kfti ); + } + else + { + kdDebug(250) << "Error: kfiletreeviewitem: "<< kfti << endl; + } +} + + +void KFileTreeBranch::slotCanceled( const KURL& url ) +{ + // ### anything else to do? + // remove the url from the childrento-recurse-list + m_openChildrenURLs.remove( url); + + // stop animations etc. + KFileTreeViewItem *item = findTVIByURL(url); + if (!item) return; // Uh oh... + emit populateFinished(item); +} + +void KFileTreeBranch::slotDirlisterClear() +{ + kdDebug(250)<< "*** Clear all !" << endl; + /* 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 ) +{ + kdDebug(250)<< "*** Clear for URL !" << url.prettyURL() << endl; + KFileItem *item = findByURL( url ); + if( item ) + { + KFileTreeViewItem *ftvi = + static_cast<KFileTreeViewItem *>(item->extraData( this )); + deleteChildrenOf( ftvi ); + } +} + +void KFileTreeBranch::deleteChildrenOf( QListViewItem *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, true )) + { + m_startURL = newUrl; + } +} + +KFileTreeViewItem* KFileTreeBranch::findTVIByURL( const KURL& url ) +{ + KFileTreeViewItem *resultItem = 0; + + if( m_startURL.equals(url, true) ) + { + kdDebug(250) << "findByURL: Returning root as a parent !" << endl; + resultItem = m_root; + } + else if( m_lastFoundURL.equals( url, true )) + { + kdDebug(250) << "findByURL: Returning from lastFoundURL!" << endl; + resultItem = m_lastFoundItem; + } + else + { + kdDebug(250) << "findByURL: searching by dirlister: " << url.url() << endl; + + KFileItem *it = findByURL( url ); + + if( it ) + { + resultItem = static_cast<KFileTreeViewItem*>(it->extraData(this)); + m_lastFoundItem = resultItem; + m_lastFoundURL = url; + } + } + + return( resultItem ); +} + + +void KFileTreeBranch::slCompleted( const KURL& url ) +{ + kdDebug(250) << "SlotCompleted hit for " << url.prettyURL() << endl; + KFileTreeViewItem *currParent = findTVIByURL( url ); + if( ! currParent ) return; + + kdDebug(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); + + kdDebug(250) << "recurseChildren: " << m_recurseChildren << endl; + kdDebug(250) << "isLocalFile: " << m_startURL.isLocalFile() << endl; + kdDebug(250) << "dirOnlyMode: " << dirOnlyMode() << endl; + + + 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, true ) ) + wantRecurseUrl = true; + } + + KFileTreeViewItem *nextChild = 0; + kdDebug(250) << "Recursing " << url.prettyURL() << "? " << wantRecurseUrl << endl; + + 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<KFileTreeViewItem*> + (static_cast<QListViewItem*>(currParent)->firstChild()); + + if( ! nextChild ) + { + /* This happens if there is no child at all */ + kdDebug( 250 ) << "No children to recuse" << endl; + } + + /* 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.remove(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()) + { + KURL recurseUrl = kfi->url(); + kdDebug(250) << "Starting to recurse NOW " << recurseUrl.prettyURL() << endl; + openURL( recurseUrl, true ); + } + } + nextChild = static_cast<KFileTreeViewItem*>(static_cast<QListViewItem*>(nextChild->nextSibling())); + // kdDebug(250) << "Next child " << m_nextChild << endl; + } + } + } + else + { + kdDebug(250) << "skipping to recurse in complete-slot" << endl; + } +} + +/* This slot is called when a treeviewitem is expanded in the gui */ +bool KFileTreeBranch::populate( const KURL& url, KFileTreeViewItem *currItem ) +{ + bool ret = false; + if( ! currItem ) + return ret; + + kdDebug(250) << "Populating <" << url.prettyURL() << ">" << endl; + + /* Add this url to the list of urls to recurse for children */ + if( m_recurseChildren ) + { + m_openChildrenURLs.append( url ); + kdDebug(250) << "Appending to list " << url.prettyURL() << endl; + } + + if( ! currItem->alreadyListed() ) + { + /* start the lister */ + ret = openURL( url, true ); + } + else + { + kdDebug(250) << "Children already existing in treeview!" << endl; + slCompleted( url ); + ret = true; + } + return ret; +} + +void KFileTreeBranch::virtual_hook( int id, void* data ) +{ KDirLister::virtual_hook( id, data ); } + +#include "kfiletreebranch.moc" + diff --git a/kio/kfile/kfiletreebranch.h b/kio/kfile/kfiletreebranch.h new file mode 100644 index 000000000..97d41d9c3 --- /dev/null +++ b/kio/kfile/kfiletreebranch.h @@ -0,0 +1,242 @@ + +/* This file is part of the KDE project + Copyright (C) 2000 David Faure <[email protected]> + 2000 Carsten Pfeiffer <[email protected]> + 2001 Klaas Freitag <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef kfile_tree_branch_h +#define kfile_tree_branch_h + +#include <qdict.h> +#include <qlistview.h> + +#include <kfileitem.h> +#include <kio/global.h> +#include <kdirlister.h> +#include <kio/job.h> +#include <kfiletreeviewitem.h> + +class KURL; +class KFileTreeView; + + +/** + * This is the branch class of the KFileTreeView, 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 KFileTreeView object. + * + */ + +class KIO_EXPORT KFileTreeBranch : public KDirLister +{ + Q_OBJECT +public: + /** + * constructs a branch for KFileTreeView. 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 KFileTreeViewItem to use as the root of the + * branch, with the default 0 meaning to let KFileTreeBranch create + * it for you. + */ + KFileTreeBranch( KFileTreeView*, const KURL& url, const QString& name, + const QPixmap& pix, bool showHidden = false, + KFileTreeViewItem *branchRoot = 0 ); + + /** + * @returns the root url of the branch. + */ + KURL rootUrl() const { return( m_startURL ); } + + /** + * sets a KFileTreeViewItem 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 KFileTreeViewItem to become the root item. + */ + virtual void setRoot( KFileTreeViewItem *r ){ m_root = r; }; + + /** + * @returns the root item. + */ + KFileTreeViewItem *root( ) { return( m_root );} + + /** + * @returns the name of the branch. + */ + QString name() const { return( m_name ); } + + /** + * sets the name of the branch. + */ + virtual void setName( const QString n ) { m_name = n; }; + + /* + * returns the current root item pixmap set in the constructor. The root + * item pixmap defaults to the icon for directories. + * @see openPixmap() + */ + const QPixmap& pixmap(){ return(m_rootIcon); } + + /* + * 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 KFileTreeView::showFolderOpenPximap weather + * open pixmap are displayed or not. + */ + const QPixmap& openPixmap() { return(m_openRootIcon); } + + /** + * @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 ) + { if( root() ) root()->setOpen( setopen ); } + + /** + * 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() + { return m_recurseChildren; } + +public slots: + /** + * populates a branch. This method must be called after a branch was added + * to a KFileTreeView 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, KFileTreeViewItem* 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 KFileTreeViewItem for the branch + * for new items. + */ + virtual KFileTreeViewItem *createTreeViewItem( KFileTreeViewItem *parent, + KFileItem *fileItem ); + +public: + /** + * find the according KFileTreeViewItem by an url + */ + virtual KFileTreeViewItem *findTVIByURL( const KURL& ); + +signals: + /** + * emitted with the item of a directory which was finished to populate + */ + void populateFinished( KFileTreeViewItem * ); + + /** + * emitted with a list of new or updated KFileTreeViewItem 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 KFileTreeViewItemList& ); + + /** + * emitted with the exact count of children for a directory. + */ + void directoryChildCount( KFileTreeViewItem* item, int count ); + +private slots: + void slotRefreshItems( const KFileItemList& ); + void addItems( const KFileItemList& ); + void slCompleted( const KURL& ); + void slotCanceled( const KURL& ); + void slotListerStarted( const KURL& ); + void slotDeleteItem( KFileItem* ); + void slotDirlisterClear(); + void slotDirlisterClearURL( const KURL& url ); + void slotRedirect( const KURL& oldUrl, const KURL&newUrl ); + +private: + KFileTreeViewItem *parentKFTVItem( KFileItem *item ); + static void deleteChildrenOf( QListViewItem *parent ); + + KFileTreeViewItem *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; + KFileTreeViewItem *m_lastFoundItem; + + bool m_recurseChildren :1; + bool m_showExtensions :1; + +protected: + virtual void virtual_hook( int id, void* data ); +private: + class KFileTreeBranchPrivate; + KFileTreeBranchPrivate *d; +}; + + +/** + * List of KFileTreeBranches + */ +typedef QPtrList<KFileTreeBranch> KFileTreeBranchList; + +/** + * Iterator for KFileTreeBranchLists + */ +typedef QPtrListIterator<KFileTreeBranch> KFileTreeBranchIterator; + +#endif + diff --git a/kio/kfile/kfiletreeview.cpp b/kio/kfile/kfiletreeview.cpp new file mode 100644 index 000000000..542e80d5b --- /dev/null +++ b/kio/kfile/kfiletreeview.cpp @@ -0,0 +1,677 @@ +/* This file is part of the KDEproject + Copyright (C) 2000 David Faure <[email protected]> + 2000 Carsten Pfeiffer <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qapplication.h> +#include <qheader.h> +#include <qtimer.h> +#include <kdebug.h> +#include <kdirnotify_stub.h> +#include <kglobalsettings.h> +#include <kfileitem.h> +#include <kfileview.h> +#include <kmimetype.h> +#include <kstandarddirs.h> +#include <stdlib.h> +#include <assert.h> +#include <kio/job.h> +#include <kio/global.h> +#include <kurldrag.h> +#include <kiconloader.h> + + +#include "kfiletreeview.h" +#include "kfiletreebranch.h" +#include "kfiletreeviewitem.h" + +KFileTreeView::KFileTreeView( QWidget *parent, const char *name ) + : KListView( parent, name ), + m_wantOpenFolderPixmaps( true ), + m_toolTip( this ) +{ + setDragEnabled(true); + setSelectionModeExt( KListView::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( QListViewItem * ) ), + this, SLOT( slotExecuted( QListViewItem * ) ) ); + connect( this, SIGNAL( expanded ( QListViewItem *) ), + this, SLOT( slotExpanded( QListViewItem *) )); + connect( this, SIGNAL( collapsed( QListViewItem *) ), + this, SLOT( slotCollapsed( QListViewItem* ))); + + + /* connections from the konqtree widget */ + connect( this, SIGNAL( selectionChanged() ), + this, SLOT( slotSelectionChanged() ) ); + connect( this, SIGNAL( onItem( QListViewItem * )), + this, SLOT( slotOnItem( QListViewItem * ) ) ); + connect( this, SIGNAL(itemRenamed(QListViewItem*, const QString &, int)), + this, SLOT(slotItemRenamed(QListViewItem*, const QString &, int))); + + + m_bDrag = false; + m_branches.setAutoDelete( true ); + + m_openFolderPixmap = DesktopIcon( "folder_open",KIcon::SizeSmall,KIcon::ActiveState ); +} + +KFileTreeView::~KFileTreeView() +{ + // 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 KFileTreeView::isValidItem( QListViewItem *item) +{ + if (!item) + return false; + QPtrList<QListViewItem> lst; + QListViewItemIterator it( this ); + while ( it.current() ) + { + if ( it.current() == item ) + return true; + ++it; + } + return false; +} + +void KFileTreeView::contentsDragEnterEvent( QDragEnterEvent *ev ) +{ + if ( ! acceptDrag( ev ) ) + { + ev->ignore(); + return; + } + ev->acceptAction(); + m_currentBeforeDropItem = selectedItem(); + + QListViewItem *item = itemAt( contentsToViewport( ev->pos() ) ); + if( item ) + { + m_dropItem = item; + m_autoOpenTimer->start( KFileView::autoOpenDelay() ); + } + else + { + m_dropItem = 0; +} +} + +void KFileTreeView::contentsDragMoveEvent( QDragMoveEvent *e ) +{ + if( ! acceptDrag( e ) ) + { + e->ignore(); + return; + } + e->acceptAction(); + + + QListViewItem *afterme; + QListViewItem *parent; + + findDrop( e->pos(), parent, afterme ); + + // "afterme" is 0 when aiming at a directory itself + QListViewItem *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 KFileTreeView::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 KFileTreeView::contentsDropEvent( QDropEvent *e ) +{ + + m_autoOpenTimer->stop(); + m_dropItem = 0; + + kdDebug(250) << "contentsDropEvent !" << endl; + if( ! acceptDrag( e ) ) { + e->ignore(); + return; + } + + e->acceptAction(); + QListViewItem *afterme; + QListViewItem *parent; + findDrop(e->pos(), parent, afterme); + + //kdDebug(250) << " parent=" << (parent?parent->text(0):QString::null) + // << " afterme=" << (afterme?afterme->text(0):QString::null) << 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; + KURLDrag::decode( e, urls ); + emit dropped( this, e, urls ); + + KURL parentURL; + if( parent ) + parentURL = static_cast<KFileTreeViewItem*>(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 KFileTreeView::acceptDrag(QDropEvent* e ) const +{ + + bool ancestOK= acceptDrops(); + // kdDebug(250) << "Do accept drops: " << ancestOK << endl; + ancestOK = ancestOK && itemsMovable(); + // kdDebug(250) << "acceptDrag: " << ancestOK << endl; + // kdDebug(250) << "canDecode: " << KURLDrag::canDecode(e) << endl; + // kdDebug(250) << "action: " << e->action() << endl; + + /* KListView::acceptDrag(e); */ + /* this is what KListView does: + * acceptDrops() && itemsMovable() && (e->source()==viewport()); + * ask acceptDrops and itemsMovable, but not the third + */ + return ancestOK && KURLDrag::canDecode( e ) && + // Why this test? All DnDs are one of those AFAIK (DF) + ( e->action() == QDropEvent::Copy + || e->action() == QDropEvent::Move + || e->action() == QDropEvent::Link ); +} + + + +QDragObject * KFileTreeView::dragObject() +{ + + KURL::List urls; + const QPtrList<QListViewItem> fileList = selectedItems(); + QPtrListIterator<QListViewItem> it( fileList ); + for ( ; it.current(); ++it ) + { + urls.append( static_cast<KFileTreeViewItem*>(it.current())->url() ); + } + QPoint hotspot; + QPixmap pixmap; + if( urls.count() > 1 ){ + pixmap = DesktopIcon( "kmultiple", 16 ); + } + if( pixmap.isNull() ) + pixmap = currentKFileTreeViewItem()->fileItem()->pixmap( 16 ); + hotspot.setX( pixmap.width() / 2 ); + hotspot.setY( pixmap.height() / 2 ); + QDragObject* dragObject = new KURLDrag( urls, this ); + if( dragObject ) + dragObject->setPixmap( pixmap, hotspot ); + return dragObject; +} + + + +void KFileTreeView::slotCollapsed( QListViewItem *item ) +{ + KFileTreeViewItem *kftvi = static_cast<KFileTreeViewItem*>(item); + kdDebug(250) << "hit slotCollapsed" << endl; + if( kftvi && kftvi->isDir()) + { + item->setPixmap( 0, itemIcon(kftvi)); + } +} + +void KFileTreeView::slotExpanded( QListViewItem *item ) +{ + kdDebug(250) << "slotExpanded here !" << endl; + + if( ! item ) return; + + KFileTreeViewItem *it = static_cast<KFileTreeViewItem*>(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 */ + kdDebug(250 ) << "starting to open " << it->url().prettyURL() << endl; + startAnimation( it ); + bool branchAnswer = branch->populate( it->url(), it ); + kdDebug(250) << "Branches answer: " << branchAnswer << endl; + if( ! branchAnswer ) + { + kdDebug(250) << "ERR: Could not populate!" << endl; + stopAnimation( it ); + } + } + + /* set a pixmap 'open folder' */ + if( it->isDir() && isOpen( item ) ) + { + kdDebug(250)<< "Setting open Pixmap" << endl; + item->setPixmap( 0, itemIcon( it )); // 0, m_openFolderPixmap ); + } +} + + + +void KFileTreeView::slotExecuted( QListViewItem *item ) +{ + if ( !item ) + return; + /* This opens the dir and causes the Expanded-slot to be called, + * which strolls through the children. + */ + if( static_cast<KFileTreeViewItem*>(item)->isDir()) + { + item->setOpen( !item->isOpen() ); + } +} + + +void KFileTreeView::slotAutoOpenFolder() +{ + m_autoOpenTimer->stop(); + + if ( !isValidItem(m_dropItem) || m_dropItem->isOpen() ) + return; + + m_dropItem->setOpen( true ); + m_dropItem->repaint(); +} + + +void KFileTreeView::slotSelectionChanged() +{ + if ( !m_dropItem ) // don't do this while the dragmove thing + { + } +} + + +KFileTreeBranch* KFileTreeView::addBranch( const KURL &path, const QString& name, + bool showHidden ) +{ + const QPixmap& folderPix = KMimeType::mimeType("inode/directory")->pixmap( KIcon::Desktop,KIcon::SizeSmall ); + + return addBranch( path, name, folderPix, showHidden); +} + +KFileTreeBranch* KFileTreeView::addBranch( const KURL &path, const QString& name, + const QPixmap& pix, bool showHidden ) +{ + kdDebug(250) << "adding another root " << path.prettyURL() << endl; + + /* Open a new branch */ + KFileTreeBranch *newBranch = new KFileTreeBranch( this, path, name, pix, + showHidden ); + return addBranch(newBranch); +} + +KFileTreeBranch *KFileTreeView::addBranch(KFileTreeBranch *newBranch) +{ + connect( newBranch, SIGNAL(populateFinished( KFileTreeViewItem* )), + this, SLOT( slotPopulateFinished( KFileTreeViewItem* ))); + + connect( newBranch, SIGNAL( newTreeViewItems( KFileTreeBranch*, + const KFileTreeViewItemList& )), + this, SLOT( slotNewTreeViewItems( KFileTreeBranch*, + const KFileTreeViewItemList& ))); + + m_branches.append( newBranch ); + return( newBranch ); +} + +KFileTreeBranch *KFileTreeView::branch( const QString& searchName ) +{ + KFileTreeBranch *branch = 0; + QPtrListIterator<KFileTreeBranch> it( m_branches ); + + while ( (branch = it.current()) != 0 ) { + ++it; + QString bname = branch->name(); + kdDebug(250) << "This is the branches name: " << bname << endl; + if( bname == searchName ) + { + kdDebug(250) << "Found branch " << bname << " and return ptr" << endl; + return( branch ); + } + } + return ( 0L ); +} + +KFileTreeBranchList& KFileTreeView::branches() +{ + return( m_branches ); +} + + +bool KFileTreeView::removeBranch( KFileTreeBranch *branch ) +{ + if(m_branches.contains(branch)) + { + delete (branch->root()); + m_branches.remove( branch ); + return true; + } + else + { + return false; + } +} + +void KFileTreeView::setDirOnlyMode( KFileTreeBranch* branch, bool bom ) +{ + if( branch ) + { + branch->setDirOnlyMode( bom ); + } +} + + +void KFileTreeView::slotPopulateFinished( KFileTreeViewItem *it ) +{ + if( it && it->isDir()) + stopAnimation( it ); +} + +void KFileTreeView::slotNewTreeViewItems( KFileTreeBranch* branch, const KFileTreeViewItemList& itemList ) +{ + if( ! branch ) return; + kdDebug(250) << "hitting slotNewTreeViewItems" << endl; + + /* 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() ) + { + KFileTreeViewItemListIterator it( itemList ); + + bool end = false; + for( ; !end && it.current(); ++it ) + { + KURL url = (*it)->url(); + + if( m_nextUrlToSelect.equals(url, true )) // ignore trailing / on dirs + { + setCurrentItem( static_cast<QListViewItem*>(*it) ); + m_nextUrlToSelect = KURL(); + end = true; + } + } + } +} + +QPixmap KFileTreeView::itemIcon( KFileTreeViewItem *item, int gap ) const +{ + QPixmap pix; + kdDebug(250) << "Setting icon for column " << gap << endl; + + 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( KIcon::SizeSmall ); // , KIcon::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<QListViewItem*>(item))) + pix = m_openFolderPixmap; + } + } + } + + return pix; +} + + +void KFileTreeView::slotAnimation() +{ + MapCurrentOpeningFolders::Iterator it = m_mapCurrentOpeningFolders.begin(); + MapCurrentOpeningFolders::Iterator end = m_mapCurrentOpeningFolders.end(); + for (; it != end;) + { + KFileTreeViewItem *item = it.key(); + if (!isValidItem(item)) + { + ++it; + m_mapCurrentOpeningFolders.remove(item); + continue; + } + + uint & iconNumber = it.data().iconNumber; + QString icon = QString::fromLatin1( it.data().iconBaseName ).append( QString::number( iconNumber ) ); + // kdDebug(250) << "Loading icon " << icon << endl; + item->setPixmap( 0, DesktopIcon( icon,KIcon::SizeSmall,KIcon::ActiveState )); // KFileTreeViewFactory::instance() ) ); + + iconNumber++; + if ( iconNumber > it.data().iconCount ) + iconNumber = 1; + + ++it; + } +} + + +void KFileTreeView::startAnimation( KFileTreeViewItem * item, const char * iconBaseName, uint iconCount ) +{ + /* TODO: allow specific icons */ + if( ! item ) + { + kdDebug(250) << " startAnimation Got called without valid item !" << endl; + return; + } + + m_mapCurrentOpeningFolders.insert( item, + AnimationInfo( iconBaseName, + iconCount, + itemIcon(item, 0) ) ); + if ( !m_animationTimer->isActive() ) + m_animationTimer->start( 50 ); +} + +void KFileTreeView::stopAnimation( KFileTreeViewItem * item ) +{ + if( ! item ) return; + + kdDebug(250) << "Stoping Animation !" << endl; + + MapCurrentOpeningFolders::Iterator it = m_mapCurrentOpeningFolders.find(item); + if ( it != m_mapCurrentOpeningFolders.end() ) + { + if( item->isDir() && isOpen( item) ) + { + kdDebug(250) << "Setting folder open pixmap !" << endl; + item->setPixmap( 0, itemIcon( item )); + } + else + { + item->setPixmap( 0, it.data().originalPixmap ); + } + m_mapCurrentOpeningFolders.remove( item ); + } + else + { + if( item ) + kdDebug(250)<< "StopAnimation - could not find item " << item->url().prettyURL()<< endl; + else + kdDebug(250)<< "StopAnimation - item is zero !" << endl; + } + if (m_mapCurrentOpeningFolders.isEmpty()) + m_animationTimer->stop(); +} + +KFileTreeViewItem * KFileTreeView::currentKFileTreeViewItem() const +{ + return static_cast<KFileTreeViewItem *>( selectedItem() ); +} + +KURL KFileTreeView::currentURL() const +{ + KFileTreeViewItem *item = currentKFileTreeViewItem(); + if ( item ) + return currentKFileTreeViewItem()->url(); + else + return KURL(); +} + +void KFileTreeView::slotOnItem( QListViewItem *item ) +{ + KFileTreeViewItem *i = static_cast<KFileTreeViewItem *>( item ); + if( i ) + { + const KURL url = i->url(); + if ( url.isLocalFile() ) + emit onItem( url.path() ); + else + emit onItem( url.prettyURL() ); + } +} + +void KFileTreeView::slotItemRenamed(QListViewItem* item, const QString &name, int col) +{ + (void) item; + kdDebug(250) << "Do not bother: " << name << col << endl; +} + +KFileTreeViewItem *KFileTreeView::findItem( const QString& branchName, const QString& relUrl ) +{ + KFileTreeBranch *br = branch( branchName ); + return( findItem( br, relUrl )); +} + +KFileTreeViewItem *KFileTreeView::findItem( KFileTreeBranch* brnch, const QString& relUrl ) +{ + KFileTreeViewItem *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 ); + + kdDebug(250) << "assembled complete dir string " << url.prettyURL() << endl; + + KFileItem *fi = brnch->findByURL( url ); + if( fi ) + { + ret = static_cast<KFileTreeViewItem*>( fi->extraData( brnch )); + kdDebug(250) << "Found item !" <<ret << endl; + } + } + else + { + ret = brnch->root(); + } + } + return( ret ); +} + +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// + + +void KFileTreeViewToolTip::maybeTip( const QPoint & ) +{ +#if 0 + QListViewItem *item = m_view->itemAt( point ); + if ( item ) { + QString text = static_cast<KFileViewItem*>( item )->toolTipText(); + if ( !text.isEmpty() ) + tip ( m_view->itemRect( item ), text ); + } +#endif +} + +void KFileTreeView::virtual_hook( int id, void* data ) +{ KListView::virtual_hook( id, data ); } + +#include "kfiletreeview.moc" diff --git a/kio/kfile/kfiletreeview.h b/kio/kfile/kfiletreeview.h new file mode 100644 index 000000000..93af623a6 --- /dev/null +++ b/kio/kfile/kfiletreeview.h @@ -0,0 +1,273 @@ +/* This file is part of the KDE project + Copyright (C) 2000 David Faure <[email protected]> + 2000 Carsten Pfeiffer <[email protected]> + 2002 Klaas Freitag <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef kfile_tree_view_h +#define kfile_tree_view_h + +#include <qmap.h> +#include <qpoint.h> +#include <qpixmap.h> +#include <qstrlist.h> +#include <qtooltip.h> + +#include <klistview.h> +#include <kdirnotify.h> +#include <kio/job.h> +#include <kfiletreeviewitem.h> +#include <kfiletreebranch.h> + +class QTimer; + + + +class KIO_EXPORT KFileTreeViewToolTip : public QToolTip +{ +public: + KFileTreeViewToolTip( QListView *view ) : QToolTip( view ), m_view( view ) {} + +protected: + virtual void maybeTip( const QPoint & ); + +private: + QListView *m_view; +}; + + +/** + * The filetreeview offers a treeview on the file system which behaves like + * a QTreeView showing files and/or directories in the file system. + * + * KFileTreeView is able to handle more than one URL, represented by + * KFileTreeBranch. + * + * Typical usage: + * 1. create a KFileTreeView fitting in your layout and add columns to it + * 2. call addBranch to create one or more branches + * 3. retrieve the root item with KFileTreeBranch::root() and set it open + * if desired. That starts the listing. + */ +class KIO_EXPORT KFileTreeView : public KListView +{ + Q_OBJECT +public: + KFileTreeView( QWidget *parent, const char *name = 0 ); + virtual ~KFileTreeView(); + + /** + * @return the current (i.e. selected) item + */ + KFileTreeViewItem * currentKFileTreeViewItem() const; + + /** + * @return the URL of the current selected item. + */ + KURL currentURL() const; + + /** + * Adds a branch to the treeview item. + * + * This high-level function creates the branch, adds it to the treeview and + * connects some signals. Note that directory listing does not start until + * a branch is expanded either by opening the root item by user or by setOpen + * on the root item. + * + * @returns a pointer to the new branch or zero + * @param path is the base url of the branch + * @param name is the name of the branch, which will be the text for column 0 + * @param showHidden says if hidden files and directories should be visible + */ + KFileTreeBranch* addBranch( const KURL &path, const QString& name, bool showHidden = false ); + + /** + * same as the function above but with a pixmap to set for the branch. + */ + virtual KFileTreeBranch* addBranch( const KURL &path, const QString& name , + const QPixmap& pix, bool showHidden = false ); + + /** + * same as the function above but letting the user create the branch. + */ + virtual KFileTreeBranch* addBranch( KFileTreeBranch * ); + + /** + * removes the branch from the treeview. + * @param branch is a pointer to the branch + * @returns true on success. + */ + virtual bool removeBranch( KFileTreeBranch *branch ); + + /** + * @returns a pointer to the KFileTreeBranch in the KFileTreeView or zero on failure. + * @param searchName is the name of a branch + */ + KFileTreeBranch *branch( const QString& searchName ); + + + /** + * @returns a list of pointers to all existing branches in the treeview. + **/ + KFileTreeBranchList& branches(); + + /** + * set the directory mode for branches. If true is passed, only directories will be loaded. + * @param branch is a pointer to a KFileTreeBranch + */ + virtual void setDirOnlyMode( KFileTreeBranch *branch, bool ); + + /** + * searches a branch for a KFileTreeViewItem identified by the relative url given as + * second parameter. The method adds the branches base url to the relative path and finds + * the item. + * @returns a pointer to the item or zero if the item does not exist. + * @param brnch is a pointer to the branch to search in + * @param relUrl is the branch relativ url + */ + KFileTreeViewItem *findItem( KFileTreeBranch* brnch, const QString& relUrl ); + + /** + * see method above, differs only in the first parameter. Finds the branch by its name. + */ + KFileTreeViewItem *findItem( const QString& branchName, const QString& relUrl ); + + /** + * @returns a flag indicating if extended folder pixmaps are displayed or not. + */ + bool showFolderOpenPixmap() const { return m_wantOpenFolderPixmaps; }; + +public slots: + + /** + * set the flag to show 'extended' folder icons on or off. If switched on, folders will + * have an open folder pixmap displayed if their children are visible, and the standard + * closed folder pixmap (from mimetype folder) if they are closed. + * If switched off, the plain mime pixmap is displayed. + * @param showIt = false displays mime type pixmap only + */ + virtual void setShowFolderOpenPixmap( bool showIt = true ) + { m_wantOpenFolderPixmaps = showIt; } + +protected: + /** + * @returns true if we can decode the drag and support the action + */ + + virtual bool acceptDrag(QDropEvent* event) const; + virtual QDragObject * dragObject(); + + virtual void startAnimation( KFileTreeViewItem* item, const char * iconBaseName = "kde", uint iconCount = 6 ); + virtual void stopAnimation( KFileTreeViewItem* item ); + virtual void contentsDragEnterEvent( QDragEnterEvent *e ); + virtual void contentsDragMoveEvent( QDragMoveEvent *e ); + virtual void contentsDragLeaveEvent( QDragLeaveEvent *e ); + virtual void contentsDropEvent( QDropEvent *ev ); + +protected slots: + virtual void slotNewTreeViewItems( KFileTreeBranch*, + const KFileTreeViewItemList& ); + + virtual void slotSetNextUrlToSelect( const KURL &url ) + { m_nextUrlToSelect = url; } + + virtual QPixmap itemIcon( KFileTreeViewItem*, int gap = 0 ) const; + +private slots: + void slotExecuted( QListViewItem * ); + void slotExpanded( QListViewItem * ); + void slotCollapsed( QListViewItem *item ); + + void slotSelectionChanged(); + + void slotAnimation(); + + void slotAutoOpenFolder(); + + void slotOnItem( QListViewItem * ); + void slotItemRenamed(QListViewItem*, const QString &, int); + + void slotPopulateFinished( KFileTreeViewItem* ); + + +signals: + + void onItem( const QString& ); + /* New signals if you like it ? */ + void dropped( QWidget*, QDropEvent* ); + void dropped( QWidget*, QDropEvent*, KURL::List& ); + void dropped( KURL::List&, KURL& ); + // The drop event allows to differentiate between move and copy + void dropped( QWidget*, QDropEvent*, KURL::List&, KURL& ); + + void dropped( QDropEvent *e, QListViewItem * after); + void dropped(KFileTreeView *, QDropEvent *, QListViewItem *); + void dropped(QDropEvent *e, QListViewItem * parent, QListViewItem * after); + void dropped(KFileTreeView *, QDropEvent *, QListViewItem *, QListViewItem *); + +protected: + KURL m_nextUrlToSelect; + + +private: + // Returns whether item is still a valid item in the tree + bool isValidItem( QListViewItem *item); + void clearTree(); + + + /* List that holds the branches */ + KFileTreeBranchList m_branches; + + + struct AnimationInfo + { + AnimationInfo( const char * _iconBaseName, uint _iconCount, const QPixmap & _originalPixmap ) + : iconBaseName(_iconBaseName), iconCount(_iconCount), iconNumber(1), originalPixmap(_originalPixmap) {} + AnimationInfo() : iconCount(0) {} + QCString iconBaseName; + uint iconCount; + uint iconNumber; + QPixmap originalPixmap; + }; + typedef QMap<KFileTreeViewItem *, AnimationInfo> MapCurrentOpeningFolders; + MapCurrentOpeningFolders m_mapCurrentOpeningFolders; + + + QTimer *m_animationTimer; + + QPoint m_dragPos; + bool m_bDrag; + + bool m_wantOpenFolderPixmaps; // Flag weather the folder should have open-folder pixmaps + + QListViewItem *m_currentBeforeDropItem; // The item that was current before the drag-enter event happened + QListViewItem *m_dropItem; // The item we are moving the mouse over (during a drag) + QStrList m_lstDropFormats; + QPixmap m_openFolderPixmap; + QTimer *m_autoOpenTimer; + + KFileTreeViewToolTip m_toolTip; + + +protected: + virtual void virtual_hook( int id, void* data ); +private: + class KFileTreeViewPrivate; + KFileTreeViewPrivate *d; +}; + +#endif diff --git a/kio/kfile/kfiletreeviewitem.cpp b/kio/kfile/kfiletreeviewitem.cpp new file mode 100644 index 000000000..96c100521 --- /dev/null +++ b/kio/kfile/kfiletreeviewitem.cpp @@ -0,0 +1,83 @@ +/* This file is part of the KDEproject + Copyright (C) 2000 David Faure <[email protected]> + 2000 Carsten Pfeiffer <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <kdebug.h> +#include <kfileitem.h> +#include <kicontheme.h> + +#include "kfiletreeviewitem.h" + +/* --- KFileTreeViewItem --- */ +/* + */ +KFileTreeViewItem::KFileTreeViewItem( KFileTreeViewItem *parent, + KFileItem* item, + KFileTreeBranch *brnch ) + : KListViewItem( parent ), + m_kfileitem( item ), + m_branch( brnch ), + m_wasListed(false) +{ + setPixmap(0, item->pixmap( KIcon::SizeSmall )); + setText( 0, item->text()); + +} + +KFileTreeViewItem::KFileTreeViewItem( KFileTreeView* parent, + KFileItem* item, + KFileTreeBranch *brnch ) + :KListViewItem( (QListView*)parent ), + m_kfileitem(item ), + m_branch( brnch ), + m_wasListed(false) +{ + setPixmap(0, item->pixmap( KIcon::SizeSmall )); + setText( 0, item->text()); +} + +KFileTreeViewItem::~KFileTreeViewItem() +{ + if ( m_kfileitem ) + m_kfileitem->removeExtraData( m_branch ); +} + +bool KFileTreeViewItem::alreadyListed() const +{ + return m_wasListed; +} + +void KFileTreeViewItem::setListed( bool wasListed ) +{ + m_wasListed = wasListed; +} + +KURL KFileTreeViewItem::url() const +{ + return m_kfileitem ? m_kfileitem->url() : KURL(); +} + +QString KFileTreeViewItem::path() const +{ + return m_kfileitem ? m_kfileitem->url().path() : QString::null; +} + +bool KFileTreeViewItem::isDir() const +{ + return m_kfileitem ? m_kfileitem->isDir() : false; +} diff --git a/kio/kfile/kfiletreeviewitem.h b/kio/kfile/kfiletreeviewitem.h new file mode 100644 index 000000000..69e4a2d90 --- /dev/null +++ b/kio/kfile/kfiletreeviewitem.h @@ -0,0 +1,106 @@ +/* This file is part of the KDE project + Copyright (C) 2000 David Faure <[email protected]> + 2000 Carsten Pfeiffer <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef kfile_tree_view_item_h +#define kfile_tree_view_item_h + +#include <qptrlist.h> +#include <klistview.h> + +#include <kfileitem.h> +#include <kio/global.h> +#include <kdirlister.h> +#include <kio/job.h> + +class KURL; +class KFileTreeView; +class KFileTreeBranch; +class KFileTreeItem; + + +/** + * An item for a KFileTreeView that knows about its own KFileItem. + */ +class KIO_EXPORT KFileTreeViewItem : public KListViewItem +{ +public: + KFileTreeViewItem( KFileTreeViewItem*, KFileItem*, KFileTreeBranch * ); + KFileTreeViewItem( KFileTreeView*, KFileItem*, KFileTreeBranch * ); + ~KFileTreeViewItem(); + + /** + * @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; } + + /** + * @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; + KFileTreeBranch *m_branch; + bool m_wasListed; + class KFileTreeViewItemPrivate; + KFileTreeViewItemPrivate *d; +}; + + +/** + * List of KFileTreeViewItems + */ +typedef QPtrList<KFileTreeViewItem> KFileTreeViewItemList; + +/** + * Iterator for KFileTreeViewItemList + */ +typedef QPtrListIterator<KFileTreeViewItem> KFileTreeViewItemListIterator; + + +#endif + diff --git a/kio/kfile/kfileview.cpp b/kio/kfile/kfileview.cpp new file mode 100644 index 000000000..976d3b815 --- /dev/null +++ b/kio/kfile/kfileview.cpp @@ -0,0 +1,429 @@ +/* This file is part of the KDE libraries + Copyright (C) 1998 Stephan Kulow <[email protected]> + 1998 Daniel Grana <[email protected]> + 2001 Carsten Pfeiffer <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <assert.h> +#include <stdlib.h> + +#include <kaction.h> +#include <kapplication.h> +#include <kdebug.h> +#include <kglobal.h> +#include <klocale.h> +#include <kstandarddirs.h> + +#include "config-kfile.h" +#include "kfileview.h" + +#ifdef Unsorted // the "I hate X.h" modus +#undef Unsorted +#endif + +QDir::SortSpec KFileView::defaultSortSpec = static_cast<QDir::SortSpec>(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; + } + } + + QGuardedPtr<KActionCollection> actions; + int dropOptions; +}; + + +KFileView::KFileView() +{ + d = new KFileViewPrivate(); + m_sorting = KFileView::defaultSortSpec; + + sig = new KFileViewSignaler(); + sig->setName("view-signaller"); + + m_selectedList = 0L; + filesNumber = 0; + dirsNumber = 0; + + view_mode = All; + selection_mode = KFile::Single; + m_viewName = i18n("Unknown View"); + + myOnlyDoubleClickSelectsFiles = false; + m_itemList.setAutoDelete( false ); // just references +} + +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 *, + const QPoint& ) ), + 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( sortingChanged( QDir::SortSpec ) ), + parent->sig, SIGNAL(sortingChanged( QDir::SortSpec))); + 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) +{ + if (!( viewMode() & Files ) && i->isFile()) + return false; + + if (!( viewMode() & Directories ) && i->isDir()) + return false; + + if (i->isDir()) + dirsNumber++; + else + filesNumber++; + + return true; +} + +void qt_qstring_stats(); + +// filter out files if we're in directory mode and count files/directories +// and insert into the view +void KFileView::addItemList(const KFileItemList& list) +{ + KFileItem *tmp; + + for (KFileItemListIterator it(list); (tmp = it.current()); ++it) + { + if (!updateNumbers(tmp)) + continue; + + insertItem( tmp ); + } + +#ifdef Q2HELPER + qt_qstring_stats(); +#endif +} + +void KFileView::insertItem( KFileItem * ) +{ +} + +void KFileView::setSorting(QDir::SortSpec new_sort) +{ + m_sorting = new_sort; +} + +void KFileView::clear() +{ + m_itemList.clear(); + filesNumber = 0; + dirsNumber = 0; + clearView(); +} + +void KFileView::sortReversed() +{ + int spec = sorting(); + + setSorting( static_cast<QDir::SortSpec>( spec ^ QDir::Reversed ) ); +} + +#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::SortSpec sort = static_cast<QDir::SortSpec>(m_sorting & QDir::SortByMask); + + //if (fi1->isDir() || fi2->isDir()) + // sort = static_cast<QDir::SortSpec>(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::UDS_MODIFICATION_TIME ); + time_t t2 = fi2->time( KIO::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 f) +{ + widget()->repaint(f); +} + +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) { + setCurrentItem( item ); + return; + } + } + } + + kdDebug(kfile_area) << "setCurrentItem: no match found: " << filename << endl; +} + +const KFileItemList * KFileView::items() const +{ + KFileItem *item = 0L; + + // only ever use m_itemList in this method! + m_itemList.clear(); + for ( (item = firstFileItem()); item; item = nextItem( item ) ) + m_itemList.append( item ); + + return &m_itemList; +} + + +const KFileItemList * KFileView::selectedItems() const +{ + if ( !m_selectedList ) + m_selectedList = new KFileItemList; + + m_selectedList->clear(); + + KFileItem *item; + for ( (item = firstFileItem()); item; item = nextItem( item ) ) { + if ( isSelected( item ) ) + m_selectedList->append( item ); + } + + return m_selectedList; +} + +void KFileView::selectAll() +{ + if (selection_mode == KFile::NoSelection || selection_mode== KFile::Single) + return; + + KFileItem *item = 0L; + for ( (item = firstFileItem()); item; item = nextItem( item ) ) + setSelected( item, true ); +} + + +void KFileView::invertSelection() +{ + KFileItem *item = 0L; + for ( (item = firstFileItem()); item; 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( ViewMode vm ) +{ + view_mode = vm; +} + +void KFileView::removeItem( const KFileItem *item ) +{ + if ( !item ) + return; + + if ( item->isDir() ) + dirsNumber--; + else + filesNumber--; + + if ( m_selectedList ) + m_selectedList->removeRef( item ); +} + +void KFileView::listingCompleted() +{ + // empty default impl. +} + +KActionCollection * KFileView::actionCollection() const +{ + if ( !d->actions ) + d->actions = new KActionCollection( widget(), "KFileView::d->actions" ); + return d->actions; +} + +void KFileView::readConfig( KConfig *, const QString& ) +{ +} + +void KFileView::writeConfig( KConfig *, const QString& ) +{ +} + +QString KFileView::sortingKey( const QString& value, bool isDir, int sortSpec ) +{ + bool reverse = sortSpec & QDir::Reversed; + bool dirsFirst = sortSpec & QDir::DirsFirst; + char start = (isDir && dirsFirst) ? (reverse ? '2' : '0') : '1'; + QString result = (sortSpec & QDir::IgnoreCase) ? value.lower() : value; + return result.prepend( start ); +} + +QString KFileView::sortingKey( KIO::filesize_t value, bool isDir, int sortSpec) +{ + bool reverse = sortSpec & QDir::Reversed; + bool dirsFirst = sortSpec & QDir::DirsFirst; + char start = (isDir && dirsFirst) ? (reverse ? '2' : '0') : '1'; + return KIO::number( value ).rightJustify( 24, '0' ).prepend( start ); +} + +void KFileView::setDropOptions(int options) +{ + virtual_hook(VIRTUAL_SET_DROP_OPTIONS, &options); // Virtual call +} + +void KFileView::setDropOptions_impl(int options) +{ + d->dropOptions = options; +} + +int KFileView::dropOptions() +{ + return d->dropOptions; +} + +int KFileView::autoOpenDelay() +{ + return (QApplication::startDragTime() * 3) / 2; +} + +void KFileView::virtual_hook( int id, void* data) +{ + switch(id) { + case VIRTUAL_SET_DROP_OPTIONS: + setDropOptions_impl(*(int *)data); + break; + default: + /*BASE::virtual_hook( id, data );*/ + break; + } +} + +#include "kfileview.moc" diff --git a/kio/kfile/kfileview.h b/kio/kfile/kfileview.h new file mode 100644 index 000000000..0b2002e3b --- /dev/null +++ b/kio/kfile/kfileview.h @@ -0,0 +1,444 @@ +// -*- c++ -*- +/* This file is part of the KDE libraries + Copyright (C) 1997 Stephan Kulow <[email protected]> + Copyright (C) 2001 Carsten Pfeiffer <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KFILEVIEW_H +#define KFILEVIEW_H + +class QPoint; +class KActionCollection; + +#include <qwidget.h> + +#include "kfileitem.h" +#include "kfile.h" + +/** + * internal class to make easier to use signals possible + * @internal + **/ +class KIO_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() ) + emit dirActivated( item ); + else + emit fileSelected( item ); + } + /** + * emits the highlighted signal for item. Call this in your view class + * whenever the selection changes. + */ + void highlightFile(const KFileItem *i) { emit fileHighlighted(i); } + + void activateMenu( const KFileItem *i, const QPoint& pos ) { + emit activatedMenu( i, pos ); + } + + void changeSorting( QDir::SortSpec sorting ) { + emit sortingChanged( sorting ); + } + + void dropURLs(const KFileItem *i, QDropEvent*e, const KURL::List&urls) { + emit dropped(i, e, urls); + } + +signals: + void dirActivated(const KFileItem*); + + void sortingChanged( QDir::SortSpec ); + + /** + * 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&); +}; + +/** + * 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 <[email protected]> + **/ +class KIO_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<KFileView*>(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; + + /** + * @returns the "current" KFileItem, e.g. where the cursor is. + * Returns 0L when there is no current item (e.g. in an empty view). + * Subclasses have to implement this. + */ + 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*); + + /** + * 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); + + /** + * 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::SortSpec sorting() const { return m_sorting; } + + /** + * 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::SortSpec sort); + + /** + * Tells whether the current items are in reversed order (shortcut to + * sorting() & QDir::Reversed). + */ + bool isReversed() const { return (m_sorting & QDir::Reversed); } + + void sortReversed(); + + /** + * @returns the number of dirs and files + **/ + uint count() const { return filesNumber + dirsNumber; } + + /** + * @returns the number of files. + **/ + uint numFiles() const { return filesNumber; } + + /** + * @returns the number of directories + **/ + uint numDirs() const { return dirsNumber; } + + 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 { return view_mode; } + + /** + * @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 { return m_viewName; } + + /** + * Sets the name of the view, which could be displayed somewhere. + * E.g. "Image Preview". + */ + void setViewName( const QString& name ) { m_viewName = 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); + + /** + * 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; + + /** + * 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; + + /** + * @returns whether the given item is currently selected. + * Must be implemented by the view. + */ + virtual bool isSelected( const KFileItem * ) const = 0; + + /** + * @returns all currently highlighted items. + */ + const KFileItemList * selectedItems() const; + + /** + * @returns all items currently available in the current sort-order + */ + const KFileItemList * items() const; + + 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 ) { + myOnlyDoubleClickSelectsFiles = enable; + } + + /** + * @returns whether files (not directories) should only be select()ed by + * double-clicks. + * @see setOnlyDoubleClickSelectsFiles + */ + bool onlyDoubleClickSelectsFiles() const { + return myOnlyDoubleClickSelectsFiles; + } + + /** + * increases the number of dirs and files. + * @returns true if the item fits the view mode + */ + 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 { return sig; } + + virtual void readConfig( KConfig *, const QString& group = QString::null ); + virtual void writeConfig( KConfig *, const QString& group = QString::null); + + /** + * 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. + * @since 3.2 + */ + enum DropOptions { + AutoOpenDirs = 1 + }; + /** + * Specify DND options. See DropOptions for details. + * All options are disabled by default. + * @since 3.2 + */ + // KDE 4: Make virtual + void setDropOptions(int options); + + /** + * Returns the DND options in effect. + * See DropOptions for details. + * @since 3.2 + */ + 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 sortSpec An ORed combination of QDir::SortSpec flags. + * Currently, the values IgnoreCase, Reversed and + * DirsFirst are taken into account. + */ + static QString sortingKey( const QString& value, bool isDir, int sortSpec); + + /** + * 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,int sortSpec); + + /** + * @internal + * delay before auto opening a directory + */ + static int autoOpenDelay(); + +protected: + /** + * @internal + * class to distribute the signals + **/ + KFileViewSignaler *sig; + +private: + static QDir::SortSpec defaultSortSpec; + QDir::SortSpec 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; + +protected: + virtual void virtual_hook( int id, void* data ); + /* @internal for virtual_hook */ + enum { VIRTUAL_SET_DROP_OPTIONS = 1 }; + void setDropOptions_impl(int options); +private: + class KFileViewPrivate; + KFileViewPrivate *d; +}; + +#endif // KFILEINFOLISTWIDGET_H diff --git a/kio/kfile/kicondialog.cpp b/kio/kfile/kicondialog.cpp new file mode 100644 index 000000000..21a6ff6cd --- /dev/null +++ b/kio/kfile/kicondialog.cpp @@ -0,0 +1,772 @@ +/* vi: ts=8 sts=4 sw=4 + * + * This file is part of the KDE project, module kfile. + * Copyright (C) 2000 Geert Jansen <[email protected]> + * (C) 2000 Kurt Granroth <[email protected]> + * (C) 1997 Christoph Neerfeld <[email protected]> + * (C) 2002 Carsten Pfeiffer <[email protected]> + * + * This is free software; it comes under the GNU Library General + * Public License, version 2. See the file "COPYING.LIB" for the + * exact licensing terms. + */ + +#include "kicondialog.h" + +#include <config.h> + +#include <assert.h> + +#include <kiconviewsearchline.h> + +#include <kapplication.h> +#include <klocale.h> +#include <kglobal.h> +#include <kstandarddirs.h> +#include <kiconloader.h> +#include <kprogress.h> +#include <kiconview.h> +#include <kfiledialog.h> +#include <kimagefilepreview.h> + +#include <qlayout.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qsortedlist.h> +#include <qimage.h> +#include <qpixmap.h> +#include <qlabel.h> +#include <qcombobox.h> +#include <qtimer.h> +#include <qbuttongroup.h> +#include <qradiobutton.h> +#include <qfileinfo.h> +#include <qtoolbutton.h> +#include <qwhatsthis.h> + +#ifdef HAVE_LIBART +#include <svgicons/ksvgiconengine.h> +#include <svgicons/ksvgiconpainter.h> +#endif + +class KIconCanvas::KIconCanvasPrivate +{ + public: + KIconCanvasPrivate() { m_bLoading = false; } + ~KIconCanvasPrivate() {} + bool m_bLoading; +}; + +/** + * Helper class for sorting icon paths by icon name + */ +class IconPath : public QString +{ +protected: + QString m_iconName; + +public: + IconPath(const QString &ip) : QString (ip) + { + int n = findRev('/'); + m_iconName = (n==-1) ? static_cast<QString>(*this) : mid(n+1); + } + + + IconPath() : QString () + { } + + bool operator== (const IconPath &ip) const + { return m_iconName == ip.m_iconName; } + + bool operator< (const IconPath &ip) const + { return m_iconName < ip.m_iconName; } + +}; + +/* + * KIconCanvas: Iconview for the iconloader dialog. + */ + +KIconCanvas::KIconCanvas(QWidget *parent, const char *name) + : KIconView(parent, name) +{ + d = new KIconCanvasPrivate; + mpTimer = new QTimer(this); + connect(mpTimer, SIGNAL(timeout()), SLOT(slotLoadFiles())); + connect(this, SIGNAL(currentChanged(QIconViewItem *)), + SLOT(slotCurrentChanged(QIconViewItem *))); + setGridX(80); + setWordWrapIconText(false); + setShowToolTips(true); +} + +KIconCanvas::~KIconCanvas() +{ + delete mpTimer; + delete d; +} + +void KIconCanvas::loadFiles(const QStringList& files) +{ + clear(); + mFiles = files; + emit startLoading(mFiles.count()); + mpTimer->start(10, true); // #86680 + d->m_bLoading = false; +} + +void KIconCanvas::slotLoadFiles() +{ + setResizeMode(Fixed); + QApplication::setOverrideCursor(waitCursor); + + // disable updates to not trigger paint events when adding child items + setUpdatesEnabled( false ); + +#ifdef HAVE_LIBART + KSVGIconEngine *svgEngine = new KSVGIconEngine(); +#endif + + d->m_bLoading = true; + int i; + QStringList::ConstIterator it; + uint emitProgress = 10; // so we will emit it once in the beginning + QStringList::ConstIterator end(mFiles.end()); + for (it=mFiles.begin(), i=0; it!=end; ++it, i++) + { + // Calling kapp->processEvents() makes the iconview flicker like hell + // (it's being repainted once for every new item), so we don't do this. + // Instead, we directly repaint the progress bar without going through + // the event-loop. We do that just once for every 10th item so that + // the progress bar doesn't flicker in turn. (pfeiffer) + if ( emitProgress >= 10 ) { + emit progress(i); + emitProgress = 0; + } + + emitProgress++; +// kapp->processEvents(); + if ( !d->m_bLoading ) // user clicked on a button that will load another set of icons + break; + QImage img; + + // Use the extension as the format. Works for XPM and PNG, but not for SVG + QString path= *it; + QString ext = path.right(3).upper(); + + if (ext != "SVG" && ext != "VGZ") + img.load(*it); +#ifdef HAVE_LIBART + else + if (svgEngine->load(60, 60, *it)) + img = *svgEngine->painter()->image(); +#endif + + if (img.isNull()) + continue; + if (img.width() > 60 || img.height() > 60) + { + if (img.width() > img.height()) + { + int height = (int) ((60.0 / img.width()) * img.height()); + img = img.smoothScale(60, height); + } else + { + int width = (int) ((60.0 / img.height()) * img.width()); + img = img.smoothScale(width, 60); + } + } + QPixmap pm; + pm.convertFromImage(img); + QFileInfo fi(*it); + QIconViewItem *item = new QIconViewItem(this, fi.baseName(), pm); + item->setKey(*it); + item->setDragEnabled(false); + item->setDropEnabled(false); + } + +#ifdef HAVE_LIBART + delete svgEngine; +#endif + + // enable updates since we have to draw the whole view now + setUpdatesEnabled( true ); + + QApplication::restoreOverrideCursor(); + d->m_bLoading = false; + emit finished(); + setResizeMode(Adjust); +} + +QString KIconCanvas::getCurrent() const +{ + if (!currentItem()) + return QString::null; + return currentItem()->key(); +} + +void KIconCanvas::stopLoading() +{ + d->m_bLoading = false; +} + +void KIconCanvas::slotCurrentChanged(QIconViewItem *item) +{ + emit nameChanged((item != 0L) ? item->text() : QString::null); +} + +class KIconDialog::KIconDialogPrivate +{ + public: + KIconDialogPrivate() { + m_bStrictIconSize = true; + m_bLockUser = false; + m_bLockCustomDir = false; + searchLine = 0; + } + ~KIconDialogPrivate() {} + bool m_bStrictIconSize, m_bLockUser, m_bLockCustomDir; + QString custom; + QString customLocation; + KIconViewSearchLine *searchLine; +}; + +/* + * KIconDialog: Dialog for selecting icons. Both system and user + * specified icons can be chosen. + */ + +KIconDialog::KIconDialog(QWidget *parent, const char *name) + : KDialogBase(parent, name, true, i18n("Select Icon"), Ok|Cancel, Ok) +{ + d = new KIconDialogPrivate; + mpLoader = KGlobal::iconLoader(); + init(); +} + +KIconDialog::KIconDialog(KIconLoader *loader, QWidget *parent, + const char *name) + : KDialogBase(parent, name, true, i18n("Select Icon"), Ok|Cancel, Ok) +{ + d = new KIconDialogPrivate; + mpLoader = loader; + init(); +} + +void KIconDialog::init() +{ + mGroupOrSize = KIcon::Desktop; + mContext = KIcon::Any; + mType = 0; + mFileList = KGlobal::dirs()->findAllResources("appicon", QString::fromLatin1("*.png")); + + QWidget *main = new QWidget( this ); + setMainWidget(main); + + QVBoxLayout *top = new QVBoxLayout(main); + top->setSpacing( spacingHint() ); + + QButtonGroup *bgroup = new QButtonGroup(0, Qt::Vertical, i18n("Icon Source"), main); + bgroup->layout()->setSpacing(KDialog::spacingHint()); + bgroup->layout()->setMargin(KDialog::marginHint()); + top->addWidget(bgroup); + connect(bgroup, SIGNAL(clicked(int)), SLOT(slotButtonClicked(int))); + QGridLayout *grid = new QGridLayout(bgroup->layout(), 3, 2); + mpRb1 = new QRadioButton(i18n("S&ystem icons:"), bgroup); + grid->addWidget(mpRb1, 1, 0); + mpCombo = new QComboBox(bgroup); + connect(mpCombo, SIGNAL(activated(int)), SLOT(slotContext(int))); + grid->addWidget(mpCombo, 1, 1); + mpRb2 = new QRadioButton(i18n("O&ther icons:"), bgroup); + grid->addWidget(mpRb2, 2, 0); + mpBrowseBut = new QPushButton(i18n("&Browse..."), bgroup); + grid->addWidget(mpBrowseBut, 2, 1); + + // + // ADD SEARCHLINE + // + QHBoxLayout *searchLayout = new QHBoxLayout(0, 0, KDialog::spacingHint()); + top->addLayout(searchLayout); + + QToolButton *clearSearch = new QToolButton(main); + clearSearch->setTextLabel(i18n("Clear Search"), true); + clearSearch->setIconSet(SmallIconSet(QApplication::reverseLayout() ? "clear_left" :"locationbar_erase")); + searchLayout->addWidget(clearSearch); + + QLabel *searchLabel = new QLabel(i18n("&Search:"), main); + searchLayout->addWidget(searchLabel); + + d->searchLine = new KIconViewSearchLine(main, "searchLine"); + searchLayout->addWidget(d->searchLine); + searchLabel->setBuddy(d->searchLine); + + + // signals and slots connections + connect(clearSearch, SIGNAL(clicked()), d->searchLine, SLOT(clear())); + + QString wtstr = i18n("Search interactively for icon names (e.g. folder)."); + QWhatsThis::add(searchLabel, wtstr); + QWhatsThis::add(d->searchLine, wtstr); + + + mpCanvas = new KIconCanvas(main); + connect(mpCanvas, SIGNAL(executed(QIconViewItem *)), SLOT(slotAcceptIcons())); + connect(mpCanvas, SIGNAL(returnPressed(QIconViewItem *)), SLOT(slotAcceptIcons())); + mpCanvas->setMinimumSize(400, 125); + top->addWidget(mpCanvas); + d->searchLine->setIconView(mpCanvas); + + mpProgress = new KProgress(main); + top->addWidget(mpProgress); + connect(mpCanvas, SIGNAL(startLoading(int)), SLOT(slotStartLoading(int))); + connect(mpCanvas, SIGNAL(progress(int)), SLOT(slotProgress(int))); + connect(mpCanvas, SIGNAL(finished()), SLOT(slotFinished())); + + // When pressing Ok or Cancel, stop loading icons + connect(this, SIGNAL(hidden()), mpCanvas, SLOT(stopLoading())); + + static const char* const context_text[] = { + I18N_NOOP( "Actions" ), + I18N_NOOP( "Animations" ), + I18N_NOOP( "Applications" ), + I18N_NOOP( "Categories" ), + I18N_NOOP( "Devices" ), + I18N_NOOP( "Emblems" ), + I18N_NOOP( "Emotes" ), + I18N_NOOP( "Filesystems" ), + I18N_NOOP( "International" ), + I18N_NOOP( "Mimetypes" ), + I18N_NOOP( "Places" ), + I18N_NOOP( "Status" ) }; + static const KIcon::Context context_id[] = { + KIcon::Action, + KIcon::Animation, + KIcon::Application, + KIcon::Category, + KIcon::Device, + KIcon::Emblem, + KIcon::Emote, + KIcon::FileSystem, + KIcon::International, + KIcon::MimeType, + KIcon::Place, + KIcon::StatusIcon }; + mNumContext = 0; + int cnt = sizeof( context_text ) / sizeof( context_text[ 0 ] ); + // check all 3 arrays have same sizes + assert( cnt == sizeof( context_id ) / sizeof( context_id[ 0 ] ) + && cnt == sizeof( mContextMap ) / sizeof( mContextMap[ 0 ] )); + for( int i = 0; + i < cnt; + ++i ) + { + if( mpLoader->hasContext( context_id[ i ] )) + { + mpCombo->insertItem(i18n( context_text[ i ] )); + mContextMap[ mNumContext++ ] = context_id[ i ]; + } + } + mpCombo->setFixedSize(mpCombo->sizeHint()); + + mpBrowseBut->setFixedWidth(mpCombo->width()); + + // Make the dialog a little taller + incInitialSize(QSize(0,100)); +} + + +KIconDialog::~KIconDialog() +{ + delete d; +} + +void KIconDialog::slotAcceptIcons() +{ + d->custom=QString::null; + slotOk(); +} + +void KIconDialog::showIcons() +{ + mpCanvas->clear(); + QStringList filelist; + if (mType == 0) + if (d->m_bStrictIconSize) + filelist=mpLoader->queryIcons(mGroupOrSize, mContext); + else + filelist=mpLoader->queryIconsByContext(mGroupOrSize, mContext); + else if ( !d->customLocation.isNull() ) + filelist=mpLoader->queryIconsByDir( d->customLocation ); + else + filelist=mFileList; + + QSortedList <IconPath>iconlist; + iconlist.setAutoDelete(true); + QStringList::Iterator it; + for( it = filelist.begin(); it != filelist.end(); ++it ) + iconlist.append(new IconPath(*it)); + + iconlist.sort(); + filelist.clear(); + + for ( IconPath *ip=iconlist.first(); ip != 0; ip=iconlist.next() ) + filelist.append(*ip); + + d->searchLine->clear(); + mpCanvas->loadFiles(filelist); +} + +void KIconDialog::setStrictIconSize(bool b) +{ + d->m_bStrictIconSize=b; +} + +bool KIconDialog::strictIconSize() const +{ + return d->m_bStrictIconSize; +} + +void KIconDialog::setIconSize( int size ) +{ + // see KIconLoader, if you think this is weird + if ( size == 0 ) + mGroupOrSize = KIcon::Desktop; // default Group + else + mGroupOrSize = -size; // yes, KIconLoader::queryIconsByContext is weird +} + +int KIconDialog::iconSize() const +{ + // 0 or any other value ==> mGroupOrSize is a group, so we return 0 + return (mGroupOrSize < 0) ? -mGroupOrSize : 0; +} + +#ifndef KDE_NO_COMPAT +QString KIconDialog::selectIcon(KIcon::Group group, KIcon::Context context, bool user) +{ + setup( group, context, false, 0, user ); + return openDialog(); +} +#endif + +void KIconDialog::setup(KIcon::Group group, KIcon::Context context, + bool strictIconSize, int iconSize, bool user ) +{ + d->m_bStrictIconSize = strictIconSize; + mGroupOrSize = (iconSize == 0) ? group : -iconSize; + mType = user ? 1 : 0; + mpRb1->setChecked(!user); + mpRb2->setChecked(user); + mpCombo->setEnabled(!user); + mpBrowseBut->setEnabled(user); + setContext( context ); +} + +void KIconDialog::setup(KIcon::Group group, KIcon::Context context, + bool strictIconSize, int iconSize, bool user, + bool lockUser, bool lockCustomDir ) +{ + d->m_bStrictIconSize = strictIconSize; + d->m_bLockUser = lockUser; + d->m_bLockCustomDir = lockCustomDir; + mGroupOrSize = (iconSize == 0) ? group : -iconSize; + mType = user ? 1 : 0; + mpRb1->setChecked(!user); + mpRb1->setEnabled( !lockUser || !user ); + mpRb2->setChecked(user); + mpRb2->setEnabled( !lockUser || user ); + mpCombo->setEnabled(!user); + mpBrowseBut->setEnabled( user && !lockCustomDir ); + setContext( context ); +} + +void KIconDialog::setContext( KIcon::Context context ) +{ + mContext = context; + for( int i = 0; + i < mNumContext; + ++i ) + if( mContextMap[ i ] == context ) + { + mpCombo->setCurrentItem( i ); + return; + } +} + +void KIconDialog::setCustomLocation( const QString& location ) +{ + d->customLocation = location; +} + +QString KIconDialog::openDialog() +{ + showIcons(); + + if ( exec() == Accepted ) + { + if (!d->custom.isNull()) + return d->custom; + QString name = mpCanvas->getCurrent(); + if (name.isEmpty() || (mType == 1)) + return name; + QFileInfo fi(name); + return fi.baseName(); + } + return QString::null; +} + +void KIconDialog::showDialog() +{ + setModal(false); + showIcons(); + show(); +} + +void KIconDialog::slotOk() +{ + QString name; + if (!d->custom.isNull()) + { + name = d->custom; + } + else + { + name = mpCanvas->getCurrent(); + if (!name.isEmpty() && (mType != 1)) + { + QFileInfo fi(name); + name = fi.baseName(); + } + } + + emit newIconName(name); + KDialogBase::slotOk(); +} + +QString KIconDialog::getIcon(KIcon::Group group, KIcon::Context context, + bool strictIconSize, int iconSize, bool user, + QWidget *parent, const QString &caption) +{ + KIconDialog dlg(parent, "icon dialog"); + dlg.setup( group, context, strictIconSize, iconSize, user ); + if (!caption.isNull()) + dlg.setCaption(caption); + + return dlg.openDialog(); +} + +void KIconDialog::slotButtonClicked(int id) +{ + QString file; + + switch (id) + { + case 0: + if(mType!=0) + { + mType = 0; + mpBrowseBut->setEnabled(false); + mpCombo->setEnabled(true); + showIcons(); + } + break; + + case 1: + if(mType!=1) + { + mType = 1; + mpBrowseBut->setEnabled( !d->m_bLockCustomDir ); + mpCombo->setEnabled(false); + showIcons(); + } + break; + case 2: + { + // Create a file dialog to select a PNG, XPM or SVG file, + // with the image previewer shown. + // KFileDialog::getImageOpenURL doesn't allow svg. + KFileDialog dlg(QString::null, i18n("*.png *.xpm *.svg *.svgz|Icon Files (*.png *.xpm *.svg *.svgz)"), + this, "filedialog", true); + dlg.setOperationMode( KFileDialog::Opening ); + dlg.setCaption( i18n("Open") ); + dlg.setMode( KFile::File ); + + KImageFilePreview *ip = new KImageFilePreview( &dlg ); + dlg.setPreviewWidget( ip ); + dlg.exec(); + + file = dlg.selectedFile(); + if (!file.isEmpty()) + { + d->custom = file; + if ( mType == 1 ) + d->customLocation = QFileInfo( file ).dirPath( true ); + slotOk(); + } + } + break; + } +} + +void KIconDialog::slotContext(int id) +{ + mContext = static_cast<KIcon::Context>( mContextMap[ id ] ); + showIcons(); +} + +void KIconDialog::slotStartLoading(int steps) +{ + if (steps < 10) + mpProgress->hide(); + else + { + mpProgress->setTotalSteps(steps); + mpProgress->setProgress(0); + mpProgress->show(); + } +} + +void KIconDialog::slotProgress(int p) +{ + mpProgress->setProgress(p); + // commented out the following since setProgress already paints ther + // progress bar. ->repaint() only makes it flicker + //mpProgress->repaint(); +} + +void KIconDialog::slotFinished() +{ + mpProgress->hide(); +} + +class KIconButton::KIconButtonPrivate +{ + public: + KIconButtonPrivate() { + m_bStrictIconSize = false; + iconSize = 0; // let KIconLoader choose the default + } + ~KIconButtonPrivate() {} + bool m_bStrictIconSize; + int iconSize; +}; + + +/* + * KIconButton: A "choose icon" pushbutton. + */ + +KIconButton::KIconButton(QWidget *parent, const char *name) + : QPushButton(parent, name) +{ + init( KGlobal::iconLoader() ); +} + +KIconButton::KIconButton(KIconLoader *loader, + QWidget *parent, const char *name) + : QPushButton(parent, name) +{ + init( loader ); +} + +void KIconButton::init( KIconLoader *loader ) +{ + d = new KIconButtonPrivate; + mGroup = KIcon::Desktop; + mContext = KIcon::Application; + mbUser = false; + + mpLoader = loader; + mpDialog = 0L; + connect(this, SIGNAL(clicked()), SLOT(slotChangeIcon())); +} + +KIconButton::~KIconButton() +{ + delete mpDialog; + delete d; +} + +void KIconButton::setStrictIconSize(bool b) +{ + d->m_bStrictIconSize=b; +} + +bool KIconButton::strictIconSize() const +{ + return d->m_bStrictIconSize; +} + +void KIconButton::setIconSize( int size ) +{ + d->iconSize = size; +} + +int KIconButton::iconSize() const +{ + return d->iconSize; +} + +void KIconButton::setIconType(KIcon::Group group, KIcon::Context context, bool user) +{ + mGroup = group; + mContext = context; + mbUser = user; +} + +void KIconButton::setIcon(const QString& icon) +{ + mIcon = icon; + setIconSet(mpLoader->loadIconSet(mIcon, mGroup, d->iconSize)); + + if (!mpDialog) + { + mpDialog = new KIconDialog(mpLoader, this); + connect(mpDialog, SIGNAL(newIconName(const QString&)), SLOT(newIconName(const QString&))); + } + + if ( mbUser ) + mpDialog->setCustomLocation( QFileInfo( mpLoader->iconPath(mIcon, mGroup, true) ).dirPath( true ) ); +} + +void KIconButton::resetIcon() +{ + mIcon = QString::null; + setIconSet(QIconSet()); +} + +void KIconButton::slotChangeIcon() +{ + if (!mpDialog) + { + mpDialog = new KIconDialog(mpLoader, this); + connect(mpDialog, SIGNAL(newIconName(const QString&)), SLOT(newIconName(const QString&))); + } + + mpDialog->setup( mGroup, mContext, d->m_bStrictIconSize, d->iconSize, mbUser ); + mpDialog->showDialog(); +} + +void KIconButton::newIconName(const QString& name) +{ + if (name.isEmpty()) + return; + + QIconSet iconset = mpLoader->loadIconSet(name, mGroup, d->iconSize); + setIconSet(iconset); + mIcon = name; + + if ( mbUser ) + mpDialog->setCustomLocation( QFileInfo( mpLoader->iconPath(mIcon, mGroup, true) ).dirPath( true ) ); + + emit iconChanged(name); +} + +void KIconCanvas::virtual_hook( int id, void* data ) +{ KIconView::virtual_hook( id, data ); } + +void KIconDialog::virtual_hook( int id, void* data ) +{ KDialogBase::virtual_hook( id, data ); } + +#include "kicondialog.moc" diff --git a/kio/kfile/kicondialog.h b/kio/kfile/kicondialog.h new file mode 100644 index 000000000..e6cf4e2fe --- /dev/null +++ b/kio/kfile/kicondialog.h @@ -0,0 +1,350 @@ +/* vi: ts=8 sts=4 sw=4 + * + * This file is part of the KDE project, module kfile. + * Copyright (C) 2000 Geert Jansen <[email protected]> + * (C) 2000 Kurt Granroth <[email protected]> + * (C) 1997 Christoph Neerfeld <[email protected]> + * (C) 2002 Carsten Pfeiffer <[email protected]> + * + * This is free software; it comes under the GNU Library General + * Public License, version 2. See the file "COPYING.LIB" for the + * exact licensing terms. + */ + +#ifndef __KIconDialog_h__ +#define __KIconDialog_h__ + +#include <qstring.h> +#include <qstringlist.h> +#include <qpushbutton.h> + +#include <kicontheme.h> +#include <kdialogbase.h> +#include <kiconview.h> + +class QComboBox; +class QTimer; +class QKeyEvent; +class QRadioButton; +class KProgress; +class KIconLoader; + +/** + * Icon canvas for KIconDialog. + */ +class KIO_EXPORT KIconCanvas: public KIconView +{ + Q_OBJECT + +public: + KIconCanvas(QWidget *parent=0L, const char *name=0L); + ~KIconCanvas(); + + /** + * Load icons into the canvas. + */ + void loadFiles(const QStringList& files); + + /** + * Returns the current icon. + */ + QString getCurrent() const; + +public slots: + void stopLoading(); + +signals: + /** + * Emitted when the current icon has changed. + */ + void nameChanged(QString); + /* KDE 4: Make it const QString & */ + + void startLoading(int); + void progress(int); + void finished(); + +private slots: + void slotLoadFiles(); + void slotCurrentChanged(QIconViewItem *item); + +private: + QStringList mFiles; + QTimer *mpTimer; + KIconLoader *mpLoader; // unused + +protected: + virtual void virtual_hook( int id, void* data ); + +private: + class KIconCanvasPrivate; + KIconCanvasPrivate *d; +}; + + +/** + * Dialog for interactive selection of icons. Use the function + * getIcon() let the user select an icon. + * + * @short An icon selection dialog. + */ +class KIO_EXPORT KIconDialog: public KDialogBase +{ + Q_OBJECT + +public: + /** + * Constructs an icon selection dialog using the global iconloader. + */ + KIconDialog(QWidget *parent=0L, const char *name=0L); + /** + * Constructs an icon selection dialog using a specific iconloader. + */ + KIconDialog(KIconLoader *loader, QWidget *parent=0, + const char *name=0); + /** + * Destructs the dialog. + */ + ~KIconDialog(); + + /** + * Sets a strict icon size policy for allowed icons. When true, + * only icons of the specified group's size in getIcon() are shown. + * When false, icons not available at the desired group's size will + * also be selectable. + */ + void setStrictIconSize(bool b); + /** + * Returns true if a strict icon size policy is set. + */ + bool strictIconSize() const; + /** + * sets a custom icon directory + * @since 3.1 + */ + void setCustomLocation( const QString& location ); + + /** + * Sets the size of the icons to be shown / selected. + * @see KIcon::StdSizes + * @see iconSize + */ + void setIconSize(int size); + + /** + * Returns the iconsize set via setIconSize() or 0, if the default + * iconsize will be used. + */ + int iconSize() const; + +#ifndef KDE_NO_COMPAT + /** + * @deprecated in KDE 3.0, use the static method getIcon instead. + */ + QString selectIcon(KIcon::Group group=KIcon::Desktop, KIcon::Context + context=KIcon::Application, bool user=false); +#endif + + /** + * Allows you to set the same parameters as in the class method + * getIcon(). + */ + void setup( KIcon::Group group, + KIcon::Context context = KIcon::Application, + bool strictIconSize = false, int iconSize = 0, + bool user = false ); + + /** + * Allows you to set the same parameters as in the class method + * getIcon(), as well as two additional parameters to lock + * the choice between system and user dirs and to lock the custom user + * dir itself. + * + * @since 3.3 + */ + + void setup( KIcon::Group group, KIcon::Context context, + bool strictIconSize, int iconSize, bool user, bool lockUser, + bool lockCustomDir ); + + /** + * exec()utes this modal dialog and returns the name of the selected icon, + * or QString::null if the dialog was aborted. + * @returns the name of the icon, suitable for loading with KIconLoader. + * @see getIcon + */ + QString openDialog(); + + /** + * show()es this dialog and emits a newIcon(const QString&) signal when + * successful. QString::null will be emitted if the dialog was aborted. + */ + void showDialog(); + + /** + * Pops up the dialog an lets the user select an icon. + * + * @param group The icon group this icon is intended for. Providing the + * group shows the icons in the dialog with the same appearance as when + * used outside the dialog. + * @param context The initial icon context. Initially, the icons having + * this context are shown in the dialog. The user can change this. + * @param strictIconSize When true, only icons of the specified group's size + * are shown, otherwise icon not available in the desired group's size + * will also be selectable. + * @param iconSize the size of the icons -- the default of the icongroup + * if set to 0 + * @param user Begin with the "user icons" instead of "system icons". + * @param parent The parent widget of the dialog. + * @param caption The caption to use for the dialog. + * @return The name of the icon, suitable for loading with KIconLoader. + * @version New in 3.0 + */ + static QString getIcon(KIcon::Group group=KIcon::Desktop, + KIcon::Context context=KIcon::Application, + bool strictIconSize=false, int iconSize = 0, + bool user=false, QWidget *parent=0, + const QString &caption=QString::null); + +signals: + void newIconName(const QString&); + +protected slots: + void slotOk(); + +private slots: + void slotButtonClicked(int); + void slotContext(int); + void slotStartLoading(int); + void slotProgress(int); + void slotFinished(); + void slotAcceptIcons(); +private: + void init(); + void showIcons(); + void setContext( KIcon::Context context ); + + int mGroupOrSize; + KIcon::Context mContext; + int mType; + + QStringList mFileList; + QComboBox *mpCombo; + QPushButton *mpBrowseBut; + QRadioButton *mpRb1, *mpRb2; + KProgress *mpProgress; + KIconLoader *mpLoader; + KIconCanvas *mpCanvas; + int mNumContext; + KIcon::Context mContextMap[ 12 ]; // must match KIcon::Context size, code has assert + +protected: + virtual void virtual_hook( int id, void* data ); +private: + class KIconDialogPrivate; + KIconDialogPrivate *d; +}; + + +/** + * A pushbutton for choosing an icon. Pressing on the button will open a + * KIconDialog for the user to select an icon. The current icon will be + * displayed on the button. + * + * @see KIconDialog + * @short A push button that allows selection of an icon. + */ +class KIO_EXPORT KIconButton: public QPushButton +{ + Q_OBJECT + Q_PROPERTY( QString icon READ icon WRITE setIcon RESET resetIcon ) + Q_PROPERTY( int iconSize READ iconSize WRITE setIconSize) + Q_PROPERTY( bool strictIconSize READ strictIconSize WRITE setStrictIconSize ) + +public: + /** + * Constructs a KIconButton using the global iconloader. + */ + KIconButton(QWidget *parent=0L, const char *name=0L); + + /** + * Constructs a KIconButton using a specific KIconLoader. + */ + KIconButton(KIconLoader *loader, QWidget *parent, const char *name=0L); + /** + * Destructs the button. + */ + ~KIconButton(); + + /** + * Sets a strict icon size policy for allowed icons. When true, + * only icons of the specified group's size in setIconType are allowed, + * and only icons of that size will be shown in the icon dialog. + */ + void setStrictIconSize(bool b); + /** + * Returns true if a strict icon size policy is set. + */ + bool strictIconSize() const; + + /** + * Sets the icon group and context. Use KIcon::NoGroup if you want to + * allow icons for any group in the given context. + */ + void setIconType(KIcon::Group group, KIcon::Context context, bool user=false); + + /** + * Sets the button's initial icon. + */ + void setIcon(const QString& icon); + + /** + * Resets the icon (reverts to an empty button). + */ + void resetIcon(); + + /** + * Returns the name of the selected icon. + */ + QString icon() const { return mIcon; } + + /** + * Sets the size of the icon to be shown / selected. + * @see KIcon::StdSizes + * @see iconSize + */ + void setIconSize( int size ); + + /** + * Returns the iconsize set via setIconSize() or 0, if the default + * iconsize will be used. + */ + int iconSize() const; + +signals: + /** + * Emitted when the icon has changed. + */ + void iconChanged(QString icon); + /* KDE 4: Make it const QString & */ + +private slots: + void slotChangeIcon(); + void newIconName(const QString& name); + +private: + void init( KIconLoader *loader ); + + bool mbUser; + KIcon::Group mGroup; + KIcon::Context mContext; + + QString mIcon; + KIconDialog *mpDialog; + KIconLoader *mpLoader; + class KIconButtonPrivate; + KIconButtonPrivate *d; +}; + + +#endif // __KIconDialog_h__ diff --git a/kio/kfile/kimagefilepreview.cpp b/kio/kfile/kimagefilepreview.cpp new file mode 100644 index 000000000..d973c6e1b --- /dev/null +++ b/kio/kfile/kimagefilepreview.cpp @@ -0,0 +1,187 @@ +/* + * This file is part of the KDE project + * Copyright (C) 2001 Martin R. Jones <[email protected]> + * 2001 Carsten Pfeiffer <[email protected]> + * + * You can Freely distribute this program under the GNU Library General Public + * License. See the file "COPYING" for the exact licensing terms. + */ + +#include <qlayout.h> +#include <qlabel.h> +#include <qcombobox.h> +#include <qcheckbox.h> +#include <qwhatsthis.h> +#include <qtimer.h> + +#include <kapplication.h> +#include <kconfig.h> +#include <kglobal.h> +#include <kiconloader.h> +#include <kpushbutton.h> +#include <kstandarddirs.h> +#include <kdebug.h> +#include <klocale.h> +#include <kfiledialog.h> +#include <kfileitem.h> +#include <kio/previewjob.h> + +#include "kimagefilepreview.h" +#include "config-kfile.h" + +/**** KImageFilePreview ****/ + +KImageFilePreview::KImageFilePreview( QWidget *parent ) + : KPreviewWidgetBase( parent ), + m_job( 0L ) +{ + KConfig *config = KGlobal::config(); + KConfigGroupSaver cs( config, ConfigGroup ); + autoMode = config->readBoolEntry( "Automatic Preview", true ); + + QVBoxLayout *vb = new QVBoxLayout( this, 0, KDialog::spacingHint() ); + + imageLabel = new QLabel( this ); + imageLabel->setFrameStyle( QFrame::NoFrame ); + imageLabel->setAlignment( Qt::AlignHCenter | Qt::AlignVCenter ); + imageLabel->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding) ); + vb->addWidget( imageLabel ); + + QHBoxLayout *hb = new QHBoxLayout( 0 ); + vb->addLayout( hb ); + + autoPreview = new QCheckBox( i18n("&Automatic preview"), this ); + autoPreview->setChecked( autoMode ); + hb->addWidget( autoPreview ); + connect( autoPreview, SIGNAL(toggled(bool)), SLOT(toggleAuto(bool)) ); + + previewButton = new KPushButton( SmallIconSet("thumbnail"), i18n("&Preview"), this ); + hb->addWidget( previewButton ); + connect( previewButton, SIGNAL(clicked()), SLOT(showPreview()) ); + + timer = new QTimer( this ); + connect( timer, SIGNAL(timeout()), SLOT(showPreview()) ); + + setSupportedMimeTypes( KIO::PreviewJob::supportedMimeTypes() ); +} + +KImageFilePreview::~KImageFilePreview() +{ + if ( m_job ) + m_job->kill(); + + KConfig *config = KGlobal::config(); + KConfigGroupSaver cs( config, ConfigGroup ); + config->writeEntry( "Automatic Preview", autoPreview->isChecked() ); +} + +void KImageFilePreview::showPreview() +{ + // Pass a copy since clearPreview() will clear currentURL + KURL url = currentURL; + showPreview( url, true ); +} + +// called via KPreviewWidgetBase interface +void KImageFilePreview::showPreview( const KURL& url ) +{ + showPreview( url, false ); +} + +void KImageFilePreview::showPreview( const KURL &url, bool force ) +{ + if ( !url.isValid() ) { + clearPreview(); + return; + } + + if ( url != currentURL || force ) + { + clearPreview(); + currentURL = url; + + if ( autoMode || force ) + { + int w = imageLabel->contentsRect().width() - 4; + int h = imageLabel->contentsRect().height() - 4; + + m_job = createJob( url, w, h ); + if ( force ) // explicitly requested previews shall always be generated! + m_job->setIgnoreMaximumSize( true ); + + connect( m_job, SIGNAL( result( KIO::Job * )), + this, SLOT( slotResult( KIO::Job * ))); + connect( m_job, SIGNAL( gotPreview( const KFileItem*, + const QPixmap& )), + SLOT( gotPreview( const KFileItem*, const QPixmap& ) )); + + connect( m_job, SIGNAL( failed( const KFileItem* )), + this, SLOT( slotFailed( const KFileItem* ) )); + } + } +} + +void KImageFilePreview::toggleAuto( bool a ) +{ + autoMode = a; + if ( autoMode ) + { + // Pass a copy since clearPreview() will clear currentURL + KURL url = currentURL; + showPreview( url, true ); + } +} + +void KImageFilePreview::resizeEvent( QResizeEvent * ) +{ + timer->start( 100, true ); // forces a new preview +} + +QSize KImageFilePreview::sizeHint() const +{ + return QSize( 20, 200 ); // otherwise it ends up huge??? +} + +KIO::PreviewJob * KImageFilePreview::createJob( const KURL& url, int w, int h ) +{ + KURL::List urls; + urls.append( url ); + return KIO::filePreview( urls, w, h, 0, 0, true, false ); +} + +void KImageFilePreview::gotPreview( const KFileItem* item, const QPixmap& pm ) +{ + if ( item->url() == currentURL ) // should always be the case + imageLabel->setPixmap( pm ); +} + +void KImageFilePreview::slotFailed( const KFileItem* item ) +{ + if ( item->isDir() ) + imageLabel->clear(); + else if ( item->url() == currentURL ) // should always be the case + imageLabel->setPixmap( SmallIcon( "file_broken", KIcon::SizeLarge, + KIcon::DisabledState )); +} + +void KImageFilePreview::slotResult( KIO::Job *job ) +{ + if ( job == m_job ) + m_job = 0L; +} + +void KImageFilePreview::clearPreview() +{ + if ( m_job ) { + m_job->kill(); + m_job = 0L; + } + + imageLabel->clear(); + currentURL = KURL(); +} + +void KImageFilePreview::virtual_hook( int id, void* data ) +{ KPreviewWidgetBase::virtual_hook( id, data ); } + +#include "kimagefilepreview.moc" diff --git a/kio/kfile/kimagefilepreview.h b/kio/kfile/kimagefilepreview.h new file mode 100644 index 000000000..cb034265a --- /dev/null +++ b/kio/kfile/kimagefilepreview.h @@ -0,0 +1,77 @@ +/* + * + * This file is part of the KDE project. + * Copyright (C) 2001 Martin R. Jones <[email protected]> + * 2001 Carsten Pfeiffer <[email protected]> + * + * You can Freely distribute this program under the GNU Library General Public + * License. See the file "COPYING" for the exact licensing terms. + */ + +#ifndef KIMAGEFILEPREVIEW_H +#define KIMAGEFILEPREVIEW_H + +#include <qpixmap.h> + +#include <kurl.h> +#include <kpreviewwidgetbase.h> + +class QCheckBox; +class QPushButton; +class QLabel; +class QTimer; + +class KFileDialog; +class KFileItem; +namespace KIO { class Job; class PreviewJob; } + +/** + * Image preview widget for the file dialog. + */ +class KIO_EXPORT KImageFilePreview : public KPreviewWidgetBase +{ + Q_OBJECT + + public: + KImageFilePreview(QWidget *parent); + ~KImageFilePreview(); + + virtual QSize sizeHint() const; + + public slots: + virtual void showPreview(const KURL &url); + virtual void clearPreview(); + + protected slots: + void showPreview(); + void showPreview( const KURL& url, bool force ); + + void toggleAuto(bool); + virtual void gotPreview( const KFileItem*, const QPixmap& ); + + protected: + virtual void resizeEvent(QResizeEvent *e); + virtual KIO::PreviewJob * createJob( const KURL& url, + int w, int h ); + + private slots: + void slotResult( KIO::Job * ); + virtual void slotFailed( const KFileItem* ); + + private: + bool autoMode; + KURL currentURL; + QTimer *timer; + QLabel *imageLabel; + QLabel *infoLabel; + QCheckBox *autoPreview; + QPushButton *previewButton; + KIO::PreviewJob *m_job; + protected: + virtual void virtual_hook( int id, void* data ); + private: + class KImageFilePreviewPrivate; + KImageFilePreviewPrivate *d; +}; + +#endif // KIMAGEFILEPREVIEW_H diff --git a/kio/kfile/kmetaprops.cpp b/kio/kfile/kmetaprops.cpp new file mode 100644 index 000000000..b3ff504fc --- /dev/null +++ b/kio/kfile/kmetaprops.cpp @@ -0,0 +1,268 @@ +/* This file is part of the KDE libraries + Copyright (C) 2001,2002 Rolf Magnus <[email protected]> + + 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. + + $Id$ + */ + +#include "kmetaprops.h" + +#include <kdebug.h> +#include <kfilemetainfowidget.h> +#include <kfilemetainfo.h> +#include <kglobal.h> +#include <kglobalsettings.h> +#include <klocale.h> +#include <kprotocolinfo.h> + +#include <qvalidator.h> +#include <qlayout.h> +#include <qlabel.h> +#include <qfileinfo.h> +#include <qdatetime.h> +#include <qstylesheet.h> +#include <qvgroupbox.h> + +#undef Bool + +class MetaPropsScrollView : public QScrollView +{ +public: + MetaPropsScrollView(QWidget* parent = 0, const char* name = 0) + : QScrollView(parent, name) + { + setFrameStyle(QFrame::NoFrame); + m_frame = new QFrame(viewport(), "MetaPropsScrollView::m_frame"); + m_frame->setFrameStyle(QFrame::NoFrame); + addChild(m_frame, 0, 0); + }; + + QFrame* frame() {return m_frame;}; + +protected: + virtual void viewportResizeEvent(QResizeEvent* ev) + { + QScrollView::viewportResizeEvent(ev); + m_frame->resize( kMax(m_frame->sizeHint().width(), ev->size().width()), + kMax(m_frame->sizeHint().height(), ev->size().height())); + }; + +private: + QFrame* m_frame; +}; + +class KFileMetaPropsPlugin::KFileMetaPropsPluginPrivate +{ +public: + KFileMetaPropsPluginPrivate() {} + ~KFileMetaPropsPluginPrivate() {} + + QFrame* m_frame; + QGridLayout* m_framelayout; + KFileMetaInfo m_info; +// QPushButton* m_add; + QPtrList<KFileMetaInfoWidget> m_editWidgets; +}; + +KFileMetaPropsPlugin::KFileMetaPropsPlugin(KPropertiesDialog* props) + : KPropsDlgPlugin(props) +{ + d = new KFileMetaPropsPluginPrivate; + + KFileItem * fileitem = properties->item(); + kdDebug(250) << "KFileMetaPropsPlugin constructor" << endl; + + d->m_info = fileitem->metaInfo(); + if (!d->m_info.isValid()) + { + d->m_info = KFileMetaInfo(properties->kurl().path(-1)); + 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()); + + kdDebug(250) << "KFileMetaPropsPlugin::createLayout" << endl; + + // is there any valid and non-empty info at all? + if ( !d->m_info.isValid() || (d->m_info.preferredKeys()).isEmpty() ) + return; + + // now get a list of groups + KFileMetaInfoProvider* prov = KFileMetaInfoProvider::self(); + QStringList groupList = d->m_info.preferredGroups(); + + const KFileMimeTypeInfo* mtinfo = prov->mimeTypeInfo(d->m_info.mimeType()); + if (!mtinfo) + { + kdDebug(7034) << "no mimetype info there\n"; + return; + } + + // let the dialog create the page frame + QFrame* topframe = properties->addPage(i18n("&Meta Info")); + topframe->setFrameStyle(QFrame::NoFrame); + QVBoxLayout* tmp = new QVBoxLayout(topframe); + + // create a scroll view in the page + MetaPropsScrollView* view = new MetaPropsScrollView(topframe); + + tmp->addWidget(view); + + d->m_frame = view->frame(); + + QVBoxLayout *toplayout = new QVBoxLayout(d->m_frame); + toplayout->setSpacing(KDialog::spacingHint()); + + for (QStringList::Iterator git=groupList.begin(); + git!=groupList.end(); ++git) + { + kdDebug(7033) << *git << endl; + + QStringList itemList = d->m_info.group(*git).preferredKeys(); + if (itemList.isEmpty()) + continue; + + QGroupBox *groupBox = new QGroupBox(2, Qt::Horizontal, + QStyleSheet::escape(mtinfo->groupInfo(*git)->translatedName()), + d->m_frame); + + toplayout->addWidget(groupBox); + + QValueList<KFileMetaInfoItem> readItems; + QValueList<KFileMetaInfoItem> editItems; + + for (QStringList::Iterator iit = itemList.begin(); + iit!=itemList.end(); ++iit) + { + KFileMetaInfoItem item = d->m_info[*git][*iit]; + if ( !item.isValid() ) continue; + + bool editable = file_info.isWritable() && item.isEditable(); + + if (editable) + editItems.append( item ); + else + readItems.append( item ); + } + + KFileMetaInfoWidget* w = 0L; + // then first add the editable items to the layout + for (QValueList<KFileMetaInfoItem>::Iterator iit= editItems.begin(); + iit!=editItems.end(); ++iit) + { + QLabel* l = new QLabel((*iit).translatedKey() + ":", groupBox); + l->setAlignment( AlignAuto | AlignTop | ExpandTabs ); + QValidator* val = mtinfo->createValidator(*git, (*iit).key()); + if (!val) kdDebug(7033) << "didn't get a validator for " << *git << "/" << (*iit).key() << endl; + w = new KFileMetaInfoWidget(*iit, val, groupBox); + d->m_editWidgets.append( w ); + connect(w, SIGNAL(valueChanged(const QVariant&)), this, SIGNAL(changed())); + } + + // and then the read only items + for (QValueList<KFileMetaInfoItem>::Iterator iit= readItems.begin(); + iit!=readItems.end(); ++iit) + { + QLabel* l = new QLabel((*iit).translatedKey() + ":", groupBox); + l->setAlignment( AlignAuto | AlignTop | ExpandTabs ); + (new KFileMetaInfoWidget(*iit, KFileMetaInfoWidget::ReadOnly, 0L, groupBox)); + } + } + + 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); + kdDebug(250) << "**first addable key is " << (*it).latin1() << "**" <<endl; + break; + } + kdDebug(250) << "**already existing key is " << (*it).latin1() << "**" <<endl; + } + } */ +} + +/*void KFileMetaPropsPlugin::slotAdd() +{ + // add a lineedit for the name + + + + // insert the item in the list + +}*/ + +KFileMetaPropsPlugin::~KFileMetaPropsPlugin() +{ + delete d; +} + +bool KFileMetaPropsPlugin::supports( KFileItemList _items ) +{ +#ifdef _GNUC +#warning TODO: Add support for more than one item +#endif + if (KExecPropsPlugin::supports(_items) || KURLPropsPlugin::supports(_items)) + return false; // Having both is redundant. + + bool metaDataEnabled = KGlobalSettings::showFilePreview(_items.first()->url()); + return _items.count() == 1 && metaDataEnabled; +} + +void KFileMetaPropsPlugin::applyChanges() +{ + kdDebug(250) << "applying changes" << endl; + // insert the fields that changed into the info object + + QPtrListIterator<KFileMetaInfoWidget> it( d->m_editWidgets ); + KFileMetaInfoWidget* w; + for (; (w = it.current()); ++it) w->apply(); + d->m_info.applyChanges(properties->kurl().path()); +} + +#include "kmetaprops.moc" diff --git a/kio/kfile/kmetaprops.h b/kio/kfile/kmetaprops.h new file mode 100644 index 000000000..dfa9b6e33 --- /dev/null +++ b/kio/kfile/kmetaprops.h @@ -0,0 +1,69 @@ +/* This file is part of the KDE libraries + Copyright (C) 2001,2002 Rolf Magnus <[email protected]> + + 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 __KMETAPROPS_H__ +#define __KMETAPROPS_H__ +#include <kpropertiesdialog.h> + + +class KFileMetaInfoItem; + +/*! + * 'MetaProps plugin + * In this plugin you can modify meta information like id3 tags of mp3 files + */ +class KIO_EXPORT KFileMetaPropsPlugin : public KPropsDlgPlugin +{ + Q_OBJECT +public: + /** + * Constructor + */ + KFileMetaPropsPlugin( KPropertiesDialog *_props ); + virtual ~KFileMetaPropsPlugin(); + + virtual void applyChanges(); + + /** + * Tests whether the file specified by _items has a 'MetaInfo' plugin. + */ + static bool supports( KFileItemList _items ); + +private: + void createLayout(); + + QWidget* makeBoolWidget(const KFileMetaInfoItem& item, QWidget* parent); + QWidget* makeIntWidget(const KFileMetaInfoItem& item, QWidget* parent, + QString& valClass); + QWidget* makeStringWidget(const KFileMetaInfoItem& item, QWidget* parent, + QString& valClass); + QWidget* makeDateTimeWidget(const KFileMetaInfoItem& item, QWidget* parent, + QString& valClass); + +private slots: + // Code disabled until the "Add" button is implemented +// void slotAdd(); + +private: + + class KFileMetaPropsPluginPrivate; + KFileMetaPropsPluginPrivate *d; +}; + +#endif diff --git a/kio/kfile/knotifydialog.cpp b/kio/kfile/knotifydialog.cpp new file mode 100644 index 000000000..6543344e2 --- /dev/null +++ b/kio/kfile/knotifydialog.cpp @@ -0,0 +1,1191 @@ +/* + Copyright (C) 2000,2002 Carsten Pfeiffer <[email protected]> + Copyright (C) 2002 Neil Stevens <[email protected]> + + This program 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 program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library, If not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include <dcopclient.h> + +#include <kaboutdata.h> +#include <kapplication.h> +#include <kaudioplayer.h> +#include <kcombobox.h> +#include <kconfig.h> +#include <kcursor.h> +#include <kdebug.h> +#include <kfiledialog.h> +#include <kiconloader.h> +#include <kicontheme.h> +#include <klineedit.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <knotifyclient.h> +#include <knotifydialog.h> +#include <kstandarddirs.h> +#include <kurlrequester.h> +#include <kio/netaccess.h> + +#include <qcheckbox.h> +#include <qgroupbox.h> +#include <qheader.h> +#include <qlabel.h> +#include <qlistview.h> +#include <qlayout.h> +#include <qptrlist.h> +#include <qpushbutton.h> +#include <qstring.h> +#include <qtooltip.h> +#include <qtimer.h> +#include <qvbox.h> +#include <qwhatsthis.h> + +using namespace KNotify; + +enum +{ + COL_EXECUTE = 0, + COL_STDERR = 1, + COL_MESSAGE = 2, + COL_LOGFILE = 3, + COL_SOUND = 4, + COL_TASKBAR = 5, + COL_EVENT = 6 +}; + +// +// I don't feel like subclassing KComboBox and find ways to insert that into +// the .ui file... +// +namespace KNotify +{ + class SelectionCombo + { + public: + // + // Mind the order in fill() and type() + // + static void fill( KComboBox *combo ) + { + combo->insertItem( i18n("Sounds") ); + combo->insertItem( i18n("Logging") ); + combo->insertItem( i18n("Program Execution") ); + combo->insertItem( i18n("Message Windows") ); + combo->insertItem( i18n("Passive Windows") ); + combo->insertItem( i18n("Standard Error Output") ); + combo->insertItem( i18n("Taskbar") ); + } + + static int type( KComboBox *combo ) + { + switch( combo->currentItem() ) + { + case 0: + return KNotifyClient::Sound; + case 1: + return KNotifyClient::Logfile; + case 2: + return KNotifyClient::Execute; + case 3: + return KNotifyClient::Messagebox; + case 4: + return KNotifyClient::PassivePopup; + case 5: + return KNotifyClient::Stderr; + case 6: + return KNotifyClient::Taskbar; + } + + return KNotifyClient::None; + } + }; + + // Needed for displaying tooltips in the listview's QHeader + class KNotifyToolTip : public QToolTip + { + public: + KNotifyToolTip( QHeader *header ) + : QToolTip( header ) + { + m_tips[COL_EXECUTE] = i18n("Execute a program"); + m_tips[COL_STDERR] = i18n("Print to Standard error output"); + m_tips[COL_MESSAGE] = i18n("Display a messagebox"); + m_tips[COL_LOGFILE] = i18n("Log to a file"); + m_tips[COL_SOUND] = i18n("Play a sound"); + m_tips[COL_TASKBAR] = i18n("Flash the taskbar entry"); + } + virtual ~KNotifyToolTip() {} + + protected: + virtual void maybeTip ( const QPoint& p ) + { + QHeader *header = static_cast<QHeader*>( parentWidget() ); + int section = 0; + + if ( header->orientation() == Horizontal ) + section= header->sectionAt( p.x() ); + else + section= header->sectionAt( p.y() ); + + if ( ( section < 0 ) || ( static_cast<uint>( section ) >= (sizeof(m_tips) / sizeof(QString)) ) ) + return; + + tip( header->sectionRect( section ), m_tips[section] ); + } + + private: + QString m_tips[6]; + }; + +} + + +int KNotifyDialog::configure( QWidget *parent, const char *name, + const KAboutData *aboutData ) +{ + KNotifyDialog dialog( parent, name, true, aboutData ); + return dialog.exec(); +} + +KNotifyDialog::KNotifyDialog( QWidget *parent, const char *name, bool modal, + const KAboutData *aboutData ) + : KDialogBase(parent, name, modal, i18n("Notification Settings"), + Ok | Apply | Cancel | Default, Ok, true ) +{ + QVBox *box = makeVBoxMainWidget(); + + m_notifyWidget = new KNotifyWidget( box, "knotify widget" ); + + if ( aboutData ) + addApplicationEvents( aboutData->appName() ); + + connect( this, SIGNAL( okClicked() ), m_notifyWidget, SLOT( save() )); + connect( this, SIGNAL( applyClicked() ), m_notifyWidget, SLOT( save() )); +} + +KNotifyDialog::~KNotifyDialog() +{ +} + +void KNotifyDialog::addApplicationEvents( const char *appName ) +{ + addApplicationEvents( QString::fromUtf8( appName ) + + QString::fromLatin1( "/eventsrc" ) ); +} + +void KNotifyDialog::addApplicationEvents( const QString& path ) +{ + Application *app = m_notifyWidget->addApplicationEvents( path ); + if ( app ) + { + m_notifyWidget->addVisibleApp( app ); + m_notifyWidget->sort(); + } +} + +void KNotifyDialog::clearApplicationEvents() +{ + m_notifyWidget->clear(); +} + +void KNotifyDialog::slotDefault() +{ + m_notifyWidget->resetDefaults( true ); // ask user +} + + +////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// + + +class KNotifyWidget::Private +{ +public: + QPixmap pixmaps[6]; + KNotifyToolTip *toolTip; +}; + +// simple access to all knotify-handled applications +KNotifyWidget::KNotifyWidget( QWidget *parent, const char *name, + bool handleAllApps ) + : KNotifyWidgetBase( parent, name ? name : "KNotifyWidget" ) +{ + d = new Private; + + m_allApps.setAutoDelete( true ); + + if ( !handleAllApps ) + { + m_affectAllApps->hide(); + m_playerButton->hide(); + } + + SelectionCombo::fill( m_comboEnable ); + SelectionCombo::fill( m_comboDisable ); + + m_listview->setFullWidth( true ); + m_listview->setAllColumnsShowFocus( true ); + + QPixmap pexec = SmallIcon("exec"); + QPixmap pstderr = SmallIcon("terminal"); + QPixmap pmessage = SmallIcon("info"); + QPixmap plogfile = SmallIcon("log"); + QPixmap psound = SmallIcon("sound"); + QPixmap ptaskbar = SmallIcon("kicker"); + + d->pixmaps[COL_EXECUTE] = pexec; + d->pixmaps[COL_STDERR] = pstderr; + d->pixmaps[COL_MESSAGE] = pmessage; + d->pixmaps[COL_LOGFILE] = plogfile; + d->pixmaps[COL_SOUND] = psound; + d->pixmaps[COL_TASKBAR] = ptaskbar; + + int w = KIcon::SizeSmall + 6; + + QHeader *header = m_listview->header(); + header->setLabel( COL_EXECUTE, pexec, QString::null, w ); + header->setLabel( COL_STDERR, pstderr, QString::null, w ); + header->setLabel( COL_MESSAGE, pmessage, QString::null, w ); + header->setLabel( COL_LOGFILE, plogfile, QString::null, w ); + header->setLabel( COL_SOUND, psound, QString::null, w ); + header->setLabel( COL_TASKBAR, ptaskbar, QString::null, w ); + + d->toolTip = new KNotifyToolTip( header ); + + m_playButton->setIconSet( SmallIconSet( "player_play" ) ); + connect( m_playButton, SIGNAL( clicked() ), SLOT( playSound() )); + + connect( m_listview, SIGNAL( currentChanged( QListViewItem * ) ), + SLOT( slotEventChanged( QListViewItem * ) )); + connect( m_listview, SIGNAL(clicked( QListViewItem *, const QPoint&, int)), + SLOT( slotItemClicked( QListViewItem *, const QPoint&, int ))); + + connect( m_playSound, SIGNAL( toggled( bool )), + SLOT( soundToggled( bool )) ); + connect( m_logToFile, SIGNAL( toggled( bool )), + SLOT( loggingToggled( bool )) ); + connect( m_execute, SIGNAL( toggled( bool )), + SLOT( executeToggled( bool )) ); + connect( m_messageBox, SIGNAL( toggled( bool )), + SLOT( messageBoxChanged() ) ); + connect( m_passivePopup, SIGNAL( toggled( bool )), + SLOT( messageBoxChanged() ) ); + connect( m_stderr, SIGNAL( toggled( bool )), + SLOT( stderrToggled( bool ) ) ); + connect( m_taskbar, SIGNAL( toggled( bool )), + SLOT( taskbarToggled( bool ) ) ); + + connect( m_soundPath, SIGNAL( textChanged( const QString& )), + SLOT( soundFileChanged( const QString& ))); + connect( m_logfilePath, SIGNAL( textChanged( const QString& )), + SLOT( logfileChanged( const QString& ) )); + connect( m_executePath, SIGNAL( textChanged( const QString& )), + SLOT( commandlineChanged( const QString& ) )); + + connect( m_soundPath, SIGNAL( openFileDialog( KURLRequester * )), + SLOT( openSoundDialog( KURLRequester * ))); + connect( m_logfilePath, SIGNAL( openFileDialog( KURLRequester * )), + SLOT( openLogDialog( KURLRequester * ))); + connect( m_executePath, SIGNAL( openFileDialog( KURLRequester * )), + SLOT( openExecDialog( KURLRequester * ))); + + connect( m_extension, SIGNAL( clicked() ), + SLOT( toggleAdvanced()) ); + + connect( m_buttonEnable, SIGNAL( clicked() ), SLOT( enableAll() )); + connect( m_buttonDisable, SIGNAL( clicked() ), SLOT( enableAll() )); + + QString whatsThis = i18n("<qt>You may use the following macros<br>" + "in the commandline:<br>" + "<b>%e</b>: for the event name,<br>" + "<b>%a</b>: for the name of the application that sent the event,<br>" + "<b>%s</b>: for the notification message,<br>" + "<b>%w</b>: for the numeric window ID where the event originated,<br>" + "<b>%i</b>: for the numeric event ID."); + QWhatsThis::add( m_execute, whatsThis ); + QWhatsThis::add( m_executePath, whatsThis ); + + showAdvanced( false ); + + slotEventChanged( 0L ); // disable widgets by default +} + +KNotifyWidget::~KNotifyWidget() +{ + delete d->toolTip; + delete d; +} + +void KNotifyWidget::toggleAdvanced() +{ + showAdvanced( m_logToFile->isHidden() ); +} + +void KNotifyWidget::showAdvanced( bool show ) +{ + if ( show ) + { + m_extension->setText( i18n("Advanced <<") ); + QToolTip::add( m_extension, i18n("Hide advanced options") ); + + m_logToFile->show(); + m_logfilePath->show(); + m_execute->show(); + m_executePath->show(); + m_messageBox->show(); + m_passivePopup->show(); + m_stderr->show(); + m_taskbar->show(); + + m_passivePopup->setEnabled( m_messageBox->isChecked() ); + m_actionsBoxLayout->setSpacing( KDialog::spacingHint() ); + } + else + { + m_extension->setText( i18n("Advanced >>") ); + QToolTip::add( m_extension, i18n("Show advanced options") ); + + m_logToFile->hide(); + m_logfilePath->hide(); + m_execute->hide(); + m_executePath->hide(); + m_messageBox->hide(); + m_passivePopup->hide(); + m_stderr->hide(); + m_taskbar->hide(); + + m_actionsBoxLayout->setSpacing( 0 ); + } +} + +Application * KNotifyWidget::addApplicationEvents( const QString& path ) +{ + kdDebug() << "**** knotify: adding path: " << path << endl; + QString relativePath = path; + + if ( path.at(0) == '/' && KStandardDirs::exists( path ) ) + relativePath = makeRelative( path ); + + if ( !relativePath.isEmpty() ) + { + Application *app = new Application( relativePath ); + m_allApps.append( app ); + return app; + } + + return 0L; +} + +void KNotifyWidget::clear() +{ + clearVisible(); + m_allApps.clear(); +} + +void KNotifyWidget::clearVisible() +{ + m_visibleApps.clear(); + m_listview->clear(); + slotEventChanged( 0L ); // disable widgets +} + +void KNotifyWidget::showEvent( QShowEvent *e ) +{ + selectItem( m_listview->firstChild() ); + KNotifyWidgetBase::showEvent( e ); +} + +void KNotifyWidget::slotEventChanged( QListViewItem *item ) +{ + bool on = (item != 0L); + + m_actionsBox->setEnabled( on ); + m_controlsBox->setEnabled( on ); + + if ( !on ) + return; + + ListViewItem *lit = static_cast<ListViewItem*>( item ); + updateWidgets( lit ); +} + +void KNotifyWidget::updateWidgets( ListViewItem *item ) +{ + bool enable; + bool checked; + + blockSignals( true ); // don't emit changed() signals + + const Event& event = item->event(); + + // sound settings + m_playButton->setEnabled( !event.soundfile.isEmpty() ); + m_soundPath->setURL( event.soundfile ); + enable = (event.dontShow & KNotifyClient::Sound) == 0; + checked = enable && !event.soundfile.isEmpty() && + (event.presentation & KNotifyClient::Sound); + m_playSound->setEnabled( enable ); + m_playSound->setChecked( checked ); + m_soundPath->setEnabled( checked ); + + + // logfile settings + m_logfilePath->setURL( event.logfile ); + enable = (event.dontShow & KNotifyClient::Logfile) == 0; + checked = enable && !event.logfile.isEmpty() && + (event.presentation & KNotifyClient::Logfile); + m_logToFile->setEnabled( enable ); + m_logToFile->setChecked( checked ); + m_logfilePath->setEnabled( checked ); + + + // execute program settings + m_executePath->setURL( event.commandline ); + enable = (event.dontShow & KNotifyClient::Execute) == 0; + checked = enable && !event.commandline.isEmpty() && + (event.presentation & KNotifyClient::Execute); + m_execute->setEnabled( enable ); + m_execute->setChecked( checked ); + m_executePath->setEnabled( checked ); + + + // other settings + m_messageBox->setChecked(event.presentation & (KNotifyClient::Messagebox | KNotifyClient::PassivePopup)); + enable = (event.dontShow & KNotifyClient::Messagebox) == 0; + m_messageBox->setEnabled( enable ); + + m_passivePopup->setChecked(event.presentation & KNotifyClient::PassivePopup); + enable = (event.dontShow & KNotifyClient::PassivePopup) == 0; + m_passivePopup->setEnabled( enable ); + + m_stderr->setChecked( event.presentation & KNotifyClient::Stderr ); + enable = (event.dontShow & KNotifyClient::Stderr) == 0; + m_stderr->setEnabled( enable ); + + m_taskbar->setChecked(event.presentation & KNotifyClient::Taskbar); + enable = (event.dontShow & KNotifyClient::Taskbar) == 0; + m_taskbar->setEnabled( enable ); + + updatePixmaps( item ); + + blockSignals( false ); +} + +void KNotifyWidget::updatePixmaps( ListViewItem *item ) +{ + QPixmap emptyPix; + Event &event = item->event(); + + bool doIt = (event.presentation & KNotifyClient::Execute) && + !event.commandline.isEmpty(); + item->setPixmap( COL_EXECUTE, doIt ? d->pixmaps[COL_EXECUTE] : emptyPix ); + + doIt = (event.presentation & KNotifyClient::Sound) && + !event.soundfile.isEmpty(); + item->setPixmap( COL_SOUND, doIt ? d->pixmaps[COL_SOUND] : emptyPix ); + + doIt = (event.presentation & KNotifyClient::Logfile) && + !event.logfile.isEmpty(); + item->setPixmap( COL_LOGFILE, doIt ? d->pixmaps[COL_LOGFILE] : emptyPix ); + + item->setPixmap( COL_MESSAGE, + (event.presentation & + (KNotifyClient::Messagebox | KNotifyClient::PassivePopup)) ? + d->pixmaps[COL_MESSAGE] : emptyPix ); + + item->setPixmap( COL_STDERR, + (event.presentation & KNotifyClient::Stderr) ? + d->pixmaps[COL_STDERR] : emptyPix ); + item->setPixmap( COL_TASKBAR, + (event.presentation & KNotifyClient::Taskbar) ? + d->pixmaps[COL_TASKBAR] : emptyPix ); +} + +void KNotifyWidget::addVisibleApp( Application *app ) +{ + if ( !app || (m_visibleApps.findRef( app ) != -1) ) + return; + + m_visibleApps.append( app ); + addToView( app->eventList() ); + + QListViewItem *item = m_listview->selectedItem(); + if ( !item ) + item = m_listview->firstChild(); + + selectItem( item ); +} + +void KNotifyWidget::addToView( const EventList& events ) +{ + ListViewItem *item = 0L; + + EventListIterator it( events ); + + for ( ; it.current(); ++it ) + { + Event *event = it.current(); + item = new ListViewItem( m_listview, event ); + + if ( (event->presentation & KNotifyClient::Execute) && + !event->commandline.isEmpty() ) + item->setPixmap( COL_EXECUTE, d->pixmaps[COL_EXECUTE] ); + if ( (event->presentation & KNotifyClient::Sound) && + !event->soundfile.isEmpty() ) + item->setPixmap( COL_SOUND, d->pixmaps[COL_SOUND] ); + if ( (event->presentation & KNotifyClient::Logfile) && + !event->logfile.isEmpty() ) + item->setPixmap( COL_LOGFILE, d->pixmaps[COL_LOGFILE] ); + if ( event->presentation & (KNotifyClient::Messagebox|KNotifyClient::PassivePopup) ) + item->setPixmap( COL_MESSAGE, d->pixmaps[COL_MESSAGE] ); + if ( event->presentation & KNotifyClient::Stderr ) + item->setPixmap( COL_STDERR, d->pixmaps[COL_STDERR] ); + if ( event->presentation & KNotifyClient::Taskbar ) + item->setPixmap( COL_TASKBAR, d->pixmaps[COL_TASKBAR] ); + } +} + +void KNotifyWidget::widgetChanged( QListViewItem *item, + int what, bool on, QWidget *buddy ) +{ + if ( signalsBlocked() ) + return; + + if ( buddy ) + buddy->setEnabled( on ); + + Event &e = static_cast<ListViewItem*>( item )->event(); + if ( on ) + { + e.presentation |= what; + if ( buddy ) + buddy->setFocus(); + } + else + e.presentation &= ~what; + + emit changed( true ); +} + +void KNotifyWidget::soundToggled( bool on ) +{ + QListViewItem *item = m_listview->currentItem(); + if ( !item ) + return; + bool doIcon = on && !m_soundPath->url().isEmpty(); + item->setPixmap( COL_SOUND, doIcon ? d->pixmaps[COL_SOUND] : QPixmap() ); + widgetChanged( item, KNotifyClient::Sound, on, m_soundPath ); +} + +void KNotifyWidget::loggingToggled( bool on ) +{ + QListViewItem *item = m_listview->currentItem(); + if ( !item ) + return; + bool doIcon = on && !m_logfilePath->url().isEmpty(); + item->setPixmap(COL_LOGFILE, doIcon ? d->pixmaps[COL_LOGFILE] : QPixmap()); + widgetChanged( item, KNotifyClient::Logfile, on, m_logfilePath ); +} + +void KNotifyWidget::executeToggled( bool on ) +{ + QListViewItem *item = m_listview->currentItem(); + if ( !item ) + return; + bool doIcon = on && !m_executePath->url().isEmpty(); + item->setPixmap(COL_EXECUTE, doIcon ? d->pixmaps[COL_EXECUTE] : QPixmap()); + widgetChanged( item, KNotifyClient::Execute, on, m_executePath ); +} + +void KNotifyWidget::messageBoxChanged() +{ + if ( signalsBlocked() ) + return; + + m_passivePopup->setEnabled( m_messageBox->isChecked() ); + + QListViewItem *item = m_listview->currentItem(); + if ( !item ) + return; + + bool on = m_passivePopup->isEnabled(); + item->setPixmap( COL_MESSAGE, on ? d->pixmaps[COL_MESSAGE] : QPixmap() ); + + Event &e = static_cast<ListViewItem*>( item )->event(); + + if ( m_messageBox->isChecked() ) { + if ( m_passivePopup->isChecked() ) { + e.presentation |= KNotifyClient::PassivePopup; + e.presentation &= ~KNotifyClient::Messagebox; + } + else { + e.presentation &= ~KNotifyClient::PassivePopup; + e.presentation |= KNotifyClient::Messagebox; + } + } + else { + e.presentation &= ~KNotifyClient::Messagebox; + e.presentation &= ~KNotifyClient::PassivePopup; + } + + emit changed( true ); +} + +void KNotifyWidget::stderrToggled( bool on ) +{ + QListViewItem *item = m_listview->currentItem(); + if ( !item ) + return; + item->setPixmap( COL_STDERR, on ? d->pixmaps[COL_STDERR] : QPixmap() ); + widgetChanged( item, KNotifyClient::Stderr, on ); +} + +void KNotifyWidget::taskbarToggled( bool on ) +{ + QListViewItem *item = m_listview->currentItem(); + if ( !item ) + return; + item->setPixmap( COL_TASKBAR, on ? d->pixmaps[COL_TASKBAR] : QPixmap() ); + widgetChanged( item, KNotifyClient::Taskbar, on ); +} + +void KNotifyWidget::soundFileChanged( const QString& text ) +{ + if ( signalsBlocked() ) + return; + + QListViewItem *item = m_listview->currentItem(); + if ( !item ) + return; + + m_playButton->setEnabled( !text.isEmpty() ); + + currentEvent()->soundfile = text; + bool ok = !text.isEmpty() && m_playSound->isChecked(); + item->setPixmap( COL_SOUND, ok ? d->pixmaps[COL_SOUND] : QPixmap() ); + + emit changed( true ); +} + +void KNotifyWidget::logfileChanged( const QString& text ) +{ + if ( signalsBlocked() ) + return; + + QListViewItem *item = m_listview->currentItem(); + if ( !item ) + return; + + currentEvent()->logfile = text; + bool ok = !text.isEmpty() && m_logToFile->isChecked(); + item->setPixmap( COL_LOGFILE, ok ? d->pixmaps[COL_LOGFILE] : QPixmap() ); + + emit changed( true ); +} + +void KNotifyWidget::commandlineChanged( const QString& text ) +{ + if ( signalsBlocked() ) + return; + + QListViewItem *item = m_listview->currentItem(); + if ( !item ) + return; + + currentEvent()->commandline = text; + bool ok = !text.isEmpty() && m_execute->isChecked(); + item->setPixmap( COL_EXECUTE, ok ? d->pixmaps[COL_EXECUTE] : QPixmap() ); + + emit changed( true ); +} + +void KNotifyWidget::slotItemClicked( QListViewItem *item, const QPoint&, + int col ) +{ + if ( !item || !item->isSelected() ) + return; + + Event *event = currentEvent(); + if ( !event ) + return; // very unlikely, but safety first + + bool doShowAdvanced = false; + + switch( col ) + { + case COL_EXECUTE: + m_execute->toggle(); + m_executePath->setFocus(); + doShowAdvanced = true; + break; + case COL_STDERR: + m_stderr->toggle(); + break; + case COL_TASKBAR: + m_taskbar->toggle(); + break; + case COL_MESSAGE: + m_passivePopup->setChecked( true ); // default to passive popups + m_messageBox->toggle(); + break; + case COL_LOGFILE: + m_logToFile->toggle(); + m_logfilePath->setFocus(); + doShowAdvanced = true; + break; + case COL_SOUND: + m_playSound->toggle(); + break; + default: // do nothing + break; + } + + if ( doShowAdvanced && !m_logToFile->isVisible() ) + { + showAdvanced( true ); + m_listview->ensureItemVisible( m_listview->currentItem() ); + } +} + +void KNotifyWidget::sort( bool ascending ) +{ + m_listview->setSorting( COL_EVENT, ascending ); + m_listview->sort(); +} + +void KNotifyWidget::selectItem( QListViewItem *item ) +{ + if ( item ) + { + m_listview->setCurrentItem( item ); + item->setSelected( true ); + slotEventChanged( item ); + } +} + +void KNotifyWidget::resetDefaults( bool ask ) +{ + if ( ask ) + { + if ( KMessageBox::warningContinueCancel(this, + i18n("This will cause the notifications " + "to be reset to their defaults."), + i18n("Are You Sure?"), + i18n("&Reset")) + != KMessageBox::Continue) + return; + } + + reload( true ); // defaults + emit changed( true ); +} + +void KNotifyWidget::reload( bool revertToDefaults ) +{ + m_listview->clear(); + ApplicationListIterator it( m_visibleApps ); + for ( ; it.current(); ++it ) + { + it.current()->reloadEvents( revertToDefaults ); + addToView( it.current()->eventList() ); + } + + m_listview->sort(); + selectItem( m_listview->firstChild() ); +} + +void KNotifyWidget::save() +{ + kdDebug() << "save\n"; + + ApplicationListIterator it( m_allApps ); + while ( it.current() ) + { + (*it)->save(); + ++it; + } + + if ( kapp ) + { + if ( !kapp->dcopClient()->isAttached() ) + kapp->dcopClient()->attach(); + kapp->dcopClient()->send("knotify", "", "reconfigure()", ""); + } + + emit changed( false ); +} + +// returns e.g. "kwin/eventsrc" from a given path +// "/opt/kde3/share/apps/kwin/eventsrc" +QString KNotifyWidget::makeRelative( const QString& fullPath ) +{ + int slash = fullPath.findRev( '/' ) - 1; + slash = fullPath.findRev( '/', slash ); + + if ( slash < 0 ) + return QString::null; + + return fullPath.mid( slash+1 ); +} + +Event * KNotifyWidget::currentEvent() +{ + QListViewItem *current = m_listview->currentItem(); + if ( !current ) + return 0L; + + return &static_cast<ListViewItem*>( current )->event(); +} + +void KNotifyWidget::openSoundDialog( KURLRequester *requester ) +{ + // only need to init this once + requester->disconnect( SIGNAL( openFileDialog( KURLRequester * )), + this, SLOT( openSoundDialog( KURLRequester * ))); + + KFileDialog *fileDialog = requester->fileDialog(); + fileDialog->setCaption( i18n("Select Sound File") ); + QStringList filters; + filters << "audio/x-wav" << "audio/x-mp3" << "application/ogg" + << "audio/x-adpcm"; + fileDialog->setMimeFilter( filters ); + + // find the first "sound"-resource that contains files + const Application *app = currentEvent()->application(); + QStringList soundDirs = + KGlobal::dirs()->findDirs("data", app->appName() + "/sounds"); + soundDirs += KGlobal::dirs()->resourceDirs( "sound" ); + + if ( !soundDirs.isEmpty() ) { + KURL soundURL; + QDir dir; + dir.setFilter( QDir::Files | QDir::Readable ); + QStringList::ConstIterator it = soundDirs.begin(); + while ( it != soundDirs.end() ) { + dir = *it; + if ( dir.isReadable() && dir.count() > 2 ) { + soundURL.setPath( *it ); + fileDialog->setURL( soundURL ); + break; + } + ++it; + } + } +} + +void KNotifyWidget::openLogDialog( KURLRequester *requester ) +{ + // only need to init this once + requester->disconnect( SIGNAL( openFileDialog( KURLRequester * )), + this, SLOT( openLogDialog( KURLRequester * ))); + + KFileDialog *fileDialog = requester->fileDialog(); + fileDialog->setCaption( i18n("Select Log File") ); + QStringList filters; + filters << "text/x-log" << "text/plain"; + fileDialog->setMimeFilter( filters ); +} + +void KNotifyWidget::openExecDialog( KURLRequester *requester ) +{ + // only need to init this once + requester->disconnect( SIGNAL( openFileDialog( KURLRequester * )), + this, SLOT( openExecDialog( KURLRequester * ))); + + + KFileDialog *fileDialog = requester->fileDialog(); + fileDialog->setCaption( i18n("Select File to Execute") ); + QStringList filters; + filters << "application/x-executable" << "application/x-shellscript" + << "application/x-perl" << "application/x-python"; + fileDialog->setMimeFilter( filters ); +} + +void KNotifyWidget::playSound() +{ + QString soundPath = m_soundPath->url(); + if (!KIO::NetAccess::exists( m_soundPath->url(), true, 0 )) { + bool foundSound=false; + + // find the first "sound"-resource that contains files + const Application *app = currentEvent()->application(); + QStringList soundDirs = KGlobal::dirs()->findDirs("data", app->appName() + "/sounds"); + soundDirs += KGlobal::dirs()->resourceDirs( "sound" ); + + if ( !soundDirs.isEmpty() ) { + QDir dir; + dir.setFilter( QDir::Files | QDir::Readable ); + QStringList::ConstIterator it = soundDirs.begin(); + while ( it != soundDirs.end() ) { + dir = *it; + if ( dir.isReadable() && dir.count() > 2 && + KIO::NetAccess::exists( *it + m_soundPath->url(), true, 0 )) { + foundSound=true; + soundPath = *it + m_soundPath->url(); + break; + } + ++it; + } + } + if ( !foundSound ) { + KMessageBox::sorry(this, i18n("The specified file does not exist." )); + return; + } + } + KAudioPlayer::play( soundPath ); +} + +void KNotifyWidget::enableAll() +{ + bool enable = (sender() == m_buttonEnable); + enableAll( SelectionCombo::type(enable ? m_comboEnable : m_comboDisable), + enable ); +} + +void KNotifyWidget::enableAll( int what, bool enable ) +{ + if ( m_listview->childCount() == 0 ) + return; + + bool affectAll = m_affectAllApps->isChecked(); // multi-apps mode + + ApplicationListIterator appIt( affectAll ? m_allApps : m_visibleApps ); + for ( ; appIt.current(); ++appIt ) + { + const EventList& events = appIt.current()->eventList(); + EventListIterator it( events ); + for ( ; it.current(); ++it ) + { + if ( enable ) + it.current()->presentation |= what; + else + it.current()->presentation &= ~what; + } + } + + // now make the listview reflect the changes + QListViewItemIterator it( m_listview->firstChild() ); + for ( ; it.current(); ++it ) + { + ListViewItem *item = static_cast<ListViewItem*>( it.current() ); + updatePixmaps( item ); + } + + QListViewItem *item = m_listview->currentItem(); + if ( !item ) + item = m_listview->firstChild(); + selectItem( item ); + + emit changed( true ); +} + + +////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// + + +// +// path must be "appname/eventsrc", i.e. a relative path +// +Application::Application( const QString &path ) +{ + QString config_file = path; + config_file[config_file.find('/')] = '.'; + m_events = 0L; + config = new KConfig(config_file, false, false); + kc = new KConfig(path, true, false, "data"); + kc->setGroup( QString::fromLatin1("!Global!") ); + m_icon = kc->readEntry(QString::fromLatin1("IconName"), + QString::fromLatin1("misc")); + m_description = kc->readEntry( QString::fromLatin1("Comment"), + i18n("No description available") ); + + int index = path.find( '/' ); + if ( index >= 0 ) + m_appname = path.left( index ); + else + kdDebug() << "Cannot determine application name from path: " << path << endl; +} + +Application::~Application() +{ + delete config; + delete kc; + delete m_events; +} + + +const EventList& Application::eventList() +{ + if ( !m_events ) { + m_events = new EventList; + m_events->setAutoDelete( true ); + reloadEvents(); + } + + return *m_events; +} + + +void Application::save() +{ + if ( !m_events ) + return; + + EventListIterator it( *m_events ); + Event *e; + while ( (e = it.current()) ) { + config->setGroup( e->configGroup ); + config->writeEntry( "presentation", e->presentation ); + config->writePathEntry( "soundfile", e->soundfile ); + config->writePathEntry( "logfile", e->logfile ); + config->writePathEntry( "commandline", e->commandline ); + + ++it; + } + config->sync(); +} + + +void Application::reloadEvents( bool revertToDefaults ) +{ + if ( m_events ) + m_events->clear(); + else + { + m_events = new EventList; + m_events->setAutoDelete( true ); + } + + Event *e = 0L; + + QString global = QString::fromLatin1("!Global!"); + QString default_group = QString::fromLatin1("<default>"); + QString name = QString::fromLatin1("Name"); + QString comment = QString::fromLatin1("Comment"); + + QStringList conflist = kc->groupList(); + QStringList::ConstIterator it = conflist.begin(); + + while ( it != conflist.end() ) { + if ( (*it) != global && (*it) != default_group ) { // event group + kc->setGroup( *it ); + + e = new Event( this ); + e->name = kc->readEntry( name ); + e->description = kc->readEntry( comment ); + e->dontShow = kc->readNumEntry("nopresentation", 0 ); + e->configGroup = *it; + if ( e->name.isEmpty() && e->description.isEmpty() ) + delete e; + else { // load the event + if( !e->name.isEmpty() && e->description.isEmpty() ) + e->description = e->name; + // default to passive popups over plain messageboxes + int default_rep = kc->readNumEntry("default_presentation", + 0 | KNotifyClient::PassivePopup); + QString default_logfile = kc->readPathEntry("default_logfile"); + QString default_soundfile = kc->readPathEntry("default_sound"); + QString default_commandline = kc->readPathEntry("default_commandline"); + + config->setGroup(*it); + + if ( revertToDefaults ) + { + e->presentation = default_rep; + e->logfile = default_logfile; + e->soundfile = default_soundfile; + e->commandline = default_commandline; + } + + else + { + e->presentation = config->readNumEntry("presentation", + default_rep); + e->logfile = config->readPathEntry("logfile", + default_logfile); + e->soundfile = config->readPathEntry("soundfile", + default_soundfile); + e->commandline = config->readPathEntry("commandline", + default_commandline); + } + + m_events->append( e ); + } + } + + ++it; + } + + return; +} + +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// + +ListViewItem::ListViewItem( QListView *view, Event *event ) + : QListViewItem( view ), + m_event( event ) +{ + setText( COL_EVENT, event->text() ); +} + +int ListViewItem::compare ( QListViewItem * i, int col, bool ascending ) const +{ + ListViewItem *item = static_cast<ListViewItem*>( i ); + int myPres = m_event->presentation; + int otherPres = item->event().presentation; + + int action = 0; + + switch ( col ) + { + case COL_EVENT: // use default sorting + return QListViewItem::compare( i, col, ascending ); + + case COL_EXECUTE: + action = KNotifyClient::Execute; + break; + case COL_LOGFILE: + action = KNotifyClient::Logfile; + break; + case COL_MESSAGE: + action = (KNotifyClient::Messagebox | KNotifyClient::PassivePopup); + break; + case COL_SOUND: + action = KNotifyClient::Sound; + break; + case COL_STDERR: + action = KNotifyClient::Stderr; + break; + case COL_TASKBAR: + action = KNotifyClient::Taskbar; + break; + } + + if ( (myPres & action) == (otherPres & action) ) + { + // default sorting by event + return QListViewItem::compare( i, COL_EVENT, true ); + } + + if ( myPres & action ) + return -1; + if ( otherPres & action ) + return 1; + + return 0; +} + +#include "knotifydialog.moc" diff --git a/kio/kfile/knotifydialog.h b/kio/kfile/knotifydialog.h new file mode 100644 index 000000000..3d4074fd0 --- /dev/null +++ b/kio/kfile/knotifydialog.h @@ -0,0 +1,341 @@ +/* + Copyright (C) 2000,2002 Carsten Pfeiffer <[email protected]> + Copyright (C) 2002 Neil Stevens <[email protected]> + + This program 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 program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library, If not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef KNOTIFYDIALOG_H +#define KNOTIFYDIALOG_H + +#include <klistview.h> +#include <kdialogbase.h> +#include <kinstance.h> +#include <kglobal.h> + +#include "knotifywidgetbase.h" + +class QShowEvent; + +namespace KNotify +{ + class KNotifyWidget; +} + +/** + * KNotifyDialog presents an interface for configuring an application's + * KNotify events. + * + * Rather than requiring the user to wade through the entire list of + * applications' events in KControl, your application can make the list + * of its own notifications available here. + * + * Typical usage is calling the static configure() method: + * \code + * (void) KNotifyDialog::configure( someParentWidget ); + * \endcode + * + * @since 3.1 + * @author Carsten Pfeiffer <[email protected]> + */ +class KIO_EXPORT KNotifyDialog : public KDialogBase +{ + Q_OBJECT + +public: + /** + * If you want a non-modal dialog, you need to instantiate KNotifyDialog + * yourself instead of using the configure() method. + * + * KDE4.0 modal default will be false. + * + * @param parent The parent widget for the dialog + * @param name The widget name + * @param modal If true, this will be a modal dialog, otherwise non-modal. + * @param aboutData A pointer to a KAboutData object. KAboutData::appName() + * will be used to find the KNotify events (in the eventsrc file). + * Set this to 0L if you want to add all events yourself with + * addApplicationEvents(). + */ + KNotifyDialog( QWidget *parent = 0, const char *name = 0, + bool modal = true, + const KAboutData *aboutData = + KGlobal::instance()->aboutData() ); + /** + * Destroys the KNotifyDialog + */ + virtual ~KNotifyDialog(); + + /** + * Convenience method to create exec() a modal KNotifyDialog. + * + * @param parent The parent widget for the dialog + * @param name The widget name + * @param aboutData A pointer to a KAboutData object. KAboutData::appName() + * will be used to find the KNotify events (in the eventsrc file). + * @see exec for the return values. + * @return The value of QDialog::exec() + */ + static int configure( QWidget *parent = 0, const char *name = 0, + const KAboutData *aboutData = KGlobal::instance()->aboutData() ); + + /** + * With this method, you can add the KNotify events of one eventsrc + * files to the view. + * KNotifyDialog can handle events for multiple applications (i.e. eventsrc files). + * Successive calls with a different @p appName will add them. + * @param appName The application's name, i.e. the name passed to the + * KApplication constructor or KAboutData. + * @see clearApplicationEvents() + */ + virtual void addApplicationEvents( const char *appName ); + + /** + * With this method, you can add the KNotify events of one eventsrc + * files to the view. + * KNotifyDialog can handle events for multiple applications (i.e. eventsrc files). + * Successive calls with a different @p path will add them. + * @param path The absolute or relative path to the eventsrc file to be configured. + * A relative path would be e.g. "kwin/eventsrc". + * @see clearApplicationEvents() + */ + virtual void addApplicationEvents( const QString& path ); + + /** + * Removes all the events added with addApplicationEvents() + * @see addApplicationEvents() + */ + virtual void clearApplicationEvents(); + +private slots: + void slotDefault(); + +private: + enum + { + COL_FILENAME = 1 + }; + + void updateView(); + + KNotify::KNotifyWidget * m_notifyWidget; + + class Private; + Private *d; +}; + + +namespace KNotify +{ + class Application; + class Event; + class ListViewItem; + typedef QPtrList<Event> EventList; + typedef QPtrListIterator<Application> ApplicationListIterator; + typedef QPtrListIterator<Event> EventListIterator; + + /** + * @internal + */ + class KIO_EXPORT Application + { + public: + Application( const QString &path ); + ~Application(); + + QString text() const { return m_description; } + QString icon() const { return m_icon; } + const EventList& eventList(); + void reloadEvents( bool revertToDefaults = false ); + void save(); + + QString appName() const { return m_appname; } + + private: + QString m_icon; + QString m_description; + QString m_appname; + EventList *m_events; + + KConfig *kc; // The file that defines the events. + KConfig *config; // The file that contains the settings for the events + }; + + + class KIO_EXPORT ApplicationList : public QPtrList<Application> + { + virtual int compareItems ( QPtrCollection::Item item1, + QPtrCollection::Item item2 ) + { + return (static_cast<Application*>( item1 )->text() >= + static_cast<Application*>( item2 )->text()) ? 1 : -1; + } + }; + + /** + * @internal + */ + class KIO_EXPORT KNotifyWidget : public KNotifyWidgetBase + { + Q_OBJECT + + public: + KNotifyWidget( QWidget* parent = 0, const char* name = 0, + bool handleAllApps = false ); + ~KNotifyWidget(); + + KListView * eventsView() { + return m_listview; + } + + void addVisibleApp( Application *app ); + ApplicationList& visibleApps() { return m_visibleApps; } + ApplicationList& allApps() { return m_allApps; } + + /** + * Returns 0L if no application events could be found + * The returned pointer must be freed by the caller (easiest done + * by putting it into an ApplicationList with setAutoDelete( true )). + */ + Application * addApplicationEvents( const QString& path ); + + void resetDefaults( bool ask ); + void sort( bool ascending = true ); + + public slots: + /** + * Clears the view and all the Application events. + */ + virtual void clear(); + /** + * Clears only the view and the visible Application events. + * E.g. useful if you want to set new visible events with + * addVisibleApp() + */ + virtual void clearVisible(); + virtual void save(); + virtual void showAdvanced( bool show ); + void toggleAdvanced(); + + + signals: + void changed( bool hasChanges ); + + protected: + /** + * May return 0L, if there is no current event selected. + */ + Event * currentEvent(); + virtual void showEvent( QShowEvent * ); + virtual void enableAll( int what, bool enable ); + + void reload( bool revertToDefaults = false ); + + protected slots: + void playSound(); + + private slots: + void slotItemClicked( QListViewItem *item, const QPoint& point, + int col ); + void slotEventChanged( QListViewItem * ); + void soundToggled( bool on ); + void loggingToggled( bool on ); + void executeToggled( bool on ); + void messageBoxChanged(); + void stderrToggled( bool on ); + void taskbarToggled( bool on ); + + void soundFileChanged( const QString& text ); + void logfileChanged( const QString& text ); + void commandlineChanged( const QString& text ); + + void openSoundDialog( KURLRequester * ); + void openLogDialog( KURLRequester * ); + void openExecDialog( KURLRequester * ); + + void enableAll(); + + private: + void updateWidgets( ListViewItem *item ); + void updatePixmaps( ListViewItem *item ); + + static QString makeRelative( const QString& ); + void addToView( const EventList& events ); + void widgetChanged( QListViewItem *item, + int what, bool on, QWidget *buddy = 0L ); + void selectItem( QListViewItem *item ); + + ApplicationList m_visibleApps; + ApplicationList m_allApps; + + class Private; + Private *d; + + }; + + +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// + + + /** + * @internal + */ + class Event + { + friend class Application; + + public: + QString text() const { return description; } + + int presentation; + int dontShow; + QString logfile; + QString soundfile; + QString commandline; + + const Application *application() const { return m_app; } + + private: + Event( const Application *app ) { + presentation = 0; + dontShow = 0; + m_app = app; + } + QString name; + QString description; + QString configGroup; + + const Application *m_app; + }; + + /** + * @internal + */ + class ListViewItem : public QListViewItem + { + public: + ListViewItem( QListView *view, Event *event ); + + Event& event() { return *m_event; } + virtual int compare (QListViewItem * i, int col, bool ascending) const; + + private: + Event * m_event; + }; + +} + + +#endif diff --git a/kio/kfile/knotifywidgetbase.ui b/kio/kfile/knotifywidgetbase.ui new file mode 100644 index 000000000..99fd07582 --- /dev/null +++ b/kio/kfile/knotifywidgetbase.ui @@ -0,0 +1,469 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>KNotifyWidgetBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>KNotifyWidgetBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>487</width> + <height>531</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="KListView"> + <column> + <property name="text"> + <string></string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>false</bool> + </property> + </column> + <column> + <property name="text"> + <string></string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>false</bool> + </property> + </column> + <column> + <property name="text"> + <string></string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>false</bool> + </property> + </column> + <column> + <property name="text"> + <string></string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>false</bool> + </property> + </column> + <column> + <property name="text"> + <string></string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>false</bool> + </property> + </column> + <column> + <property name="text"> + <string></string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>false</bool> + </property> + </column> + <column> + <property name="text"> + <string>Events</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>m_listview</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>10</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>m_controlsBox</cstring> + </property> + <property name="title"> + <string>Quick Controls</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="4"> + <property name="name"> + <cstring>m_affectAllApps</cstring> + </property> + <property name="text"> + <string>Apply to &all applications</string> + </property> + </widget> + <spacer row="1" column="0" rowspan="2" colspan="1"> + <property name="name"> + <cstring>Spacer3_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>30</width> + <height>20</height> + </size> + </property> + </spacer> + <spacer row="2" column="3"> + <property name="name"> + <cstring>Spacer17</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>318</width> + <height>16</height> + </size> + </property> + </spacer> + <widget class="QPushButton" row="2" column="1"> + <property name="name"> + <cstring>m_buttonDisable</cstring> + </property> + <property name="text"> + <string>Turn O&ff All</string> + </property> + <property name="toolTip" stdset="0"> + <string>Allows you to change the behavior for all events at once</string> + </property> + </widget> + <widget class="QPushButton" row="1" column="1"> + <property name="name"> + <cstring>m_buttonEnable</cstring> + </property> + <property name="text"> + <string>Turn O&n All</string> + </property> + <property name="toolTip" stdset="0"> + <string>Allows you to change the behavior for all events at once</string> + </property> + </widget> + <widget class="KComboBox" row="1" column="2"> + <property name="name"> + <cstring>m_comboEnable</cstring> + </property> + </widget> + <widget class="KComboBox" row="2" column="2"> + <property name="name"> + <cstring>m_comboDisable</cstring> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>m_actionsBox</cstring> + </property> + <property name="title"> + <string>Actions</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox" row="5" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>m_stderr</cstring> + </property> + <property name="text"> + <string>Print a message to standard &error output</string> + </property> + </widget> + <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>m_messageBox</cstring> + </property> + <property name="text"> + <string>Show a &message in a pop-up window</string> + </property> + </widget> + <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>m_execute</cstring> + </property> + <property name="text"> + <string>E&xecute a program:</string> + </property> + </widget> + <widget class="QLayoutWidget" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>Layout25</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_playSound</cstring> + </property> + <property name="text"> + <string>Play a &sound:</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_playButton</cstring> + </property> + <property name="text"> + <string></string> + </property> + <property name="toolTip" stdset="0"> + <string>Test the Sound</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QCheckBox" row="6" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>m_taskbar</cstring> + </property> + <property name="text"> + <string>Mark &taskbar entry</string> + </property> + </widget> + <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>m_logToFile</cstring> + </property> + <property name="text"> + <string>&Log to a file:</string> + </property> + </widget> + <spacer row="4" column="0"> + <property name="name"> + <cstring>Spacer3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>30</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KURLRequester" row="2" column="2"> + <property name="name"> + <cstring>m_executePath</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="KURLRequester" row="1" column="2"> + <property name="name"> + <cstring>m_logfilePath</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="KURLRequester" row="0" column="2"> + <property name="name"> + <cstring>m_soundPath</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="QCheckBox" row="4" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>m_passivePopup</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>&Use a passive window that does not interrupt other work</string> + </property> + </widget> + </grid> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout8</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_extension</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Less Options</string> + </property> + <property name="toggleButton"> + <bool>false</bool> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>Spacer7</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_playerButton</cstring> + </property> + <property name="text"> + <string>Player Settings</string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>m_messageBox</sender> + <signal>toggled(bool)</signal> + <receiver>m_passivePopup</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_execute</sender> + <signal>toggled(bool)</signal> + <receiver>m_executePath</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_playSound</sender> + <signal>toggled(bool)</signal> + <receiver>m_soundPath</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_logToFile</sender> + <signal>toggled(bool)</signal> + <receiver>m_logfilePath</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<tabstops> + <tabstop>m_listview</tabstop> + <tabstop>m_playSound</tabstop> + <tabstop>m_playButton</tabstop> + <tabstop>m_soundPath</tabstop> + <tabstop>m_logToFile</tabstop> + <tabstop>m_logfilePath</tabstop> + <tabstop>m_execute</tabstop> + <tabstop>m_executePath</tabstop> + <tabstop>m_messageBox</tabstop> + <tabstop>m_passivePopup</tabstop> + <tabstop>m_stderr</tabstop> + <tabstop>m_affectAllApps</tabstop> + <tabstop>m_buttonEnable</tabstop> + <tabstop>m_comboEnable</tabstop> + <tabstop>m_buttonDisable</tabstop> + <tabstop>m_comboDisable</tabstop> + <tabstop>m_extension</tabstop> + <tabstop>m_playerButton</tabstop> +</tabstops> +<includes> + <include location="global" impldecl="in implementation">kdialog.h</include> + <include location="global" impldecl="in implementation">klistview.h</include> + <include location="global" impldecl="in implementation">kurlrequester.h</include> + <include location="global" impldecl="in implementation">klineedit.h</include> + <include location="global" impldecl="in implementation">kpushbutton.h</include> + <include location="global" impldecl="in implementation">kcombobox.h</include> +</includes> +<layoutdefaults spacing="6" margin="11"/> +<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/> +</UI> diff --git a/kio/kfile/kopenwith.cpp b/kio/kfile/kopenwith.cpp new file mode 100644 index 000000000..f1a71341b --- /dev/null +++ b/kio/kfile/kopenwith.cpp @@ -0,0 +1,851 @@ +/* This file is part of the KDE libraries + + Copyright (C) 1997 Torben Weis <[email protected]> + Copyright (C) 1999 Dirk Mueller <[email protected]> + Portions copyright (C) 1999 Preston Brown <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qfile.h> +#include <qdir.h> +#include <qdialog.h> +#include <qimage.h> +#include <qpixmap.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qpushbutton.h> +#include <qtoolbutton.h> +#include <qcheckbox.h> +#include <qtooltip.h> +#include <qstyle.h> +#include <qwhatsthis.h> + +#include <kapplication.h> +#include <kbuttonbox.h> +#include <kcombobox.h> +#include <kdesktopfile.h> +#include <kdialog.h> +#include <kglobal.h> +#include <klineedit.h> +#include <klocale.h> +#include <kiconloader.h> +#include <kmimemagic.h> +#include <krun.h> +#include <kstandarddirs.h> +#include <kstringhandler.h> +#include <kuserprofile.h> +#include <kurlcompletion.h> +#include <kurlrequester.h> +#include <dcopclient.h> +#include <kmimetype.h> +#include <kservicegroup.h> +#include <klistview.h> +#include <ksycoca.h> +#include <kstdguiitem.h> + +#include "kopenwith.h" +#include "kopenwith_p.h" + +#include <kdebug.h> +#include <assert.h> +#include <stdlib.h> + +#define SORT_SPEC (QDir::DirsFirst | QDir::Name | QDir::IgnoreCase) + + +// ---------------------------------------------------------------------- + +KAppTreeListItem::KAppTreeListItem( KListView* parent, const QString & name, + const QPixmap& pixmap, bool parse, bool dir, const QString &p, const QString &c ) + : QListViewItem( parent, name ) +{ + init(pixmap, parse, dir, p, c); +} + + +// ---------------------------------------------------------------------- + +KAppTreeListItem::KAppTreeListItem( QListViewItem* parent, const QString & name, + const QPixmap& pixmap, bool parse, bool dir, const QString &p, const QString &c ) + : QListViewItem( parent, name ) +{ + init(pixmap, parse, dir, p, c); +} + + +// ---------------------------------------------------------------------- + +void KAppTreeListItem::init(const QPixmap& pixmap, bool parse, bool dir, const QString &_path, const QString &_exec) +{ + setPixmap(0, pixmap); + parsed = parse; + directory = dir; + path = _path; // relative path + exec = _exec; +} + + +/* Ensures that directories sort before non-directories */ +int KAppTreeListItem::compare(QListViewItem *i, int col, bool ascending) const +{ + KAppTreeListItem *other = dynamic_cast<KAppTreeListItem *>(i); + + // Directories sort first + if (directory && !other->directory) + return -1; + + else if (!directory && other->directory) + return 1; + + else // both directories or both not + return QListViewItem::compare(i, col, ascending); +} + +// ---------------------------------------------------------------------- +// Ensure that case is ignored +QString KAppTreeListItem::key(int column, bool /*ascending*/) const +{ + return text(column).upper(); +} + +void KAppTreeListItem::activate() +{ + if ( directory ) + setOpen(!isOpen()); +} + +void KAppTreeListItem::setOpen( bool o ) +{ + if( o && !parsed ) { // fill the children before opening + ((KApplicationTree *) parent())->addDesktopGroup( path, this ); + parsed = true; + } + QListViewItem::setOpen( o ); +} + +bool KAppTreeListItem::isDirectory() +{ + return directory; +} + +// ---------------------------------------------------------------------- + +KApplicationTree::KApplicationTree( QWidget *parent ) + : KListView( parent ), currentitem(0) +{ + addColumn( i18n("Known Applications") ); + setRootIsDecorated( true ); + + addDesktopGroup( QString::null ); + cleanupTree(); + + connect( this, SIGNAL( currentChanged(QListViewItem*) ), + SLOT( slotItemHighlighted(QListViewItem*) ) ); + connect( this, SIGNAL( selectionChanged(QListViewItem*) ), + SLOT( slotSelectionChanged(QListViewItem*) ) ); +} + +// ---------------------------------------------------------------------- + +bool KApplicationTree::isDirSel() +{ + if (!currentitem) return false; // if currentitem isn't set + return currentitem->isDirectory(); +} + +// ---------------------------------------------------------------------- + +static QPixmap appIcon(const QString &iconName) +{ + QPixmap normal = KGlobal::iconLoader()->loadIcon(iconName, KIcon::Small, 0, KIcon::DefaultState, 0L, true); + // make sure they are not larger than 20x20 + if (normal.width() > 20 || normal.height() > 20) + { + QImage tmp = normal.convertToImage(); + tmp = tmp.smoothScale(20, 20); + normal.convertFromImage(tmp); + } + return normal; +} + +void KApplicationTree::addDesktopGroup( const QString &relPath, KAppTreeListItem *item) +{ + KServiceGroup::Ptr root = KServiceGroup::group(relPath); + if (!root || !root->isValid()) return; + + KServiceGroup::List list = root->entries(); + + KAppTreeListItem * newItem; + for( KServiceGroup::List::ConstIterator it = list.begin(); + it != list.end(); it++) + { + QString icon; + QString text; + QString relPath; + QString exec; + bool isDir = false; + KSycocaEntry *p = (*it); + if (p->isType(KST_KService)) + { + KService *service = static_cast<KService *>(p); + + if (service->noDisplay()) + continue; + + icon = service->icon(); + text = service->name(); + exec = service->exec(); + } + else if (p->isType(KST_KServiceGroup)) + { + KServiceGroup *serviceGroup = static_cast<KServiceGroup *>(p); + + if (serviceGroup->noDisplay() || serviceGroup->childCount() == 0) + continue; + + icon = serviceGroup->icon(); + text = serviceGroup->caption(); + relPath = serviceGroup->relPath(); + isDir = true; + } + else + { + kdWarning(250) << "KServiceGroup: Unexpected object in list!" << endl; + continue; + } + + QPixmap pixmap = appIcon( icon ); + + if (item) + newItem = new KAppTreeListItem( item, text, pixmap, false, isDir, + relPath, exec ); + else + newItem = new KAppTreeListItem( this, text, pixmap, false, isDir, + relPath, exec ); + if (isDir) + newItem->setExpandable( true ); + } +} + + +// ---------------------------------------------------------------------- + +void KApplicationTree::slotItemHighlighted(QListViewItem* i) +{ + // i may be 0 (see documentation) + if(!i) + return; + + KAppTreeListItem *item = (KAppTreeListItem *) i; + + currentitem = item; + + if( (!item->directory ) && (!item->exec.isEmpty()) ) + emit highlighted( item->text(0), item->exec ); +} + + +// ---------------------------------------------------------------------- + +void KApplicationTree::slotSelectionChanged(QListViewItem* i) +{ + // i may be 0 (see documentation) + if(!i) + return; + + KAppTreeListItem *item = (KAppTreeListItem *) i; + + currentitem = item; + + if( ( !item->directory ) && (!item->exec.isEmpty() ) ) + emit selected( item->text(0), item->exec ); +} + +// ---------------------------------------------------------------------- + +void KApplicationTree::resizeEvent( QResizeEvent * e) +{ + setColumnWidth(0, width()-QApplication::style().pixelMetric(QStyle::PM_ScrollBarExtent) + -2*QApplication::style().pixelMetric(QStyle::PM_DefaultFrameWidth)); + KListView::resizeEvent(e); +} + +// Prune empty directories from the tree +void KApplicationTree::cleanupTree() +{ + QListViewItem *item=firstChild(); + while(item!=0) + { + if(item->isExpandable()) + { + QListViewItem *temp=item->itemBelow(); + if(item->text(0)!=i18n("Applications")) + item->setOpen(false); + item=temp; + continue; + } + item=item->itemBelow(); + } +} + +/*************************************************************** + * + * KOpenWithDlg + * + ***************************************************************/ +class KOpenWithDlgPrivate +{ +public: + KOpenWithDlgPrivate() : saveNewApps(false) { }; + QPushButton* ok; + bool saveNewApps; + KService::Ptr curService; +}; + +KOpenWithDlg::KOpenWithDlg( const KURL::List& _urls, QWidget* parent ) + :QDialog( parent, "openwith", true ) +{ + setCaption( i18n( "Open With" ) ); + QString text; + if( _urls.count() == 1 ) + { + text = i18n("<qt>Select the program that should be used to open <b>%1</b>. " + "If the program is not listed, enter the name or click " + "the browse button.</qt>").arg( _urls.first().fileName() ); + } + else + // Should never happen ?? + text = i18n( "Choose the name of the program with which to open the selected files." ); + setServiceType( _urls ); + init( text, QString() ); +} + +KOpenWithDlg::KOpenWithDlg( const KURL::List& _urls, const QString&_text, + const QString& _value, QWidget *parent) + :QDialog( parent, "openwith", true ) +{ + QString caption = KStringHandler::csqueeze( _urls.first().prettyURL() ); + if (_urls.count() > 1) + caption += QString::fromLatin1("..."); + setCaption(caption); + setServiceType( _urls ); + init( _text, _value ); +} + +KOpenWithDlg::KOpenWithDlg( const QString &serviceType, const QString& value, + QWidget *parent) + :QDialog( parent, "openwith", true ) +{ + setCaption(i18n("Choose Application for %1").arg(serviceType)); + QString text = i18n("<qt>Select the program for the file type: <b>%1</b>. " + "If the program is not listed, enter the name or click " + "the browse button.</qt>").arg(serviceType); + qServiceType = serviceType; + init( text, value ); + if (remember) + remember->hide(); +} + +KOpenWithDlg::KOpenWithDlg( QWidget *parent) + :QDialog( parent, "openwith", true ) +{ + setCaption(i18n("Choose Application")); + QString text = i18n("<qt>Select a program. " + "If the program is not listed, enter the name or click " + "the browse button.</qt>"); + qServiceType = QString::null; + init( text, QString::null ); +} + +void KOpenWithDlg::setServiceType( const KURL::List& _urls ) +{ + if ( _urls.count() == 1 ) + { + qServiceType = KMimeType::findByURL( _urls.first())->name(); + if (qServiceType == QString::fromLatin1("application/octet-stream")) + qServiceType = QString::null; + } + else + qServiceType = QString::null; +} + +void KOpenWithDlg::init( const QString& _text, const QString& _value ) +{ + d = new KOpenWithDlgPrivate; + bool bReadOnly = kapp && !kapp->authorize("shell_access"); + m_terminaldirty = false; + m_pTree = 0L; + m_pService = 0L; + d->curService = 0L; + + QBoxLayout *topLayout = new QVBoxLayout( this, KDialog::marginHint(), + KDialog::spacingHint() ); + label = new QLabel( _text, this ); + topLayout->addWidget(label); + + QHBoxLayout* hbox = new QHBoxLayout(topLayout); + + QToolButton *clearButton = new QToolButton( this ); + clearButton->setIconSet( BarIcon( "locationbar_erase" ) ); + clearButton->setFixedSize( clearButton->sizeHint() ); + connect( clearButton, SIGNAL( clicked() ), SLOT( slotClear() ) ); + QToolTip::add( clearButton, i18n( "Clear input field" ) ); + + hbox->addWidget( clearButton ); + + if (!bReadOnly) + { + // init the history combo and insert it into the URL-Requester + KHistoryCombo *combo = new KHistoryCombo(); + combo->setDuplicatesEnabled( false ); + KConfig *kc = KGlobal::config(); + KConfigGroupSaver ks( kc, QString::fromLatin1("Open-with settings") ); + int max = kc->readNumEntry( QString::fromLatin1("Maximum history"), 15 ); + combo->setMaxCount( max ); + int mode = kc->readNumEntry(QString::fromLatin1("CompletionMode"), + KGlobalSettings::completionMode()); + combo->setCompletionMode((KGlobalSettings::Completion)mode); + QStringList list = kc->readListEntry( QString::fromLatin1("History") ); + combo->setHistoryItems( list, true ); + edit = new KURLRequester( combo, this ); + } + else + { + clearButton->hide(); + edit = new KURLRequester( this ); + edit->lineEdit()->setReadOnly(true); + edit->button()->hide(); + } + + edit->setURL( _value ); + QWhatsThis::add(edit,i18n( + "Following the command, you can have several place holders which will be replaced " + "with the actual values when the actual program is run:\n" + "%f - a single file name\n" + "%F - a list of files; use for applications that can open several local files at once\n" + "%u - a single URL\n" + "%U - a list of URLs\n" + "%d - the directory of the file to open\n" + "%D - a list of directories\n" + "%i - the icon\n" + "%m - the mini-icon\n" + "%c - the comment")); + + hbox->addWidget(edit); + + if ( edit->comboBox() ) { + KURLCompletion *comp = new KURLCompletion( KURLCompletion::ExeCompletion ); + edit->comboBox()->setCompletionObject( comp ); + edit->comboBox()->setAutoDeleteCompletionObject( true ); + } + + connect ( edit, SIGNAL(returnPressed()), SLOT(slotOK()) ); + connect ( edit, SIGNAL(textChanged(const QString&)), SLOT(slotTextChanged()) ); + + m_pTree = new KApplicationTree( this ); + topLayout->addWidget(m_pTree); + + connect( m_pTree, SIGNAL( selected( const QString&, const QString& ) ), + SLOT( slotSelected( const QString&, const QString& ) ) ); + connect( m_pTree, SIGNAL( highlighted( const QString&, const QString& ) ), + SLOT( slotHighlighted( const QString&, const QString& ) ) ); + connect( m_pTree, SIGNAL( doubleClicked(QListViewItem*) ), + SLOT( slotDbClick() ) ); + + terminal = new QCheckBox( i18n("Run in &terminal"), this ); + if (bReadOnly) + terminal->hide(); + connect(terminal, SIGNAL(toggled(bool)), SLOT(slotTerminalToggled(bool))); + + topLayout->addWidget(terminal); + + QBoxLayout* nocloseonexitLayout = new QHBoxLayout( 0, 0, KDialog::spacingHint() ); + QSpacerItem* spacer = new QSpacerItem( 20, 0, QSizePolicy::Fixed, QSizePolicy::Minimum ); + nocloseonexitLayout->addItem( spacer ); + + nocloseonexit = new QCheckBox( i18n("&Do not close when command exits"), this ); + nocloseonexit->setChecked( false ); + nocloseonexit->setDisabled( true ); + + // check to see if we use konsole if not disable 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")); + + if (bReadOnly || preferredTerminal != "konsole") + nocloseonexit->hide(); + + nocloseonexitLayout->addWidget( nocloseonexit ); + topLayout->addLayout( nocloseonexitLayout ); + + if (!qServiceType.isNull()) + { + remember = new QCheckBox(i18n("&Remember application association for this type of file"), this); + // remember->setChecked(true); + topLayout->addWidget(remember); + } + else + remember = 0L; + + // Use KButtonBox for the aligning pushbuttons nicely + KButtonBox* b = new KButtonBox( this ); + b->addStretch( 2 ); + + d->ok = b->addButton( KStdGuiItem::ok() ); + d->ok->setDefault( true ); + connect( d->ok, SIGNAL( clicked() ), SLOT( slotOK() ) ); + + QPushButton* cancel = b->addButton( KStdGuiItem::cancel() ); + connect( cancel, SIGNAL( clicked() ), SLOT( reject() ) ); + + b->layout(); + topLayout->addWidget( b ); + + //edit->setText( _value ); + // This is what caused "can't click on items before clicking on Name header". + // Probably due to the resizeEvent handler using width(). + //resize( minimumWidth(), sizeHint().height() ); + edit->setFocus(); + slotTextChanged(); +} + + +// ---------------------------------------------------------------------- + +KOpenWithDlg::~KOpenWithDlg() +{ + delete d; + d = 0; +} + +// ---------------------------------------------------------------------- + +void KOpenWithDlg::slotClear() +{ + edit->setURL(QString::null); + edit->setFocus(); +} + + +// ---------------------------------------------------------------------- + +void KOpenWithDlg::slotSelected( const QString& /*_name*/, const QString& _exec ) +{ + kdDebug(250)<<"KOpenWithDlg::slotSelected"<<endl; + KService::Ptr pService = d->curService; + edit->setURL( _exec ); // calls slotTextChanged :( + d->curService = pService; +} + + +// ---------------------------------------------------------------------- + +void KOpenWithDlg::slotHighlighted( const QString& _name, const QString& ) +{ + kdDebug(250)<<"KOpenWithDlg::slotHighlighted"<<endl; + qName = _name; + d->curService = KService::serviceByName( qName ); + if (!m_terminaldirty) + { + // ### indicate that default value was restored + terminal->setChecked(d->curService->terminal()); + QString terminalOptions = d->curService->terminalOptions(); + nocloseonexit->setChecked( (terminalOptions.contains( "--noclose" ) > 0) ); + m_terminaldirty = false; // slotTerminalToggled changed it + } +} + +// ---------------------------------------------------------------------- + +void KOpenWithDlg::slotTextChanged() +{ + kdDebug(250)<<"KOpenWithDlg::slotTextChanged"<<endl; + // Forget about the service + d->curService = 0L; + d->ok->setEnabled( !edit->url().isEmpty()); +} + +// ---------------------------------------------------------------------- + +void KOpenWithDlg::slotTerminalToggled(bool) +{ + // ### indicate that default value was overridden + m_terminaldirty = true; + nocloseonexit->setDisabled( ! terminal->isChecked() ); +} + +// ---------------------------------------------------------------------- + +void KOpenWithDlg::slotDbClick() +{ + if (m_pTree->isDirSel() ) return; // check if a directory is selected + slotOK(); +} + +void KOpenWithDlg::setSaveNewApplications(bool b) +{ + d->saveNewApps = b; +} + +void KOpenWithDlg::slotOK() +{ + QString typedExec(edit->url()); + QString fullExec(typedExec); + + QString serviceName; + QString initialServiceName; + QString preferredTerminal; + m_pService = d->curService; + if (!m_pService) { + // No service selected - check the command line + + // Find out the name of the service from the command line, removing args and paths + serviceName = KRun::binaryName( typedExec, true ); + if (serviceName.isEmpty()) + { + // TODO add a KMessageBox::error here after the end of the message freeze + return; + } + initialServiceName = serviceName; + kdDebug(250) << "initialServiceName=" << initialServiceName << endl; + int i = 1; // We have app, app-2, app-3... Looks better for the user. + bool ok = false; + // Check if there's already a service by that name, with the same Exec line + do { + kdDebug(250) << "looking for service " << serviceName << endl; + KService::Ptr serv = KService::serviceByDesktopName( serviceName ); + ok = !serv; // ok if no such service yet + // also ok if we find the exact same service (well, "kwrite" == "kwrite %U" + if ( serv && serv->type() == "Application") + { + QString exec = serv->exec(); + fullExec = exec; + exec.replace("%u", "", false); + exec.replace("%f", "", false); + exec.replace("-caption %c", ""); + exec.replace("-caption \"%c\"", ""); + exec.replace("%i", ""); + exec.replace("%m", ""); + exec = exec.simplifyWhiteSpace(); + if (exec == typedExec) + { + ok = true; + m_pService = serv; + kdDebug(250) << k_funcinfo << "OK, found identical service: " << serv->desktopEntryPath() << endl; + } + } + if (!ok) // service was found, but it was different -> keep looking + { + ++i; + serviceName = initialServiceName + "-" + QString::number(i); + } + } + while (!ok); + } + if ( m_pService ) + { + // Existing service selected + serviceName = m_pService->name(); + initialServiceName = serviceName; + fullExec = m_pService->exec(); + } + + if (terminal->isChecked()) + { + KConfigGroup confGroup( KGlobal::config(), QString::fromLatin1("General") ); + preferredTerminal = confGroup.readPathEntry("TerminalApplication", QString::fromLatin1("konsole")); + m_command = preferredTerminal; + // only add --noclose when we are sure it is konsole we're using + if (preferredTerminal == "konsole" && nocloseonexit->isChecked()) + m_command += QString::fromLatin1(" --noclose"); + m_command += QString::fromLatin1(" -e "); + m_command += edit->url(); + kdDebug(250) << "Setting m_command to " << m_command << endl; + } + if ( m_pService && terminal->isChecked() != m_pService->terminal() ) + m_pService = 0L; // It's not exactly this service we're running + + bool bRemember = remember && remember->isChecked(); + + if ( !bRemember && m_pService) + { + accept(); + return; + } + + if (!bRemember && !d->saveNewApps) + { + // Create temp service + m_pService = new KService(initialServiceName, fullExec, QString::null); + if (terminal->isChecked()) + { + m_pService->setTerminal(true); + // only add --noclose when we are sure it is konsole we're using + if (preferredTerminal == "konsole" && nocloseonexit->isChecked()) + m_pService->setTerminalOptions("--noclose"); + } + accept(); + return; + } + + // if we got here, we can't seem to find a service for what they + // wanted. The other possibility is that they have asked for the + // association to be remembered. Create/update service. + + QString newPath; + QString oldPath; + QString menuId; + if (m_pService) + { + oldPath = m_pService->desktopEntryPath(); + newPath = m_pService->locateLocal(); + menuId = m_pService->menuId(); + kdDebug(250) << "Updating exitsing service " << m_pService->desktopEntryPath() << " ( " << newPath << " ) " << endl; + } + else + { + newPath = KService::newServicePath(false /* hidden */, serviceName, &menuId); + kdDebug(250) << "Creating new service " << serviceName << " ( " << newPath << " ) " << endl; + } + + int maxPreference = 1; + if (!qServiceType.isEmpty()) + { + KServiceTypeProfile::OfferList offerList = KServiceTypeProfile::offers( qServiceType ); + if (!offerList.isEmpty()) + maxPreference = offerList.first().preference(); + } + + KDesktopFile *desktop = 0; + if (!oldPath.isEmpty() && (oldPath != newPath)) + { + KDesktopFile orig(oldPath, true); + desktop = orig.copyTo(newPath); + } + else + { + desktop = new KDesktopFile(newPath); + } + desktop->writeEntry("Type", QString::fromLatin1("Application")); + desktop->writeEntry("Name", initialServiceName); + desktop->writePathEntry("Exec", fullExec); + if (terminal->isChecked()) + { + desktop->writeEntry("Terminal", true); + // only add --noclose when we are sure it is konsole we're using + if (preferredTerminal == "konsole" && nocloseonexit->isChecked()) + desktop->writeEntry("TerminalOptions", "--noclose"); + } + else + { + desktop->writeEntry("Terminal", false); + } + desktop->writeEntry("InitialPreference", maxPreference + 1); + + + if (bRemember || d->saveNewApps) + { + QStringList mimeList = desktop->readListEntry("MimeType", ';'); + if (!qServiceType.isEmpty() && !mimeList.contains(qServiceType)) + mimeList.append(qServiceType); + desktop->writeEntry("MimeType", mimeList, ';'); + + if ( !qServiceType.isEmpty() ) + { + // Also make sure the "auto embed" setting for this mimetype is off + KDesktopFile mimeDesktop( locateLocal( "mime", qServiceType + ".desktop" ) ); + mimeDesktop.writeEntry( "X-KDE-AutoEmbed", false ); + mimeDesktop.sync(); + } + } + + // write it all out to the file + desktop->sync(); + delete desktop; + + KService::rebuildKSycoca(this); + + m_pService = KService::serviceByMenuId( menuId ); + + Q_ASSERT( m_pService ); + + accept(); +} + +QString KOpenWithDlg::text() const +{ + if (!m_command.isEmpty()) + return m_command; + else + return edit->url(); +} + +void KOpenWithDlg::hideNoCloseOnExit() +{ + // uncheck the checkbox because the value could be used when "Run in Terminal" is selected + nocloseonexit->setChecked( false ); + nocloseonexit->hide(); +} + +void KOpenWithDlg::hideRunInTerminal() +{ + terminal->hide(); + hideNoCloseOnExit(); +} + +void KOpenWithDlg::accept() +{ + KHistoryCombo *combo = static_cast<KHistoryCombo*>( edit->comboBox() ); + if ( combo ) { + combo->addToHistory( edit->url() ); + + KConfig *kc = KGlobal::config(); + KConfigGroupSaver ks( kc, QString::fromLatin1("Open-with settings") ); + kc->writeEntry( QString::fromLatin1("History"), combo->historyItems() ); + kc->writeEntry(QString::fromLatin1("CompletionMode"), + combo->completionMode()); + // don't store the completion-list, as it contains all of KURLCompletion's + // executables + kc->sync(); + } + + QDialog::accept(); +} + + +/////////////// + +#ifndef KDE_NO_COMPAT +bool KFileOpenWithHandler::displayOpenWithDialog( const KURL::List& urls ) +{ + KOpenWithDlg l( urls, i18n("Open with:"), QString::null, 0L ); + if ( l.exec() ) + { + KService::Ptr service = l.service(); + if ( !!service ) + return KRun::run( *service, urls ); + + kdDebug(250) << "No service set, running " << l.text() << endl; + return KRun::run( l.text(), urls ); + } + return false; +} +#endif + +#include "kopenwith.moc" +#include "kopenwith_p.moc" + diff --git a/kio/kfile/kopenwith.h b/kio/kfile/kopenwith.h new file mode 100644 index 000000000..c343816aa --- /dev/null +++ b/kio/kfile/kopenwith.h @@ -0,0 +1,209 @@ +// +/* This file is part of the KDE libraries + Copyright (C) 2000 David Faure <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef __open_with_h__ +#define __open_with_h__ + +#include <qdialog.h> + +#include <kurl.h> +#include <krun.h> +#include <kservice.h> + +class KApplicationTree; +class KURLRequester; + +class QWidget; +class QCheckBox; +class QPushButton; +class QLabel; + +class KOpenWithDlgPrivate; + +/* ------------------------------------------------------------------------- */ +/** + * "Open with" dialog box. + * Used automatically by KRun, and used by libkonq. + * + * @author David Faure <[email protected]> + */ +class KIO_EXPORT KOpenWithDlg : public QDialog //#TODO: Use KDialogBase for KDE4 +{ + Q_OBJECT +public: + + /** + * Create a dialog that asks for a application to open a given + * URL(s) with. + * + * @param urls the URLs that should be opened. The list can be empty, + * if the dialog is used to choose an application but not for some particular URLs. + * @param parent parent widget + */ + KOpenWithDlg( const KURL::List& urls, QWidget *parent = 0L ); + + /** + * Create a dialog that asks for a application to open a given + * URL(s) with. + * + * @param urls is the URL that should be opened + * @param text appears as a label on top of the entry box. + * @param value is the initial value of the line + * @param parent parent widget + */ + KOpenWithDlg( const KURL::List& urls, const QString& text, const QString& value, + QWidget *parent = 0L ); + + /** + * Create a dialog to select a service for a given service type. + * Note that this dialog doesn't apply to URLs. + * + * @param serviceType the service type we want to choose an application for. + * @param value is the initial value of the line + * @param parent parent widget + */ + KOpenWithDlg( const QString& serviceType, const QString& value, + QWidget *parent = 0L ); + + /** + * Create a dialog to select an application + * Note that this dialog doesn't apply to URLs. + * + * @param parent parent widget + * @since 3.1 + */ + KOpenWithDlg( QWidget *parent = 0L ); + + /** + * Destructor + */ + ~KOpenWithDlg(); + + /** + * @return the text the user entered + */ + QString text() const; + /** + * Hide the "Do not &close when command exits" Checkbox + */ + void hideNoCloseOnExit(); + /** + * Hide the "Run in &terminal" Checkbox + */ + void hideRunInTerminal(); + /** + * @return the chosen service in the application tree + * Can be null, if the user typed some text and didn't select a service. + */ + KService::Ptr service() const { return m_pService; } + /** + * Set whether a new .desktop file should be created if the user selects an + * application for which no corresponding .desktop file can be found. + * + * Regardless of this setting a new .desktop file may still be created if + * the user has chosen to remember the file association. + * + * The default is false: no .desktop files are created. + * @since 3.2 + */ + void setSaveNewApplications(bool b); + +public slots: + /** + * The slot for clearing the edit widget + */ + void slotClear(); + void slotSelected( const QString&_name, const QString& _exec ); + void slotHighlighted( const QString& _name, const QString& _exec ); + void slotTextChanged(); + void slotTerminalToggled(bool); + void slotDbClick(); + void slotOK(); + +protected slots: + /** + * Reimplemented from QDialog::accept() to save history of the combobox + */ + virtual void accept(); + +protected: + + /** + * Determine service type from URLs + */ + void setServiceType( const KURL::List& _urls ); + + /** + * Create a dialog that asks for a application to open a given + * URL(s) with. + * + * @param text appears as a label on top of the entry box. + * @param value is the initial value of the line + */ + void init( const QString& text, const QString& value ); + + KURLRequester * edit; + QString m_command; + + KApplicationTree* m_pTree; + QLabel *label; + + QString qName, qServiceType; + bool m_terminaldirty; + QCheckBox *terminal, *remember, *nocloseonexit; + QPushButton *UNUSED; + QPushButton *UNUSED2; + + KService::Ptr m_pService; + + KOpenWithDlgPrivate *d; +}; + +/* ------------------------------------------------------------------------- */ + +#ifndef KDE_NO_COMPAT +/** + * This class handles the displayOpenWithDialog call, made by KRun + * when it has no idea what to do with a URL. + * It displays the open-with dialog box. + * + * If you use KRun you _need_ to create an instance of KFileOpenWithHandler + * (except if you can make sure you only use it for executables or + * Type=Application desktop files) + * + * + */ +class KIO_EXPORT_DEPRECATED KFileOpenWithHandler : public KOpenWithHandler +{ +public: + KFileOpenWithHandler() : KOpenWithHandler() {} + virtual ~KFileOpenWithHandler() {} + + /** + * Opens an open-with dialog box for @p urls + * @returns true if the operation succeeded + */ + virtual bool displayOpenWithDialog( const KURL::List& urls ); +}; +#endif + + +/* ------------------------------------------------------------------------- */ + +#endif diff --git a/kio/kfile/kopenwith_p.h b/kio/kfile/kopenwith_p.h new file mode 100644 index 000000000..ccee6bd22 --- /dev/null +++ b/kio/kfile/kopenwith_p.h @@ -0,0 +1,101 @@ +// +/* This file is part of the KDE libraries + Copyright (C) 2000 David Faure <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef __open_with_p_h__ +#define __open_with_p_h__ + +#include <kurl.h> +#include <klistview.h> + +class KURLRequester; + +class QWidget; +class QCheckBox; +class QPushButton; +class QLabel; +class QStringList; + + +/* ------------------------------------------------------------------------- */ + +/** + * @internal + */ +class KAppTreeListItem : public QListViewItem +{ + bool parsed; + bool directory; + QString path; + QString exec; + +protected: + int compare(QListViewItem *i, int col, bool ascending ) const; + QString key(int column, bool ascending) const; + + void init(const QPixmap& pixmap, bool parse, bool dir, const QString &_path, const QString &exec); + +public: + KAppTreeListItem( KListView* parent, const QString & name, const QPixmap& pixmap, + bool parse, bool dir, const QString &p, const QString &c ); + KAppTreeListItem( QListViewItem* parent, const QString & name, const QPixmap& pixmap, + bool parse, bool dir, const QString &p, const QString &c ); + bool isDirectory(); + +protected: + virtual void activate(); + virtual void setOpen( bool o ); + + friend class KApplicationTree; +}; + +/* ------------------------------------------------------------------------- */ + +/** + * @internal + */ +class KApplicationTree : public KListView +{ + Q_OBJECT +public: + KApplicationTree( QWidget *parent ); + + /** + * Add a group of .desktop/.kdelnk entries + */ + void addDesktopGroup( const QString &relPath, KAppTreeListItem *item = 0 ); + + bool isDirSel(); + +protected: + void resizeEvent( QResizeEvent *_ev ); + KAppTreeListItem* currentitem; + void cleanupTree(); + +public slots: + void slotItemHighlighted(QListViewItem* i); + void slotSelectionChanged(QListViewItem* i); + +signals: + void selected( const QString& _name, const QString& _exec ); + void highlighted( const QString& _name, const QString& _exec ); +}; + +/* ------------------------------------------------------------------------- */ + +#endif diff --git a/kio/kfile/kpreviewprops.cpp b/kio/kfile/kpreviewprops.cpp new file mode 100644 index 000000000..986e46597 --- /dev/null +++ b/kio/kfile/kpreviewprops.cpp @@ -0,0 +1,89 @@ +/* This file is part of the KDE libraries + Copyright (C) 2005 Stephan Binner <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + */ + +#include "kpreviewprops.h" + +#include <qlayout.h> + +#include <kfilemetapreview.h> +#include <kglobalsettings.h> +#include <klocale.h> + +class KPreviewPropsPlugin::KPreviewPropsPluginPrivate +{ +public: + KPreviewPropsPluginPrivate() {} + ~KPreviewPropsPluginPrivate() {} +}; + +KPreviewPropsPlugin::KPreviewPropsPlugin(KPropertiesDialog* props) + : KPropsDlgPlugin(props) +{ + d = new KPreviewPropsPluginPrivate; + + if (properties->items().count()>1) + return; + + createLayout(); +} + +void KPreviewPropsPlugin::createLayout() +{ + // let the dialog create the page frame + QFrame* topframe = properties->addPage(i18n("P&review")); + topframe->setFrameStyle(QFrame::NoFrame); + + QVBoxLayout* tmp = new QVBoxLayout(topframe, 0, 0); + + preview = new KFileMetaPreview(topframe); + + tmp->addWidget(preview) ; + connect( properties, SIGNAL( aboutToShowPage( QWidget * ) ), SLOT( aboutToShowPage( QWidget* ) ) ); +} + +KPreviewPropsPlugin::~KPreviewPropsPlugin() +{ + delete d; +} + +bool KPreviewPropsPlugin::supports( KFileItemList _items ) +{ + if ( _items.count() != 1) + return false; + if( !KGlobalSettings::showFilePreview(_items.first()->url())) + return false; + KMimeType::Ptr mt = KMimeType::findByURL( _items.first()->url() ); + if ( mt->inherits("inode/directory") || mt->name() == "application/octet-stream" ) + return false; + + //TODO Copy everything of KFileMetaPreview::previewProviderFor() ? + + return true; +} + +void KPreviewPropsPlugin::aboutToShowPage( QWidget* widget ) +{ + if ( widget != preview->parent() ) + return; + + disconnect( properties, SIGNAL( aboutToShowPage( QWidget * ) ), this, SLOT( aboutToShowPage( QWidget* ) ) ); + preview->showPreview(properties->item()->url()); +} + +#include "kpreviewprops.moc" diff --git a/kio/kfile/kpreviewprops.h b/kio/kfile/kpreviewprops.h new file mode 100644 index 000000000..be3019083 --- /dev/null +++ b/kio/kfile/kpreviewprops.h @@ -0,0 +1,57 @@ +/* This file is part of the KDE libraries + Copyright (C) 2005 Stephan Binner <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + */ + +#ifndef __KPREVIEWPROPS_H__ +#define __KPREVIEWPROPS_H__ + +#include <kpropertiesdialog.h> + +class KFileMetaPreview; + +/*! + * PreviewProps plugin + * This plugin displays a preview of the given file + * @since 3.5 + */ +class KIO_EXPORT KPreviewPropsPlugin : public KPropsDlgPlugin +{ + Q_OBJECT + +public: + + KPreviewPropsPlugin( KPropertiesDialog *_props ); + virtual ~KPreviewPropsPlugin(); + + /** + * Tests whether a preview for the first item should be shown + */ + static bool supports( KFileItemList _items ); + +private slots: + void aboutToShowPage( QWidget* ); + +private: + KFileMetaPreview* preview; + void createLayout(); + + class KPreviewPropsPluginPrivate; + KPreviewPropsPluginPrivate *d; +}; + +#endif diff --git a/kio/kfile/kpreviewwidgetbase.cpp b/kio/kfile/kpreviewwidgetbase.cpp new file mode 100644 index 000000000..43b2b5fdc --- /dev/null +++ b/kio/kfile/kpreviewwidgetbase.cpp @@ -0,0 +1,49 @@ +/* + * This file is part of the KDE project. + * Copyright (C) 2003 Carsten Pfeiffer <[email protected]> + * + * You can Freely distribute this program under the GNU Library General Public + * License. See the file "COPYING" for the exact licensing terms. + */ + +#include "kpreviewwidgetbase.h" +#include <qstringlist.h> + +class KPreviewWidgetBase::KPreviewWidgetBasePrivate +{ +public: + QStringList supportedMimeTypes; +}; + +QPtrDict<KPreviewWidgetBase::KPreviewWidgetBasePrivate> * KPreviewWidgetBase::s_private; + +KPreviewWidgetBase::KPreviewWidgetBase( QWidget *parent, const char *name ) + : QWidget( parent, name ) +{ + if ( !s_private ) + s_private = new QPtrDict<KPreviewWidgetBasePrivate>(); + + s_private->insert( this, new KPreviewWidgetBasePrivate() ); +} + +KPreviewWidgetBase::~KPreviewWidgetBase() +{ + s_private->remove( this ); + if ( s_private->isEmpty() ) + { + delete s_private; + s_private = 0L; + } +} + +void KPreviewWidgetBase::setSupportedMimeTypes( const QStringList& mimeTypes ) +{ + d()->supportedMimeTypes = mimeTypes; +} + +QStringList KPreviewWidgetBase::supportedMimeTypes() const +{ + return d()->supportedMimeTypes; +} + +#include "kpreviewwidgetbase.moc" diff --git a/kio/kfile/kpreviewwidgetbase.h b/kio/kfile/kpreviewwidgetbase.h new file mode 100644 index 000000000..80f3f4ff4 --- /dev/null +++ b/kio/kfile/kpreviewwidgetbase.h @@ -0,0 +1,92 @@ +/* This file is part of the KDE libraries + * Copyright (C) 2001 Frerich Raabe <[email protected]> + * 2003 Carsten Pfeiffer <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __KPREVIEWWIDGETBASE_H__ +#define __KPREVIEWWIDGETBASE_H__ + +#include <qptrdict.h> +#include <qwidget.h> + +#include <kdelibs_export.h> + +class KURL; + +/** + * Abstract baseclass for all preview widgets which shall be used via + * KFileDialog::setPreviewWidget(const KPreviewWidgetBase *). + * Ownership will be transferred to KFileDialog, so you have to create + * the preview with "new" and let KFileDialog delete it. + * + * Just derive your custom preview widget from KPreviewWidgetBase and implement + * all the pure virtual methods. The slot showPreview(const KURL &) is called + * every time the file selection changes. + * + * @short Abstract baseclass for all preview widgets. + * @author Frerich Raabe <[email protected]> + */ +class KIO_EXPORT KPreviewWidgetBase : public QWidget +{ + Q_OBJECT + +public: + /** + * Constructor. Construct the user interface of your preview widget here + * and pass the KFileDialog this preview widget is going to be used in as + * the parent. + * + * @param parent The KFileDialog this preview widget is going to be used in + * @param name The internal name of this object + */ + KPreviewWidgetBase(QWidget *parent, const char *name=0); + ~KPreviewWidgetBase(); + +public slots: + /** + * This slot is called every time the user selects another file in the + * file dialog. Implement the stuff necessary to reflect the change here. + * + * @param url The URL of the currently selected file. + */ + virtual void showPreview(const KURL &url) = 0; + + /** + * Reimplement this to clear the preview. This is called when e.g. the + * selection is cleared or when multiple selections exist, or the directory + * is changed. + */ + virtual void clearPreview() = 0; + + QStringList supportedMimeTypes() const; + +protected: + void setSupportedMimeTypes( const QStringList& mimeTypes ); + +protected: + virtual void virtual_hook( int, void* ) {}; + +private: + class KPreviewWidgetBasePrivate; + KPreviewWidgetBasePrivate * d() const { + return s_private->find( const_cast<KPreviewWidgetBase*>( this ) ); + } + static QPtrDict<KPreviewWidgetBasePrivate> * s_private; +}; + +#endif diff --git a/kio/kfile/kpropertiesdesktopadvbase.ui b/kio/kfile/kpropertiesdesktopadvbase.ui new file mode 100644 index 000000000..fe1122136 --- /dev/null +++ b/kio/kfile/kpropertiesdesktopadvbase.ui @@ -0,0 +1,279 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>KPropertiesDesktopAdvBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>widget11</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>463</width> + <height>294</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>buttonGroup2</cstring> + </property> + <property name="title"> + <string>Terminal</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer row="1" column="0" rowspan="2" colspan="1"> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Minimum</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>terminalCheck</cstring> + </property> + <property name="text"> + <string>&Run in terminal</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Check this option if the application you want to run is a text mode application or if you want the information that is provided by the terminal emulator window.</string> + </property> + </widget> + <widget class="QLabel" row="2" column="1"> + <property name="name"> + <cstring>terminalEditLabel</cstring> + </property> + <property name="text"> + <string>&Terminal options:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>terminalEdit</cstring> + </property> + </widget> + <widget class="QCheckBox" row="1" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>terminalCloseCheck</cstring> + </property> + <property name="text"> + <string>Do not &close when command exits</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Check this option if the text mode application offers relevant information on exit. Keeping the terminal emulator open allows you to retrieve this information.</string> + </property> + </widget> + <widget class="KLineEdit" row="2" column="2"> + <property name="name"> + <cstring>terminalEdit</cstring> + </property> + </widget> + </grid> + </widget> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>buttonGroup2_2</cstring> + </property> + <property name="title"> + <string>User</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>suidCheck</cstring> + </property> + <property name="text"> + <string>Ru&n as a different user</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Check this option if you want to run this application with a different user id. Every process has a different user id associated with it. This id code determines file access and other permissions. The password of the user is required to use this option.</string> + </property> + </widget> + <spacer row="1" column="0"> + <property name="name"> + <cstring>spacer3_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Minimum</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLabel" row="1" column="1"> + <property name="name"> + <cstring>suidEditLabel</cstring> + </property> + <property name="text"> + <string>&Username:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>suidEdit</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>Enter the user name you want to run the application as.</string> + </property> + </widget> + <widget class="KLineEdit" row="1" column="2"> + <property name="name"> + <cstring>suidEdit</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>Enter the user name you want to run the application as here.</string> + </property> + </widget> + </grid> + </widget> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>buttonGroup4</cstring> + </property> + <property name="title"> + <string>Startup</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>startupInfoCheck</cstring> + </property> + <property name="text"> + <string>Enable &launch feedback</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Check this option if you want to make clear that your application has started. This visual feedback may appear as a busy cursor or in the taskbar.</string> + </property> + </widget> + <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>systrayCheck</cstring> + </property> + <property name="text"> + <string>&Place in system tray</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Check this option if you want to have a system tray handle for your application.</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel12</cstring> + </property> + <property name="text"> + <string>&DCOP registration:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>dcopCombo</cstring> + </property> + </widget> + <widget class="QComboBox" row="2" column="1"> + <item> + <property name="text"> + <string>None</string> + </property> + </item> + <item> + <property name="text"> + <string>Multiple Instances</string> + </property> + </item> + <item> + <property name="text"> + <string>Single Instance</string> + </property> + </item> + <item> + <property name="text"> + <string>Run Until Finished</string> + </property> + </item> + <property name="name"> + <cstring>dcopCombo</cstring> + </property> + </widget> + <spacer row="2" column="2"> + <property name="name"> + <cstring>spacer33</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>50</width> + <height>20</height> + </size> + </property> + </spacer> + </grid> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>terminalCheck</sender> + <signal>toggled(bool)</signal> + <receiver>terminalCloseCheck</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>terminalCheck</sender> + <signal>toggled(bool)</signal> + <receiver>terminalEdit</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>terminalCheck</sender> + <signal>toggled(bool)</signal> + <receiver>terminalEditLabel</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>suidCheck</sender> + <signal>toggled(bool)</signal> + <receiver>suidEdit</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>suidCheck</sender> + <signal>toggled(bool)</signal> + <receiver>suidEditLabel</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<includes> + <include location="global" impldecl="in implementation">klineedit.h</include> +</includes> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kio/kfile/kpropertiesdesktopbase.ui b/kio/kfile/kpropertiesdesktopbase.ui new file mode 100644 index 000000000..7809d5135 --- /dev/null +++ b/kio/kfile/kpropertiesdesktopbase.ui @@ -0,0 +1,316 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>KPropertiesDesktopBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>KPropertiesDesktopBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>449</width> + <height>304</height> + </rect> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <spacer row="5" column="3" rowspan="1" colspan="2"> + <property name="name"> + <cstring>spacer6</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Minimum</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + <widget class="QLabel" row="6" column="0" rowspan="1" colspan="7"> + <property name="name"> + <cstring>textLabel7</cstring> + </property> + <property name="text"> + <string>&Supported file types:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>filetypeList</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string><qt><p>This list should show the types of file that your application can handle. This list is organized by <u>mimetypes</u>.</p> +<p>MIME, Multipurpose Internet (e)Mail Extension, is a standard protocol for identifying the type of data based on filename extensions and correspondent <u>mimetypes</u>. Example: the "bmp" part that comes after the dot in flower.bmp indicates that it is a specific kind of image, <u>image/x-bmp</u>. To know which application should open each type of file, the system should be informed about the abilities of each application to handle these extensions and mimetypes.</p> +<p>If you want to associate this application with one or more mimetypes that are not in this list, click on the button <b>Add</b> below. If there are one or more filetypes that this application cannot handle, you may want to remove them from the list clicking on the button <b>Remove</b> below.</p></qt></string> + </property> + </widget> + <widget class="KListView" row="7" column="0" rowspan="1" colspan="7"> + <column> + <property name="text"> + <string>Mimetype</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Description</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>filetypeList</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>0</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="fullWidth"> + <bool>true</bool> + </property> + <property name="whatsThis" stdset="0"> + <string><qt><p>This list should show the types of file that your application can handle. This list is organized by <u>mimetypes</u>.</p> +<p>MIME, Multipurpose Internet (e)Mail Extension, is a standard protocol for identifying the type of data based on filename extensions and correspondent <u>mimetypes</u>. Example: the "bmp" part that comes after the dot in flower.bmp indicates that it is a specific kind of image, <u>image/x-bmp</u>. To know which application should open each type of file, the system should be informed about the abilities of each application to handle these extensions and mimetypes.</p> +<p>If you want to associate this application with one or more mimetypes that are not in this list, click on the button <b>Add</b> below. If there are one or more filetypes that this application cannot handle, you may want to remove them from the list clicking on the button <b>Remove</b> below.</p></qt></string> + </property> + </widget> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>nameLabel</cstring> + </property> + <property name="text"> + <string>&Name:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>nameEdit</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>Type the name you want to give to this application here. This application will appear under this name in the applications menu and in the panel.</string> + </property> + </widget> + <widget class="KLineEdit" row="0" column="2" rowspan="1" colspan="5"> + <property name="name"> + <cstring>nameEdit</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>Type the name you want to give to this application here. This application will appear under this name in the applications menu and in the panel.</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>&Description:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>genNameEdit</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>Type the description of this application, based on its use, here. Examples: a dial up application (KPPP) would be "Dial up tool".</string> + </property> + </widget> + <widget class="KLineEdit" row="1" column="2" rowspan="1" colspan="5"> + <property name="name"> + <cstring>genNameEdit</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>Type the description of this application, based on its use, here. Examples: a dial up application (KPPP) would be "Dial up tool".</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Comm&ent:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>commentEdit</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>Type any comment you think is useful here.</string> + </property> + </widget> + <widget class="KLineEdit" row="2" column="2" rowspan="1" colspan="5"> + <property name="name"> + <cstring>commentEdit</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>Type any comment you think is useful here.</string> + </property> + </widget> + <widget class="QLabel" row="3" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>Co&mmand:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>commandEdit</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>Type the command to start this application here. + +Following the command, you can have several place holders which will be replaced with the actual values when the actual program is run: +%f - a single file name +%F - a list of files; use for applications that can open several local files at once +%u - a single URL +%U - a list of URLs +%d - the directory of the file to open +%D - a list of directories +%i - the icon +%m - the mini-icon +%c - the caption</string> + </property> + </widget> + <widget class="KLineEdit" row="3" column="2" rowspan="1" colspan="4"> + <property name="name"> + <cstring>commandEdit</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>Type the command to start this application here. + +Following the command, you can have several place holders which will be replaced with the actual values when the actual program is run: +%f - a single file name +%F - a list of files; use for applications that can open several local files at once +%u - a single URL +%U - a list of URLs +%d - the directory of the file to open +%D - a list of directories +%i - the icon +%m - the mini-icon +%c - the caption</string> + </property> + </widget> + <widget class="QPushButton" row="3" column="6"> + <property name="name"> + <cstring>browseButton</cstring> + </property> + <property name="text"> + <string>&Browse...</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Click here to browse your file system in order to find the desired executable.</string> + </property> + </widget> + <widget class="QLabel" row="4" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>textLabel5</cstring> + </property> + <property name="text"> + <string>&Work path:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>pathEdit</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>Sets the working directory for your application.</string> + </property> + </widget> + <widget class="KURLRequester" row="4" column="2" rowspan="1" colspan="5"> + <property name="name"> + <cstring>pathEdit</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>Sets the working directory for your application.</string> + </property> + </widget> + <widget class="QPushButton" row="8" column="0"> + <property name="name"> + <cstring>addFiletypeButton</cstring> + </property> + <property name="text"> + <string>Add...</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Click on this button if you want to add a type of file (mimetype) that your application can handle.</string> + </property> + </widget> + <spacer row="8" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>spacer31_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>MinimumExpanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>53</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton" row="8" column="3"> + <property name="name"> + <cstring>delFiletypeButton</cstring> + </property> + <property name="text"> + <string>Remove</string> + </property> + <property name="whatsThis" stdset="0"> + <string>If you want to remove a type of file (mimetype) that your application cannot handle, select the mimetype in the list above and click on this button.</string> + </property> + </widget> + <spacer row="8" column="4"> + <property name="name"> + <cstring>spacer31_3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>MinimumExpanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>53</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton" row="8" column="5" rowspan="1" colspan="2"> + <property name="name"> + <cstring>advancedButton</cstring> + </property> + <property name="text"> + <string>Ad&vanced Options</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Click here to modify the way this application will run, launch feedback, DCOP options or to run it as a different user.</string> + </property> + </widget> + </grid> +</widget> +<includes> + <include location="global" impldecl="in implementation">klineedit.h</include> + <include location="global" impldecl="in implementation">kurlrequester.h</include> + <include location="global" impldecl="in implementation">klistview.h</include> +</includes> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kio/kfile/kpropertiesdialog.cpp b/kio/kfile/kpropertiesdialog.cpp new file mode 100644 index 000000000..fd08d906d --- /dev/null +++ b/kio/kfile/kpropertiesdialog.cpp @@ -0,0 +1,4157 @@ +/* This file is part of the KDE project + + Copyright (C) 1998, 1999 Torben Weis <[email protected]> + Copyright (c) 1999, 2000 Preston Brown <[email protected]> + Copyright (c) 2000 Simon Hausmann <[email protected]> + Copyright (c) 2000 David Faure <[email protected]> + Copyright (c) 2003 Waldo Bastian <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/* + * kpropertiesdialog.cpp + * View/Edit Properties of files, locally or remotely + * + * some FilePermissionsPropsPlugin-changes by + * Henner Zeller <[email protected]> + * some layout management by + * Bertrand Leconte <[email protected]> + * the rest of the layout management, bug fixes, adaptation to libkio, + * template feature by + * David Faure <[email protected]> + * More layout, cleanups, and fixes by + * Preston Brown <[email protected]> + * Plugin capability, cleanups and port to KDialogBase by + * Simon Hausmann <[email protected]> + * KDesktopPropsPlugin by + * Waldo Bastian <[email protected]> + */ + +#include <config.h> +extern "C" { +#include <pwd.h> +#include <grp.h> +#include <time.h> +#include <sys/types.h> +} +#include <unistd.h> +#include <errno.h> +#include <assert.h> +#include <algorithm> +#include <functional> + +#include <qfile.h> +#include <qdir.h> +#include <qlabel.h> +#include <qpushbutton.h> +#include <qcheckbox.h> +#include <qstrlist.h> +#include <qstringlist.h> +#include <qtextstream.h> +#include <qpainter.h> +#include <qlayout.h> +#include <qcombobox.h> +#include <qgroupbox.h> +#include <qwhatsthis.h> +#include <qtooltip.h> +#include <qstyle.h> +#include <qprogressbar.h> +#include <qvbox.h> +#include <qvaluevector.h> + +#ifdef USE_POSIX_ACL +extern "C" { +#include <sys/param.h> +#ifdef HAVE_SYS_MOUNT_H +#include <sys/mount.h> +#endif +#ifdef HAVE_SYS_XATTR_H +#include <sys/xattr.h> +#endif +} +#endif + +#include <kapplication.h> +#include <kdialog.h> +#include <kdirsize.h> +#include <kdirwatch.h> +#include <kdirnotify_stub.h> +#include <kdiskfreesp.h> +#include <kdebug.h> +#include <kdesktopfile.h> +#include <kicondialog.h> +#include <kurl.h> +#include <kurlrequester.h> +#include <klocale.h> +#include <kglobal.h> +#include <kglobalsettings.h> +#include <kstandarddirs.h> +#include <kio/job.h> +#include <kio/chmodjob.h> +#include <kio/renamedlg.h> +#include <kio/netaccess.h> +#include <kio/kservicetypefactory.h> +#include <kfiledialog.h> +#include <kmimetype.h> +#include <kmountpoint.h> +#include <kiconloader.h> +#include <kmessagebox.h> +#include <kservice.h> +#include <kcompletion.h> +#include <klineedit.h> +#include <kseparator.h> +#include <ksqueezedtextlabel.h> +#include <klibloader.h> +#include <ktrader.h> +#include <kparts/componentfactory.h> +#include <kmetaprops.h> +#include <kpreviewprops.h> +#include <kprocess.h> +#include <krun.h> +#include <klistview.h> +#include <kacl.h> +#include "kfilesharedlg.h" + +#include "kpropertiesdesktopbase.h" +#include "kpropertiesdesktopadvbase.h" +#include "kpropertiesmimetypebase.h" +#ifdef USE_POSIX_ACL +#include "kacleditwidget.h" +#endif + +#include "kpropertiesdialog.h" + +#ifdef Q_WS_WIN +# include <win32_utils.h> +#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() + { + m_aborted = false; + fileSharePage = 0; + } + ~KPropertiesDialogPrivate() + { + } + bool m_aborted:1; + QWidget* fileSharePage; +}; + +KPropertiesDialog::KPropertiesDialog (KFileItem* item, + QWidget* parent, const char* name, + bool modal, bool autoShow) + : KDialogBase (KDialogBase::Tabbed, i18n( "Properties for %1" ).arg(KIO::decodeFileName(item->url().fileName())), + KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok, + parent, name, modal) +{ + d = new KPropertiesDialogPrivate; + assert( item ); + m_items.append( new KFileItem(*item) ); // deep copy + + m_singleUrl = item->url(); + assert(!m_singleUrl.isEmpty()); + + init (modal, autoShow); +} + +KPropertiesDialog::KPropertiesDialog (const QString& title, + QWidget* parent, const char* name, bool modal) + : KDialogBase (KDialogBase::Tabbed, i18n ("Properties for %1").arg(title), + KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok, + parent, name, modal) +{ + d = new KPropertiesDialogPrivate; + + init (modal, false); +} + +KPropertiesDialog::KPropertiesDialog (KFileItemList _items, + QWidget* parent, const char* name, + bool modal, bool autoShow) + : KDialogBase (KDialogBase::Tabbed, + // TODO: replace <never used> with "Properties for 1 item". It's very confusing how it has to be translated otherwise + // (empty translation before the "\n" is not allowed by msgfmt...) + _items.count()>1 ? i18n( "<never used>","Properties for %n Selected Items",_items.count()) : + i18n( "Properties for %1" ).arg(KIO::decodeFileName(_items.first()->url().fileName())), + KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok, + parent, name, modal) +{ + d = new KPropertiesDialogPrivate; + + assert( !_items.isEmpty() ); + m_singleUrl = _items.first()->url(); + assert(!m_singleUrl.isEmpty()); + + KFileItemListIterator it ( _items ); + // Deep copy + for ( ; it.current(); ++it ) + m_items.append( new KFileItem( **it ) ); + + init (modal, autoShow); +} + +#ifndef KDE_NO_COMPAT +KPropertiesDialog::KPropertiesDialog (const KURL& _url, mode_t /* _mode is now unused */, + QWidget* parent, const char* name, + bool modal, bool autoShow) + : KDialogBase (KDialogBase::Tabbed, + i18n( "Properties for %1" ).arg(KIO::decodeFileName(_url.fileName())), + KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok, + parent, name, modal), + m_singleUrl( _url ) +{ + d = new KPropertiesDialogPrivate; + + KIO::UDSEntry entry; + + KIO::NetAccess::stat(_url, entry, parent); + + m_items.append( new KFileItem( entry, _url ) ); + init (modal, autoShow); +} +#endif + +KPropertiesDialog::KPropertiesDialog (const KURL& _url, + QWidget* parent, const char* name, + bool modal, bool autoShow) + : KDialogBase (KDialogBase::Tabbed, + i18n( "Properties for %1" ).arg(KIO::decodeFileName(_url.fileName())), + KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok, + parent, name, modal), + m_singleUrl( _url ) +{ + d = new KPropertiesDialogPrivate; + + KIO::UDSEntry entry; + + KIO::NetAccess::stat(_url, entry, parent); + + m_items.append( new KFileItem( entry, _url ) ); + init (modal, autoShow); +} + +KPropertiesDialog::KPropertiesDialog (const KURL& _tempUrl, const KURL& _currentDir, + const QString& _defaultName, + QWidget* parent, const char* name, + bool modal, bool autoShow) + : KDialogBase (KDialogBase::Tabbed, + i18n( "Properties for %1" ).arg(KIO::decodeFileName(_tempUrl.fileName())), + KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok, + parent, name, modal), + + m_singleUrl( _tempUrl ), + m_defaultName( _defaultName ), + m_currentDir( _currentDir ) +{ + d = new KPropertiesDialogPrivate; + + assert(!m_singleUrl.isEmpty()); + + // Create the KFileItem for the _template_ file, in order to read from it. + m_items.append( new KFileItem( KFileItem::Unknown, KFileItem::Unknown, m_singleUrl ) ); + init (modal, autoShow); +} + +bool KPropertiesDialog::showDialog(KFileItem* item, QWidget* parent, + const char* name, bool modal) +{ +#ifdef Q_WS_WIN + QString localPath = item->localPath(); + if (!localPath.isEmpty()) + return showWin32FilePropertyDialog(localPath); +#endif + new KPropertiesDialog(item, parent, name, modal); + return true; +} + +bool KPropertiesDialog::showDialog(const KURL& _url, QWidget* parent, + const char* name, bool modal) +{ +#ifdef Q_WS_WIN + if (_url.isLocalFile()) + return showWin32FilePropertyDialog( _url.path() ); +#endif + new KPropertiesDialog(_url, parent, name, modal); + return true; +} + +bool KPropertiesDialog::showDialog(const KFileItemList& _items, QWidget* parent, + const char* name, bool modal) +{ + if (_items.count()==1) + return KPropertiesDialog::showDialog(_items.getFirst(), parent, name, modal); + new KPropertiesDialog(_items, parent, name, modal); + return true; +} + +void KPropertiesDialog::init (bool modal, bool autoShow) +{ + m_pageList.setAutoDelete( true ); + m_items.setAutoDelete( true ); + + insertPages(); + + if (autoShow) + { + if (!modal) + show(); + else + exec(); + } +} + +void KPropertiesDialog::showFileSharingPage() +{ + if (d->fileSharePage) { + showPage( pageIndex( d->fileSharePage)); + } +} + +void KPropertiesDialog::setFileSharingPage(QWidget* page) { + d->fileSharePage = page; +} + + +void KPropertiesDialog::setFileNameReadOnly( bool ro ) +{ + KPropsDlgPlugin *it; + + for ( it=m_pageList.first(); it != 0L; it=m_pageList.next() ) + { + KFilePropsPlugin* plugin = dynamic_cast<KFilePropsPlugin*>(it); + if ( plugin ) { + plugin->setFileNameReadOnly( ro ); + break; + } + } +} + +void KPropertiesDialog::slotStatResult( KIO::Job * ) +{ +} + +KPropertiesDialog::~KPropertiesDialog() +{ + m_pageList.clear(); + delete d; +} + +void KPropertiesDialog::insertPlugin (KPropsDlgPlugin* plugin) +{ + connect (plugin, SIGNAL (changed ()), + plugin, SLOT (setDirty ())); + + m_pageList.append (plugin); +} + +bool KPropertiesDialog::canDisplay( 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() +{ + KPropsDlgPlugin *page; + d->m_aborted = false; + + KFilePropsPlugin * filePropsPlugin = 0L; + if ( m_pageList.first()->isA("KFilePropsPlugin") ) + filePropsPlugin = static_cast<KFilePropsPlugin *>(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 ( page = m_pageList.first(); page != 0L; page = m_pageList.next() ) + if ( page->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 ( page = m_pageList.first(); page != 0L && !d->m_aborted; page = m_pageList.next() ) + if ( page->isDirty() ) + { + kdDebug( 250 ) << "applying changes for " << page->className() << endl; + page->applyChanges(); + // applyChanges may change d->m_aborted. + } + else + kdDebug( 250 ) << "skipping page " << page->className() << endl; + + if ( !d->m_aborted && filePropsPlugin ) + filePropsPlugin->postApplyChanges(); + + if ( !d->m_aborted ) + { + emit applied(); + emit propertiesClosed(); + deleteLater(); + accept(); + } // else, keep dialog open for user to fix the problem. +} + +void KPropertiesDialog::slotCancel() +{ + emit canceled(); + emit propertiesClosed(); + + deleteLater(); + done( Rejected ); +} + +void KPropertiesDialog::insertPages() +{ + if (m_items.isEmpty()) + return; + + if ( KFilePropsPlugin::supports( m_items ) ) + { + KPropsDlgPlugin *p = new KFilePropsPlugin( this ); + insertPlugin (p); + } + + if ( KFilePermissionsPropsPlugin::supports( m_items ) ) + { + KPropsDlgPlugin *p = new KFilePermissionsPropsPlugin( this ); + insertPlugin (p); + } + + if ( KDesktopPropsPlugin::supports( m_items ) ) + { + KPropsDlgPlugin *p = new KDesktopPropsPlugin( this ); + insertPlugin (p); + } + + if ( KBindingPropsPlugin::supports( m_items ) ) + { + KPropsDlgPlugin *p = new KBindingPropsPlugin( this ); + insertPlugin (p); + } + + if ( KURLPropsPlugin::supports( m_items ) ) + { + KPropsDlgPlugin *p = new KURLPropsPlugin( this ); + insertPlugin (p); + } + + if ( KDevicePropsPlugin::supports( m_items ) ) + { + KPropsDlgPlugin *p = new KDevicePropsPlugin( this ); + insertPlugin (p); + } + + if ( KFileMetaPropsPlugin::supports( m_items ) ) + { + KPropsDlgPlugin *p = new KFileMetaPropsPlugin( this ); + insertPlugin (p); + } + + if ( KPreviewPropsPlugin::supports( m_items ) ) + { + KPropsDlgPlugin *p = new KPreviewPropsPlugin( this ); + insertPlugin (p); + } + + if ( kapp->authorizeKAction("sharefile") && + KFileSharePropsPlugin::supports( m_items ) ) + { + KPropsDlgPlugin *p = new KFileSharePropsPlugin( this ); + insertPlugin (p); + } + + //plugins + + if ( m_items.count() != 1 ) + return; + + KFileItem *item = m_items.first(); + QString mimetype = item->mimetype(); + + if ( mimetype.isEmpty() ) + return; + + QString query = QString::fromLatin1( + "('KPropsDlg/Plugin' in ServiceTypes) and " + "((not exist [X-KDE-Protocol]) or " + " ([X-KDE-Protocol] == '%1' ) )" ).arg(item->url().protocol()); + + kdDebug( 250 ) << "trader query: " << query << endl; + KTrader::OfferList offers = KTrader::self()->query( mimetype, query ); + KTrader::OfferList::ConstIterator it = offers.begin(); + KTrader::OfferList::ConstIterator end = offers.end(); + for (; it != end; ++it ) + { + KPropsDlgPlugin *plugin = KParts::ComponentFactory + ::createInstanceFromLibrary<KPropsDlgPlugin>( (*it)->library().local8Bit().data(), + this, + (*it)->name().latin1() ); + if ( !plugin ) + continue; + + insertPlugin( plugin ); + } +} + +void KPropertiesDialog::updateUrl( const KURL& _newUrl ) +{ + Q_ASSERT( m_items.count() == 1 ); + kdDebug(250) << "KPropertiesDialog::updateUrl (pre)" << _newUrl.url() << endl; + KURL newUrl = _newUrl; + emit saveAs(m_singleUrl, newUrl); + kdDebug(250) << "KPropertiesDialog::updateUrl (post)" << newUrl.url() << endl; + + m_singleUrl = newUrl; + m_items.first()->setURL( newUrl ); + assert(!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) + for ( QPtrListIterator<KPropsDlgPlugin> it(m_pageList); it.current(); ++it ) + if ( it.current()->isA("KExecPropsPlugin") || // KDE4 remove me + it.current()->isA("KURLPropsPlugin") || + it.current()->isA("KDesktopPropsPlugin")) + { + //kdDebug(250) << "Setting page dirty" << endl; + it.current()->setDirty(); + break; + } +} + +void KPropertiesDialog::rename( const QString& _name ) +{ + Q_ASSERT( m_items.count() == 1 ); + kdDebug(250) << "KPropertiesDialog::rename " << _name << endl; + KURL newUrl; + // if we're creating from a template : use currentdir + if ( !m_currentDir.isEmpty() ) + { + newUrl = m_currentDir; + newUrl.addPath( _name ); + } + else + { + QString tmpurl = 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 KPropsDlgPlugin::KPropsDlgPluginPrivate +{ +public: + KPropsDlgPluginPrivate() + { + } + ~KPropsDlgPluginPrivate() + { + } + + bool m_bDirty; +}; + +KPropsDlgPlugin::KPropsDlgPlugin( KPropertiesDialog *_props ) +: QObject( _props, 0L ) +{ + d = new KPropsDlgPluginPrivate; + properties = _props; + fontHeight = 2*properties->fontMetrics().height(); + d->m_bDirty = false; +} + +KPropsDlgPlugin::~KPropsDlgPlugin() +{ + delete d; +} + +bool KPropsDlgPlugin::isDesktopFile( KFileItem * _item ) +{ + // only local files + bool isLocal; + KURL url = _item->mostLocalURL( isLocal ); + if ( !isLocal ) + return false; + + // only regular files + 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" ); +} + +void KPropsDlgPlugin::setDirty( bool b ) +{ + d->m_bDirty = b; +} + +void KPropsDlgPlugin::setDirty() +{ + d->m_bDirty = true; +} + +bool KPropsDlgPlugin::isDirty() const +{ + return d->m_bDirty; +} + +void KPropsDlgPlugin::applyChanges() +{ + kdWarning(250) << "applyChanges() not implemented in page !" << endl; +} + +/////////////////////////////////////////////////////////////////////////////// + +class KFilePropsPlugin::KFilePropsPluginPrivate +{ +public: + KFilePropsPluginPrivate() + { + dirSizeJob = 0L; + dirSizeUpdateTimer = 0L; + m_lined = 0; + m_freeSpaceLabel = 0; + } + ~KFilePropsPluginPrivate() + { + if ( dirSizeJob ) + dirSizeJob->kill(); + } + + KDirSize * dirSizeJob; + QTimer *dirSizeUpdateTimer; + QFrame *m_frame; + bool bMultiple; + bool bIconChanged; + bool bKDesktopMode; + bool bDesktopFile; + QLabel *m_freeSpaceLabel; + QString mimeType; + QString oldFileName; + KLineEdit* m_lined; +}; + +KFilePropsPlugin::KFilePropsPlugin( KPropertiesDialog *_props ) + : KPropsDlgPlugin( _props ) +{ + d = new KFilePropsPluginPrivate; + d->bMultiple = (properties->items().count() > 1); + d->bIconChanged = false; + d->bKDesktopMode = (QCString(qApp->name()) == "kdesktop"); // nasty heh? + d->bDesktopFile = KDesktopPropsPlugin::supports(properties->items()); + kdDebug(250) << "KFilePropsPlugin::KFilePropsPlugin bMultiple=" << d->bMultiple << endl; + + // 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(); + bool bDesktopFile = isDesktopFile(item); + kdDebug() << "url=" << url << " bDesktopFile=" << bDesktopFile << " isLocal=" << isLocal << " isReallyLocal=" << isReallyLocal << endl; + mode_t mode = item->mode(); + bool hasDirs = item->isDir() && !item->isLink(); + bool hasRoot = url.path() == QString::fromLatin1("/"); + QString iconStr = KMimeType::iconForURL(url, mode); + QString directory = properties->kurl().directory(); + QString protocol = properties->kurl().protocol(); + QString mimeComment = item->mimeComment(); + d->mimeType = item->mimetype(); + bool hasTotalSize; + KIO::filesize_t totalSize = item->size(hasTotalSize); + 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::null; + bool isTrash = false; + bool isDevice = false; + m_bFromTemplate = false; + + // And those only to 'multiple' mode + uint iDirCount = hasDirs ? 1 : 0; + uint iFileCount = 1-iDirCount; + + d->m_frame = properties->addPage (i18n("&General")); + + QVBoxLayout *vbl = new QVBoxLayout( d->m_frame, 0, + KDialog::spacingHint(), "vbl"); + QGridLayout *grid = new QGridLayout(0, 3); // unknown rows + grid->setColStretch(0, 0); + grid->setColStretch(1, 0); + grid->setColStretch(2, 1); + grid->addColSpacing(1, KDialog::spacingHint()); + vbl->addLayout(grid); + int curRow = 0; + + if ( !d->bMultiple ) + { + QString path; + if ( !m_bFromTemplate ) { + isTrash = ( properties->kurl().protocol().find( "trash", 0, false)==0 ); + if ( properties->kurl().protocol().find("device", 0, false)==0) + isDevice = true; + // 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(1) + properties->defaultName(); + directory = properties->currentDir().prettyURL(); + } + + if (KExecPropsPlugin::supports(properties->items()) || // KDE4 remove me + 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 + } else { + 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(), true /* readonly */ ); + if ( config.hasKey( "Name" ) ) { + filename = config.readName(); + } + } + + oldName = filename; + } + else + { + // Multiple items: see what they have in common + KFileItemList items = properties->items(); + KFileItemListIterator it( items ); + for ( ++it /*no need to check the first one again*/ ; it.current(); ++it ) + { + KURL url = (*it)->url(); + kdDebug(250) << "KFilePropsPlugin::KFilePropsPlugin " << url.prettyURL() << endl; + // 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(*it) != bDesktopFile ) + bDesktopFile = false; // not all desktop files + if ( (*it)->mode() != mode ) + mode = (mode_t)0; + if ( KMimeType::iconForURL(url, mode) != iconStr ) + iconStr = "kmultiple"; + if ( url.directory() != directory ) + directory = QString::null; + if ( url.protocol() != protocol ) + protocol = QString::null; + if ( !mimeComment.isNull() && (*it)->mimeComment() != mimeComment ) + mimeComment = QString::null; + if ( isLocal && !magicMimeComment.isNull() ) { + KMimeType::Ptr magicMimeType = KMimeType::findByFileContent( url.path() ); + if ( magicMimeType->comment() != magicMimeComment ) + magicMimeComment = QString::null; + } + + if ( url.path() == QString::fromLatin1("/") ) + hasRoot = true; + if ( (*it)->isDir() && !(*it)->isLink() ) + { + iDirCount++; + hasDirs = true; + } + else + { + iFileCount++; + bool hasSize; + totalSize += (*it)->size(hasSize); + hasTotalSize = hasTotalSize || hasSize; + } + } + } + + 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 )->icon( url, isLocal ); + if ( bDesktopFile && isLocal ) + { + KDesktopFile config( url.path(), true ); + config.setDesktopGroup(); + iconStr = config.readEntry( "Icon" ); + if ( config.hasDeviceType() ) + iconButton->setIconType( KIcon::Desktop, KIcon::Device ); + else + iconButton->setIconType( KIcon::Desktop, KIcon::Application ); + } else + iconButton->setIconType( KIcon::Desktop, KIcon::FileSystem ); + iconButton->setIcon(iconStr); + iconArea = iconButton; + connect( iconButton, SIGNAL( iconChanged(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( KGlobal::iconLoader()->loadIcon( iconStr, KIcon::Desktop, 48) ); + iconArea = iconLabel; + } + grid->addWidget(iconArea, curRow, 0, 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 ); + nameArea = lab; + } else + { + d->m_lined = new KLineEdit( d->m_frame ); + d->m_lined->setText(filename); + nameArea = d->m_lined; + d->m_lined->setFocus(); + + // Enhanced rename: Don't highlight the file extension. + QString pattern; + KServiceTypeFactory::self()->findFromPattern( filename, &pattern ); + if (!pattern.isEmpty() && pattern.at(0)=='*' && pattern.find('*',1)==-1) + d->m_lined->setSelection(0, filename.length()-pattern.stripWhiteSpace().length()+1); + else + { + int lastDot = filename.findRev('.'); + if (lastDot > 0) + d->m_lined->setSelection(0, lastDot); + } + + connect( d->m_lined, SIGNAL( textChanged( const QString & ) ), + this, SLOT( nameFileChanged(const QString & ) ) ); + } + + grid->addWidget(nameArea, curRow++, 2); + + KSeparator* sep = new KSeparator( KSeparator::HLine, d->m_frame); + grid->addMultiCellWidget(sep, curRow, curRow, 0, 2); + ++curRow; + + QLabel *l; + if ( !mimeComment.isEmpty() && !isDevice && !isTrash) + { + l = new QLabel(i18n("Type:"), d->m_frame ); + + grid->addWidget(l, curRow, 0); + + QHBox *box = new QHBox(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); + + QIconSet iconSet = SmallIconSet(QString::fromLatin1("configure")); + QPixmap pixMap = iconSet.pixmap( QIconSet::Small, QIconSet::Normal ); + button->setIconSet( iconSet ); + button->setFixedSize( pixMap.width()+8, pixMap.height()+8 ); + if ( d->mimeType == KMimeType::defaultMimeType() ) + QToolTip::add(button, i18n("Create new file type")); + else + QToolTip::add(button, i18n("Edit file type")); + + connect( button, SIGNAL( clicked() ), SLOT( slotEditFileType() )); + + if (!kapp->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( d->m_frame ); + l->setText( directory ); + grid->addWidget(l, curRow++, 2); + } + + if( hasDirs || hasTotalSize ) { + l = new QLabel(i18n("Size:"), d->m_frame ); + grid->addWidget(l, curRow, 0); + + m_sizeLabel = new QLabel( d->m_frame ); + grid->addWidget( m_sizeLabel, curRow++, 2 ); + } else { + m_sizeLabel = 0; + } + + if ( !hasDirs ) // Only files [and symlinks] + { + if(hasTotalSize) { + m_sizeLabel->setText(KIO::convertSizeWithBytes(totalSize)); + } + + m_sizeDetermineButton = 0L; + m_sizeStopButton = 0L; + } + else // Directory + { + QHBoxLayout * sizelay = new QHBoxLayout(KDialog::spacingHint()); + grid->addLayout( sizelay, curRow++, 2 ); + + // buttons + m_sizeDetermineButton = new QPushButton( i18n("Calculate"), d->m_frame ); + m_sizeStopButton = new QPushButton( i18n("Stop"), d->m_frame ); + connect( m_sizeDetermineButton, SIGNAL( clicked() ), this, SLOT( slotSizeDetermine() ) ); + connect( m_sizeStopButton, SIGNAL( clicked() ), this, SLOT( slotSizeStop() ) ); + sizelay->addWidget(m_sizeDetermineButton, 0); + sizelay->addWidget(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 ) + { + m_sizeDetermineButton->setText( i18n("Refresh") ); + slotSizeDetermine(); + } + else + m_sizeStopButton->setEnabled( false ); + } + + 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 ); + grid->addWidget(l, curRow++, 2); + } + + if (!d->bMultiple) // Dates for multiple don't make much sense... + { + QDateTime dt; + bool hasTime; + time_t tim = item->time(KIO::UDS_CREATION_TIME, hasTime); + if ( hasTime ) + { + l = new QLabel(i18n("Created:"), d->m_frame ); + grid->addWidget(l, curRow, 0); + + dt.setTime_t( tim ); + l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame ); + grid->addWidget(l, curRow++, 2); + } + + tim = item->time(KIO::UDS_MODIFICATION_TIME, hasTime); + if ( hasTime ) + { + l = new QLabel(i18n("Modified:"), d->m_frame ); + grid->addWidget(l, curRow, 0); + + dt.setTime_t( tim ); + l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame ); + grid->addWidget(l, curRow++, 2); + } + + tim = item->time(KIO::UDS_ACCESS_TIME, hasTime); + if ( hasTime ) + { + l = new QLabel(i18n("Accessed:"), d->m_frame ); + grid->addWidget(l, curRow, 0); + + dt.setTime_t( tim ); + l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame ); + grid->addWidget(l, curRow++, 2); + } + } + + if ( isLocal && hasDirs ) // only for directories + { + sep = new KSeparator( KSeparator::HLine, d->m_frame); + grid->addMultiCellWidget(sep, curRow, curRow, 0, 2); + ++curRow; + + QString mountPoint = KIO::findPathMountPoint( url.path() ); + + if (mountPoint != "/") + { + l = new QLabel(i18n("Mounted on:"), d->m_frame ); + grid->addWidget(l, curRow, 0); + + l = new KSqueezedTextLabel( 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 ); + + KDiskFreeSp * job = new KDiskFreeSp; + connect( job, SIGNAL( foundMountPoint( const unsigned long&, const unsigned long&, + const unsigned long&, const QString& ) ), + this, SLOT( slotFoundMountPoint( const unsigned long&, const unsigned long&, + const unsigned long&, const QString& ) ) ); + job->readDF( 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 + QPushButton *button = properties->actionButton(KDialogBase::Ok); + if (button) + button->setFocus(); + } + } +} + +void KFilePropsPlugin::slotEditFileType() +{ +#ifdef Q_WS_X11 + QString mime; + if ( d->mimeType == KMimeType::defaultMimeType() ) { + int pos = d->oldFileName.findRev( '.' ); + 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()) + + " " + KProcess::quote(mime), + keditfiletype, keditfiletype /*unused*/); +#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())) + { + m_sRelativePath =KGlobal::dirs()->relativeLocation("mime", path); + if (m_sRelativePath.startsWith("/")) + m_sRelativePath = QString::null; + } + else + { + m_sRelativePath =KGlobal::dirs()->relativeLocation("apps", path); + if (m_sRelativePath.startsWith("/")) + { + m_sRelativePath =KGlobal::dirs()->relativeLocation("xdgdata-apps", path); + if (m_sRelativePath.startsWith("/")) + m_sRelativePath = QString::null; + else + m_sRelativePath = path; + } + } + if ( m_sRelativePath.isEmpty() ) + { + if (KBindingPropsPlugin::supports(properties->items())) + kdWarning(250) << "Warning : editing a mimetype file out of the mimetype dirs!" << endl; + } +} + +void KFilePropsPlugin::slotFoundMountPoint( const QString&, + unsigned long kBSize, + unsigned long /*kBUsed*/, + unsigned long kBAvail ) +{ + d->m_freeSpaceLabel->setText( + // xgettext:no-c-format -- Don't warn about translating the %1 out of %2 part. + i18n("Available space out of total partition size (percent used)", "%1 out of %2 (%3% used)") + .arg(KIO::convertSizeFromKB(kBAvail)) + .arg(KIO::convertSizeFromKB(kBSize)) + .arg( 100 - (int)(100.0 * kBAvail / kBSize) )); +} + +// attention: copy&paste below, due to compiler bug +// it doesn't like those unsigned long parameters -- unsigned long& are ok :-/ +void KFilePropsPlugin::slotFoundMountPoint( const unsigned long& kBSize, + const unsigned long& /*kBUsed*/, + const unsigned long& kBAvail, + const QString& ) +{ + d->m_freeSpaceLabel->setText( + // xgettext:no-c-format -- Don't warn about translating the %1 out of %2 part. + i18n("Available space out of total partition size (percent used)", "%1 out of %2 (%3% used)") + .arg(KIO::convertSizeFromKB(kBAvail)) + .arg(KIO::convertSizeFromKB(kBSize)) + .arg( 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(); + m_sizeLabel->setText( i18n("Calculating... %1 (%2)\n%3, %4") + .arg(KIO::convertSize(totalSize)) + .arg(KGlobal::locale()->formatNumber(totalSize, 0)) + .arg(i18n("1 file","%n files",totalFiles)) + .arg(i18n("1 sub-folder","%n sub-folders",totalSubdirs))); +} + +void KFilePropsPlugin::slotDirSizeFinished( KIO::Job * job ) +{ + if (job->error()) + m_sizeLabel->setText( job->errorString() ); + else + { + KIO::filesize_t totalSize = static_cast<KDirSize*>(job)->totalSize(); + KIO::filesize_t totalFiles = static_cast<KDirSize*>(job)->totalFiles(); + KIO::filesize_t totalSubdirs = static_cast<KDirSize*>(job)->totalSubdirs(); + m_sizeLabel->setText( QString::fromLatin1("%1 (%2)\n%3, %4") + .arg(KIO::convertSize(totalSize)) + .arg(KGlobal::locale()->formatNumber(totalSize, 0)) + .arg(i18n("1 file","%n files",totalFiles)) + .arg(i18n("1 sub-folder","%n sub-folders",totalSubdirs))); + } + m_sizeStopButton->setEnabled(false); + // just in case you change something and try again :) + m_sizeDetermineButton->setText( i18n("Refresh") ); + m_sizeDetermineButton->setEnabled(true); + d->dirSizeJob = 0L; + delete d->dirSizeUpdateTimer; + d->dirSizeUpdateTimer = 0L; +} + +void KFilePropsPlugin::slotSizeDetermine() +{ + m_sizeLabel->setText( i18n("Calculating...") ); + kdDebug(250) << " KFilePropsPlugin::slotSizeDetermine() properties->item()=" << properties->item() << endl; + kdDebug(250) << " URL=" << properties->item()->url().url() << endl; + d->dirSizeJob = KDirSize::dirSizeJob( properties->items() ); + d->dirSizeUpdateTimer = new QTimer(this); + connect( d->dirSizeUpdateTimer, SIGNAL( timeout() ), + SLOT( slotDirSizeUpdate() ) ); + d->dirSizeUpdateTimer->start(500); + connect( d->dirSizeJob, SIGNAL( result( KIO::Job * ) ), + SLOT( slotDirSizeFinished( KIO::Job * ) ) ); + m_sizeStopButton->setEnabled(true); + 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 ); + QString mountPoint = KIO::findPathMountPoint( url.path() ); + + KDiskFreeSp * job = new KDiskFreeSp; + connect( job, SIGNAL( foundMountPoint( const unsigned long&, const unsigned long&, + const unsigned long&, const QString& ) ), + this, SLOT( slotFoundMountPoint( const unsigned long&, const unsigned long&, + const unsigned long&, const QString& ) ) ); + job->readDF( mountPoint ); + } +} + +void KFilePropsPlugin::slotSizeStop() +{ + if ( d->dirSizeJob ) + { + m_sizeLabel->setText( i18n("Stopped") ); + d->dirSizeJob->kill(); + d->dirSizeJob = 0; + } + if ( d->dirSizeUpdateTimer ) + d->dirSizeUpdateTimer->stop(); + + m_sizeStopButton->setEnabled(false); + m_sizeDetermineButton->setEnabled(true); +} + +KFilePropsPlugin::~KFilePropsPlugin() +{ + delete d; +} + +bool KFilePropsPlugin::supports( KFileItemList /*_items*/ ) +{ + return true; +} + +// Don't do this at home +void qt_enter_modal( QWidget *widget ); +void qt_leave_modal( QWidget *widget ); + +void KFilePropsPlugin::applyChanges() +{ + if ( d->dirSizeJob ) + slotSizeStop(); + + kdDebug(250) << "KFilePropsPlugin::applyChanges" << endl; + + if (nameArea->inherits("QLineEdit")) + { + QString n = ((QLineEdit *) 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 ? + kdDebug(250) << "oldname = " << oldName << endl; + kdDebug(250) << "newname = " << n << endl; + if ( oldName != n || 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 ( !m_sRelativePath.isEmpty() ) + determineRelativePath( properties->kurl().path() ); + + kdDebug(250) << "New URL = " << properties->kurl().url() << endl; + kdDebug(250) << "old = " << oldurl.url() << endl; + + // Don't remove the template !! + if ( !m_bFromTemplate ) // (normal renaming) + job = KIO::move( oldurl, properties->kurl() ); + else // Copying a template + job = KIO::copy( oldurl, properties->kurl() ); + + connect( job, SIGNAL( result( KIO::Job * ) ), + SLOT( slotCopyFinished( KIO::Job * ) ) ); + connect( job, SIGNAL( renamed( KIO::Job *, const KURL &, const KURL & ) ), + SLOT( slotFileRenamed( KIO::Job *, const KURL &, const KURL & ) ) ); + // wait for job + QWidget dummy(0,0,WType_Dialog|WShowModal); + qt_enter_modal(&dummy); + qApp->enter_loop(); + qt_leave_modal(&dummy); + return; + } + properties->updateUrl(properties->kurl()); + // Update also relative path (for apps and mimetypes) + if ( !m_sRelativePath.isEmpty() ) + determineRelativePath( properties->kurl().path() ); + } + + // No job, keep going + slotCopyFinished( 0L ); +} + +void KFilePropsPlugin::slotCopyFinished( KIO::Job * job ) +{ + kdDebug(250) << "KFilePropsPlugin::slotCopyFinished" << endl; + if (job) + { + // allow apply() to return + qApp->exit_loop(); + if ( job->error() ) + { + job->showErrorDialog( d->m_frame ); + // Didn't work. Revert the URL to the old one + properties->updateUrl( static_cast<KIO::CopyJob*>(job)->srcURLs().first() ); + properties->abortApplying(); // Don't apply the changes to the wrong file ! + return; + } + } + + assert( properties->item() ); + assert( !properties->item()->url().isEmpty() ); + + // Save the file where we can -> usually in ~/.kde/... + if (KBindingPropsPlugin::supports(properties->items()) && !m_sRelativePath.isEmpty()) + { + KURL newURL; + newURL.setPath( locateLocal("mime", m_sRelativePath) ); + properties->updateUrl( newURL ); + } + else if (d->bDesktopFile && !m_sRelativePath.isEmpty()) + { + kdDebug(250) << "KFilePropsPlugin::slotCopyFinished " << m_sRelativePath << endl; + KURL newURL; + newURL.setPath( KDesktopFile::locateLocal(m_sRelativePath) ); + kdDebug(250) << "KFilePropsPlugin::slotCopyFinished path=" << newURL.path() << endl; + properties->updateUrl( newURL ); + } + + if ( d->bKDesktopMode && d->bDesktopFile ) { + // Renamed? Update Name field + if ( d->oldFileName != properties->kurl().fileName() || m_bFromTemplate ) { + KDesktopFile config( properties->kurl().path() ); + QString nameStr = nameFromFileName(properties->kurl().fileName()); + config.writeEntry( "Name", nameStr ); + config.writeEntry( "Name", nameStr, true, false, true ); + } + } +} + +void KFilePropsPlugin::applyIconChanges() +{ + KIconButton *iconButton = ::qt_cast<KIconButton *>( 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())) + { + path = url.path(1) + 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(), + true )->KServiceType::icon(); + // Is it another one than the default ? + QString sIcon; + if ( str != iconButton->icon() ) + sIcon = iconButton->icon(); + // (otherwise write empty value) + + kdDebug(250) << "**" << path << "**" << endl; + QFile f( path ); + + // If default icon and no .directory file -> don't create one + if ( !sIcon.isEmpty() || f.exists() ) + { + if ( !f.open( IO_ReadWrite ) ) { + KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not " + "have sufficient access to write to <b>%1</b>.</qt>").arg(path)); + return; + } + f.close(); + + KDesktopFile cfg(path); + kdDebug(250) << "sIcon = " << (sIcon) << endl; + kdDebug(250) << "str = " << (str) << endl; + cfg.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(); + + KURL::List lst; + KFileItemList items = properties->items(); + for ( KFileItemListIterator it( items ); it.current(); ++it ) + lst.append((*it)->url()); + KDirNotify_stub allDirNotify("*", "KDirNotify*"); + allDirNotify.FilesChanged( lst ); +} + +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; +}; + +#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 ) + : KPropsDlgPlugin( _props ) +{ + d = new KFilePermissionsPropsPluginPrivate; + d->cbRecursive = 0L; + grpCombo = 0L; grpEdit = 0; + usrEdit = 0L; + QString path = properties->kurl().path(-1); + QString fname = properties->kurl().fileName(); + bool isLocal = properties->kurl().isLocalFile(); + bool isTrash = ( properties->kurl().protocol().find("trash", 0, false)==0 ); + 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 + permissions = item->permissions(); // common permissions to all files + d->partialPermissions = permissions; // permissions that only some files have (at first we take everything) + d->isIrregular = isIrregular(permissions, isDir, isLink); + strOwner = item->user(); + 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 + KFileItemList items = properties->items(); + KFileItemListIterator it( items ); + for ( ++it /*no need to check the first one again*/ ; it.current(); ++it ) + { + if (!d->isIrregular) + 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 ) + isDir = false; + hasDir |= (*it)->isDir(); + if ( (*it)->permissions() != permissions ) + { + permissions &= (*it)->permissions(); + d->partialPermissions |= (*it)->permissions(); + } + if ( (*it)->user() != strOwner ) + strOwner = QString::null; + if ( (*it)->group() != strGroup ) + strGroup = QString::null; + } + } + + 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 & ~permissions; + + bool isMyFile = false; + + if (isLocal && !strOwner.isEmpty()) { // local files, and all owned by the same person + struct passwd *myself = getpwuid( geteuid() ); + if ( myself != 0L ) + { + isMyFile = (strOwner == QString::fromLocal8Bit(myself->pw_name)); + } else + kdWarning() << "I don't exist ?! geteuid=" << geteuid() << endl; + } 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 = properties->addPage(i18n("&Permissions")); + + QBoxLayout *box = new QVBoxLayout( d->m_frame, 0, KDialog::spacingHint() ); + + QWidget *l; + QLabel *lbl; + QGroupBox *gb; + QGridLayout *gl; + QPushButton* pbAdvancedPerm = 0; + + /* Group: Access Permissions */ + gb = new QGroupBox ( 0, Qt::Vertical, i18n("Access Permissions"), d->m_frame ); + gb->layout()->setSpacing(KDialog::spacingHint()); + gb->layout()->setMargin(KDialog::marginHint()); + box->addWidget (gb); + + gl = new QGridLayout (gb->layout(), 7, 2); + gl->setColStretch(1, 1); + + l = d->explanationLabel = new QLabel( "", gb ); + if (isLink) + d->explanationLabel->setText(i18n("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->addMultiCellWidget(l, 0, 0, 0, 1); + + 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() )); + QWhatsThis::add(l, 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() )); + QWhatsThis::add(l, 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() )); + QWhatsThis::add(l, 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); + QWhatsThis::add(l, 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->addMultiCell(spacer, 5, 5, 0, 1); + + pbAdvancedPerm = new QPushButton(i18n("A&dvanced Permissions"), gb); + gl->addMultiCellWidget(pbAdvancedPerm, 6, 6, 0, 1, AlignRight); + connect(pbAdvancedPerm, SIGNAL( clicked() ), this, SLOT( slotShowAdvancedPermissions() )); + } + else + d->extraCheckbox = 0; + + + /**** Group: Ownership ****/ + gb = new QGroupBox ( 0, Qt::Vertical, i18n("Ownership"), d->m_frame ); + gb->layout()->setSpacing(KDialog::spacingHint()); + gb->layout()->setMargin(KDialog::marginHint()); + box->addWidget (gb); + + gl = new QGridLayout (gb->layout(), 4, 3); + gl->addRowSpacing(0, 10); + + /*** 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) + { + usrEdit = new KLineEdit( gb ); + KCompletion *kcom = usrEdit->completionObject(); + kcom->setOrder(KCompletion::Sorted); + setpwent(); + for (i=0; ((user = getpwent()) != 0L) && (i < maxEntries); i++) + kcom->addItem(QString::fromLatin1(user->pw_name)); + endpwent(); + usrEdit->setCompletionMode((i < maxEntries) ? KGlobalSettings::CompletionAuto : + KGlobalSettings::CompletionNone); + usrEdit->setText(strOwner); + gl->addWidget(usrEdit, 1, 1); + connect( usrEdit, SIGNAL( textChanged( const QString & ) ), + this, SIGNAL( changed() ) ); + } + else + { + l = new QLabel(strOwner, gb); + gl->addWidget(l, 1, 1); + } + + /*** Set Group ***/ + + QStringList groupList; + QCString 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.find(name) == groupList.end()) + groupList += name; + } + + bool isMyGroup = groupList.contains(strGroup); + + /* add the group the file currently belongs to .. + * .. if its not there already + */ + if (!isMyGroup) + groupList += 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) + { + grpEdit = new KLineEdit(gb); + KCompletion *kcom = new KCompletion; + kcom->setItems(groupList); + grpEdit->setCompletionObject(kcom, true); + grpEdit->setAutoDeleteCompletionObject( true ); + grpEdit->setCompletionMode(KGlobalSettings::CompletionAuto); + grpEdit->setText(strGroup); + gl->addWidget(grpEdit, 2, 1); + connect( grpEdit, SIGNAL( textChanged( const QString & ) ), + this, SIGNAL( changed() ) ); + } + else if ((groupList.count() > 1) && isMyFile && isLocal) + { + grpCombo = new QComboBox(gb, "combogrouplist"); + grpCombo->insertStringList(groupList); + grpCombo->setCurrentItem(groupList.findIndex(strGroup)); + gl->addWidget(grpCombo, 2, 1); + connect( grpCombo, SIGNAL( activated( int ) ), + this, SIGNAL( changed() ) ); + } + else + { + l = new QLabel(strGroup, gb); + gl->addWidget(l, 2, 1); + } + + gl->setColStretch(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 || !d->canChangePermissions ) + { + //don't allow to change properties for file into trash + enableAccessControls(false); + if ( pbAdvancedPerm && !d->hasExtendedACL ) + pbAdvancedPerm->setEnabled(false); + } + + box->addStretch (10); +} + +#ifdef USE_POSIX_ACL +static bool fileSystemSupportsACL( const QCString& pathCString ) +{ + bool fileSystemSupportsACLs = false; +#ifdef Q_OS_FREEBSD + struct statfs buf; + fileSystemSupportsACLs = ( statfs( pathCString.data(), &buf ) == 0 ) && ( buf.f_flags & MNT_ACLS ); +#else + fileSystemSupportsACLs = + getxattr( pathCString.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); + KDialogBase dlg(properties, 0, true, i18n("Advanced Permissions"), + KDialogBase::Ok|KDialogBase::Cancel); + + QLabel *l, *cl[3]; + QGroupBox *gb; + QGridLayout *gl; + + QVBox *mainVBox = dlg.makeVBoxMainWidget(); + + // Group: Access Permissions + gb = new QGroupBox ( 0, Qt::Vertical, i18n("Access Permissions"), mainVBox ); + gb->layout()->setSpacing(KDialog::spacingHint()); + gb->layout()->setMargin(KDialog::marginHint()); + + gl = new QGridLayout (gb->layout(), 6, 6); + gl->addRowSpacing(0, 10); + + QValueVector<QWidget*> 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."); + QWhatsThis::add(l, 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."); + QWhatsThis::add(l, writeWhatsThis); + + QString execWhatsThis; + if (isDir) { + l = new QLabel( i18n("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."); + } + QWhatsThis::add(l, 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->addMultiCellWidget(l, 1, 1, 4, 5); + 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."); + QWhatsThis::add(l, 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."); + QWhatsThis::add(l, 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."); + QWhatsThis::add(l, setGidWhatsThis); + + l = new QLabel(i18n("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"); + QWhatsThis::add(l, 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 = 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->setNoChange(); + } + else if (d->cbRecursive && d->cbRecursive->isChecked()) + cb->setTristate(); + + cb->setEnabled( d->canChangePermissions ); + gl->addWidget (cb, row+2, col+1); + switch(col) { + case 0: + QWhatsThis::add(cb, readWhatsThis); + break; + case 1: + QWhatsThis::add(cb, writeWhatsThis); + break; + case 2: + QWhatsThis::add(cb, execWhatsThis); + break; + case 3: + switch(row) { + case 0: + QWhatsThis::add(cb, setUidWhatsThis); + break; + case 1: + QWhatsThis::add(cb, setGidWhatsThis); + break; + case 2: + QWhatsThis::add(cb, stickyWhatsThis); + break; + } + break; + } + } + } + gl->setColStretch(6, 10); + +#ifdef USE_POSIX_ACL + KACLEditWidget *extendedACLs = 0; + + // FIXME make it work with partial entries + if ( properties->items().count() == 1 ) { + QCString pathCString = QFile::encodeName( properties->item()->url().path() ); + d->fileSystemSupportsACLs = fileSystemSupportsACL( pathCString ); + } + if ( d->fileSystemSupportsACLs ) { + std::for_each( theNotSpecials.begin(), theNotSpecials.end(), std::mem_fun( &QWidget::hide ) ); + extendedACLs = new KACLEditWidget( mainVBox ); + 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() ) + extendedACLs->setAllowDefaults( true ); + if ( !d->canChangePermissions ) + extendedACLs->setReadOnly( true ); + + } +#endif + if (dlg.exec() != KDialogBase::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]->state()) + { + case QCheckBox::On: + orPermissions |= fperm[row][col]; + //fall through + case QCheckBox::Off: + andPermissions &= ~fperm[row][col]; + break; + default: // NoChange + break; + } + } + + d->isIrregular = false; + KFileItemList items = properties->items(); + for (KFileItemListIterator it(items); it.current(); ++it) { + if (isIrregular(((*it)->permissions() & andPermissions) | orPermissions, + (*it)->isDir(), (*it)->isLink())) { + d->isIrregular = true; + break; + } + } + + permissions = orPermissions; + d->partialPermissions = andPermissions; + +#ifdef USE_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(); + permissions = d->extendedACL.basePermissions(); + 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( KFileItemList _items ) +{ + KFileItemList::const_iterator it = _items.constBegin(); + for ( ; it != _items.constEnd(); ++it ) { + KFileItem *item = *it; + if( !item->user().isEmpty() || !item->group().isEmpty() ) + return true; + } + return false; +} + +// 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->insertItem(i18n("Link")); + combo->setCurrentItem(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->insertItem(i18n(permissionsTexts[(int)d->pmode][i])); + + if (partial & tMask & ~UniExec) { + combo->insertItem(i18n("Varying (No Change)")); + combo->setCurrentItem(3); + } + else + combo->setCurrentItem(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, + permissions, d->partialPermissions); + setComboContent(d->groupPermCombo, PermissionsGroup, + permissions, d->partialPermissions); + setComboContent(d->othersPermCombo, PermissionsOthers, + 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 ? + i18n("This file uses advanced permissions", + "These files use advanced permissions.", + properties->items().count()) : ""); + if (d->partialPermissions & UniExec) { + d->extraCheckbox->setTristate(); + d->extraCheckbox->setNoChange(); + } + else { + d->extraCheckbox->setTristate(false); + d->extraCheckbox->setChecked(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 ? + i18n("This folder uses advanced permissions.", + "These folders use advanced permissions.", + properties->items().count()) : ""); + if (d->partialPermissions & S_ISVTX) { + d->extraCheckbox->setTristate(); + d->extraCheckbox->setNoChange(); + } + else { + d->extraCheckbox->setTristate(false); + d->extraCheckbox->setChecked(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->setNoChange(); + } + else { + d->extraCheckbox->setTristate(false); + d->extraCheckbox->setChecked(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->currentItem()]; + if (m != (mode_t) -1) { + orFilePermissions |= m & UniOwner; + if ((m & UniOwner) && + ((d->pmode == PermissionsMixed) || + ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->state() == QButton::NoChange)))) + andFilePermissions &= ~(S_IRUSR | S_IWUSR); + else { + andFilePermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR); + if ((m & S_IRUSR) && (d->extraCheckbox->state() == QButton::On)) + orFilePermissions |= S_IXUSR; + } + + orDirPermissions |= m & UniOwner; + if (m & S_IRUSR) + orDirPermissions |= S_IXUSR; + andDirPermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR); + } + + m = standardPermissions[d->groupPermCombo->currentItem()]; + if (m != (mode_t) -1) { + orFilePermissions |= m & UniGroup; + if ((m & UniGroup) && + ((d->pmode == PermissionsMixed) || + ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->state() == QButton::NoChange)))) + andFilePermissions &= ~(S_IRGRP | S_IWGRP); + else { + andFilePermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP); + if ((m & S_IRGRP) && (d->extraCheckbox->state() == QButton::On)) + orFilePermissions |= S_IXGRP; + } + + orDirPermissions |= m & UniGroup; + if (m & S_IRGRP) + orDirPermissions |= S_IXGRP; + andDirPermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP); + } + + m = standardPermissions[d->othersPermCombo->currentItem()]; + if (m != (mode_t) -1) { + orFilePermissions |= m & UniOthers; + if ((m & UniOthers) && + ((d->pmode == PermissionsMixed) || + ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->state() == QButton::NoChange)))) + andFilePermissions &= ~(S_IROTH | S_IWOTH); + else { + andFilePermissions &= ~(S_IROTH | S_IWOTH | S_IXOTH); + if ((m & S_IROTH) && (d->extraCheckbox->state() == QButton::On)) + 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->state() != QButton::NoChange)) { + andDirPermissions &= ~S_ISVTX; + if (d->extraCheckbox->state() == QButton::On) + 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 = permissions; + andFilePermissions = d->partialPermissions; + orDirPermissions = permissions; + andDirPermissions = d->partialPermissions; + } + + QString owner, group; + if (usrEdit) + owner = usrEdit->text(); + if (grpEdit) + group = grpEdit->text(); + else if (grpCombo) + group = grpCombo->currentText(); + + if (owner == strOwner) + owner = QString::null; // no change + + if (group == strGroup) + group = QString::null; + + bool recursive = d->cbRecursive && d->cbRecursive->isChecked(); + bool permissionChange = false; + + KFileItemList files, dirs; + KFileItemList items = properties->items(); + for (KFileItemListIterator it(items); it.current(); ++it) { + if ((*it)->isDir()) { + dirs.append(*it); + if ((*it)->permissions() != (((*it)->permissions() & andDirPermissions) | orDirPermissions)) + permissionChange = true; + } + else if ((*it)->isFile()) { + files.append(*it); + 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() ); + + 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( KIO::Job * ) ), + SLOT( slotChmodResult( KIO::Job * ) ) ); + // Wait for job + QWidget dummy(0,0,WType_Dialog|WShowModal); + qt_enter_modal(&dummy); + qApp->enter_loop(); + qt_leave_modal(&dummy); + } + 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( KIO::Job * ) ), + SLOT( slotChmodResult( KIO::Job * ) ) ); + // Wait for job + QWidget dummy(0,0,WType_Dialog|WShowModal); + qt_enter_modal(&dummy); + qApp->enter_loop(); + qt_leave_modal(&dummy); + } +} + +void KFilePermissionsPropsPlugin::slotChmodResult( KIO::Job * job ) +{ + kdDebug(250) << "KFilePermissionsPropsPlugin::slotChmodResult" << endl; + if (job->error()) + job->showErrorDialog( d->m_frame ); + // allow apply() to return + qApp->exit_loop(); +} + + + + +class KURLPropsPlugin::KURLPropsPluginPrivate +{ +public: + KURLPropsPluginPrivate() + { + } + ~KURLPropsPluginPrivate() + { + } + + QFrame *m_frame; +}; + +KURLPropsPlugin::KURLPropsPlugin( KPropertiesDialog *_props ) + : KPropsDlgPlugin( _props ) +{ + d = new KURLPropsPluginPrivate; + d->m_frame = properties->addPage(i18n("U&RL")); + QVBoxLayout *layout = new QVBoxLayout(d->m_frame, 0, KDialog::spacingHint()); + + QLabel *l; + l = new QLabel( d->m_frame, "Label_1" ); + l->setText( i18n("URL:") ); + layout->addWidget(l); + + URLEdit = new KURLRequester( d->m_frame, "URL Requester" ); + layout->addWidget(URLEdit); + + QString path = properties->kurl().path(); + + QFile f( path ); + if ( !f.open( IO_ReadOnly ) ) + return; + f.close(); + + KSimpleConfig config( path ); + config.setDesktopGroup(); + URLStr = config.readPathEntry( "URL" ); + + if ( !URLStr.isNull() ) + URLEdit->setURL( URLStr ); + + connect( 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( KFileItemList _items ) +{ + if ( _items.count() != 1 ) + return false; + KFileItem * item = _items.first(); + // check if desktop file + if ( !KPropsDlgPlugin::isDesktopFile( item ) ) + return false; + + // open file and check type + KDesktopFile config( item->url().path(), true /* readonly */ ); + return config.hasLinkType(); +} + +void KURLPropsPlugin::applyChanges() +{ + QString path = properties->kurl().path(); + + QFile f( path ); + if ( !f.open( IO_ReadWrite ) ) { + KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have " + "sufficient access to write to <b>%1</b>.</qt>").arg(path)); + return; + } + f.close(); + + KSimpleConfig config( path ); + config.setDesktopGroup(); + config.writeEntry( "Type", QString::fromLatin1("Link")); + config.writePathEntry( "URL", URLEdit->url() ); + // Users can't create a Link .desktop file with a Name field, + // but distributions can. Update the Name field in that case. + if ( config.hasKey("Name") ) + { + QString nameStr = nameFromFileName(properties->kurl().fileName()); + config.writeEntry( "Name", nameStr ); + config.writeEntry( "Name", nameStr, true, false, true ); + + } +} + + +/* ---------------------------------------------------- + * + * KBindingPropsPlugin + * + * -------------------------------------------------- */ + +class KBindingPropsPlugin::KBindingPropsPluginPrivate +{ +public: + KBindingPropsPluginPrivate() + { + } + ~KBindingPropsPluginPrivate() + { + } + + QFrame *m_frame; +}; + +KBindingPropsPlugin::KBindingPropsPlugin( KPropertiesDialog *_props ) : KPropsDlgPlugin( _props ) +{ + d = new KBindingPropsPluginPrivate; + d->m_frame = properties->addPage(i18n("A&ssociation")); + patternEdit = new KLineEdit( d->m_frame, "LineEdit_1" ); + commentEdit = new KLineEdit( d->m_frame, "LineEdit_2" ); + mimeEdit = new KLineEdit( d->m_frame, "LineEdit_3" ); + + QBoxLayout *mainlayout = new QVBoxLayout(d->m_frame, 0, KDialog::spacingHint()); + QLabel* tmpQLabel; + + tmpQLabel = new QLabel( d->m_frame, "Label_1" ); + tmpQLabel->setText( i18n("Pattern ( example: *.html;*.htm )") ); + tmpQLabel->setMinimumSize(tmpQLabel->sizeHint()); + mainlayout->addWidget(tmpQLabel, 1); + + //patternEdit->setGeometry( 10, 40, 210, 30 ); + //patternEdit->setText( "" ); + patternEdit->setMaxLength( 512 ); + patternEdit->setMinimumSize( patternEdit->sizeHint() ); + patternEdit->setFixedHeight( fontHeight ); + mainlayout->addWidget(patternEdit, 1); + + tmpQLabel = new QLabel( d->m_frame, "Label_2" ); + tmpQLabel->setText( i18n("Mime Type") ); + tmpQLabel->setMinimumSize(tmpQLabel->sizeHint()); + mainlayout->addWidget(tmpQLabel, 1); + + //mimeEdit->setGeometry( 10, 160, 210, 30 ); + mimeEdit->setMaxLength( 256 ); + mimeEdit->setMinimumSize( mimeEdit->sizeHint() ); + mimeEdit->setFixedHeight( fontHeight ); + mainlayout->addWidget(mimeEdit, 1); + + tmpQLabel = new QLabel( d->m_frame, "Label_3" ); + tmpQLabel->setText( i18n("Comment") ); + tmpQLabel->setMinimumSize(tmpQLabel->sizeHint()); + mainlayout->addWidget(tmpQLabel, 1); + + //commentEdit->setGeometry( 10, 100, 210, 30 ); + commentEdit->setMaxLength( 256 ); + commentEdit->setMinimumSize( commentEdit->sizeHint() ); + commentEdit->setFixedHeight( fontHeight ); + mainlayout->addWidget(commentEdit, 1); + + cbAutoEmbed = new QCheckBox( i18n("Left click previews"), d->m_frame, "cbAutoEmbed" ); + mainlayout->addWidget(cbAutoEmbed, 1); + + mainlayout->addStretch (10); + mainlayout->activate(); + + QFile f( _props->kurl().path() ); + if ( !f.open( IO_ReadOnly ) ) + return; + f.close(); + + KSimpleConfig config( _props->kurl().path() ); + config.setDesktopGroup(); + QString patternStr = config.readEntry( "Patterns" ); + QString iconStr = config.readEntry( "Icon" ); + QString commentStr = config.readEntry( "Comment" ); + m_sMimeStr = config.readEntry( "MimeType" ); + + if ( !patternStr.isEmpty() ) + patternEdit->setText( patternStr ); + if ( !commentStr.isEmpty() ) + commentEdit->setText( commentStr ); + if ( !m_sMimeStr.isEmpty() ) + mimeEdit->setText( m_sMimeStr ); + cbAutoEmbed->setTristate(); + if ( config.hasKey( "X-KDE-AutoEmbed" ) ) + cbAutoEmbed->setChecked( config.readBoolEntry( "X-KDE-AutoEmbed" ) ); + else + cbAutoEmbed->setNoChange(); + + connect( patternEdit, SIGNAL( textChanged( const QString & ) ), + this, SIGNAL( changed() ) ); + connect( commentEdit, SIGNAL( textChanged( const QString & ) ), + this, SIGNAL( changed() ) ); + connect( mimeEdit, SIGNAL( textChanged( const QString & ) ), + this, SIGNAL( changed() ) ); + connect( cbAutoEmbed, SIGNAL( toggled( bool ) ), + this, SIGNAL( changed() ) ); +} + +KBindingPropsPlugin::~KBindingPropsPlugin() +{ + delete d; +} + +// QString KBindingPropsPlugin::tabName () const +// { +// return i18n ("A&ssociation"); +// } + +bool KBindingPropsPlugin::supports( KFileItemList _items ) +{ + if ( _items.count() != 1 ) + return false; + KFileItem * item = _items.first(); + // check if desktop file + if ( !KPropsDlgPlugin::isDesktopFile( item ) ) + return false; + + // open file and check type + KDesktopFile config( item->url().path(), true /* readonly */ ); + return config.hasMimeTypeType(); +} + +void KBindingPropsPlugin::applyChanges() +{ + QString path = properties->kurl().path(); + QFile f( path ); + + if ( !f.open( IO_ReadWrite ) ) + { + KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have " + "sufficient access to write to <b>%1</b>.</qt>").arg(path)); + return; + } + f.close(); + + KSimpleConfig config( path ); + config.setDesktopGroup(); + config.writeEntry( "Type", QString::fromLatin1("MimeType") ); + + config.writeEntry( "Patterns", patternEdit->text() ); + config.writeEntry( "Comment", commentEdit->text() ); + config.writeEntry( "Comment", + commentEdit->text(), true, false, true ); // for compat + config.writeEntry( "MimeType", mimeEdit->text() ); + if ( cbAutoEmbed->state() == QButton::NoChange ) + config.deleteEntry( "X-KDE-AutoEmbed", false ); + else + config.writeEntry( "X-KDE-AutoEmbed", cbAutoEmbed->isChecked() ); + config.sync(); +} + +/* ---------------------------------------------------- + * + * KDevicePropsPlugin + * + * -------------------------------------------------- */ + +class KDevicePropsPlugin::KDevicePropsPluginPrivate +{ +public: + KDevicePropsPluginPrivate() + { + } + ~KDevicePropsPluginPrivate() + { + } + + QFrame *m_frame; + QStringList mountpointlist; + QLabel *m_freeSpaceText; + QLabel *m_freeSpaceLabel; + QProgressBar *m_freeSpaceBar; +}; + +KDevicePropsPlugin::KDevicePropsPlugin( KPropertiesDialog *_props ) : KPropsDlgPlugin( _props ) +{ + d = new KDevicePropsPluginPrivate; + d->m_frame = properties->addPage(i18n("De&vice")); + + QStringList devices; + KMountPoint::List mountPoints = KMountPoint::possibleMountPoints(); + + for(KMountPoint::List::ConstIterator it = mountPoints.begin(); + it != mountPoints.end(); ++it) + { + KMountPoint *mp = *it; + QString mountPoint = mp->mountPoint(); + QString device = mp->mountedFrom(); + kdDebug()<<"mountPoint :"<<mountPoint<<" device :"<<device<<" mp->mountType() :"<<mp->mountType()<<endl; + + if ((mountPoint != "-") && (mountPoint != "none") && !mountPoint.isEmpty() + && device != "none") + { + devices.append( device + QString::fromLatin1(" (") + + mountPoint + QString::fromLatin1(")") ); + m_devicelist.append(device); + d->mountpointlist.append(mountPoint); + } + } + + QGridLayout *layout = new QGridLayout( d->m_frame, 0, 2, 0, + KDialog::spacingHint()); + layout->setColStretch(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); + + device = new QComboBox( true, d->m_frame, "ComboBox_device" ); + device->insertStringList( devices ); + layout->addWidget(device, 0, 1); + connect( device, SIGNAL( activated( int ) ), + this, SLOT( slotActivated( int ) ) ); + + readonly = new QCheckBox( d->m_frame, "CheckBox_readonly" ); + readonly->setText( i18n("Read only") ); + layout->addWidget(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); + + mountpoint = new QLabel( d->m_frame, "LineEdit_mountpoint" ); + + layout->addWidget(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, "freeSpaceBar" ); + layout->addMultiCellWidget(d->m_freeSpaceBar, 5, 5, 0, 1); + + // 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( KSeparator::HLine, d->m_frame); + layout->addMultiCellWidget(sep, 6, 6, 0, 1); + + unmounted = new KIconButton( d->m_frame ); + int bsize = 66 + 2 * unmounted->style().pixelMetric(QStyle::PM_ButtonMargin); + unmounted->setFixedSize(bsize, bsize); + unmounted->setIconType(KIcon::Desktop, KIcon::Device); + layout->addWidget(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( IO_ReadOnly ) ) + return; + f.close(); + + KSimpleConfig config( path ); + config.setDesktopGroup(); + QString deviceStr = config.readEntry( "Dev" ); + QString mountPointStr = config.readEntry( "MountPoint" ); + bool ro = config.readBoolEntry( "ReadOnly", false ); + QString unmountedStr = config.readEntry( "UnmountIcon" ); + + fileSystem->setText( i18n(config.readEntry("FSType").local8Bit()) ); + + device->setEditText( deviceStr ); + if ( !deviceStr.isEmpty() ) { + // Set default options for this device (first matching entry) + int index = m_devicelist.findIndex(deviceStr); + if (index != -1) + { + //kdDebug(250) << "found it " << index << endl; + slotActivated( index ); + } + } + + if ( !mountPointStr.isEmpty() ) + { + mountpoint->setText( mountPointStr ); + updateInfo(); + } + + readonly->setChecked( ro ); + + if ( unmountedStr.isEmpty() ) + unmountedStr = KMimeType::defaultMimeTypePtr()->KServiceType::icon(); // default icon + + unmounted->setIcon( unmountedStr ); + + connect( device, SIGNAL( activated( int ) ), + this, SIGNAL( changed() ) ); + connect( device, SIGNAL( textChanged( const QString & ) ), + this, SIGNAL( changed() ) ); + connect( readonly, SIGNAL( toggled( bool ) ), + this, SIGNAL( changed() ) ); + connect( unmounted, SIGNAL( iconChanged( QString ) ), + this, SIGNAL( changed() ) ); + + connect( 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 ( !mountpoint->text().isEmpty() ) + { + KDiskFreeSp * job = new KDiskFreeSp; + connect( job, SIGNAL( foundMountPoint( const unsigned long&, const unsigned long&, + const unsigned long&, const QString& ) ), + this, SLOT( slotFoundMountPoint( const unsigned long&, const unsigned long&, + const unsigned long&, const QString& ) ) ); + + job->readDF( mountpoint->text() ); + } +} + +void KDevicePropsPlugin::slotActivated( int index ) +{ + // Update mountpoint so that it matches the device that was selected in the combo + device->setEditText( m_devicelist[index] ); + mountpoint->setText( d->mountpointlist[index] ); + + updateInfo(); +} + +void KDevicePropsPlugin::slotDeviceChanged() +{ + // Update mountpoint so that it matches the typed device + int index = m_devicelist.findIndex( device->currentText() ); + if ( index != -1 ) + mountpoint->setText( d->mountpointlist[index] ); + else + mountpoint->setText( QString::null ); + + updateInfo(); +} + +void KDevicePropsPlugin::slotFoundMountPoint( const unsigned long& kBSize, + const unsigned long& /*kBUsed*/, + const unsigned long& kBAvail, + const QString& ) +{ + d->m_freeSpaceText->show(); + d->m_freeSpaceLabel->show(); + + int percUsed = 100 - (int)(100.0 * kBAvail / kBSize); + + d->m_freeSpaceLabel->setText( + // xgettext:no-c-format -- Don't warn about translating the %1 out of %2 part. + i18n("Available space out of total partition size (percent used)", "%1 out of %2 (%3% used)") + .arg(KIO::convertSizeFromKB(kBAvail)) + .arg(KIO::convertSizeFromKB(kBSize)) + .arg( 100 - (int)(100.0 * kBAvail / kBSize) )); + + d->m_freeSpaceBar->setProgress(percUsed, 100); + d->m_freeSpaceBar->show(); +} + +bool KDevicePropsPlugin::supports( KFileItemList _items ) +{ + if ( _items.count() != 1 ) + return false; + KFileItem * item = _items.first(); + // check if desktop file + if ( !KPropsDlgPlugin::isDesktopFile( item ) ) + return false; + // open file and check type + KDesktopFile config( item->url().path(), true /* readonly */ ); + return config.hasDeviceType(); +} + +void KDevicePropsPlugin::applyChanges() +{ + QString path = properties->kurl().path(); + QFile f( path ); + if ( !f.open( IO_ReadWrite ) ) + { + KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have sufficient " + "access to write to <b>%1</b>.</qt>").arg(path)); + return; + } + f.close(); + + KSimpleConfig config( path ); + config.setDesktopGroup(); + config.writeEntry( "Type", QString::fromLatin1("FSDevice") ); + + config.writeEntry( "Dev", device->currentText() ); + config.writeEntry( "MountPoint", mountpoint->text() ); + + config.writeEntry( "UnmountIcon", unmounted->icon() ); + kdDebug(250) << "unmounted->icon() = " << unmounted->icon() << endl; + + config.writeEntry( "ReadOnly", readonly->isChecked() ); + + config.sync(); +} + + +/* ---------------------------------------------------- + * + * KDesktopPropsPlugin + * + * -------------------------------------------------- */ + + +KDesktopPropsPlugin::KDesktopPropsPlugin( KPropertiesDialog *_props ) + : KPropsDlgPlugin( _props ) +{ + QFrame *frame = properties->addPage(i18n("&Application")); + QVBoxLayout *mainlayout = new QVBoxLayout( frame, 0, KDialog::spacingHint() ); + + w = new KPropertiesDesktopBase(frame); + mainlayout->addWidget(w); + + bool bKDesktopMode = (QCString(qApp->name()) == "kdesktop"); // nasty heh? + + if (bKDesktopMode) + { + // Hide Name entry + w->nameEdit->hide(); + w->nameLabel->hide(); + } + + w->pathEdit->setMode(KFile::Directory | KFile::LocalOnly); + w->pathEdit->lineEdit()->setAcceptDrops(false); + + connect( w->nameEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) ); + connect( w->genNameEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) ); + connect( w->commentEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) ); + connect( w->commandEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) ); + connect( w->pathEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) ); + + connect( w->browseButton, SIGNAL( clicked() ), this, SLOT( slotBrowseExec() ) ); + connect( w->addFiletypeButton, SIGNAL( clicked() ), this, SLOT( slotAddFiletype() ) ); + connect( w->delFiletypeButton, SIGNAL( clicked() ), this, SLOT( slotDelFiletype() ) ); + connect( w->advancedButton, SIGNAL( clicked() ), this, SLOT( slotAdvanced() ) ); + + // now populate the page + QString path = _props->kurl().path(); + QFile f( path ); + if ( !f.open( IO_ReadOnly ) ) + return; + f.close(); + + KDesktopFile config( path ); + QString nameStr = config.readName(); + QString genNameStr = config.readGenericName(); + QString commentStr = config.readComment(); + QString commandStr = config.readPathEntry( "Exec" ); + if (commandStr.left(12) == "ksystraycmd ") + { + commandStr.remove(0, 12); + m_systrayBool = true; + } + else + m_systrayBool = false; + + m_origCommandStr = commandStr; + QString pathStr = config.readPathEntry( "Path" ); + m_terminalBool = config.readBoolEntry( "Terminal" ); + m_terminalOptionStr = config.readEntry( "TerminalOptions" ); + m_suidBool = config.readBoolEntry( "X-KDE-SubstituteUID" ); + m_suidUserStr = config.readEntry( "X-KDE-Username" ); + if( config.hasKey( "StartupNotify" )) + m_startupBool = config.readBoolEntry( "StartupNotify", true ); + else + m_startupBool = config.readBoolEntry( "X-KDE-StartupNotify", true ); + m_dcopServiceType = config.readEntry("X-DCOP-ServiceType").lower(); + + QStringList mimeTypes = config.readListEntry( "MimeType", ';' ); + + 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 ) + w->nameEdit->setText(nameStr); + + w->genNameEdit->setText( genNameStr ); + w->commentEdit->setText( commentStr ); + w->commandEdit->setText( commandStr ); + w->pathEdit->lineEdit()->setText( pathStr ); + w->filetypeList->setAllColumnsShowFocus(true); + + 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)) + { + new QListViewItem(w->filetypeList, p->name(), p->comment(), preference); + } + } + +} + +KDesktopPropsPlugin::~KDesktopPropsPlugin() +{ +} + +void KDesktopPropsPlugin::slotSelectMimetype() +{ + QListView *w = (QListView*)sender(); + QListViewItem *item = w->firstChild(); + while(item) + { + if (item->isSelected()) + w->setSelected(item, false); + item = item->nextSibling(); + } +} + +void KDesktopPropsPlugin::slotAddFiletype() +{ + KDialogBase dlg(w, "KPropertiesMimetypes", true, + i18n("Add File Type for %1").arg(properties->kurl().fileName()), + KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok); + + KGuiItem okItem(i18n("&Add"), QString::null /* no icon */, + i18n("Add the selected file types to\nthe list of supported file types."), + i18n("Add the selected file types to\nthe list of supported file types.")); + dlg.setButtonOK(okItem); + + KPropertiesMimetypeBase *mw = new KPropertiesMimetypeBase(&dlg); + + dlg.setMainWidget(mw); + + { + mw->listView->setRootIsDecorated(true); + mw->listView->setSelectionMode(QListView::Extended); + mw->listView->setAllColumnsShowFocus(true); + mw->listView->setFullWidth(true); + mw->listView->setMinimumSize(500,400); + + connect(mw->listView, SIGNAL(selectionChanged()), + this, SLOT(slotSelectMimetype())); + connect(mw->listView, SIGNAL(doubleClicked( QListViewItem *, const QPoint &, int )), + &dlg, SLOT( slotOk())); + + QMap<QString,QListViewItem*> majorMap; + QListViewItem *majorGroup; + KMimeType::List mimetypes = KMimeType::allMimeTypes(); + QValueListIterator<KMimeType::Ptr> it(mimetypes.begin()); + for (; it != mimetypes.end(); ++it) { + QString mimetype = (*it)->name(); + if (mimetype == KMimeType::defaultMimeType()) + continue; + int index = mimetype.find("/"); + QString maj = mimetype.left(index); + QString min = mimetype.mid(index+1); + + QMapIterator<QString,QListViewItem*> mit = majorMap.find( maj ); + if ( mit == majorMap.end() ) { + majorGroup = new QListViewItem( mw->listView, maj ); + majorGroup->setExpandable(true); + mw->listView->setOpen(majorGroup, true); + majorMap.insert( maj, majorGroup ); + } + else + { + majorGroup = mit.data(); + } + + QListViewItem *item = new QListViewItem(majorGroup, min, (*it)->comment()); + item->setPixmap(0, (*it)->pixmap(KIcon::Small, IconSize(KIcon::Small))); + } + QMapIterator<QString,QListViewItem*> mit = majorMap.find( "all" ); + if ( mit != majorMap.end()) + { + mw->listView->setCurrentItem(mit.data()); + mw->listView->ensureItemVisible(mit.data()); + } + } + + if (dlg.exec() == KDialogBase::Accepted) + { + KMimeType::Ptr defaultMimetype = KMimeType::defaultMimeTypePtr(); + QListViewItem *majorItem = mw->listView->firstChild(); + while(majorItem) + { + QString major = majorItem->text(0); + + QListViewItem *minorItem = majorItem->firstChild(); + while(minorItem) + { + if (minorItem->isSelected()) + { + QString mimetype = major + "/" + minorItem->text(0); + KMimeType::Ptr p = KMimeType::mimeType(mimetype); + if (p && (p != defaultMimetype)) + { + mimetype = p->name(); + bool found = false; + QListViewItem *item = w->filetypeList->firstChild(); + while (item) + { + if (mimetype == item->text(0)) + { + found = true; + break; + } + item = item->nextSibling(); + } + if (!found) { + new QListViewItem(w->filetypeList, p->name(), p->comment()); + emit changed(); + } + } + } + minorItem = minorItem->nextSibling(); + } + + majorItem = majorItem->nextSibling(); + } + + } +} + +void KDesktopPropsPlugin::slotDelFiletype() +{ + delete w->filetypeList->currentItem(); + emit changed(); +} + +void KDesktopPropsPlugin::checkCommandChanged() +{ + if (KRun::binaryName(w->commandEdit->text(), true) != + KRun::binaryName(m_origCommandStr, true)) + { + QString m_origCommandStr = w->commandEdit->text(); + m_dcopServiceType= QString::null; // Reset + } +} + +void KDesktopPropsPlugin::applyChanges() +{ + kdDebug(250) << "KDesktopPropsPlugin::applyChanges" << endl; + QString path = properties->kurl().path(); + + QFile f( path ); + + if ( !f.open( IO_ReadWrite ) ) { + KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have " + "sufficient access to write to <b>%1</b>.</qt>").arg(path)); + return; + } + f.close(); + + // If the command is changed we reset certain settings that are strongly + // coupled to the command. + checkCommandChanged(); + + KSimpleConfig config( path ); + config.setDesktopGroup(); + config.writeEntry( "Type", QString::fromLatin1("Application")); + config.writeEntry( "Comment", w->commentEdit->text() ); + config.writeEntry( "Comment", w->commentEdit->text(), true, false, true ); // for compat + config.writeEntry( "GenericName", w->genNameEdit->text() ); + config.writeEntry( "GenericName", w->genNameEdit->text(), true, false, true ); // for compat + + if (m_systrayBool) + config.writePathEntry( "Exec", w->commandEdit->text().prepend("ksystraycmd ") ); + else + config.writePathEntry( "Exec", w->commandEdit->text() ); + config.writePathEntry( "Path", w->pathEdit->lineEdit()->text() ); + + // Write mimeTypes + QStringList mimeTypes; + for( QListViewItem *item = w->filetypeList->firstChild(); + item; item = item->nextSibling() ) + { + QString preference = item->text(2); + mimeTypes.append(item->text(0)); + if (!preference.isEmpty()) + mimeTypes.append(preference); + } + + config.writeEntry( "MimeType", mimeTypes, ';' ); + + if ( !w->nameEdit->isHidden() ) { + QString nameStr = w->nameEdit->text(); + config.writeEntry( "Name", nameStr ); + config.writeEntry( "Name", nameStr, true, false, true ); + } + + config.writeEntry("Terminal", m_terminalBool); + config.writeEntry("TerminalOptions", m_terminalOptionStr); + config.writeEntry("X-KDE-SubstituteUID", m_suidBool); + config.writeEntry("X-KDE-Username", m_suidUserStr); + config.writeEntry("StartupNotify", m_startupBool); + config.writeEntry("X-DCOP-ServiceType", m_dcopServiceType); + 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) + KService::rebuildKSycoca(w); +} + + +void KDesktopPropsPlugin::slotBrowseExec() +{ + KURL f = KFileDialog::getOpenURL( QString::null, + QString::null, w ); + if ( f.isEmpty() ) + return; + + if ( !f.isLocalFile()) { + KMessageBox::sorry(w, i18n("Only executables on local file systems are supported.")); + return; + } + + QString path = f.path(); + KRun::shellQuote( path ); + w->commandEdit->setText( path ); +} + +void KDesktopPropsPlugin::slotAdvanced() +{ + KDialogBase dlg(w, "KPropertiesDesktopAdv", true, + i18n("Advanced Options for %1").arg(properties->kurl().fileName()), + KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok); + KPropertiesDesktopAdvBase *w = new KPropertiesDesktopAdvBase(&dlg); + + dlg.setMainWidget(w); + + // 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 = (m_terminalOptionStr.contains( "--noclose" ) > 0); + w->terminalCloseCheck->setChecked(terminalCloseBool); + m_terminalOptionStr.replace( "--noclose", ""); + } + else + { + w->terminalCloseCheck->hide(); + } + + w->terminalCheck->setChecked(m_terminalBool); + w->terminalEdit->setText(m_terminalOptionStr); + w->terminalCloseCheck->setEnabled(m_terminalBool); + w->terminalEdit->setEnabled(m_terminalBool); + w->terminalEditLabel->setEnabled(m_terminalBool); + + w->suidCheck->setChecked(m_suidBool); + w->suidEdit->setText(m_suidUserStr); + w->suidEdit->setEnabled(m_suidBool); + w->suidEditLabel->setEnabled(m_suidBool); + + w->startupInfoCheck->setChecked(m_startupBool); + w->systrayCheck->setChecked(m_systrayBool); + + if (m_dcopServiceType == "unique") + w->dcopCombo->setCurrentItem(2); + else if (m_dcopServiceType == "multi") + w->dcopCombo->setCurrentItem(1); + else if (m_dcopServiceType == "wait") + w->dcopCombo->setCurrentItem(3); + else + w->dcopCombo->setCurrentItem(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->dcopCombo, SIGNAL( highlighted( int ) ), + this, SIGNAL( changed() ) ); + + if ( dlg.exec() == QDialog::Accepted ) + { + m_terminalOptionStr = w->terminalEdit->text().stripWhiteSpace(); + m_terminalBool = w->terminalCheck->isChecked(); + m_suidBool = w->suidCheck->isChecked(); + m_suidUserStr = w->suidEdit->text().stripWhiteSpace(); + m_startupBool = w->startupInfoCheck->isChecked(); + m_systrayBool = w->systrayCheck->isChecked(); + + if (w->terminalCloseCheck->isChecked()) + { + m_terminalOptionStr.append(" --noclose"); + } + + switch(w->dcopCombo->currentItem()) + { + case 1: m_dcopServiceType = "multi"; break; + case 2: m_dcopServiceType = "unique"; break; + case 3: m_dcopServiceType = "wait"; break; + default: m_dcopServiceType = "none"; break; + } + } +} + +bool KDesktopPropsPlugin::supports( KFileItemList _items ) +{ + if ( _items.count() != 1 ) + return false; + KFileItem * item = _items.first(); + // check if desktop file + if ( !KPropsDlgPlugin::isDesktopFile( item ) ) + return false; + // open file and check type + KDesktopFile config( item->url().path(), true /* readonly */ ); + return config.hasApplicationType() && kapp->authorize("run_desktop_files") && kapp->authorize("shell_access"); +} + +void KPropertiesDialog::virtual_hook( int id, void* data ) +{ KDialogBase::virtual_hook( id, data ); } + +void KPropsDlgPlugin::virtual_hook( int, void* ) +{ /*BASE::virtual_hook( id, data );*/ } + + + + + +/** + * The following code is obsolete and only kept for binary compatibility + * To be removed in KDE 4 + */ + +class KExecPropsPlugin::KExecPropsPluginPrivate +{ +public: + KExecPropsPluginPrivate() + { + } + ~KExecPropsPluginPrivate() + { + } + + QFrame *m_frame; + QCheckBox *nocloseonexitCheck; +}; + +KExecPropsPlugin::KExecPropsPlugin( KPropertiesDialog *_props ) + : KPropsDlgPlugin( _props ) +{ + d = new KExecPropsPluginPrivate; + d->m_frame = properties->addPage(i18n("E&xecute")); + QVBoxLayout * mainlayout = new QVBoxLayout( d->m_frame, 0, + KDialog::spacingHint()); + + // Now the widgets in the top layout + + QLabel* l; + l = new QLabel( i18n( "Comman&d:" ), d->m_frame ); + mainlayout->addWidget(l); + + QHBoxLayout * hlayout; + hlayout = new QHBoxLayout(KDialog::spacingHint()); + mainlayout->addLayout(hlayout); + + execEdit = new KLineEdit( d->m_frame ); + QWhatsThis::add(execEdit,i18n( + "Following the command, you can have several place holders which will be replaced " + "with the actual values when the actual program is run:\n" + "%f - a single file name\n" + "%F - a list of files; use for applications that can open several local files at once\n" + "%u - a single URL\n" + "%U - a list of URLs\n" + "%d - the folder of the file to open\n" + "%D - a list of folders\n" + "%i - the icon\n" + "%m - the mini-icon\n" + "%c - the caption")); + hlayout->addWidget(execEdit, 1); + + l->setBuddy( execEdit ); + + execBrowse = new QPushButton( d->m_frame ); + execBrowse->setText( i18n("&Browse...") ); + hlayout->addWidget(execBrowse); + + // The groupbox about swallowing + QGroupBox* tmpQGroupBox; + tmpQGroupBox = new QGroupBox( i18n("Panel Embedding"), d->m_frame ); + tmpQGroupBox->setColumnLayout( 0, Qt::Horizontal ); + + mainlayout->addWidget(tmpQGroupBox); + + QGridLayout *grid = new QGridLayout(tmpQGroupBox->layout(), 2, 2); + grid->setSpacing( KDialog::spacingHint() ); + grid->setColStretch(1, 1); + + l = new QLabel( i18n( "&Execute on click:" ), tmpQGroupBox ); + grid->addWidget(l, 0, 0); + + swallowExecEdit = new KLineEdit( tmpQGroupBox ); + grid->addWidget(swallowExecEdit, 0, 1); + + l->setBuddy( swallowExecEdit ); + + l = new QLabel( i18n( "&Window title:" ), tmpQGroupBox ); + grid->addWidget(l, 1, 0); + + swallowTitleEdit = new KLineEdit( tmpQGroupBox ); + grid->addWidget(swallowTitleEdit, 1, 1); + + l->setBuddy( swallowTitleEdit ); + + // The groupbox about run in terminal + + tmpQGroupBox = new QGroupBox( d->m_frame ); + tmpQGroupBox->setColumnLayout( 0, Qt::Horizontal ); + + mainlayout->addWidget(tmpQGroupBox); + + grid = new QGridLayout(tmpQGroupBox->layout(), 3, 2); + grid->setSpacing( KDialog::spacingHint() ); + grid->setColStretch(1, 1); + + terminalCheck = new QCheckBox( tmpQGroupBox ); + terminalCheck->setText( i18n("&Run in terminal") ); + grid->addMultiCellWidget(terminalCheck, 0, 0, 0, 1); + + // 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")); + + int posOptions = 1; + d->nocloseonexitCheck = 0L; + if (preferredTerminal == "konsole") + { + posOptions = 2; + d->nocloseonexitCheck = new QCheckBox( tmpQGroupBox ); + d->nocloseonexitCheck->setText( i18n("Do not &close when command exits") ); + grid->addMultiCellWidget(d->nocloseonexitCheck, 1, 1, 0, 1); + } + + terminalLabel = new QLabel( i18n( "&Terminal options:" ), tmpQGroupBox ); + grid->addWidget(terminalLabel, posOptions, 0); + + terminalEdit = new KLineEdit( tmpQGroupBox ); + grid->addWidget(terminalEdit, posOptions, 1); + + terminalLabel->setBuddy( terminalEdit ); + + // The groupbox about run with substituted uid. + + tmpQGroupBox = new QGroupBox( d->m_frame ); + tmpQGroupBox->setColumnLayout( 0, Qt::Horizontal ); + + mainlayout->addWidget(tmpQGroupBox); + + grid = new QGridLayout(tmpQGroupBox->layout(), 2, 2); + grid->setSpacing(KDialog::spacingHint()); + grid->setColStretch(1, 1); + + suidCheck = new QCheckBox(tmpQGroupBox); + suidCheck->setText(i18n("Ru&n as a different user")); + grid->addMultiCellWidget(suidCheck, 0, 0, 0, 1); + + suidLabel = new QLabel(i18n( "&Username:" ), tmpQGroupBox); + grid->addWidget(suidLabel, 1, 0); + + suidEdit = new KLineEdit(tmpQGroupBox); + grid->addWidget(suidEdit, 1, 1); + + suidLabel->setBuddy( suidEdit ); + + mainlayout->addStretch(1); + + // now populate the page + QString path = _props->kurl().path(); + QFile f( path ); + if ( !f.open( IO_ReadOnly ) ) + return; + f.close(); + + KSimpleConfig config( path ); + config.setDollarExpansion( false ); + config.setDesktopGroup(); + execStr = config.readPathEntry( "Exec" ); + swallowExecStr = config.readPathEntry( "SwallowExec" ); + swallowTitleStr = config.readEntry( "SwallowTitle" ); + termBool = config.readBoolEntry( "Terminal" ); + termOptionsStr = config.readEntry( "TerminalOptions" ); + suidBool = config.readBoolEntry( "X-KDE-SubstituteUID" ); + suidUserStr = config.readEntry( "X-KDE-Username" ); + + if ( !swallowExecStr.isNull() ) + swallowExecEdit->setText( swallowExecStr ); + if ( !swallowTitleStr.isNull() ) + swallowTitleEdit->setText( swallowTitleStr ); + + if ( !execStr.isNull() ) + execEdit->setText( execStr ); + + if ( d->nocloseonexitCheck ) + { + d->nocloseonexitCheck->setChecked( (termOptionsStr.contains( "--noclose" ) > 0) ); + termOptionsStr.replace( "--noclose", ""); + } + if ( !termOptionsStr.isNull() ) + terminalEdit->setText( termOptionsStr ); + + terminalCheck->setChecked( termBool ); + enableCheckedEdit(); + + suidCheck->setChecked( suidBool ); + suidEdit->setText( suidUserStr ); + enableSuidEdit(); + + // 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) + { + suidEdit->setCompletionObject(kcom, true); + suidEdit->setAutoDeleteCompletionObject( true ); + suidEdit->setCompletionMode(KGlobalSettings::CompletionAuto); + } + else + { + delete kcom; + } + + connect( swallowExecEdit, SIGNAL( textChanged( const QString & ) ), + this, SIGNAL( changed() ) ); + connect( swallowTitleEdit, SIGNAL( textChanged( const QString & ) ), + this, SIGNAL( changed() ) ); + connect( execEdit, SIGNAL( textChanged( const QString & ) ), + this, SIGNAL( changed() ) ); + connect( terminalEdit, SIGNAL( textChanged( const QString & ) ), + this, SIGNAL( changed() ) ); + if (d->nocloseonexitCheck) + connect( d->nocloseonexitCheck, SIGNAL( toggled( bool ) ), + this, SIGNAL( changed() ) ); + connect( terminalCheck, SIGNAL( toggled( bool ) ), + this, SIGNAL( changed() ) ); + connect( suidCheck, SIGNAL( toggled( bool ) ), + this, SIGNAL( changed() ) ); + connect( suidEdit, SIGNAL( textChanged( const QString & ) ), + this, SIGNAL( changed() ) ); + + connect( execBrowse, SIGNAL( clicked() ), this, SLOT( slotBrowseExec() ) ); + connect( terminalCheck, SIGNAL( clicked() ), this, SLOT( enableCheckedEdit() ) ); + connect( suidCheck, SIGNAL( clicked() ), this, SLOT( enableSuidEdit() ) ); + +} + +KExecPropsPlugin::~KExecPropsPlugin() +{ + delete d; +} + +void KExecPropsPlugin::enableCheckedEdit() +{ + bool checked = terminalCheck->isChecked(); + terminalLabel->setEnabled( checked ); + if (d->nocloseonexitCheck) + d->nocloseonexitCheck->setEnabled( checked ); + terminalEdit->setEnabled( checked ); +} + +void KExecPropsPlugin::enableSuidEdit() +{ + bool checked = suidCheck->isChecked(); + suidLabel->setEnabled( checked ); + suidEdit->setEnabled( checked ); +} + +bool KExecPropsPlugin::supports( KFileItemList _items ) +{ + if ( _items.count() != 1 ) + return false; + KFileItem * item = _items.first(); + // check if desktop file + if ( !KPropsDlgPlugin::isDesktopFile( item ) ) + return false; + // open file and check type + KDesktopFile config( item->url().path(), true /* readonly */ ); + return config.hasApplicationType() && kapp->authorize("run_desktop_files") && kapp->authorize("shell_access"); +} + +void KExecPropsPlugin::applyChanges() +{ + kdDebug(250) << "KExecPropsPlugin::applyChanges" << endl; + QString path = properties->kurl().path(); + + QFile f( path ); + + if ( !f.open( IO_ReadWrite ) ) { + KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have " + "sufficient access to write to <b>%1</b>.</qt>").arg(path)); + return; + } + f.close(); + + KSimpleConfig config( path ); + config.setDesktopGroup(); + config.writeEntry( "Type", QString::fromLatin1("Application")); + config.writePathEntry( "Exec", execEdit->text() ); + config.writePathEntry( "SwallowExec", swallowExecEdit->text() ); + config.writeEntry( "SwallowTitle", swallowTitleEdit->text() ); + config.writeEntry( "Terminal", terminalCheck->isChecked() ); + QString temp = terminalEdit->text(); + if (d->nocloseonexitCheck ) + if ( d->nocloseonexitCheck->isChecked() ) + temp += QString::fromLatin1("--noclose "); + temp = temp.stripWhiteSpace(); + config.writeEntry( "TerminalOptions", temp ); + config.writeEntry( "X-KDE-SubstituteUID", suidCheck->isChecked() ); + config.writeEntry( "X-KDE-Username", suidEdit->text() ); +} + + +void KExecPropsPlugin::slotBrowseExec() +{ + KURL f = KFileDialog::getOpenURL( QString::null, + QString::null, 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(); + KRun::shellQuote( path ); + execEdit->setText( path ); +} + +class KApplicationPropsPlugin::KApplicationPropsPluginPrivate +{ +public: + KApplicationPropsPluginPrivate() + { + m_kdesktopMode = QCString(qApp->name()) == "kdesktop"; // nasty heh? + } + ~KApplicationPropsPluginPrivate() + { + } + + QFrame *m_frame; + bool m_kdesktopMode; +}; + +KApplicationPropsPlugin::KApplicationPropsPlugin( KPropertiesDialog *_props ) + : KPropsDlgPlugin( _props ) +{ + d = new KApplicationPropsPluginPrivate; + d->m_frame = properties->addPage(i18n("&Application")); + QVBoxLayout *toplayout = new QVBoxLayout( d->m_frame, 0, KDialog::spacingHint()); + + QIconSet iconSet; + QPixmap pixMap; + + addExtensionButton = new QPushButton( QString::null, d->m_frame ); + iconSet = SmallIconSet( "back" ); + addExtensionButton->setIconSet( iconSet ); + pixMap = iconSet.pixmap( QIconSet::Small, QIconSet::Normal ); + addExtensionButton->setFixedSize( pixMap.width()+8, pixMap.height()+8 ); + connect( addExtensionButton, SIGNAL( clicked() ), + SLOT( slotAddExtension() ) ); + + delExtensionButton = new QPushButton( QString::null, d->m_frame ); + iconSet = SmallIconSet( "forward" ); + delExtensionButton->setIconSet( iconSet ); + delExtensionButton->setFixedSize( pixMap.width()+8, pixMap.height()+8 ); + connect( delExtensionButton, SIGNAL( clicked() ), + SLOT( slotDelExtension() ) ); + + QLabel *l; + + QGridLayout *grid = new QGridLayout(2, 2); + grid->setColStretch(1, 1); + toplayout->addLayout(grid); + + if ( d->m_kdesktopMode ) + { + // in kdesktop the name field comes from the first tab + nameEdit = 0L; + } + else + { + l = new QLabel(i18n("Name:"), d->m_frame, "Label_4" ); + grid->addWidget(l, 0, 0); + + nameEdit = new KLineEdit( d->m_frame, "LineEdit_3" ); + grid->addWidget(nameEdit, 0, 1); + } + + l = new QLabel(i18n("Description:"), d->m_frame, "Label_5" ); + grid->addWidget(l, 1, 0); + + genNameEdit = new KLineEdit( d->m_frame, "LineEdit_4" ); + grid->addWidget(genNameEdit, 1, 1); + + l = new QLabel(i18n("Comment:"), d->m_frame, "Label_3" ); + grid->addWidget(l, 2, 0); + + commentEdit = new KLineEdit( d->m_frame, "LineEdit_2" ); + grid->addWidget(commentEdit, 2, 1); + + l = new QLabel(i18n("File types:"), d->m_frame); + toplayout->addWidget(l, 0, AlignLeft); + + grid = new QGridLayout(4, 3); + grid->setColStretch(0, 1); + grid->setColStretch(2, 1); + grid->setRowStretch( 0, 1 ); + grid->setRowStretch( 3, 1 ); + toplayout->addLayout(grid, 2); + + extensionsList = new QListBox( d->m_frame ); + extensionsList->setSelectionMode( QListBox::Extended ); + grid->addMultiCellWidget(extensionsList, 0, 3, 0, 0); + + grid->addWidget(addExtensionButton, 1, 1); + grid->addWidget(delExtensionButton, 2, 1); + + availableExtensionsList = new QListBox( d->m_frame ); + availableExtensionsList->setSelectionMode( QListBox::Extended ); + grid->addMultiCellWidget(availableExtensionsList, 0, 3, 2, 2); + + QString path = properties->kurl().path() ; + QFile f( path ); + if ( !f.open( IO_ReadOnly ) ) + return; + f.close(); + + KDesktopFile config( path ); + QString commentStr = config.readComment(); + QString genNameStr = config.readGenericName(); + + QStringList selectedTypes = config.readListEntry( "ServiceTypes" ); + // For compatibility with KDE 1.x + selectedTypes += config.readListEntry( "MimeType", ';' ); + + QString nameStr = config.readName(); + if ( nameStr.isEmpty() || d->m_kdesktopMode ) { + // 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(); + } + + commentEdit->setText( commentStr ); + genNameEdit->setText( genNameStr ); + if ( nameEdit ) + nameEdit->setText( nameStr ); + + selectedTypes.sort(); + QStringList::Iterator sit = selectedTypes.begin(); + for( ; sit != selectedTypes.end(); ++sit ) { + if ( !((*sit).isEmpty()) ) + extensionsList->insertItem( *sit ); + } + + KMimeType::List mimeTypes = KMimeType::allMimeTypes(); + QValueListIterator<KMimeType::Ptr> it2 = mimeTypes.begin(); + for ( ; it2 != mimeTypes.end(); ++it2 ) + addMimeType ( (*it2)->name() ); + + updateButton(); + + connect( extensionsList, SIGNAL( highlighted( int ) ), + this, SLOT( updateButton() ) ); + connect( availableExtensionsList, SIGNAL( highlighted( int ) ), + this, SLOT( updateButton() ) ); + + connect( addExtensionButton, SIGNAL( clicked() ), + this, SIGNAL( changed() ) ); + connect( delExtensionButton, SIGNAL( clicked() ), + this, SIGNAL( changed() ) ); + if ( nameEdit ) + connect( nameEdit, SIGNAL( textChanged( const QString & ) ), + this, SIGNAL( changed() ) ); + connect( commentEdit, SIGNAL( textChanged( const QString & ) ), + this, SIGNAL( changed() ) ); + connect( genNameEdit, SIGNAL( textChanged( const QString & ) ), + this, SIGNAL( changed() ) ); + connect( availableExtensionsList, SIGNAL( selected( int ) ), + this, SIGNAL( changed() ) ); + connect( extensionsList, SIGNAL( selected( int ) ), + this, SIGNAL( changed() ) ); +} + +KApplicationPropsPlugin::~KApplicationPropsPlugin() +{ + delete d; +} + +// QString KApplicationPropsPlugin::tabName () const +// { +// return i18n ("&Application"); +// } + +void KApplicationPropsPlugin::updateButton() +{ + addExtensionButton->setEnabled(availableExtensionsList->currentItem()>-1); + delExtensionButton->setEnabled(extensionsList->currentItem()>-1); +} + +void KApplicationPropsPlugin::addMimeType( const QString & name ) +{ + // Add a mimetype to the list of available mime types if not in the extensionsList + + bool insert = true; + + for ( uint i = 0; i < extensionsList->count(); i++ ) + if ( extensionsList->text( i ) == name ) + insert = false; + + if ( insert ) + { + availableExtensionsList->insertItem( name ); + availableExtensionsList->sort(); + } +} + +bool KApplicationPropsPlugin::supports( KFileItemList _items ) +{ + // same constraints as KExecPropsPlugin : desktop file with Type = Application + return KExecPropsPlugin::supports( _items ); +} + +void KApplicationPropsPlugin::applyChanges() +{ + QString path = properties->kurl().path(); + + QFile f( path ); + + if ( !f.open( IO_ReadWrite ) ) { + KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not " + "have sufficient access to write to <b>%1</b>.</qt>").arg(path)); + return; + } + f.close(); + + KSimpleConfig config( path ); + config.setDesktopGroup(); + config.writeEntry( "Type", QString::fromLatin1("Application")); + config.writeEntry( "Comment", commentEdit->text() ); + config.writeEntry( "Comment", commentEdit->text(), true, false, true ); // for compat + config.writeEntry( "GenericName", genNameEdit->text() ); + config.writeEntry( "GenericName", genNameEdit->text(), true, false, true ); // for compat + + QStringList selectedTypes; + for ( uint i = 0; i < extensionsList->count(); i++ ) + selectedTypes.append( extensionsList->text( i ) ); + + config.writeEntry( "MimeType", selectedTypes, ';' ); + config.writeEntry( "ServiceTypes", "" ); + // hmm, actually it should probably be the contrary (but see also typeslistitem.cpp) + + QString nameStr = nameEdit ? nameEdit->text() : QString::null; + if ( nameStr.isEmpty() ) // nothing entered, or widget not existing at all (kdesktop mode) + nameStr = nameFromFileName(properties->kurl().fileName()); + + config.writeEntry( "Name", nameStr ); + config.writeEntry( "Name", nameStr, true, false, true ); + + config.sync(); +} + +void KApplicationPropsPlugin::slotAddExtension() +{ + QListBoxItem *item = availableExtensionsList->firstItem(); + QListBoxItem *nextItem; + + while ( item ) + { + nextItem = item->next(); + + if ( item->isSelected() ) + { + extensionsList->insertItem( item->text() ); + availableExtensionsList->removeItem( availableExtensionsList->index( item ) ); + } + + item = nextItem; + } + + extensionsList->sort(); + updateButton(); +} + +void KApplicationPropsPlugin::slotDelExtension() +{ + QListBoxItem *item = extensionsList->firstItem(); + QListBoxItem *nextItem; + + while ( item ) + { + nextItem = item->next(); + + if ( item->isSelected() ) + { + availableExtensionsList->insertItem( item->text() ); + extensionsList->removeItem( extensionsList->index( item ) ); + } + + item = nextItem; + } + + availableExtensionsList->sort(); + updateButton(); +} + + + +#include "kpropertiesdialog.moc" diff --git a/kio/kfile/kpropertiesdialog.h b/kio/kfile/kpropertiesdialog.h new file mode 100644 index 000000000..b71ad7948 --- /dev/null +++ b/kio/kfile/kpropertiesdialog.h @@ -0,0 +1,918 @@ +/* This file is part of the KDE project + Copyright (C) 1998, 1999 Torben Weis <[email protected]> + Copyright (c) 1999, 2000 Preston Brown <[email protected]> + Copyright (c) 2000 Simon Hausmann <[email protected]> + Copyright (c) 2000 David Faure <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/* + * This file holds the definitions for all classes used to + * display a properties dialog. + */ + +#ifndef __propsdlg_h +#define __propsdlg_h + +#include <qstring.h> +#include <qptrlist.h> + +#include <kdemacros.h> +#include <kurl.h> +#include <kfileitem.h> +#include <kdialogbase.h> + +class QLineEdit; +class QCheckBox; +class QPushButton; +class KLineEdit; +class KURLRequester; +class QButton; +class KIconButton; +class KPropsDlgPlugin; +class QComboBox; + +#define KPropsPage KPropsDlgPlugin + +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" + * + * This class must be created with (void)new KPropertiesDialog(...) + * It will take care of deleting itself. + * + * If you are looking for more flexibility, see KFileMetaInfo and + * KFileMetaInfoWidget. + */ +class KIO_EXPORT KPropertiesDialog : public KDialogBase +{ + 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( 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. + * @param modal tells the dialog whether it should be modal. + * @param autoShow tells the dialog whether it should show itself automatically. + */ + KPropertiesDialog( KFileItem * item, + QWidget* parent = 0L, const char* name = 0L, + bool modal = false, bool autoShow = true); + + /** + * \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. + * @param modal tells the dialog whether it should be modal. + * @param autoShow tells the dialog whether it should show itself automatically. + */ + KPropertiesDialog( KFileItemList _items, + QWidget *parent = 0L, const char* name = 0L, + bool modal = false, bool autoShow = true); + +#ifndef KDE_NO_COMPAT + /** + * @deprecated You should use the following constructor instead of this one. + * The only change that is required is to delete the _mode argument. + * + * @param _url the URL whose properties should be displayed + * @param _mode unused. + * @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. + * @param autoShow tells the dialog whether it should show itself automatically. */ + KPropertiesDialog( const KURL& _url, mode_t _mode, + QWidget* parent = 0L, const char* name = 0L, + bool modal = false, bool autoShow = true) KDE_DEPRECATED; +#endif + + /** + * 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. + * @param modal tells the dialog whether it should be modal. + * IMPORTANT: This constructor, together with modal=true, 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. + * For local files with a known mimetype, simply create a KFileItem and pass + * it to the other constructor. + * + * @param autoShow tells the dialog whethr it should show itself automatically. + */ + KPropertiesDialog( const KURL& _url, + QWidget* parent = 0L, const char* name = 0L, + bool modal = false, bool autoShow = true); + + /** + * 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. + * @param modal tells the dialog whether it should be modal. + * @param autoShow tells the dialog whethr it should show itself automatically. + */ + KPropertiesDialog( const KURL& _tempUrl, const KURL& _currentDir, + const QString& _defaultName, + QWidget* parent = 0L, const char* name = 0L, + bool modal = false, bool autoShow = true); + + /** + * 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. + */ + KPropertiesDialog (const QString& title, + QWidget* parent = 0L, const char* name = 0L, bool modal = false); + + /** + * 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 succesfull dialog displaying (can be false on win32). + * @since 3.4 + */ + static bool showDialog(KFileItem* item, QWidget* parent = 0, + const char* name = 0, bool modal = false); + + /** + * 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 succesfull dialog displaying (can be false on win32). + * @since 3.4 + */ + static bool showDialog(const KURL& _url, QWidget* parent = 0, + const char* name = 0, bool modal = false); + + /** + * 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 succesfull dialog displaying (can be false on win32). + * @since 3.4 + */ + static bool showDialog(const KFileItemList& _items, QWidget* parent = 0, + const char* name = 0, bool modal = false); + + /** + * 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 KPropsDlgPlugin + * 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 KPropsDlgPlugin. The Properties + * dialog will do destruction for you. The KPropsDlgPlugin \b must + * have been created with the KPropertiesDialog as its parent. + * @see KPropsDlgPlugin + */ + void insertPlugin (KPropsDlgPlugin *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. + */ + const KURL& kurl() const { return m_singleUrl; } + + /** + * @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() { return m_items.first(); } + + /** + * @return the items for which the dialog is shown + */ + KFileItemList items() const { return m_items; } + + /** + * @return a pointer to the dialog + * @deprecated KPropertiesDialog directly inherits from KDialogBase, so use \a this instead + */ + KDE_DEPRECATED KDialogBase* dialog() { return this; } + /** + * @return a pointer to the dialog + * @deprecated KPropertiesDialog directly inherits from KDialogBase, so use \a this instead + */ + KDE_DEPRECATED const KDialogBase* dialog() const { return this; } + + /** + * If the dialog is being built from a template, this method + * returns the current directory. If no template, it returns QString::null. + * See the template form of the constructor. + * + * @return the current directory or QString::null + */ + const KURL& currentDir() const { return m_currentDir; } + + /** + * If the dialog is being built from a template, this method + * returns the default name. If no template, it returns QString::null. + * See the template form of the constructor. + * @return the default name or QString::null + */ + const QString& defaultName() const { return m_defaultName; } + + /** + * 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 FilePropsDlgPlugin::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 + * @since 3.1 + */ + void showFileSharingPage(); + + /** + * Sets the file sharing page. + * This page is shown when calling showFileSharingPage(). + * + * @param page the page to set + * \see showFileSharingPage + * @since 3.3 + */ + 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 + * @since 3.2 + */ + void setFileNameReadOnly( bool ro ); + +public 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 + +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); + +private: + + /** + * Common initialization for all constructors + */ + void init (bool modal = false, bool autoShow = true); + + /** + * Inserts all pages in the dialog. + */ + void insertPages(); + + /** + * 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 ) + */ + QPtrList<KPropsDlgPlugin> m_pageList; + +private slots: + void slotStatResult( KIO::Job * ); // No longer used +protected: + virtual void virtual_hook( int id, void* data ); +private: + class KPropertiesDialogPrivate; + KPropertiesDialogPrivate *d; +}; + +/** + * 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 KPropsDlg/Plugin + * servicetype, as well as the mimetypes for which the plugin should be created. + * For instance, ServiceTypes=KPropsDlg/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 KPropsDlgPlugin : public QObject +{ + Q_OBJECT +public: + /** + * Constructor + * To insert tabs into the properties dialog, use the add methods provided by + * KDialogBase (the properties dialog is a KDialogBase). + */ + KPropsDlgPlugin( KPropertiesDialog *_props ); + virtual ~KPropsDlgPlugin(); + + /** + * 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 ); + + void setDirty( bool b ); + bool isDirty() const; + +public slots: + void setDirty(); // same as setDirty( true ) + +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 before. + */ + void changed(); + +protected: + /** + * Pointer to the dialog + */ + KPropertiesDialog *properties; + + int fontHeight; +protected: + virtual void virtual_hook( int id, void* data ); +private: + class KPropsDlgPluginPrivate; + KPropsDlgPluginPrivate *d; +}; + +/** + * 'General' plugin + * This plugin displays the name of the file, its size and access times. + * @internal + */ +class KIO_EXPORT KFilePropsPlugin : public KPropsDlgPlugin +{ + 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( KFileItemList _items ); + + /** + * Called after all plugins applied their changes + */ + void postApplyChanges(); + + void setFileNameReadOnly( bool ro ); + +protected slots: + void slotEditFileType(); + void slotCopyFinished( KIO::Job * ); + void slotFileRenamed( KIO::Job *, const KURL &, const KURL & ); + void slotDirSizeUpdate(); + void slotDirSizeFinished( KIO::Job * ); + void slotFoundMountPoint( const QString& mp, unsigned long kBSize, + unsigned long kBUsed, unsigned long kBAvail ); + void slotSizeStop(); + void slotSizeDetermine(); + +private slots: + // workaround for compiler bug + void slotFoundMountPoint( const unsigned long& kBSize, const unsigned long& + kBUsed, const unsigned long& kBAvail, const QString& mp ); + void nameFileChanged(const QString &text ); + void slotIconChanged(); + +private: + void determineRelativePath( const QString & path ); + void applyIconChanges(); + + QWidget *iconArea; + QWidget *nameArea; + + QLabel *m_sizeLabel; + QPushButton *m_sizeDetermineButton; + QPushButton *m_sizeStopButton; + + QString m_sRelativePath; + bool m_bFromTemplate; + + /** + * The initial filename + */ + QString oldName; + + class KFilePropsPluginPrivate; + KFilePropsPluginPrivate *d; +}; + +/** + * 'Permissions' plugin + * In this plugin you can modify permissions and change + * the owner of a file. + * @internal + */ +class KIO_EXPORT KFilePermissionsPropsPlugin : public KPropsDlgPlugin +{ + 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( KFileItemList _items ); + +private slots: + + void slotChmodResult( KIO::Job * ); + void slotShowAdvancedPermissions(); + +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]; + + // unused, for binary compatibility! + QCheckBox *permBox[3][4]; + + QComboBox *grpCombo; + + KLineEdit *usrEdit, *grpEdit; + + /** + * Old permissions + */ + mode_t permissions; + /** + * Old group + */ + QString strGroup; + /** + * Old owner + */ + QString strOwner; + + // unused, for compatibility + static mode_t fperm[3][4]; + + class KFilePermissionsPropsPluginPrivate; + KFilePermissionsPropsPluginPrivate *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 KPropsDlgPlugin +{ + Q_OBJECT +public: + /** + * Constructor + */ + KURLPropsPlugin( KPropertiesDialog *_props ); + virtual ~KURLPropsPlugin(); + + virtual void applyChanges(); + + static bool supports( KFileItemList _items ); + +private: + KURLRequester *URLEdit; + KIconButton *iconBox; + + QString URLStr; + QString iconStr; + + QPixmap pixmap; + QString pixmapFile; +private: + class KURLPropsPluginPrivate; + KURLPropsPluginPrivate *d; +}; + + +/** + * Used to edit the files containing + * [Desktop Entry] + * Type=MimeType + * @internal + */ +class KIO_EXPORT KBindingPropsPlugin : public KPropsDlgPlugin +{ + Q_OBJECT +public: + /** + * Constructor + */ + KBindingPropsPlugin( KPropertiesDialog *_props ); + virtual ~KBindingPropsPlugin(); + + virtual void applyChanges(); + static bool supports( KFileItemList _items ); + +private: + + QLineEdit *commentEdit; + QLineEdit *patternEdit; + QLineEdit *mimeEdit; + QString m_sMimeStr; + + QCheckBox * cbAutoEmbed; + + class KBindingPropsPluginPrivate; + KBindingPropsPluginPrivate *d; +}; + +/** + * Properties plugin for device .desktop files + * @internal + */ +class KIO_EXPORT KDevicePropsPlugin : public KPropsDlgPlugin +{ + Q_OBJECT +public: + KDevicePropsPlugin( KPropertiesDialog *_props ); + virtual ~KDevicePropsPlugin(); + + virtual void applyChanges(); + + static bool supports( KFileItemList _items ); + +private slots: + void slotActivated( int ); + void slotDeviceChanged(); + void slotFoundMountPoint( const unsigned long& kBSize, + const unsigned long& /*kBUsed*/, + const unsigned long& kBAvail, + const QString& ); + +private: + void updateInfo(); + +private: + QComboBox* device; + QLabel* mountpoint; + QCheckBox* readonly; + void* unused; + //KIconButton* mounted; + KIconButton* unmounted; + + QStringList m_devicelist; + int indexDevice; + int indexMountPoint; + int indexFSType; + + QPixmap pixmap; + QString pixmapFile; + + class KDevicePropsPluginPrivate; + KDevicePropsPluginPrivate *d; +}; + +class 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 KPropsDlgPlugin +{ + Q_OBJECT +public: + /** + * Constructor + */ + KDesktopPropsPlugin( KPropertiesDialog *_props ); + virtual ~KDesktopPropsPlugin(); + + virtual void applyChanges(); + + static bool supports( KFileItemList _items ); + +public slots: + void slotAddFiletype(); + void slotDelFiletype(); + void slotBrowseExec(); + void slotAdvanced(); + void slotSelectMimetype(); + +private: + void checkCommandChanged(); + +private: + KPropertiesDesktopBase* w; + + QString m_origCommandStr; + QString m_terminalOptionStr; + QString m_suidUserStr; + QString m_dcopServiceType; + bool m_terminalBool; + bool m_terminalCloseBool; + bool m_suidBool; + bool m_startupBool; + bool m_systrayBool; + + class KDesktopPropsPluginPrivate; + KDesktopPropsPluginPrivate *d; +}; + +/** + * Used to edit the files containing + * [Desktop Entry] + * Type=Application + * + * Such files are used to represent a program in kicker and konqueror. + * @internal + * @deprecated replaced with KDesktopPropsPlugin + */ + /// Remove in KDE4 +class KIO_EXPORT_DEPRECATED KExecPropsPlugin : public KPropsDlgPlugin +{ + Q_OBJECT +public: + /** + * Constructor + */ + KExecPropsPlugin( KPropertiesDialog *_props ); + virtual ~KExecPropsPlugin(); + + virtual void applyChanges(); + + static bool supports( KFileItemList _items ); + +public slots: + void slotBrowseExec(); + +private slots: + void enableCheckedEdit(); + void enableSuidEdit(); + +private: + + QLabel *terminalLabel; + QLabel *suidLabel; + KLineEdit *execEdit; + QCheckBox *terminalCheck; + QCheckBox *suidCheck; + KLineEdit *terminalEdit; + KLineEdit *suidEdit; + KLineEdit *swallowExecEdit; + KLineEdit *swallowTitleEdit; + QButton *execBrowse; + + QString execStr; + QString swallowExecStr; + QString swallowTitleStr; + QString termOptionsStr; + bool termBool; + bool suidBool; + QString suidUserStr; + + class KExecPropsPluginPrivate; + KExecPropsPluginPrivate *d; +}; + +/** + * Used to edit the files containing + * [Desktop Entry] + * Type=Application + * + * Such files are used to represent a program in kicker and konqueror. + * @internal + * @deprecated replaced with KDesktopPropsPlugin + */ + /// Remove in KDE4 +class KIO_EXPORT_DEPRECATED KApplicationPropsPlugin : public KPropsDlgPlugin +{ + Q_OBJECT +public: + /** + * Constructor + */ + KApplicationPropsPlugin( KPropertiesDialog *_props ); + virtual ~KApplicationPropsPlugin(); + + virtual void applyChanges(); + + static bool supports( KFileItemList _items ); + +public slots: + void slotDelExtension(); + void slotAddExtension(); + +private slots: + void updateButton(); + +private: + void addMimeType( const QString & name ); + + QLineEdit *commentEdit; + QLineEdit *genNameEdit; + QLineEdit *nameEdit; + QListBox *extensionsList; + QListBox *availableExtensionsList; + QPushButton *addExtensionButton; + QPushButton *delExtensionButton; + + class KApplicationPropsPluginPrivate; + KApplicationPropsPluginPrivate *d; +}; + +#endif + diff --git a/kio/kfile/kpropertiesmimetypebase.ui b/kio/kfile/kpropertiesmimetypebase.ui new file mode 100644 index 000000000..0223f22a6 --- /dev/null +++ b/kio/kfile/kpropertiesmimetypebase.ui @@ -0,0 +1,70 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>KPropertiesMimetypeBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>widget2</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>265</width> + <height>213</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Select one or more file types to add:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>listView</cstring> + </property> + </widget> + <widget class="KListView"> + <column> + <property name="text"> + <string>Mimetype</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Description</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>listView</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string><qt><p>Select one or more types of file that your application can handle here. This list is organized by <u>mimetypes</u>.</p> +<p>MIME, Multipurpose Internet (e)Mail Extension, is a standard protocol for identifying the type of data based on filename extensions and correspondent <u>mimetypes</u>. Example: the "bmp" part that comes after the dot in flower.bmp indicates that it is a specific kind of image, <u>image/x-bmp</u>. To know which application should open each type of file, the system should be informed about the abilities of each application to handle these extensions and mimetypes.</p></string> + </property> + </widget> + </vbox> +</widget> +<includes> + <include location="global" impldecl="in implementation">klistview.h</include> +</includes> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kio/kfile/kpropsdlg.h b/kio/kfile/kpropsdlg.h new file mode 100644 index 000000000..502a346e9 --- /dev/null +++ b/kio/kfile/kpropsdlg.h @@ -0,0 +1,4 @@ +// This is now called kpropertiesdialog.h +#ifndef KDE_NO_COMPAT +#include <kpropertiesdialog.h> +#endif diff --git a/kio/kfile/kpropsdlgplugin.desktop b/kio/kfile/kpropsdlgplugin.desktop new file mode 100644 index 000000000..cb4405bb2 --- /dev/null +++ b/kio/kfile/kpropsdlgplugin.desktop @@ -0,0 +1,87 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=KPropsDlg/Plugin +Comment=Plugin for the Properties Dialog +Comment[af]=Inprop module vir die Eienskappe Dialoog +Comment[ar]=ملحق لمربع حوار خصائص +Comment[az]=Seçənəklər Rabitə Qutusu Üçün Əlavə +Comment[bg]=Приставка за диалога "Информация" +Comment[bn]=বৈশিষ্ট্যাবলী ডায়ালগ-এর জন্য প্লাগ-ইন +Comment[br]=Adveziant evit kendiviz ar perzhioù +Comment[bs]=Dodatak za Properties dijalog +Comment[ca]=Connector per al diàleg de les propietats +Comment[cs]=Modul pro dialog vlastností +Comment[csb]=Plugins dialogòwégò òkna Swòjiznë +Comment[cy]=Ategyn am yr Ymgom Priodweddau +Comment[da]=Plugin til egenskabsdialog +Comment[de]=Erweiterung für den Eigenschaften-Dialog +Comment[el]=Πρόσθετο για το Διάλογο ιδιοτήτων +Comment[en_GB]=Plugin for the Properties Dialogue +Comment[eo]=Internaĵo por la Eco-dialogo +Comment[es]=Plugin para el diálogo de propiedades +Comment[et]=Omaduste dialoogi plugin +Comment[eu]=Propietateen elkarrizketa-koadroaren plugin-a +Comment[fa]=وصله برای محاورۀ ویژگیها +Comment[fi]=Lisäosa asetusikkunalle +Comment[fr]=Module externe pour la boîte de dialogue des propriétés +Comment[fy]=Plugin foar de 'Eigenskippen'-dialooch +Comment[gl]=Plugin para o Diálogo de Propriedades +Comment[he]=תוסף לדו־שיח המאפיינים +Comment[hi]=विशेषता संवाद के लिए प्लगइन +Comment[hr]=Dodatak dijaloga 'Svojstva' +Comment[hu]=Beépülő modul a tulajdonságok párbeszédablakhoz +Comment[id]=Plugin untuk Dialog Properties +Comment[is]=Íforrit fyrir stillingarforritið +Comment[it]=Plugin per la finestra delle proprietà +Comment[ja]=設定ダイアログプラグイン +Comment[ka]=პარამეტრების გამართვის დიალოგის მოდული +Comment[kk]=Қасиеттер диалогтың модулі +Comment[km]=កម្មវិធីជំនួយខាងក្នុងសម្រាប់ប្រអប់ លក្ខណៈសម្បត្តិ +Comment[ko]=대화창 특성을 위한 플러그인 +Comment[lb]=Plugin fir den Eegeschaften-Dialog +Comment[lt]=Priedas savybių dialogui +Comment[lv]=Īpašību Dialoga Iespraudnis +Comment[mk]=Приклучок за дијалогот за својства +Comment[mn]=Шинж чанарууд диалогийн Plugin +Comment[ms]=Plugmasuk untuk Dialog Ciri-ciri +Comment[mt]=Plugin għad-djalogu tal-propjetajiet +Comment[nb]=Programtillegg for dialogvinduet for egenskaper +Comment[nds]=Plugin för den Egenschappen-Dialoog +Comment[ne]=विशेषता संवादका लागि प्लगइन +Comment[nl]=Plugin voor de 'Eigenschappen'-dialoog +Comment[nn]=Tillegg til eigenskapar-dialogen +Comment[nso]=Tsenyo ya Poledisano ya Dithoto +Comment[oc]=Branquament pel dialeg de propietats +Comment[pa]=ਵਿਸ਼ੇਸਤਾ ਵਾਰਤਾਲਾਪ ਲਈ ਪਲੱਗਿੰਨ +Comment[pl]=Wtyczka do okna dialogowego Właściwości +Comment[pt]='Plugin' para o diálogo de propriedades +Comment[pt_BR]=Plug-in para a janela de Propriedades +Comment[ro]=Modul pentru dialogul de proprietăţi +Comment[ru]=Модуль для диалога настроек +Comment[rw]=Icomeka ry'Ikiganiro cy'Ibiranga +Comment[se]=Lassemoduvla iešvuođahtaláseža várás +Comment[sk]=modul pre ialóg ílastnosti +Comment[sl]=Vstavek za pogovorno okno z lastnostmi +Comment[sq]=Shtojcë për Dialogun e Rekuizitave +Comment[sr]=Прикључак за дијалог са својствима +Comment[sr@Latn]=Priključak za dijalog sa svojstvima +Comment[sv]=Insticksprogram för egenskapsdialogen +Comment[ta]=பண்புகள் உரையாடலுக்கான சொருகுப்பொருள் +Comment[te]=లక్షణాల సంభాషణ కొరకు ప్లగిన్ +Comment[tg]=Штепсели барои Хусусиятҳои Тирезаи Диалогӣ Андохтан +Comment[th]=ปลั๊กอินสำหรับกล่องคุณสมบัติ +Comment[tr]=Özellikler İletişim Kutusu İçin Eklenti +Comment[tt]=Caylaw Dialogı öçen Östämä +Comment[uk]=Втулок для діалогу властивостей +Comment[uz]=Xossalar dialogi uchun plagin +Comment[uz@cyrillic]=Хоссалар диалоги учун плагин +Comment[ven]=U pulaga ha zwishumiswa zwa nyambedzano +Comment[vi]=Bộ cầm phít cho hộp thoại đặc tả. +Comment[xh]=Iplagi yangaphakathi Yezinto zobumnini Zencoko yababini +Comment[zh_CN]=属性对话的插件 +Comment[zh_HK]=屬性對話盒的外掛程式 +Comment[zh_TW]=屬性對話盒的外掛程式 +Comment[zu]=I-plugin Yengxoxo Yezinkomba zobunini + +[PropertyDef::X-KDE-Protocol] +Type=QString diff --git a/kio/kfile/krecentdirs.cpp b/kio/kfile/krecentdirs.cpp new file mode 100644 index 000000000..b32dd0481 --- /dev/null +++ b/kio/kfile/krecentdirs.cpp @@ -0,0 +1,99 @@ +/* -*- c++ -*- + * Copyright (C)2000 Waldo Bastian <[email protected]> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +#include <krecentdirs.h> +#include <ksimpleconfig.h> +#include <kstandarddirs.h> +#include <kglobalsettings.h> + +#define MAX_DIR_HISTORY 3 + +static void recentdirs_done(KConfig *config) +{ + if (config == KGlobal::config()) + { + config->sync(); + } + else + { + delete config; + } +} + +static KConfig *recentdirs_readList(QString &key, QStringList &result, bool readOnly) +{ + KConfig *config; + if ((key.length() < 2) || (key[0] != ':')) + key = ":default"; + if (key[1] == ':') + { + key = key.mid(2); + config = new KSimpleConfig(QString::fromLatin1("krecentdirsrc"), readOnly); + } + else + { + key = key.mid(1); + config = KGlobal::config(); + config->setGroup(QString::fromLatin1("Recent Dirs")); + } + + result=config->readPathListEntry(key); + if (result.isEmpty()) + { + result.append(KGlobalSettings::documentPath()); + } + return config; +} + +QStringList KRecentDirs::list(const QString &fileClass) +{ + QString key = fileClass; + QStringList result; + recentdirs_done(recentdirs_readList(key, result, true)); + return result; +} + +QString KRecentDirs::dir(const QString &fileClass) +{ + QStringList result = list(fileClass); + return result[0]; +} + +void KRecentDirs::add(const QString &fileClass, const QString &directory) +{ + QString key = fileClass; + QStringList result; + KConfig *config = recentdirs_readList(key, result, false); + // make sure the dir is first in history + result.remove(directory); + result.prepend(directory); + while(result.count() > MAX_DIR_HISTORY) + result.remove(result.fromLast()); + config->writePathEntry(key, result); + recentdirs_done(config); +} + diff --git a/kio/kfile/krecentdirs.h b/kio/kfile/krecentdirs.h new file mode 100644 index 000000000..078efcc50 --- /dev/null +++ b/kio/kfile/krecentdirs.h @@ -0,0 +1,70 @@ +/* -*- c++ -*- + * Copyright (C)2000 Waldo Bastian <[email protected]> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +#ifndef __KRECENTDIRS_H +#define __KRECENTDIRS_H + +#include <qstringlist.h> + +#include <kdelibs_export.h> + +/** + * The goal of this class is to make sure that, when the user needs to + * specify a file via the file selection dialog, this dialog will start + * in the directory most likely to contain the desired files. + * + * This works as follows: Each time the file selection dialog is + * shown, the programmer can specify a "file-class". The file-dialog will + * then start with the directory associated with this file-class. When + * the dialog closes, the directory currently shown in the file-dialog + * will be associated with the file-class. + * + * A file-class can either start with ':' or with '::'. If it starts with + * a single ':' the file-class is specific to the current application. + * If the file-class starts with '::' it is global to all applications. + */ +class KIO_EXPORT KRecentDirs +{ +public: + /** + * Returns a list of directories associated with this file-class. + * The most recently used directory is at the front of the list. + */ + static QStringList list(const QString &fileClass); + + /** + * Returns the most recently used directory accociated with this file-class. + */ + static QString dir(const QString &fileClass); + + /** + * Associates @p directory with @p fileClass + */ + static void add(const QString &fileClass, const QString &directory); +}; + +#endif diff --git a/kio/kfile/krecentdocument.cpp b/kio/kfile/krecentdocument.cpp new file mode 100644 index 000000000..9b3d4f75f --- /dev/null +++ b/kio/kfile/krecentdocument.cpp @@ -0,0 +1,177 @@ +/* -*- c++ -*- + * Copyright (C)2000 Daniel M. Duley <[email protected]> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +#include <krecentdocument.h> +#include <ksimpleconfig.h> +#include <kstandarddirs.h> +#include <kapplication.h> +#include <kurl.h> +#include <kdebug.h> +#include <kmimetype.h> +#include <kdesktopfile.h> +#include <qdir.h> +#include <qfileinfo.h> +#include <qtextstream.h> +#include <qstringlist.h> +#include <qregexp.h> + +#include <sys/types.h> +#include <utime.h> + +QString KRecentDocument::recentDocumentDirectory() +{ + // need to change this path, not sure where + return locateLocal("data", QString::fromLatin1("RecentDocuments/")); +} + +QStringList KRecentDocument::recentDocuments() +{ + QDir d(recentDocumentDirectory(), "*.desktop", QDir::Time, + QDir::Files | QDir::Readable | QDir::Hidden); + + if (!d.exists()) + d.mkdir(recentDocumentDirectory()); + + QStringList list = d.entryList(); + QStringList fullList; + + for (QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) { + QString pathDesktop = d.absFilePath( *it ); + KDesktopFile tmpDesktopFile( pathDesktop, false); + KURL urlDesktopFile(tmpDesktopFile.readURL()); + if( urlDesktopFile.isLocalFile() && !QFile(urlDesktopFile.path()).exists()) + d.remove(pathDesktop); + else + fullList.append( pathDesktop ); + } + + return fullList; +} + +void KRecentDocument::add(const KURL& url) +{ + KRecentDocument::add(url, qApp->argv()[0]); // ### argv[0] might not match the service filename! +} + +void KRecentDocument::add(const KURL& url, const QString& desktopEntryName) +{ + if ( url.isLocalFile() && !KGlobal::dirs()->relativeLocation("tmp", url.path()).startsWith("/")) + return; + + QString openStr = url.url(); + openStr.replace( QRegExp("\\$"), "$$" ); // Desktop files with type "Link" are $-variable expanded + + kdDebug(250) << "KRecentDocument::add for " << openStr << endl; + KConfig *config = KGlobal::config(); + QString oldGrp = config->group(); + config->setGroup(QString::fromLatin1("RecentDocuments")); + bool useRecent = config->readBoolEntry(QString::fromLatin1("UseRecent"), true); + int maxEntries = config->readNumEntry(QString::fromLatin1("MaxEntries"), 10); + + config->setGroup(oldGrp); + if(!useRecent) + return; + + QString path = recentDocumentDirectory(); + + QString dStr = path + url.fileName(); + + QString ddesktop = dStr + QString::fromLatin1(".desktop"); + + int i=1; + // check for duplicates + while(QFile::exists(ddesktop)){ + // see if it points to the same file and application + KSimpleConfig tmp(ddesktop); + tmp.setDesktopGroup(); + if(tmp.readEntry(QString::fromLatin1("X-KDE-LastOpenedWith")) + == desktopEntryName) + { + utime(QFile::encodeName(ddesktop), NULL); + return; + } + // if not append a (num) to it + ++i; + if ( i > maxEntries ) + break; + ddesktop = dStr + QString::fromLatin1("[%1].desktop").arg(i); + } + + QDir dir(path); + // check for max entries, delete oldest files if exceeded + QStringList list = dir.entryList(QDir::Files | QDir::Hidden, QDir::Time | QDir::Reversed); + i = list.count(); + if(i > maxEntries-1){ + QStringList::Iterator it; + it = list.begin(); + while(i > maxEntries-1){ + QFile::remove(dir.absPath() + QString::fromLatin1("/") + (*it)); + --i, ++it; + } + } + + // create the applnk + KSimpleConfig conf(ddesktop); + conf.setDesktopGroup(); + conf.writeEntry( QString::fromLatin1("Type"), QString::fromLatin1("Link") ); + conf.writePathEntry( QString::fromLatin1("URL"), openStr ); + // If you change the line below, change the test in the above loop + conf.writeEntry( QString::fromLatin1("X-KDE-LastOpenedWith"), desktopEntryName ); + QString name = url.fileName(); + if (name.isEmpty()) + name = openStr; + conf.writeEntry( QString::fromLatin1("Name"), name ); + conf.writeEntry( QString::fromLatin1("Icon"), KMimeType::iconForURL( url ) ); +} + +void KRecentDocument::add(const QString &openStr, bool isUrl) +{ + if( isUrl ) { + add( KURL( openStr ) ); + } else { + KURL url; + url.setPath( openStr ); + add( url ); + } +} + +void KRecentDocument::clear() +{ + QStringList list = recentDocuments(); + QDir dir; + for(QStringList::Iterator it = list.begin(); it != list.end() ; ++it) + dir.remove(*it); +} + +int KRecentDocument::maximumItems() +{ + KConfig *config = KGlobal::config(); + KConfigGroupSaver sa(config, QString::fromLatin1("RecentDocuments")); + return config->readNumEntry(QString::fromLatin1("MaxEntries"), 10); +} + + diff --git a/kio/kfile/krecentdocument.h b/kio/kfile/krecentdocument.h new file mode 100644 index 000000000..5ec06c162 --- /dev/null +++ b/kio/kfile/krecentdocument.h @@ -0,0 +1,105 @@ +/* -*- c++ -*- + * Copyright (C)2000 Daniel M. Duley <[email protected]> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +#ifndef __KRECENTDOCUMENT_H +#define __KRECENTDOCUMENT_H + +#include <qstring.h> +#include <kurl.h> + +/** + * Manage the "Recent Document Menu" entries displayed by + * applications such as Kicker and Konqueror. + * + * These entries are automatically generated .desktop files pointing + * to the current application and document. You should call the + * static add() method whenever the user opens or saves a new + * document if you want it to show up in the menu. + * + * You don't have to worry about this if you are using any + * KFileDialog derived class to open and save documents, as it + * already calls this class. User defined limits on the maximum + * number of documents to save, etc... are all automatically handled. + * + * @author Daniel M. Duley <[email protected]> + */ +class KIO_EXPORT KRecentDocument +{ +public: + + /** + * + * Return a list of absolute paths to recent document .desktop files, + * sorted by date. + * + */ + static QStringList recentDocuments(); + + /** + * Add a new item to the Recent Document menu. + * + * @param url The url to add. + */ + static void add(const KURL& url); + + /** + * Add a new item to the Recent Document menu, specifying the application to open it with. + * The above add() method uses argv[0] for the app name, which isn't always flexible enough. + * This method is used when an application launches another one to open a document. + * + * @param url The url to add. + * @param desktopEntryName The desktopEntryName of the service to use for opening this document. + */ + static void add(const KURL& url, const QString& desktopEntryName); + + /** + * + * Add a new item to the Recent Document menu. Calls add( url ). + * + * @param documentStr The full path to the document or URL to add. + * @param isURL Set to @p true if @p documentStr is an URL and not a local file path. + */ + static void add(const QString &documentStr, bool isURL = false); + + /** + * Clear the recent document menu of all entries. + */ + static void clear(); + + /** + * Returns the maximum amount of recent document entries allowed. + */ + static int maximumItems(); + + /** + * Returns the path to the directory where recent document .desktop files + * are stored. + */ + static QString recentDocumentDirectory(); +}; + +#endif diff --git a/kio/kfile/kurlbar.cpp b/kio/kfile/kurlbar.cpp new file mode 100644 index 000000000..446087522 --- /dev/null +++ b/kio/kfile/kurlbar.cpp @@ -0,0 +1,1036 @@ +/* This file is part of the KDE libraries + Copyright (C) 2001,2002,2003 Carsten Pfeiffer <[email protected]> + + 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, version 2. + + 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 <unistd.h> + +#include <qapplication.h> +#include <qcheckbox.h> +#include <qdrawutil.h> +#include <qfontmetrics.h> +#include <qlabel.h> +#include <qgrid.h> +#include <qpainter.h> +#include <qpopupmenu.h> +#include <qstyle.h> +#include <qvbox.h> +#include <qwhatsthis.h> + +#include <kaboutdata.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kglobal.h> +#include <kicondialog.h> +#include <kiconloader.h> +#include <kinstance.h> +#include <klineedit.h> +#include <klocale.h> +#include <kmimetype.h> +#include <kprotocolinfo.h> +#include <kstringhandler.h> +#include <kurldrag.h> +#include <kurlrequester.h> + +#include "kurlbar.h" + +/** + * Handles tooltips in the KURLBar + * @internal + */ +class KURLBarToolTip : public QToolTip +{ +public: + KURLBarToolTip( QListBox *view ) : QToolTip( view ), m_view( view ) {} + +protected: + virtual void maybeTip( const QPoint& point ) { + QListBoxItem *item = m_view->itemAt( point ); + if ( item ) { + QString text = static_cast<KURLBarItem*>( item )->toolTip(); + if ( !text.isEmpty() ) + tip( m_view->itemRect( item ), text ); + } + } + +private: + QListBox *m_view; +}; + + +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// + +class KURLBarItem::KURLBarItemPrivate +{ +public: + KURLBarItemPrivate() + { + isPersistent = true; + } + + bool isPersistent; +}; + +KURLBarItem::KURLBarItem( KURLBar *parent, + const KURL& url, bool persistent, const QString& description, + const QString& icon, KIcon::Group group ) + : QListBoxPixmap( KIconLoader::unknown() /*, parent->listBox()*/ ), + m_url( url ), + m_pixmap( 0L ), + m_parent( parent ), + m_appLocal( true ) +{ + init( icon, group, description, persistent ); +} + +KURLBarItem::KURLBarItem( KURLBar *parent, + const KURL& url, const QString& description, + const QString& icon, KIcon::Group group ) + : QListBoxPixmap( KIconLoader::unknown() /*, parent->listBox()*/ ), + m_url( url ), + m_pixmap( 0L ), + m_parent( parent ), + m_appLocal( true ) +{ + init( icon, group, description, true /*persistent*/ ); +} + +void KURLBarItem::init( const QString& icon, KIcon::Group group, + const QString& description, bool persistent ) +{ + d = new KURLBarItemPrivate; + d->isPersistent = persistent; + + setCustomHighlighting( true ); + setIcon( icon, group ); + setDescription( description ); +} + +KURLBarItem::~KURLBarItem() +{ + delete d; +} + +void KURLBarItem::setURL( const KURL& url ) +{ + m_url = url; + if ( m_description.isEmpty() ) + setText( url.fileName() ); +} + +void KURLBarItem::setIcon( const QString& icon, KIcon::Group group ) +{ + m_icon = icon; + m_group = group; + + if ( icon.isEmpty() ) + m_pixmap = KMimeType::pixmapForURL( m_url, 0, group, iconSize() ); + else + m_pixmap = KGlobal::iconLoader()->loadIcon( icon, group, iconSize(), + KIcon::DefaultState ); +} + +void KURLBarItem::setDescription( const QString& desc ) +{ + m_description = desc; + setText( desc.isEmpty() ? m_url.fileName() : desc ); +} + +void KURLBarItem::setApplicationLocal( bool local ) +{ + if ( !local && !isPersistent() ) + { + kdWarning() << "KURLBar: dynamic (non-persistent) items can not be global." << endl; + return; + } + + m_appLocal = local; +} + +void KURLBarItem::setToolTip( const QString& tip ) +{ + m_toolTip = tip; +} + +QString KURLBarItem::toolTip() const +{ + return m_toolTip.isEmpty() ? m_url.prettyURL() : m_toolTip; +} + +int KURLBarItem::iconSize() const +{ + return m_parent->iconSize(); +} + +void KURLBarItem::paint( QPainter *p ) +{ + QListBox *box = listBox(); + int w = width( box ); + static const int margin = KDialog::spacingHint(); + + // draw sunken selection + if ( isCurrent() || isSelected() ) { + int h = height( box ); + + QBrush brush = box->colorGroup().brush( QColorGroup::Highlight ); + p->fillRect( 0, 0, w, h, brush ); + QPen pen = p->pen(); + QPen oldPen = pen; + pen.setColor( box->colorGroup().mid() ); + p->setPen( pen ); + + p->drawPoint( 0, 0 ); + p->drawPoint( 0, h - 1 ); + p->drawPoint( w - 1, 0 ); + p->drawPoint( w - 1, h - 1 ); + + p->setPen( oldPen ); + } + + if ( m_parent->iconSize() < KIcon::SizeMedium ) { + // small icon -> draw icon next to text + + // ### mostly cut & paste of QListBoxPixmap::paint() until Qt 3.1 + // (where it will properly use pixmap() instead of the internal pixmap) + const QPixmap *pm = pixmap(); + int yPos = QMAX( 0, (height(box) - pm->height())/2 ); + + p->drawPixmap( margin, yPos, *pm ); + if ( !text().isEmpty() ) { + QFontMetrics fm = p->fontMetrics(); + if ( pm->height() < fm.height() ) + yPos = fm.ascent() + fm.leading()/2; + else + yPos = pm->height()/2 - fm.height()/2 + fm.ascent(); + + yPos += margin; + int stringWidth = box->width() - pm->width() - 2 - (margin * 2); + QString visibleText = KStringHandler::rPixelSqueeze( text(), fm, stringWidth ); + int xPos = pm->width() + margin + 2; + + if ( isCurrent() || isSelected() ) { + p->setPen( box->colorGroup().highlight().dark(115) ); + p->drawText( xPos + ( QApplication::reverseLayout() ? -1 : 1), + yPos + 1, visibleText ); + p->setPen( box->colorGroup().highlightedText() ); + } + + p->drawText( xPos, yPos, visibleText ); + } + // end cut & paste (modulo pixmap centering) + } + + else { + // big icons -> draw text below icon + int y = margin; + const QPixmap *pm = pixmap(); + + if ( !pm->isNull() ) { + int x = (w - pm->width()) / 2; + x = QMAX( x, margin ); + p->drawPixmap( x, y, *pm ); + } + + if ( !text().isEmpty() ) { + QFontMetrics fm = p->fontMetrics(); + y += pm->height() + fm.height() - fm.descent(); + + int stringWidth = box->width() - (margin * 2); + QString visibleText = KStringHandler::rPixelSqueeze( text(), fm, stringWidth ); + int x = (w - fm.width( visibleText )) / 2; + x = QMAX( x, margin ); + + if ( isCurrent() || isSelected() ) { + p->setPen( box->colorGroup().highlight().dark(115) ); + p->drawText( x + ( QApplication::reverseLayout() ? -1 : 1), + y + 1, visibleText ); + p->setPen( box->colorGroup().highlightedText() ); + } + + p->drawText( x, y, visibleText ); + } + } +} + +QSize KURLBarItem::sizeHint() const +{ + int wmin = 0; + int hmin = 0; + const KURLBarListBox *lb =static_cast<const KURLBarListBox*>(listBox()); + + if ( m_parent->iconSize() < KIcon::SizeMedium ) { + wmin = QListBoxPixmap::width( lb ) + KDialog::spacingHint() * 2; + hmin = QListBoxPixmap::height( lb ) + KDialog::spacingHint() * 2; + } + else { + wmin = QMAX(lb->fontMetrics().width(text()), pixmap()->width()) + KDialog::spacingHint() * 2; + hmin = lb->fontMetrics().lineSpacing() + pixmap()->height() + KDialog::spacingHint() * 2; + } + + if ( lb->isVertical() ) + wmin = QMIN( wmin, lb->viewport()->sizeHint().width() ); + else + hmin = QMIN( hmin, lb->viewport()->sizeHint().height() ); + + return QSize( wmin, hmin ); +} + +int KURLBarItem::width( const QListBox *lb ) const +{ + if ( static_cast<const KURLBarListBox *>( lb )->isVertical() ) + return QMAX( sizeHint().width(), lb->viewport()->width() ); + else + return sizeHint().width(); +} + +int KURLBarItem::height( const QListBox *lb ) const +{ + if ( static_cast<const KURLBarListBox *>( lb )->isVertical() ) + return sizeHint().height(); + else + return QMAX( sizeHint().height(), lb->viewport()->height() ); +} + +bool KURLBarItem::isPersistent() const +{ + return d->isPersistent; +} + +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// + +class KURLBar::KURLBarPrivate +{ +public: + KURLBarPrivate() + { + currentURL.setPath( QDir::homeDirPath() ); + defaultIconSize = 0; + } + + int defaultIconSize; + KURL currentURL; +}; + + +KURLBar::KURLBar( bool useGlobalItems, QWidget *parent, const char *name, WFlags f ) + : QFrame( parent, name, f ), + m_activeItem( 0L ), + m_useGlobal( useGlobalItems ), + m_isModified( false ), + m_isImmutable( false ), + m_listBox( 0L ), + m_iconSize( KIcon::SizeMedium ) +{ + d = new KURLBarPrivate(); + + setListBox( 0L ); + setSizePolicy( QSizePolicy( isVertical() ? + QSizePolicy::Maximum : + QSizePolicy::Preferred, + isVertical() ? + QSizePolicy::Preferred : + QSizePolicy::Maximum )); + QWhatsThis::add(this, i18n("<qt>The <b>Quick Access</b> panel provides easy access to commonly used file locations.<p>" + "Clicking on one of the shortcut entries will take you to that location.<p>" + "By right clicking on an entry you can add, edit and remove shortcuts.</qt>")); +} + +KURLBar::~KURLBar() +{ + delete d; +} + +KURLBarItem * KURLBar::insertItem(const KURL& url, const QString& description, + bool applicationLocal, + const QString& icon, KIcon::Group group ) +{ + KURLBarItem *item = new KURLBarItem(this, url, description, icon, group); + item->setApplicationLocal( applicationLocal ); + m_listBox->insertItem( item ); + return item; +} + +KURLBarItem * KURLBar::insertDynamicItem(const KURL& url, const QString& description, + const QString& icon, KIcon::Group group ) +{ + KURLBarItem *item = new KURLBarItem(this, url, false, description, icon, group); + m_listBox->insertItem( item ); + return item; +} + +void KURLBar::setOrientation( Qt::Orientation orient ) +{ + m_listBox->setOrientation( orient ); + setSizePolicy( QSizePolicy( isVertical() ? + QSizePolicy::Maximum : + QSizePolicy::Preferred, + isVertical() ? + QSizePolicy::Preferred : + QSizePolicy::Maximum )); +} + +Qt::Orientation KURLBar::orientation() const +{ + return m_listBox->orientation(); +} + +void KURLBar::setListBox( KURLBarListBox *view ) +{ + delete m_listBox; + + if ( !view ) { + m_listBox = new KURLBarListBox( this, "urlbar listbox" ); + setOrientation( Vertical ); + } + else { + m_listBox = view; + if ( m_listBox->parentWidget() != this ) + m_listBox->reparent( this, QPoint(0,0) ); + m_listBox->resize( width(), height() ); + } + + m_listBox->setSelectionMode( KListBox::Single ); + paletteChange( palette() ); + m_listBox->setFocusPolicy( TabFocus ); + + connect( m_listBox, SIGNAL( mouseButtonClicked( int, QListBoxItem *, const QPoint & ) ), + SLOT( slotSelected( int, QListBoxItem * ))); + connect( m_listBox, SIGNAL( dropped( QDropEvent * )), + this, SLOT( slotDropped( QDropEvent * ))); + connect( m_listBox, SIGNAL( contextMenuRequested( QListBoxItem *, + const QPoint& )), + SLOT( slotContextMenuRequested( QListBoxItem *, const QPoint& ))); + connect( m_listBox, SIGNAL( returnPressed( QListBoxItem * ) ), + SLOT( slotSelected( QListBoxItem * ) )); +} + +void KURLBar::setIconSize( int size ) +{ + if ( size == m_iconSize ) + return; + + m_iconSize = size; + + // reload the icons with the new size + KURLBarItem *item = static_cast<KURLBarItem*>( m_listBox->firstItem() ); + while ( item ) { + item->setIcon( item->icon(), item->iconGroup() ); + item = static_cast<KURLBarItem*>( item->next() ); + } + + resize( sizeHint() ); + updateGeometry(); +} + +void KURLBar::clear() +{ + m_listBox->clear(); +} + +void KURLBar::resizeEvent( QResizeEvent *e ) +{ + QFrame::resizeEvent( e ); + m_listBox->resize( width(), height() ); +} + +void KURLBar::paletteChange( const QPalette & ) +{ + QPalette pal = palette(); + QColor gray = pal.color( QPalette::Normal, QColorGroup::Background ); + QColor selectedTextColor = pal.color( QPalette::Normal, QColorGroup::BrightText ); + QColor foreground = pal.color( QPalette::Normal, QColorGroup::Foreground ); + pal.setColor( QPalette::Normal, QColorGroup::Base, gray ); + pal.setColor( QPalette::Normal, QColorGroup::HighlightedText, selectedTextColor ); + pal.setColor( QPalette::Normal, QColorGroup::Text, foreground ); + pal.setColor( QPalette::Inactive, QColorGroup::Base, gray ); + pal.setColor( QPalette::Inactive, QColorGroup::HighlightedText, selectedTextColor ); + pal.setColor( QPalette::Inactive, QColorGroup::Text, foreground ); + + setPalette( pal ); +} + +QSize KURLBar::sizeHint() const +{ + return m_listBox->sizeHint(); + +#if 0 + // this code causes vertical and or horizontal scrollbars appearing + // depending on the text, font, moonphase and earth rotation. Just using + // m_listBox->sizeHint() fixes this (although the widget can then be + // resized to a smaller size so that scrollbars appear). + int w = 0; + int h = 0; + KURLBarItem *item; + bool vertical = isVertical(); + + for ( item = static_cast<KURLBarItem*>( m_listBox->firstItem() ); + item; + item = static_cast<KURLBarItem*>( item->next() ) ) { + + QSize sh = item->sizeHint(); + + if ( vertical ) { + w = QMAX( w, sh.width() ); + h += sh.height(); + } + else { + w += sh.width(); + h = QMAX( h, sh.height() ); + } + } + +// if ( vertical && m_listBox->verticalScrollBar()->isVisible() ) +// w += m_listBox->verticalScrollBar()->width(); +// else if ( !vertical && m_listBox->horizontalScrollBar()->isVisible() ) +// h += m_listBox->horizontalScrollBar()->height(); + + if ( w == 0 && h == 0 ) + return QSize( 100, 200 ); + else + return QSize( 6 + w, h ); +#endif +} + +QSize KURLBar::minimumSizeHint() const +{ + QSize s = sizeHint(); // ### + int w = s.width() + m_listBox->verticalScrollBar()->width(); + int h = s.height() + m_listBox->horizontalScrollBar()->height(); + return QSize( w, h ); +} + +void KURLBar::slotSelected( int button, QListBoxItem *item ) +{ + if ( button != Qt::LeftButton ) + return; + + slotSelected( item ); +} + +void KURLBar::slotSelected( QListBoxItem *item ) +{ + if ( item && item != m_activeItem ) + m_activeItem = static_cast<KURLBarItem*>( item ); + + if ( m_activeItem ) { + m_listBox->setCurrentItem( m_activeItem ); + emit activated( m_activeItem->url() ); + } +} + +void KURLBar::setCurrentItem( const KURL& url ) +{ + d->currentURL = url; + + QString u = url.url(-1); + + if ( m_activeItem && m_activeItem->url().url(-1) == u ) + return; + + bool hasURL = false; + QListBoxItem *item = m_listBox->firstItem(); + while ( item ) { + if ( static_cast<KURLBarItem*>( item )->url().url(-1) == u ) { + m_activeItem = static_cast<KURLBarItem*>( item ); + m_listBox->setCurrentItem( item ); + m_listBox->setSelected( item, true ); + hasURL = true; + break; + } + item = item->next(); + } + + if ( !hasURL ) { + m_activeItem = 0L; + m_listBox->clearSelection(); + } +} + +KURLBarItem * KURLBar::currentItem() const +{ + QListBoxItem *item = m_listBox->item( m_listBox->currentItem() ); + if ( item ) + return static_cast<KURLBarItem *>( item ); + return 0L; +} + +KURL KURLBar::currentURL() const +{ + KURLBarItem *item = currentItem(); + return item ? item->url() : KURL(); +} + +void KURLBar::readConfig( KConfig *appConfig, const QString& itemGroup ) +{ + m_isImmutable = appConfig->groupIsImmutable( itemGroup ); + KConfigGroupSaver cs( appConfig, itemGroup ); + d->defaultIconSize = m_iconSize; + m_iconSize = appConfig->readNumEntry( "Speedbar IconSize", m_iconSize ); + + if ( m_useGlobal ) { // read global items + KConfig *globalConfig = KGlobal::config(); + KConfigGroupSaver cs( globalConfig, (QString)(itemGroup +" (Global)")); + int num = globalConfig->readNumEntry( "Number of Entries" ); + for ( int i = 0; i < num; i++ ) { + readItem( i, globalConfig, false ); + } + } + + // read application local items + int num = appConfig->readNumEntry( "Number of Entries" ); + for ( int i = 0; i < num; i++ ) { + readItem( i, appConfig, true ); + } +} + +void KURLBar::readItem( int i, KConfig *config, bool applicationLocal ) +{ + QString number = QString::number( i ); + KURL url = KURL::fromPathOrURL( config->readPathEntry( QString("URL_") + number )); + if ( !url.isValid() || !KProtocolInfo::isKnownProtocol( url )) + return; // nothing we could do. + + insertItem( url, + config->readEntry( QString("Description_") + number ), + applicationLocal, + config->readEntry( QString("Icon_") + number ), + static_cast<KIcon::Group>( + config->readNumEntry( QString("IconGroup_") + number )) ); +} + +void KURLBar::writeConfig( KConfig *config, const QString& itemGroup ) +{ + KConfigGroupSaver cs1( config, itemGroup ); + if(!config->hasDefault("Speedbar IconSize") && m_iconSize == d->defaultIconSize ) + config->revertToDefault("Speedbar IconSize"); + else + config->writeEntry( "Speedbar IconSize", m_iconSize ); + + if ( !m_isModified ) + return; + + int i = 0; + int numLocal = 0; + KURLBarItem *item = static_cast<KURLBarItem*>( m_listBox->firstItem() ); + + while ( item ) + { + if ( item->isPersistent() ) // we only save persistent items + { + if ( item->applicationLocal() ) + { + writeItem( item, numLocal, config, false ); + numLocal++; + } + + i++; + } + item = static_cast<KURLBarItem*>( item->next() ); + } + config->writeEntry("Number of Entries", numLocal); + + + // write the global entries to kdeglobals, if any + bool haveGlobalEntries = (i > numLocal); + if ( m_useGlobal && haveGlobalEntries ) { + config->setGroup( itemGroup + " (Global)" ); + + int numGlobals = 0; + item = static_cast<KURLBarItem*>( m_listBox->firstItem() ); + + while ( item ) + { + if ( item->isPersistent() ) // we only save persistent items + { + if ( !item->applicationLocal() ) + { + writeItem( item, numGlobals, config, true ); + numGlobals++; + } + } + + item = static_cast<KURLBarItem*>( item->next() ); + } + config->writeEntry("Number of Entries", numGlobals, true, true); + } + + m_isModified = false; +} + +void KURLBar::writeItem( KURLBarItem *item, int i, KConfig *config, + bool global ) +{ + if ( !item->isPersistent() ) + return; + + QString Description = "Description_"; + QString URL = "URL_"; + QString Icon = "Icon_"; + QString IconGroup = "IconGroup_"; + + QString number = QString::number( i ); + config->writePathEntry( URL + number, item->url().prettyURL(), true, global ); + + config->writeEntry( Description + number, item->description(),true,global); + config->writeEntry( Icon + number, item->icon(), true, global ); + config->writeEntry( IconGroup + number, item->iconGroup(), true, global ); +} + + +void KURLBar::slotDropped( QDropEvent *e ) +{ + KURL::List urls; + if ( KURLDrag::decode( e, urls ) ) { + KURL url; + QString description; + QString icon; + bool appLocal = false; + + KURL::List::Iterator it = urls.begin(); + for ( ; it != urls.end(); ++it ) { + (void) insertItem( *it, description, appLocal, icon ); + m_isModified = true; + updateGeometry(); + } + } +} + +void KURLBar::slotContextMenuRequested( QListBoxItem *_item, const QPoint& pos ) +{ + if (m_isImmutable) + return; + + KURLBarItem *item = dynamic_cast<KURLBarItem*>( _item ); + + static const int IconSize = 10; + static const int AddItem = 20; + static const int EditItem = 30; + static const int RemoveItem = 40; + + KURL lastURL = m_activeItem ? m_activeItem->url() : KURL(); + + bool smallIcons = m_iconSize < KIcon::SizeMedium; + QPopupMenu *popup = new QPopupMenu(); + popup->insertItem( smallIcons ? + i18n("&Large Icons") : i18n("&Small Icons"), + IconSize ); + popup->insertSeparator(); + + if (item != 0L && item->isPersistent()) + { + popup->insertItem(SmallIconSet("edit"), i18n("&Edit Entry..."), EditItem); + popup->insertSeparator(); + } + + popup->insertItem(SmallIconSet("filenew"), i18n("&Add Entry..."), AddItem); + + if (item != 0L && item->isPersistent()) + { + popup->insertItem( SmallIconSet("editdelete"), i18n("&Remove Entry"), + RemoveItem ); + } + + int result = popup->exec( pos ); + switch ( result ) { + case IconSize: + setIconSize( smallIcons ? KIcon::SizeMedium : KIcon::SizeSmallMedium ); + m_listBox->triggerUpdate( true ); + break; + case AddItem: + addNewItem(); + break; + case EditItem: + editItem( static_cast<KURLBarItem *>( item ) ); + break; + case RemoveItem: + delete item; + m_isModified = true; + break; + default: // abort + break; + } + + // reset current item + m_activeItem = 0L; + setCurrentItem( lastURL ); +} + +bool KURLBar::addNewItem() +{ + KURLBarItem *item = new KURLBarItem( this, d->currentURL, + i18n("Enter a description") ); + if ( editItem( item ) ) { + m_listBox->insertItem( item ); + return true; + } + + delete item; + return false; +} + +bool KURLBar::editItem( KURLBarItem *item ) +{ + if ( !item || !item->isPersistent() ) // should never happen tho + return false; + + KURL url = item->url(); + QString description = item->description(); + QString icon = item->icon(); + bool appLocal = item->applicationLocal(); + + if ( KURLBarItemDialog::getInformation( m_useGlobal, + url, description, + icon, appLocal, + m_iconSize, this )) + { + item->setURL( url ); + item->setDescription( description ); + item->setIcon( icon ); + item->setApplicationLocal( appLocal ); + m_listBox->triggerUpdate( true ); + m_isModified = true; + updateGeometry(); + return true; + } + + return false; +} + +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// + + +KURLBarListBox::KURLBarListBox( QWidget *parent, const char *name ) + : KListBox( parent, name ) +{ + m_toolTip = new KURLBarToolTip( this ); + setAcceptDrops( true ); + viewport()->setAcceptDrops( true ); +} + +KURLBarListBox::~KURLBarListBox() +{ + delete m_toolTip; +} + +void KURLBarListBox::paintEvent( QPaintEvent* ) +{ + QPainter p(this); + p.setPen( colorGroup().mid() ); + p.drawRect( 0, 0, width(), height() ); +} + +QDragObject * KURLBarListBox::dragObject() +{ + KURL::List urls; + KURLBarItem *item = static_cast<KURLBarItem*>( firstItem() ); + + while ( item ) { + if ( item->isSelected() ) + urls.append( item->url() ); + item = static_cast<KURLBarItem*>( item->next() ); + } + + if ( !urls.isEmpty() ) // ### use custom drag-object with description etc.? + return new KURLDrag( urls, this, "urlbar drag" ); + + return 0L; +} + +void KURLBarListBox::contentsDragEnterEvent( QDragEnterEvent *e ) +{ + e->accept( KURLDrag::canDecode( e )); +} + +void KURLBarListBox::contentsDropEvent( QDropEvent *e ) +{ + emit dropped( e ); +} + +void KURLBarListBox::contextMenuEvent( QContextMenuEvent *e ) +{ + if (e) + { + emit contextMenuRequested( itemAt( e->globalPos() ), e->globalPos() ); + e->consume(); // Consume the event to avoid multiple contextMenuEvent calls... + } +} + +void KURLBarListBox::setOrientation( Qt::Orientation orient ) +{ + if ( orient == Vertical ) { + setColumnMode( 1 ); + setRowMode( Variable ); + } + else { + setRowMode( 1 ); + setColumnMode( Variable ); + } + + m_orientation = orient; +} + +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// + + +bool KURLBarItemDialog::getInformation( bool allowGlobal, KURL& url, + QString& description, QString& icon, + bool& appLocal, int iconSize, + QWidget *parent ) +{ + KURLBarItemDialog *dialog = new KURLBarItemDialog( allowGlobal, url, + description, icon, + appLocal, + iconSize, parent ); + if ( dialog->exec() == QDialog::Accepted ) { + // set the return parameters + url = dialog->url(); + description = dialog->description(); + icon = dialog->icon(); + appLocal = dialog->applicationLocal(); + + delete dialog; + return true; + } + + delete dialog; + return false; +} + +KURLBarItemDialog::KURLBarItemDialog( bool allowGlobal, const KURL& url, + const QString& description, + QString icon, bool appLocal, + int iconSize, + QWidget *parent, const char *name ) + : KDialogBase( parent, name, true, + i18n("Edit Quick Access Entry"), Ok | Cancel, Ok, true ) +{ + QVBox *box = new QVBox( this ); + QString text = i18n("<qt><b>Please provide a description, URL and icon for this Quick Access entry.</b></br></qt>"); + QLabel *label = new QLabel( text, box ); + box->setSpacing( spacingHint() ); + + QGrid *grid = new QGrid( 2, box ); + grid->setSpacing( spacingHint() ); + + QString whatsThisText = i18n("<qt>This is the text that will appear in the Quick Access panel.<p>" + "The description should consist of one or two words " + "that will help you remember what this entry refers to.</qt>"); + label = new QLabel( i18n("&Description:"), grid ); + m_edit = new KLineEdit( grid, "description edit" ); + m_edit->setText( description.isEmpty() ? url.fileName() : description ); + label->setBuddy( m_edit ); + QWhatsThis::add( label, whatsThisText ); + QWhatsThis::add( m_edit, whatsThisText ); + + whatsThisText = i18n("<qt>This is the location associated with the entry. Any valid URL may be used. For example:<p>" + "%1<br>http://www.kde.org<br>ftp://ftp.kde.org/pub/kde/stable<p>" + "By clicking on the button next to the text edit box you can browse to an " + "appropriate URL.</qt>").arg(QDir::homeDirPath()); + label = new QLabel( i18n("&URL:"), grid ); + m_urlEdit = new KURLRequester( url.prettyURL(), grid ); + m_urlEdit->setMode( KFile::Directory ); + label->setBuddy( m_urlEdit ); + QWhatsThis::add( label, whatsThisText ); + QWhatsThis::add( m_urlEdit, whatsThisText ); + + whatsThisText = i18n("<qt>This is the icon that will appear in the Quick Access panel.<p>" + "Click on the button to select a different icon.</qt>"); + label = new QLabel( i18n("Choose an &icon:"), grid ); + m_iconButton = new KIconButton( grid, "icon button" ); + m_iconButton->setIconSize( iconSize ); + if ( icon.isEmpty() ) + icon = KMimeType::iconForURL( url ); + m_iconButton->setIcon( icon ); + label->setBuddy( m_iconButton ); + QWhatsThis::add( label, whatsThisText ); + QWhatsThis::add( m_iconButton, whatsThisText ); + + if ( allowGlobal ) { + QString appName; + if ( KGlobal::instance()->aboutData() ) + appName = KGlobal::instance()->aboutData()->programName(); + if ( appName.isEmpty() ) + appName = QString::fromLatin1( KGlobal::instance()->instanceName() ); + m_appLocal = new QCheckBox( i18n("&Only show when using this application (%1)").arg( appName ), box ); + m_appLocal->setChecked( appLocal ); + QWhatsThis::add( m_appLocal, + i18n("<qt>Select this setting if you want this " + "entry to show only when using the current application (%1).<p>" + "If this setting is not selected, the entry will be available in all " + "applications.</qt>") + .arg(appName)); + } + else + m_appLocal = 0L; + connect(m_urlEdit->lineEdit(),SIGNAL(textChanged ( const QString & )),this,SLOT(urlChanged(const QString & ))); + m_edit->setFocus(); + setMainWidget( box ); +} + +KURLBarItemDialog::~KURLBarItemDialog() +{ +} + +void KURLBarItemDialog::urlChanged(const QString & text ) +{ + enableButtonOK( !text.isEmpty() ); +} + +KURL KURLBarItemDialog::url() const +{ + QString text = m_urlEdit->url(); + KURL u; + if ( text.at(0) == '/' ) + u.setPath( text ); + else + u = text; + + return u; +} + +QString KURLBarItemDialog::description() const +{ + return m_edit->text(); +} + +QString KURLBarItemDialog::icon() const +{ + return m_iconButton->icon(); +} + +bool KURLBarItemDialog::applicationLocal() const +{ + if ( !m_appLocal ) + return true; + + return m_appLocal->isChecked(); +} + +void KURLBarItem::virtual_hook( int, void* ) +{ /*BASE::virtual_hook( id, data );*/ } + +void KURLBar::virtual_hook( int, void* ) +{ /*BASE::virtual_hook( id, data );*/ } + +void KURLBarListBox::virtual_hook( int id, void* data ) +{ KListBox::virtual_hook( id, data ); } + + +#include "kurlbar.moc" diff --git a/kio/kfile/kurlbar.h b/kio/kfile/kurlbar.h new file mode 100644 index 000000000..6bc5de6d5 --- /dev/null +++ b/kio/kfile/kurlbar.h @@ -0,0 +1,660 @@ +/* This file is part of the KDE libraries + Copyright (C) 2001,2002,2003 Carsten Pfeiffer <[email protected]> + + 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, version 2. + + 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 KURLBAR_H +#define KURLBAR_H + +#include <qevent.h> +#include <qframe.h> +#include <qtooltip.h> + +#include <kdialogbase.h> +#include <kicontheme.h> +#include <klistbox.h> +#include <kurl.h> + +class KConfig; +class KURLBar; + +/** + * An item to be used in KURLBar / KURLBarListBox. All the properties + * (url, icon, description, tooltip) can be changed dynamically. + * + * @author Carsten Pfeiffer <[email protected]> + * @see KURLBar + * @see KURLBarListBox + */ +class KIO_EXPORT KURLBarItem : public QListBoxPixmap +{ +public: + /** + * Creates a KURLBarItem to be used in the @p parent KURLBar. You need + * to insert the item into the listbox manually, if you don't use + * KURLBar::insertItem(). + * + * If description is empty, it will try to use the filename/directory + * of @p url, which will be shown as text of the item. + * @p url will be used as tooltip, unless you set a different tip with + * setToolTip(). + * @p persistent specifies whether this item is a persistent item or a + * dynamic item, that is not saved with KURLBar::writeConfig(). + * @since 3.2 + */ + KURLBarItem( KURLBar *parent, const KURL& url, bool persistent, + const QString& description = QString::null, + const QString& icon = QString::null, + KIcon::Group group = KIcon::Panel ); + + /** + * Creates a persistent KURLBarItem to be used in the @p parent KURLBar. You need + * to insert the item into the listbox manually, if you don't use + * KURLBar::insertItem(). + * + * If description is empty, it will try to use the filename/directory + * of @p url, which will be shown as text of the item. + * @p url will be used as tooltip, unless you set a different tip with + * setToolTip(). + * @p persistent specifies whether this item is a persistent item or a + * dynamic item, that is not saved with KURLBar::writeConfig(). + */ + KURLBarItem( KURLBar *parent, const KURL& url, + const QString& description = QString::null, + const QString& icon = QString::null, + KIcon::Group group = KIcon::Panel ); + + /** + * Destroys the item + */ + ~KURLBarItem(); + + /** + * Sets @p url for this item. Also updates the visible text to the + * filename/directory of the url, if no description is set. + * @see url + */ + void setURL( const KURL& url ); + /** + * @p sets the icon for this item. See KIconLoader for a description + * of the icon groups. + * @see icon + */ + void setIcon( const QString& icon, KIcon::Group group = KIcon::Panel ); + /** + * Sets the description of this item that will be shown as item-text. + * @see description + */ + void setDescription( const QString& desc ); + /** + * Sets a tooltip to be used for this item. + * @see toolTip + */ + void setToolTip( const QString& tip ); + + /** + * returns the preferred size of this item + * @since 3.1 + */ + QSize sizeHint() const; + + /** + * returns the width of this item. + */ + virtual int width( const QListBox * ) const; + /** + * returns the height of this item. + */ + virtual int height( const QListBox * ) const; + + /** + * returns the url of this item. + * @see setURL + */ + const KURL& url() const { return m_url; } + /** + * returns the description of this item. + * @see setDescription + */ + const QString& description() const { return m_description; } + /** + * returns the icon of this item. + * @see setIcon + */ + const QString& icon() const { return m_icon; } + /** + * returns the tooltip of this item. + * @see setToolTip + */ + QString toolTip() const; + /** + * returns the icon-group of this item (determines icon-effects). + * @see setIcon + */ + KIcon::Group iconGroup() const { return m_group; } + /** + * returns the pixmap of this item. + */ + virtual const QPixmap * pixmap() const { return &m_pixmap; } + + /** + * Makes this item a local or global one. This has only an effect + * on persistent items of course. + * @see isPersistent + * @see applicationLocal + */ + void setApplicationLocal( bool local ); + + /** + * returns whether this is a global item or a local one. KURLBar + * can differentiate between global and local items (only for the current + * application) for easy extensiblity. + * @see setApplicationLocal + */ + bool applicationLocal() const { return m_appLocal; } + + /** + * returns whether this item is persistent (via KURLBar::writeConfig() + * and KURLBar::readConfig()) or not. + * @since 3.2 + */ + bool isPersistent() const; + +protected: + virtual void paint( QPainter *p ); + +private: + int iconSize() const; + void init( const QString& icon, KIcon::Group group, + const QString& description, bool persistent ); + + KURL m_url; + QString m_description; + QString m_icon; + QString m_toolTip; + QPixmap m_pixmap; + KIcon::Group m_group; + KURLBar *m_parent; + bool m_appLocal :1; + +protected: + virtual void virtual_hook( int id, void* data ); +private: + class KURLBarItemPrivate; + KURLBarItemPrivate *d; +}; + + +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// + + +class KURLBarListBox; + +/** + * KURLBar is a widget that displays icons together with a description. They + * can be arranged either horizontally or vertically. Clicking on an item + * will cause the activated() signal to be emitted. The user can edit + * existing items by choosing "Edit entry" in the contextmenu. He can also + * remove or add new entries (via drag&drop or the context menu). + * + * KURLBar offers the methods readConfig() and writeConfig() to + * read and write the configuration of all the entries. It can differentiate + * between global and local entries -- global entries will be saved in the + * global configuration (kdeglobals), while local entries will be saved in + * your application's KConfig object. + * + * Due to the configurability, you usually only insert some default entries + * once and then solely use the read and writeConfig methods to preserve the + * user's configuration. + * + * The widget has a "current" item, that is visualized to differentiate it + * from others. + * + * @author Carsten Pfeiffer <[email protected]> + * @short A URL-bar widget, as used in the KFileDialog + */ +class KIO_EXPORT KURLBar : public QFrame +{ + Q_OBJECT + +public: + /** + * Constructs a KURLBar. Set @p useGlobalItems to true if you want to + * allow global/local item separation. + */ + KURLBar( bool useGlobalItems, + QWidget *parent = 0, const char *name = 0, WFlags f = 0 ); + /** + * Destroys the KURLBar. + */ + ~KURLBar(); + + /** + * Inserts a new item into the KURLBar and returns the created + * KURLBarItem. + * + * @p url the url of the item + * @p description the description of the item (shown in the view) + * @p applicationLocal whether this should be a global or a local item + * @p icon an icon -- if empty, the default icon for the url will be used + * @p group the icon-group for using icon-effects + */ + virtual KURLBarItem * insertItem( const KURL& url, + const QString& description, + bool applicationLocal = true, + const QString& icon = QString::null, + KIcon::Group group = KIcon::Panel ); + /** + * Inserts a new dynamic item into the KURLBar and returns the created + * KURLBarItem. + * + * @p url the url of the item + * @p description the description of the item (shown in the view) + * @p icon an icon -- if empty, the default icon for the url will be used + * @p group the icon-group for using icon-effects + * @since 3.2 + */ + virtual KURLBarItem * insertDynamicItem( const KURL& url, + const QString& description, + const QString& icon = QString::null, + KIcon::Group group = KIcon::Panel ); + /** + * The items can be arranged either vertically in one column or + * horizontally in one row. + * @see orientation + */ + virtual void setOrientation( Qt::Orientation orient ); + /** + * @returns the current orientation mode. + * @see setOrientation + */ + Orientation orientation() const; + + /** + * Allows to set a custom KURLBarListBox. + * Note: The previous listbox will be deleted. Items of the previous + * listbox will not be moved to the new box. + * @see listBox + */ + virtual void setListBox( KURLBarListBox * ); + /** + * @returns the KURLBarListBox that is used. + * @see setListBox + */ + KURLBarListBox *listBox() const { return m_listBox; } + + /** + * Sets the default iconsize to be used for items inserted with + * insertItem. By default KIcon::SizeMedium. + * @see iconsize + */ + virtual void setIconSize( int size ); + /** + * @returns the default iconsize used for items inserted with + * insertItem. By default KIcon::SizeMedium + * @see setIconSize + */ + int iconSize() const { return m_iconSize; } + + /** + * Clears the view, removes all items. + */ + virtual void clear(); + + /** + * @returns a proper sizehint, depending on the orientation and the number + * of items available. + */ + virtual QSize sizeHint() const; + + /** + * @returns a proper minimum size (reimplemented) + */ + virtual QSize minimumSizeHint() const; + + /** + * Call this method to read a saved configuration from @p config, + * inside the group @p itemGroup. All items in there will be restored. + * The reading of every item is delegated to the readItem() method. + */ + virtual void readConfig( KConfig *config, const QString& itemGroup ); + /** + * Call this method to save the current configuration into @p config, + * inside the group @p iconGroup. The writeItem() method is used + * to save each item. + */ + virtual void writeConfig( KConfig *config, const QString& itemGroup ); + + /** + * Called from readConfig() to read the i'th from @p config. + * After reading a KURLBarItem is created and initialized with the read + * values (as well as the given @p applicationLocal). + */ + virtual void readItem( int i, KConfig *config, bool applicationLocal ); + /** + * Called from writeConfig() to save the KURLBarItem @p item as the + * i'th entry in the config-object. + * @p global tell whether it should be saved in the global configuration + * or not (using KConfig::writeEntry( key, value, true, global ) ). + */ + virtual void writeItem( KURLBarItem *item, int i, KConfig *, bool global ); + + /** + * @returns the current KURLBarItem, or 0L if none. + * @see setCurrentItem + * @see currentURL + */ + KURLBarItem * currentItem() const; + /** + * @returns the url of the current item or an invalid url, if there is + * no current item. + * @see currentItem + * @see setCurrentItem + */ + KURL currentURL() const; + + /** + * @returns true when the urlbar was modified by the user (e.g. by + * editing/adding/removing one or more entries). Will be reset to false + * after calling writeConfig(). + */ + bool isModified() const { return m_isModified; } + + /** + * @returns true when the urlbar may not be modified by the user + */ + bool isImmutable() const { return m_isImmutable; } + + /** + * @returns true if the bar is in vertical mode. + */ + bool isVertical() const { return orientation() == Vertical; } + +public slots: + /** + * Makes the item with the url @p url the current item. Does nothing + * if no item with that url is available. + * @see currentItem + * @see currentURL + */ + virtual void setCurrentItem( const KURL& url ); + +signals: + /** + * This signal is emitted when the user activated an item, e.g., by + * clicking on it. + */ + void activated( const KURL& url ); + +protected: + /** + * Pops up a KURLBarItemDialog to let the user add a new item. + * Uses editItem() to do the job. + * @returns false if the user aborted the dialog and no item is added. + */ + virtual bool addNewItem(); + /** + * Pops up a KURLBarItemDialog to let the user edit the properties + * of @p item. Invoked e.g. by addNewItem(), when the user drops + * a url onto the bar or from the contextmenu. + * @returns false if the user aborted the dialog and @p item is not + * changed. + */ + virtual bool editItem( KURLBarItem *item ); + + virtual void resizeEvent( QResizeEvent * ); + + virtual void paletteChange( const QPalette & ); + + /** + * The currently active item. + */ + KURLBarItem * m_activeItem; + /** + * Whether we support global entries or just local ones. + */ + bool m_useGlobal :1; + + /** + * Whether the urlbar was modified by the user (e.g. by + * editing/adding/removing an item). + */ + bool m_isModified :1; + + /** + * Whether the urlbar may be modified by the user. + * If immutable is true, the urlbar can not be modified. + */ + bool m_isImmutable :1; + +protected slots: + /** + * Reimplemented to show a contextmenu, allowing the user to add, edit + * or remove items, or change the iconsize. + */ + virtual void slotContextMenuRequested( QListBoxItem *, const QPoint& pos ); + /** + * Called when an item has been selected. Emits the activated() + * signal. + */ + virtual void slotSelected( QListBoxItem * ); + + /** + * Called when a url was dropped onto the bar to show a + * KURLBarItemDialog. + */ + virtual void slotDropped( QDropEvent * ); + +private slots: + void slotSelected( int button, QListBoxItem * ); + +private: + KURLBarListBox *m_listBox; + int m_iconSize; + + +protected: + virtual void virtual_hook( int id, void* data ); +private: + class KURLBarPrivate; + KURLBarPrivate *d; +}; + + +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// + + +class QDragObject; +class KURLBarToolTip; + +/** + * This is the listbox used in KURLBar. It is a subclass of KListBox to support + * drag & drop and to set up the row / column mode. + * + * The widget has just one row or one column, depending on orientation(). + * + * @author Carsten Pfeiffer <[email protected]> + */ +class KIO_EXPORT KURLBarListBox : public KListBox +{ + Q_OBJECT + +public: + /** + * Constructs a KURLBarListBox. + */ + KURLBarListBox( QWidget *parent = 0, const char *name = 0 ); + /** + * Destroys the box. + */ + ~KURLBarListBox(); + + /** + * Sets the orientation of the widget. Horizontal means, all items are + * arranged in one row. Vertical means, all items are arranged in one + * column. + * @see orientation + */ + virtual void setOrientation( Qt::Orientation orient ); + /** + * @returns the current orientation. + * @see setOrientation + */ + Qt::Orientation orientation() const { return m_orientation; } + + bool isVertical() const { return m_orientation == Qt::Vertical; } + +signals: + /** + * Emitted when a drop-event happened. + */ + void dropped( QDropEvent *e ); + +protected: + /** + * @returns a suitable QDragObject when an item is dragged. + */ + virtual QDragObject * dragObject(); + + virtual void contentsDragEnterEvent( QDragEnterEvent * ); + virtual void contentsDropEvent( QDropEvent * ); + virtual void contextMenuEvent( QContextMenuEvent * ); + virtual void paintEvent( QPaintEvent* ); + +private: + Qt::Orientation m_orientation; + KURLBarToolTip *m_toolTip; + +protected: + virtual void virtual_hook( int id, void* data ); +private: + class KURLBarListBoxPrivate; + KURLBarListBoxPrivate *d; +}; + + +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// + + +class QCheckBox; +class KIconButton; +class KLineEdit; +class KURLRequester; + +/** + * A dialog that allows editing entries of a KURLBar ( KURLBarItem). + * The dialog offers to configure a given url, description and icon. + * See the class-method getInformation() for easy usage. + * + * @author Carsten Pfeiffer <[email protected]> + */ +class KIO_EXPORT KURLBarItemDialog : public KDialogBase +{ + Q_OBJECT + +public: + /** + * A convenience method to show the dialog and retrieve all the + * properties via the given parameters. The parameters are used to + * initialize the dialog and then return the user-configured values. + * + * See the KURLBarItem constructor for the parameter description. + */ + static bool getInformation( bool allowGlobal, KURL& url, + QString& description, QString& icon, + bool& appLocal, int iconSize, + QWidget *parent = 0 ); + + /** + * Constructs a KURLBarItemDialog. + * + * @p allowGlobal if you set this to true, the dialog will have a checkbox + * for the user to decide if he wants the entry to be + * available globally or just for the current application. + * @p url the url of the item + * @p description a short, translated description of the item + * @p icon an icon for the item + * @p appLocal tells whether the item should be local for this application + * or be available globally + * @p iconSize determines the size of the icon that is shown/selectable + * @p parent the parent-widget for the dialog + * + * If you leave the icon empty, the default icon for the given url will be + * used (KMimeType::pixmapForURL()). + */ + KURLBarItemDialog( bool allowGlobal, const KURL& url, + const QString& description, QString icon, + bool appLocal = true, + int iconSize = KIcon::SizeMedium, + QWidget *parent = 0, const char *name = 0 ); + /** + * Destroys the dialog. + */ + ~KURLBarItemDialog(); + + /** + * @returns the configured url + */ + KURL url() const; + + /** + * @returns the configured description + */ + QString description() const; + + /** + * @returns the configured icon + */ + QString icon() const; + + /** + * @returns whether the item should be local to the application or global. + * If allowGlobal was set to false in the constructor, this will always + * return true. + */ + bool applicationLocal() const; + +protected: + /** + * The KURLRequester used for editing the url + */ + KURLRequester * m_urlEdit; + /** + * The KLineEdit used for editing the description + */ + KLineEdit * m_edit; + /** + * The KIconButton to configure the icon + */ + KIconButton * m_iconButton; + /** + * The QCheckBox to modify the local/global setting + */ + QCheckBox * m_appLocal; + +public slots: + void urlChanged(const QString & ); + +private: + class KURLBarItemDialogPrivate; + KURLBarItemDialogPrivate *d; +}; + + +#endif // KURLBAR_H diff --git a/kio/kfile/kurlcombobox.cpp b/kio/kfile/kurlcombobox.cpp new file mode 100644 index 000000000..128e8a22c --- /dev/null +++ b/kio/kfile/kurlcombobox.cpp @@ -0,0 +1,363 @@ +/* This file is part of the KDE libraries + Copyright (C) 2000,2001 Carsten Pfeiffer <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qdir.h> +#include <qlistbox.h> + +#include <kdebug.h> +#include <kglobal.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kmimetype.h> + +#include <kurlcombobox.h> + +class KURLComboBox::KURLComboBoxPrivate +{ +public: + KURLComboBoxPrivate() { + dirpix = SmallIcon(QString::fromLatin1("folder")); + } + + QPixmap dirpix; +}; + + +KURLComboBox::KURLComboBox( Mode mode, QWidget *parent, const char *name ) + : KComboBox( parent, name ) +{ + init( mode ); +} + + +KURLComboBox::KURLComboBox( Mode mode, bool rw, QWidget *parent, + const char *name ) + : KComboBox( rw, parent, name ) +{ + init( mode ); +} + + +KURLComboBox::~KURLComboBox() +{ + delete d; +} + + +void KURLComboBox::init( Mode mode ) +{ + d = new KURLComboBoxPrivate(); + + myMode = mode; + urlAdded = false; + myMaximum = 10; // default + itemList.setAutoDelete( true ); + defaultList.setAutoDelete( true ); + setInsertionPolicy( NoInsertion ); + setTrapReturnKey( true ); + setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed )); + + opendirPix = SmallIcon(QString::fromLatin1("folder_open")); + + connect( this, SIGNAL( activated( int )), SLOT( slotActivated( int ))); +} + + +QStringList KURLComboBox::urls() const +{ + kdDebug(250) << "::urls()" << endl; + //static const QString &fileProt = KGlobal::staticQString("file:"); + QStringList list; + QString url; + for ( int i = defaultList.count(); i < count(); i++ ) { + url = text( i ); + if ( !url.isEmpty() ) { + //if ( url.at(0) == '/' ) + // list.append( url.prepend( fileProt ) ); + //else + list.append( url ); + } + } + + return list; +} + + +void KURLComboBox::addDefaultURL( const KURL& url, const QString& text ) +{ + addDefaultURL( url, getPixmap( url ), text ); +} + + +void KURLComboBox::addDefaultURL( const KURL& url, const QPixmap& pix, + const QString& text ) +{ + KURLComboItem *item = new KURLComboItem; + item->url = url; + item->pixmap = pix; + if ( text.isEmpty() ) + if ( url.isLocalFile() ) + item->text = url.path( myMode ); + else + item->text = url.prettyURL( myMode ); + else + item->text = text; + + defaultList.append( item ); +} + + +void KURLComboBox::setDefaults() +{ + clear(); + itemMapper.clear(); + + KURLComboItem *item; + for ( unsigned int id = 0; id < defaultList.count(); id++ ) { + item = defaultList.at( id ); + insertURLItem( item ); + } +} + +void KURLComboBox::setURLs( QStringList urls ) +{ + setURLs( urls, RemoveBottom ); +} + +void KURLComboBox::setURLs( QStringList urls, OverLoadResolving remove ) +{ + setDefaults(); + itemList.clear(); + + if ( urls.isEmpty() ) + return; + + QStringList::Iterator it = urls.begin(); + + // kill duplicates + QString text; + while ( it != urls.end() ) { + while ( urls.contains( *it ) > 1 ) { + it = urls.remove( it ); + continue; + } + ++it; + } + + // limit to myMaximum items + /* Note: overload is an (old) C++ keyword, some compilers (KCC) choke + on that, so call it Overload (capital 'O'). (matz) */ + int Overload = urls.count() - myMaximum + defaultList.count(); + while ( Overload > 0 ) { + urls.remove((remove == RemoveBottom) ? urls.fromLast() : urls.begin()); + Overload--; + } + + it = urls.begin(); + + KURLComboItem *item = 0L; + KURL u; + + while ( it != urls.end() ) { + if ( (*it).isEmpty() ) { + ++it; + continue; + } + u = KURL::fromPathOrURL( *it ); + + // Don't restore if file doesn't exist anymore + if (u.isLocalFile() && !QFile::exists(u.path())) { + ++it; + continue; + } + + item = new KURLComboItem; + item->url = u; + item->pixmap = getPixmap( u ); + + if ( u.isLocalFile() ) + item->text = u.path( myMode ); // don't show file:/ + else + item->text = *it; + + insertURLItem( item ); + itemList.append( item ); + ++it; + } +} + + +void KURLComboBox::setURL( const KURL& url ) +{ + if ( url.isEmpty() ) + return; + + blockSignals( true ); + + // check for duplicates + QMap<int,const KURLComboItem*>::ConstIterator mit = itemMapper.begin(); + QString urlToInsert = url.url(-1); + while ( mit != itemMapper.end() ) { + if ( urlToInsert == mit.data()->url.url(-1) ) { + setCurrentItem( mit.key() ); + + if ( myMode == Directories ) + updateItem( mit.data(), mit.key(), opendirPix ); + + blockSignals( false ); + return; + } + ++mit; + } + + // not in the combo yet -> create a new item and insert it + + // first remove the old item + if ( urlAdded ) { + itemList.removeLast(); + urlAdded = false; + } + + setDefaults(); + + QPtrListIterator<KURLComboItem> it( itemList ); + for( ; it.current(); ++it ) + insertURLItem( it.current() ); + + KURLComboItem *item = new KURLComboItem; + item->url = url; + item->pixmap = getPixmap( url ); + if ( url.isLocalFile() ) + item->text = url.path( myMode ); + else + item->text = url.prettyURL( myMode ); + kdDebug(250) << "setURL: text=" << item->text << endl; + + int id = count(); + QString text = /*isEditable() ? item->url.prettyURL( myMode ) : */ item->text; + + if ( myMode == Directories ) + KComboBox::insertItem( opendirPix, text, id ); + else + KComboBox::insertItem( item->pixmap, text, id ); + itemMapper.insert( id, item ); + itemList.append( item ); + + setCurrentItem( id ); + urlAdded = true; + blockSignals( false ); +} + + +void KURLComboBox::slotActivated( int index ) +{ + const KURLComboItem *item = itemMapper[ index ]; + + if ( item ) { + setURL( item->url ); + emit urlActivated( item->url ); + } +} + + +void KURLComboBox::insertURLItem( const KURLComboItem *item ) +{ +// kdDebug(250) << "insertURLItem " << item->text << endl; + int id = count(); + KComboBox::insertItem( item->pixmap, item->text, id ); + itemMapper.insert( id, item ); +} + + +void KURLComboBox::setMaxItems( int max ) +{ + myMaximum = max; + + if ( count() > myMaximum ) { + int oldCurrent = currentItem(); + + setDefaults(); + + QPtrListIterator<KURLComboItem> it( itemList ); + int Overload = itemList.count() - myMaximum + defaultList.count(); + for ( int i = 0; i <= Overload; i++ ) + ++it; + + for( ; it.current(); ++it ) + insertURLItem( it.current() ); + + if ( count() > 0 ) { // restore the previous currentItem + if ( oldCurrent >= count() ) + oldCurrent = count() -1; + setCurrentItem( oldCurrent ); + } + } +} + + +void KURLComboBox::removeURL( const KURL& url, bool checkDefaultURLs ) +{ + QMap<int,const KURLComboItem*>::ConstIterator mit = itemMapper.begin(); + while ( mit != itemMapper.end() ) { + if ( url.url(-1) == mit.data()->url.url(-1) ) { + if ( !itemList.remove( mit.data() ) && checkDefaultURLs ) + defaultList.remove( mit.data() ); + } + ++mit; + } + + blockSignals( true ); + setDefaults(); + QPtrListIterator<KURLComboItem> it( itemList ); + while ( it.current() ) { + insertURLItem( *it ); + ++it; + } + blockSignals( false ); +} + + +QPixmap KURLComboBox::getPixmap( const KURL& url ) const +{ + if ( myMode == Directories ) + return d->dirpix; + else + return KMimeType::pixmapForURL( url, 0, KIcon::Small ); +} + + +// updates "item" with pixmap "pixmap" and sets the URL instead of text +// works around a Qt bug. +void KURLComboBox::updateItem( const KURLComboItem *item, + int index, const QPixmap& pixmap ) +{ + // QComboBox::changeItem() doesn't honor the pixmap when + // using an editable combobox, so we just remove and insert + if ( editable() ) { + removeItem( index ); + insertItem( pixmap, + item->url.isLocalFile() ? item->url.path( myMode ) : + item->url.prettyURL( myMode ), + index ); + } + else + changeItem( pixmap, item->text, index ); +} + + +#include "kurlcombobox.moc" diff --git a/kio/kfile/kurlcombobox.h b/kio/kfile/kurlcombobox.h new file mode 100644 index 000000000..7485bfed9 --- /dev/null +++ b/kio/kfile/kurlcombobox.h @@ -0,0 +1,229 @@ +/* This file is part of the KDE libraries + Copyright (C) 2000 Carsten Pfeiffer <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KURLCOMBOBOX_H +#define KURLCOMBOBOX_H + +#include <qevent.h> +#include <qptrlist.h> +#include <qmap.h> +#include <qpixmap.h> +#include <qstringlist.h> + +#include <kcombobox.h> +#include <kurl.h> + +/** + * This combobox shows a number of recent URLs/directories, as well as some + * default directories. + * It will manage the default dirs root-directory, home-directory and + * Desktop-directory, as well as a number of URLs set via setURLs() + * and one additional entry to be set via setURL(). + * + * @short A combo box showing a number of recent URLs/directories + * @author Carsten Pfeiffer <[email protected]> + */ +class KIO_EXPORT KURLComboBox : public KComboBox +{ + Q_OBJECT + Q_PROPERTY(QStringList urls READ urls WRITE setURLs DESIGNABLE true) + Q_PROPERTY(int maxItems READ maxItems WRITE setMaxItems DESIGNABLE true) + +public: + /** + * This enum describes which kind of items is shown in the combo box. + */ + enum Mode { Files = -1, Directories = 1, Both = 0 }; + /** + * This Enumeration is used in setURL() to determine which items + * will be removed when the given list is larger than maxItems(). + * + * @li RemoveTop means that items will be removed from top + * @li RemoveBottom means, that items will be removed from the bottom + */ + enum OverLoadResolving { RemoveTop, RemoveBottom }; + + /** + * Constructs a KURLComboBox. + * @param mode is either Files, Directories or Both and controls the + * following behavior: + * @li Files all inserted URLs will be treated as files, therefore the + * url shown in the combo will never show a trailing / + * the icon will be the one associated with the file's mimetype. + * @li Directories all inserted URLs will be treated as directories, will + * have a trailing slash in the combobox. The current + * directory will show the "open folder" icon, other + * directories the "folder" icon. + * @li Both Don't mess with anything, just show the url as given. + * @param parent The parent object of this widget. + * @param name The name of this widget. + */ + KURLComboBox( Mode mode, QWidget *parent=0, const char *name=0 ); + KURLComboBox( Mode mode, bool rw, QWidget *parent=0, const char *name=0 ); + /** + * Destructs the combo box. + */ + ~KURLComboBox(); + + /** + * Sets the current url. This combo handles exactly one url additionally + * to the default items and those set via setURLs(). So you can call + * setURL() as often as you want, it will always replace the previous one + * set via setURL(). + * If @p url is already in the combo, the last item will stay there + * and the existing item becomes the current item. + * The current item will always have the open-directory-pixmap as icon. + * + * Note that you won't receive any signals, e.g. textChanged(), + * returnPressed() or activated() upon calling this method. + */ + void setURL( const KURL& url ); + + /** + * Inserts @p urls into the combobox below the "default urls" (see + * addDefaultURL). + * + * If the list of urls contains more items than maxItems, the first items + * will be stripped. + */ + void setURLs( QStringList urls ); + + /** + * Inserts @p urls into the combobox below the "default urls" (see + * addDefaultURL). + * + * If the list of urls contains more items than maxItems, the @p remove + * parameter determines whether the first or last items will be stripped. + */ + void setURLs( QStringList urls, OverLoadResolving remove ); + + /** + * @returns a list of all urls currently handled. The list contains at most + * maxItems() items. + * Use this to save the list of urls in a config-file and reinsert them + * via setURLs() next time. + * Note that all default urls set via addDefaultURL() are not + * returned, they will automatically be set via setURLs() or setURL(). + * You will always get fully qualified urls, i.e. with protocol like + * file:/ + */ + QStringList urls() const; + + /** + * Sets how many items should be handled and displayed by the combobox. + * @see maxItems + */ + void setMaxItems( int ); + + /** + * @returns the maximum of items the combobox handles. + * @see setMaxItems + */ + int maxItems() const { return myMaximum; } + + /** + * Adds a url that will always be shown in the combobox, it can't be + * "rotated away". Default urls won't be returned in urls() and don't + * have to be set via setURLs(). + * If you want to specify a special pixmap, use the overloaded method with + * the pixmap parameter. + * Default URLs will be inserted into the combobox by setDefaults() + */ + void addDefaultURL( const KURL& url, const QString& text = QString::null ); + + /** + * Adds a url that will always be shown in the combobox, it can't be + * "rotated away". Default urls won't be returned in urls() and don't + * have to be set via setURLs(). + * If you don't need to specify a pixmap, use the overloaded method without + * the pixmap parameter. + * Default URLs will be inserted into the combobox by setDefaults() + */ + void addDefaultURL( const KURL& url, const QPixmap& pix, + const QString& text = QString::null ); + + /** + * Clears all items and inserts the default urls into the combo. Will be + * called implicitly upon the first call to setURLs() or setURL() + * @see addDefaultURL + */ + void setDefaults(); + + /** + * Removes any occurrence of @p url. If @p checkDefaultURLs is false + * default-urls won't be removed. + */ + void removeURL( const KURL& url, bool checkDefaultURLs = true ); + +signals: + /** + * Emitted when an item was clicked at. + * @param url is the url of the now current item. If it is a local url, + * it won't have a protocol (file:/), otherwise it will. + */ + void urlActivated( const KURL& url ); + + +protected slots: + void slotActivated( int ); + + +protected: + struct _KURLComboItem { + QString text; + KURL url; + QPixmap pixmap; + }; + typedef _KURLComboItem KURLComboItem; + QPtrList<KURLComboItem> itemList; + QPtrList<KURLComboItem> defaultList; + QMap<int,const KURLComboItem*> itemMapper; + + void init( Mode mode ); + void insertURLItem( const KURLComboItem * ); + + /** + * Uses KMimeType::pixmapForURL() to return a proper pixmap for @p url. + * In directory mode, a folder icon is always returned. + */ + QPixmap getPixmap( const KURL& url ) const; + + /** + * Updates @p item with @p pixmap and sets the url instead of the text + * of the KURLComboItem. + * Also works around a Qt bug. + */ + void updateItem( const KURLComboItem *item, int index, const QPixmap& pix); + + QPixmap opendirPix; + int firstItemIndex; + + +private: + bool urlAdded; + int myMaximum; + Mode myMode; // can be used as parameter to KUR::path( int ) or url( int ) + // to specify if we want a trailing slash or not + +private: + class KURLComboBoxPrivate; + KURLComboBoxPrivate *d; +}; + + +#endif // KURLCOMBOBOX_H diff --git a/kio/kfile/kurlrequester.cpp b/kio/kfile/kurlrequester.cpp new file mode 100644 index 000000000..0f4619fd8 --- /dev/null +++ b/kio/kfile/kurlrequester.cpp @@ -0,0 +1,430 @@ +/* This file is part of the KDE libraries + Copyright (C) 1999,2000,2001 Carsten Pfeiffer <[email protected]> + + 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 <sys/stat.h> +#include <unistd.h> + +#include <qstring.h> +#include <qtooltip.h> +#include <qapplication.h> + +#include <kaccel.h> +#include <kcombobox.h> +#include <kdebug.h> +#include <kdialog.h> +#include <kdirselectdialog.h> +#include <kfiledialog.h> +#include <kglobal.h> +#include <kiconloader.h> +#include <klineedit.h> +#include <klocale.h> +#include <kurlcompletion.h> +#include <kurldrag.h> +#include <kprotocolinfo.h> + +#include "kurlrequester.h" + + +class KURLDragPushButton : public KPushButton +{ +public: + KURLDragPushButton( QWidget *parent, const char *name=0 ) + : KPushButton( parent, name ) { + setDragEnabled( true ); + } + ~KURLDragPushButton() {} + + void setURL( const KURL& url ) { + m_urls.clear(); + m_urls.append( url ); + } + + /* not needed so far + void setURLs( const KURL::List& urls ) { + m_urls = urls; + } + const KURL::List& urls() const { return m_urls; } + */ + +protected: + virtual QDragObject *dragObject() { + if ( m_urls.isEmpty() ) + return 0L; + + QDragObject *drag = new KURLDrag( m_urls, this, "url drag" ); + return drag; + } + +private: + KURL::List m_urls; + +}; + + +/* +************************************************************************* +*/ + +class KURLRequester::KURLRequesterPrivate +{ +public: + KURLRequesterPrivate() { + edit = 0L; + combo = 0L; + fileDialogMode = KFile::File | KFile::ExistingOnly | KFile::LocalOnly; + } + + void setText( const QString& text ) { + if ( combo ) + { + if (combo->editable()) + { + combo->setEditText( text ); + } + else + { + combo->insertItem( text ); + combo->setCurrentItem( combo->count()-1 ); + } + } + else + { + edit->setText( text ); + } + } + + void connectSignals( QObject *receiver ) { + QObject *sender; + if ( combo ) + sender = combo; + else + sender = edit; + + connect( sender, SIGNAL( textChanged( const QString& )), + receiver, SIGNAL( textChanged( const QString& ))); + connect( sender, SIGNAL( returnPressed() ), + receiver, SIGNAL( returnPressed() )); + connect( sender, SIGNAL( returnPressed( const QString& ) ), + receiver, SIGNAL( returnPressed( const QString& ) )); + } + + void setCompletionObject( KCompletion *comp ) { + if ( combo ) + combo->setCompletionObject( comp ); + else + edit->setCompletionObject( comp ); + } + + /** + * replaces ~user or $FOO, if necessary + */ + QString url() { + QString txt = combo ? combo->currentText() : edit->text(); + KURLCompletion *comp; + if ( combo ) + comp = dynamic_cast<KURLCompletion*>(combo->completionObject()); + else + comp = dynamic_cast<KURLCompletion*>(edit->completionObject()); + + if ( comp ) + return comp->replacedPath( txt ); + else + return txt; + } + + KLineEdit *edit; + KComboBox *combo; + int fileDialogMode; + QString fileDialogFilter; +}; + + + +KURLRequester::KURLRequester( QWidget *editWidget, QWidget *parent, + const char *name ) + : QHBox( parent, name ) +{ + d = new KURLRequesterPrivate; + + // must have this as parent + editWidget->reparent( this, 0, QPoint(0,0) ); + d->edit = dynamic_cast<KLineEdit*>( editWidget ); + d->combo = dynamic_cast<KComboBox*>( editWidget ); + + init(); +} + + +KURLRequester::KURLRequester( QWidget *parent, const char *name ) + : QHBox( parent, name ) +{ + d = new KURLRequesterPrivate; + init(); +} + + +KURLRequester::KURLRequester( const QString& url, QWidget *parent, + const char *name ) + : QHBox( parent, name ) +{ + d = new KURLRequesterPrivate; + init(); + setKURL( KURL::fromPathOrURL( url ) ); +} + + +KURLRequester::~KURLRequester() +{ + delete myCompletion; + delete myFileDialog; + delete d; +} + + +void KURLRequester::init() +{ + myFileDialog = 0L; + myShowLocalProt = false; + + if ( !d->combo && !d->edit ) + d->edit = new KLineEdit( this, "line edit" ); + + myButton = new KURLDragPushButton( this, "kfile button"); + QIconSet iconSet = SmallIconSet(QString::fromLatin1("fileopen")); + QPixmap pixMap = iconSet.pixmap( QIconSet::Small, QIconSet::Normal ); + myButton->setIconSet( iconSet ); + myButton->setFixedSize( pixMap.width()+8, pixMap.height()+8 ); + QToolTip::add(myButton, i18n("Open file dialog")); + + connect( myButton, SIGNAL( pressed() ), SLOT( slotUpdateURL() )); + + setSpacing( KDialog::spacingHint() ); + + QWidget *widget = d->combo ? (QWidget*) d->combo : (QWidget*) d->edit; + widget->installEventFilter( this ); + setFocusProxy( widget ); + + d->connectSignals( this ); + connect( myButton, SIGNAL( clicked() ), this, SLOT( slotOpenDialog() )); + + myCompletion = new KURLCompletion(); + d->setCompletionObject( myCompletion ); + + KAccel *accel = new KAccel( this ); + accel->insert( KStdAccel::Open, this, SLOT( slotOpenDialog() )); + accel->readSettings(); +} + + +void KURLRequester::setURL( const QString& url ) +{ + if ( myShowLocalProt ) + { + d->setText( url ); + } + else + { + // ### This code is broken (e.g. for paths with '#') + if ( url.startsWith("file://") ) + d->setText( url.mid( 7 ) ); + else if ( url.startsWith("file:") ) + d->setText( url.mid( 5 ) ); + else + d->setText( url ); + } +} + +void KURLRequester::setKURL( const KURL& url ) +{ + if ( myShowLocalProt ) + d->setText( url.url() ); + else + d->setText( url.pathOrURL() ); +} + +void KURLRequester::setCaption( const QString& caption ) +{ + QWidget::setCaption( caption ); + if (myFileDialog) + myFileDialog->setCaption( caption ); +} + +QString KURLRequester::url() const +{ + return d->url(); +} + +void KURLRequester::slotOpenDialog() +{ + KURL newurl; + if ( (d->fileDialogMode & KFile::Directory) && !(d->fileDialogMode & KFile::File) || + /* catch possible fileDialog()->setMode( KFile::Directory ) changes */ + (myFileDialog && ( (myFileDialog->mode() & KFile::Directory) && + (myFileDialog->mode() & (KFile::File | KFile::Files)) == 0 ) ) ) + { + newurl = KDirSelectDialog::selectDirectory(url(), d->fileDialogMode & KFile::LocalOnly); + if ( !newurl.isValid() ) + { + return; + } + } + else + { + emit openFileDialog( this ); + + KFileDialog *dlg = fileDialog(); + if ( !d->url().isEmpty() ) { + KURL u( url() ); + // If we won't be able to list it (e.g. http), then don't try :) + if ( KProtocolInfo::supportsListing( u ) ) + dlg->setSelection( u.url() ); + } + + if ( dlg->exec() != QDialog::Accepted ) + { + return; + } + + newurl = dlg->selectedURL(); + } + + setKURL( newurl ); + emit urlSelected( d->url() ); +} + +void KURLRequester::setMode(uint mode) +{ + Q_ASSERT( (mode & KFile::Files) == 0 ); + d->fileDialogMode = mode; + if ( (mode & KFile::Directory) && !(mode & KFile::File) ) + myCompletion->setMode( KURLCompletion::DirCompletion ); + + if (myFileDialog) + myFileDialog->setMode( d->fileDialogMode ); +} + +unsigned int KURLRequester::mode() const +{ + return d->fileDialogMode; +} + +void KURLRequester::setFilter(const QString &filter) +{ + d->fileDialogFilter = filter; + if (myFileDialog) + myFileDialog->setFilter( d->fileDialogFilter ); +} + +QString KURLRequester::filter( ) const +{ + return d->fileDialogFilter; +} + + +KFileDialog * KURLRequester::fileDialog() const +{ + if ( !myFileDialog ) { + QWidget *p = parentWidget(); + myFileDialog = new KFileDialog( QString::null, d->fileDialogFilter, p, + "file dialog", true ); + + myFileDialog->setMode( d->fileDialogMode ); + myFileDialog->setCaption( caption() ); + } + + return myFileDialog; +} + + +void KURLRequester::setShowLocalProtocol( bool b ) +{ + if ( myShowLocalProt == b ) + return; + + myShowLocalProt = b; + setKURL( url() ); +} + +void KURLRequester::clear() +{ + d->setText( QString::null ); +} + +KLineEdit * KURLRequester::lineEdit() const +{ + return d->edit; +} + +KComboBox * KURLRequester::comboBox() const +{ + return d->combo; +} + +void KURLRequester::slotUpdateURL() +{ + // bin compat, myButton is declared as QPushButton + KURL u; + u = KURL( KURL( QDir::currentDirPath() + '/' ), url() ); + (static_cast<KURLDragPushButton *>( myButton ))->setURL( u ); +} + +bool KURLRequester::eventFilter( QObject *obj, QEvent *ev ) +{ + if ( ( d->edit == obj ) || ( d->combo == obj ) ) + { + if (( ev->type() == QEvent::FocusIn ) || ( ev->type() == QEvent::FocusOut )) + // Forward focusin/focusout events to the urlrequester; needed by file form element in khtml + QApplication::sendEvent( this, ev ); + } + return QWidget::eventFilter( obj, ev ); +} + +KPushButton * KURLRequester::button() const +{ + return myButton; +} + +KEditListBox::CustomEditor KURLRequester::customEditor() +{ + setSizePolicy(QSizePolicy( QSizePolicy::Preferred, + QSizePolicy::Fixed)); + + KLineEdit *edit = d->edit; + if ( !edit && d->combo ) + edit = dynamic_cast<KLineEdit*>( d->combo->lineEdit() ); + +#ifndef NDEBUG + if ( !edit ) + kdWarning() << "KURLRequester's lineedit is not a KLineEdit!??\n"; +#endif + + KEditListBox::CustomEditor editor( this, edit ); + return editor; +} + +void KURLRequester::virtual_hook( int, void* ) +{ /*BASE::virtual_hook( id, data );*/ } + +KURLComboRequester::KURLComboRequester( QWidget *parent, + const char *name ) + : KURLRequester( new KComboBox(false), parent, name) +{ +} + +#include "kurlrequester.moc" diff --git a/kio/kfile/kurlrequester.h b/kio/kfile/kurlrequester.h new file mode 100644 index 000000000..1a7016b24 --- /dev/null +++ b/kio/kfile/kurlrequester.h @@ -0,0 +1,301 @@ +/* This file is part of the KDE libraries + Copyright (C) 1999,2000,2001 Carsten Pfeiffer <[email protected]> + + 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 KURLREQUESTER_H +#define KURLREQUESTER_H + +#include <qhbox.h> + +#include <keditlistbox.h> +#include <kfile.h> +#include <kpushbutton.h> +#include <kurl.h> + +class KComboBox; +class KFileDialog; +class KLineEdit; +class KURLCompletion; +class KURLDragPushButton; + +class QString; +class QTimer; + +/** + * This class is a widget showing a lineedit and a button, which invokes a + * filedialog. File name completion is available in the lineedit. + * + * The defaults for the filedialog are to ask for one existing local file, i.e. + * KFileDialog::setMode( KFile::File | KFile::ExistingOnly | KFile::LocalOnly ) + * The default filter is "*", i.e. show all files, and the start directory is + * the current working directory, or the last directory where a file has been + * selected. + * + * You can change this behavior by using setMode() or setFilter(). + * + * \image html kurlrequester.png "KDE URL Requester" + * + * @short A widget to request a filename/url from the user + * @author Carsten Pfeiffer <[email protected]> + */ +class KIO_EXPORT KURLRequester : public QHBox +{ + Q_OBJECT + Q_PROPERTY( QString url READ url WRITE setURL ) + Q_PROPERTY( bool showLocalProtocol READ showLocalProtocol WRITE setShowLocalProtocol ) + Q_PROPERTY( QString filter READ filter WRITE setFilter ) + Q_PROPERTY( uint mode READ mode WRITE setMode ) + +public: + /** + * Constructs a KURLRequester widget. + */ + KURLRequester( QWidget *parent=0, const char *name=0 ); + + /** + * Constructs a KURLRequester widget with the initial URL @p url. + * // TODO KDE4: Use KURL instead + */ + KURLRequester( const QString& url, QWidget *parent=0, const char *name=0 ); + + /** + * Special constructor, which creates a KURLRequester widget with a custom + * edit-widget. The edit-widget can be either a KComboBox or a KLineEdit + * (or inherited thereof). Note: for geometry management reasons, the + * edit-widget is reparented to have the KURLRequester as parent. + */ + KURLRequester( QWidget *editWidget, QWidget *parent, const char *name=0 ); + /** + * Destructs the KURLRequester. + */ + ~KURLRequester(); + + /** + * @returns the current url in the lineedit. May be malformed, if the user + * entered something weird. ~user or environment variables are substituted + * for local files. + * // TODO KDE4: Use KURL so that the result is properly defined + */ + QString url() const; + + /** + * Enables/disables showing file:/ in the lineedit, when a local file has + * been selected in the filedialog or was set via setURL(). + * Default is false, not showing file:/ + * @see showLocalProtocol + */ + void setShowLocalProtocol( bool b ); + + /** + * Sets the mode of the file dialog. + * Note: you can only select one file with the filedialog, + * so KFile::Files doesn't make much sense. + * @see KFileDialog::setMode() + */ + void setMode( uint m ); + + /** + * Returns the current mode + * @see KFileDialog::mode() + * @since 3.3 + */ + uint mode() const; + + + /** + * Sets the filter for the file dialog. + * @see KFileDialog::setFilter() + */ + void setFilter( const QString& filter ); + + /** + * Returns the current filter for the file dialog. + * @see KFileDialog::filter() + * @since 3.3 + */ + QString filter() const; + + /** + * @returns whether local files will be prefixed with file:/ in the + * lineedit + * @see setShowLocalProtocol + */ + bool showLocalProtocol() const { return myShowLocalProt; } + // ## KDE4: there's no reason to keep this, it should always be false + + /** + * @returns a pointer to the filedialog + * You can use this to customize the dialog, e.g. to specify a filter. + * Never returns 0L. + * + * Remove in KDE4? KURLRequester should use KDirSelectDialog for + * (mode & KFile::Directory) && !(mode & KFile::File) + */ + virtual KFileDialog * fileDialog() const; + + /** + * @returns a pointer to the lineedit, either the default one, or the + * special one, if you used the special constructor. + * + * It is provided so that you can e.g. set an own completion object + * (e.g. KShellCompletion) into it. + */ + KLineEdit * lineEdit() const; + + /** + * @returns a pointer to the combobox, in case you have set one using the + * special constructor. Returns 0L otherwise. + */ + KComboBox * comboBox() const; + + /** + * @returns a pointer to the pushbutton. It is provided so that you can + * specify an own pixmap or a text, if you really need to. + */ + KPushButton * button() const; + + /** + * @returns the KURLCompletion object used in the lineedit/combobox. + */ + KURLCompletion *completionObject() const { return myCompletion; } + + /** + * @returns an object, suitable for use with KEditListBox. It allows you + * to put this KURLRequester into a KEditListBox. + * Basically, do it like this: + * \code + * KURLRequester *req = new KURLRequester( someWidget ); + * [...] + * KEditListBox *editListBox = new KEditListBox( i18n("Some Title"), req->customEditor(), someWidget ); + * \endcode + * @since 3.1 + */ + KEditListBox::CustomEditor customEditor(); + +public slots: + /** + * Sets the url in the lineedit to @p url. Depending on the state of + * showLocalProtocol(), file:/ on local files will be shown or not. + * @since 3.1 + * // TODO KDE4: Use KURL instead + */ + void setURL( const QString& url ); + + /** + * Sets the url in the lineedit to @p url. + * @since 3.4 + * // TODO KDE4: rename to setURL + */ + void setKURL( const KURL& url ); + + /** + * Sets the caption of the file dialog. + * @since 3.1 + */ + virtual void setCaption( const QString& caption ); + + /** + * Clears the lineedit/combobox. + */ + void clear(); + +signals: + // forwards from LineEdit + /** + * Emitted when the text in the lineedit changes. + * The parameter contains the contents of the lineedit. + * @since 3.1 + */ + void textChanged( const QString& ); + + /** + * Emitted when return or enter was pressed in the lineedit. + */ + void returnPressed(); + + /** + * Emitted when return or enter was pressed in the lineedit. + * The parameter contains the contents of the lineedit. + */ + void returnPressed( const QString& ); + + /** + * Emitted before the filedialog is going to open. Connect + * to this signal to "configure" the filedialog, e.g. set the + * filefilter, the mode, a preview-widget, etc. It's usually + * not necessary to set a URL for the filedialog, as it will + * get set properly from the editfield contents. + * + * If you use multiple KURLRequesters, you can connect all of them + * to the same slot and use the given KURLRequester pointer to know + * which one is going to open. + */ + void openFileDialog( KURLRequester * ); + + /** + * Emitted when the user changed the URL via the file dialog. + * The parameter contains the contents of the lineedit. + * // TODO KDE4: Use KURL instead + */ + void urlSelected( const QString& ); + +protected: + void init(); + + KURLCompletion * myCompletion; + + +private: + KURLDragPushButton * myButton; + bool myShowLocalProt; + mutable KFileDialog * myFileDialog; + + +protected slots: + /** + * Called when the button is pressed to open the filedialog. + * Also called when KStdAccel::Open (default is Ctrl-O) is pressed. + */ + void slotOpenDialog(); + +private slots: + void slotUpdateURL(); + +protected: + virtual void virtual_hook( int id, void* data ); + bool eventFilter( QObject *obj, QEvent *ev ); +private: + class KURLRequesterPrivate; + KURLRequesterPrivate *d; +}; + +/** + * URL requester with a combo box, for use in Designer + */ +class KIO_EXPORT KURLComboRequester : public KURLRequester +{ + Q_OBJECT +public: + /** + * Constructs a KURLRequester widget with a combobox. + */ + KURLComboRequester( QWidget *parent=0, const char *name=0 ); +}; + + +#endif // KURLREQUESTER_H diff --git a/kio/kfile/kurlrequesterdlg.cpp b/kio/kfile/kurlrequesterdlg.cpp new file mode 100644 index 000000000..ed89f599b --- /dev/null +++ b/kio/kfile/kurlrequesterdlg.cpp @@ -0,0 +1,135 @@ +/* This file is part of the KDE libraries + Copyright (C) 2000 Wilco Greven <[email protected]> + + 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 <sys/stat.h> +#include <unistd.h> + +#include <qlabel.h> +#include <qlayout.h> +#include <qstring.h> +#include <qtoolbutton.h> + +#include <kaccel.h> +#include <kfiledialog.h> +#include <kglobal.h> +#include <kiconloader.h> +#include <klineedit.h> +#include <klocale.h> +#include <krecentdocument.h> +#include <kurl.h> +#include <kurlrequester.h> + +#include "kurlrequesterdlg.h" + + +KURLRequesterDlg::KURLRequesterDlg( const QString& urlName, QWidget *parent, + const char *name, bool modal ) + : KDialogBase( Plain, QString::null, Ok|Cancel|User1, Ok, parent, name, + modal, true, KStdGuiItem::clear() ) +{ + initDialog(i18n( "Location:" ), urlName); +} + +KURLRequesterDlg::KURLRequesterDlg( const QString& urlName, const QString& _text, QWidget *parent, const char *name, bool modal ) + : KDialogBase( Plain, QString::null, Ok|Cancel|User1, Ok, parent, name, + modal, true, KStdGuiItem::clear() ) +{ + initDialog(_text, urlName); +} + +KURLRequesterDlg::~KURLRequesterDlg() +{ +} + +void KURLRequesterDlg::initDialog(const QString &text,const QString &urlName) +{ + QVBoxLayout * topLayout = new QVBoxLayout( plainPage(), 0, + spacingHint() ); + + QLabel * label = new QLabel( text , plainPage() ); + topLayout->addWidget( label ); + + urlRequester_ = new KURLRequester( urlName, plainPage(), "urlRequester" ); + urlRequester_->setMinimumWidth( urlRequester_->sizeHint().width() * 3 ); + topLayout->addWidget( urlRequester_ ); + urlRequester_->setFocus(); + connect( urlRequester_->lineEdit(), SIGNAL(textChanged(const QString&)), + SLOT(slotTextChanged(const QString&)) ); + bool state = !urlName.isEmpty(); + enableButtonOK( state ); + enableButton( KDialogBase::User1, state ); + /* + KFile::Mode mode = static_cast<KFile::Mode>( KFile::File | + KFile::ExistingOnly ); + urlRequester_->setMode( mode ); + */ + connect( this, SIGNAL( user1Clicked() ), SLOT( slotClear() ) ); +} + +void KURLRequesterDlg::slotTextChanged(const QString & text) +{ + bool state = !text.stripWhiteSpace().isEmpty(); + enableButtonOK( state ); + enableButton( KDialogBase::User1, state ); +} + +void KURLRequesterDlg::slotClear() +{ + urlRequester_->clear(); +} + +KURL KURLRequesterDlg::selectedURL() const +{ + if ( result() == QDialog::Accepted ) + return KURL::fromPathOrURL( urlRequester_->url() ); + else + return KURL(); +} + + +KURL KURLRequesterDlg::getURL(const QString& dir, QWidget *parent, + const QString& caption) +{ + KURLRequesterDlg dlg(dir, parent, "filedialog", true); + + dlg.setCaption(caption.isNull() ? i18n("Open") : caption); + + dlg.exec(); + + const KURL& url = dlg.selectedURL(); + if (url.isValid()) + KRecentDocument::add(url); + + return url; +} + +KFileDialog * KURLRequesterDlg::fileDialog() +{ + return urlRequester_->fileDialog(); +} + +KURLRequester * KURLRequesterDlg::urlRequester() +{ + return urlRequester_; +} + +#include "kurlrequesterdlg.moc" + +// vim:ts=4:sw=4:tw=78 diff --git a/kio/kfile/kurlrequesterdlg.h b/kio/kfile/kurlrequesterdlg.h new file mode 100644 index 000000000..a4f2cf0f3 --- /dev/null +++ b/kio/kfile/kurlrequesterdlg.h @@ -0,0 +1,114 @@ +/* This file is part of the KDE libraries + Copyright (C) 2000 Wilco Greven <[email protected]> + + 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 KURLREQUESTERDIALOG_H +#define KURLREQUESTERDIALOG_H + +#include <kdialogbase.h> +#include <kurl.h> + +class KURLCompletion; +class KURLRequester; +class KFileDialog; +/** + * Dialog in which a user can enter a filename or url. It is a dialog + * encapsulating KURLRequester. The API is derived from + * KFileDialog. + * + * @short Simple dialog to enter a filename/url. + * @author Wilco Greven <[email protected]> + */ +class KIO_EXPORT KURLRequesterDlg : public KDialogBase +{ + Q_OBJECT + +public: + /** + * Constructs a KURLRequesterDlg. + * + * @param url The url of the directory to start in. Use QString::null + * to start in the current working directory, or the last + * directory where a file has been selected. + * @param parent The parent object of this widget. + * @param name The name of this widget. + * @param modal Specifies whether the dialog should be opened as modal + * or not. + */ + KURLRequesterDlg( const QString& url, QWidget *parent, + const char *name, bool modal = true ); + + /** + * Constructs a KURLRequesterDlg. + * + * @param url The url of the directory to start in. Use QString::null + * to start in the current working directory, or the last + * directory where a file has been selected. + * @param text Text of the label + * @param parent The parent object of this widget. + * @param name The name of this widget. + * @param modal Specifies whether the dialog should be opened as modal + * or not. + */ + KURLRequesterDlg( const QString& url, const QString& text, + QWidget *parent, const char *name, bool modal=true ); + /** + * Destructs the dialog. + */ + ~KURLRequesterDlg(); + + /** + * Returns the fully qualified filename. + */ + KURL selectedURL() const; + + /** + * Creates a modal dialog, executes it and returns the selected URL. + * + * @param url This specifies the initial path of the input line. + * @param parent The widget the dialog will be centered on initially. + * @param caption The caption to use for the dialog. + */ + static KURL getURL(const QString& url = QString::null, + QWidget *parent= 0, const QString& caption = QString::null); + + /** + * Returns a pointer to the file dialog used by the KURLRequester. + */ + KFileDialog * fileDialog(); + /** + * Returns a pointer to the KURLRequester. + */ + KURLRequester *urlRequester(); + +private slots: + void slotClear(); + void slotTextChanged(const QString &); +private: + void initDialog(const QString &text, const QString &url); + KURLRequester *urlRequester_; + + class KURLRequesterDlgPrivate; + KURLRequesterDlgPrivate *d; + +}; + +#endif // KURLREQUESTERDIALOG_H + +// vim:ts=4:sw=4:tw=78 diff --git a/kio/kfile/tests/Makefile.am b/kio/kfile/tests/Makefile.am new file mode 100644 index 000000000..9689f7a27 --- /dev/null +++ b/kio/kfile/tests/Makefile.am @@ -0,0 +1,41 @@ +# This file is part of the KDE libraries +# Copyright (C) 1996-1997 Matthias Kalle Dalheimer ([email protected]) +# (C) 1997-1998 Stephan Kulow ([email protected]) + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. + +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. + +# You should have received a copy of the GNU Library General Public License +# along with this library; see the file COPYING.LIB. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. + +INCLUDES = $(all_includes) + +AM_LDFLAGS = $(QT_LDFLAGS) $(X_LDFLAGS) $(KDE_RPATH) + +check_PROGRAMS = kfstest kurlrequestertest kfiletreeviewtest \ + kopenwithtest kdirselectdialogtest kicondialogtest \ + knotifytest kcustommenueditortest + +# noinst_HEADERS = + +METASOURCES = AUTO + +LDADD = $(LIB_KIO) +kcustommenueditortest_SOURCES = kcustommenueditortest.cpp +kurlrequestertest_SOURCES = kurlrequestertest.cpp +kfstest_SOURCES = kfstest.cpp kfdtest.cpp +kfiletreeviewtest_SOURCES = kfiletreeviewtest.cpp +kopenwithtest_SOURCES = kopenwithtest.cpp +kdirselectdialogtest_SOURCES = kdirselectdialogtest.cpp +kicondialogtest_SOURCES = kicondialogtest.cpp +knotifytest_SOURCES = knotifytest.cpp + diff --git a/kio/kfile/tests/kcustommenueditortest.cpp b/kio/kfile/tests/kcustommenueditortest.cpp new file mode 100644 index 000000000..532297d0c --- /dev/null +++ b/kio/kfile/tests/kcustommenueditortest.cpp @@ -0,0 +1,19 @@ +#include "kcustommenueditor.h" +#include <kapplication.h> +#include <klocale.h> +#include <kconfig.h> + +int main(int argc, char** argv) +{ + KLocale::setMainCatalogue("kdelibs"); + KApplication app(argc, argv, "KCustomMenuEditorTest"); + KCustomMenuEditor editor(0); + KConfig *cfg = new KConfig("kdesktop_custom_menu2"); + editor.load(cfg); + if (editor.exec()) + { + editor.save(cfg); + cfg->sync(); + } +} + diff --git a/kio/kfile/tests/kdirselectdialogtest.cpp b/kio/kfile/tests/kdirselectdialogtest.cpp new file mode 100644 index 000000000..e2eb52f1b --- /dev/null +++ b/kio/kfile/tests/kdirselectdialogtest.cpp @@ -0,0 +1,17 @@ +#include <kapplication.h> +#include <kdirselectdialog.h> +#include <kmessagebox.h> +#include <kurl.h> + +int main( int argc, char **argv ) +{ + KApplication app(argc, argv, "kdirselectdialogtest"); + + KURL u = KDirSelectDialog::selectDirectory( (argc >= 1) ? argv[1] : QString::null ); + if ( u.isValid() ) + KMessageBox::information( 0L, + QString::fromLatin1("You selected the url: %1") + .arg( u.prettyURL() ), "Selected URL" ); + + return 0; +} diff --git a/kio/kfile/tests/kfdtest.cpp b/kio/kfile/tests/kfdtest.cpp new file mode 100644 index 000000000..d9341a7ae --- /dev/null +++ b/kio/kfile/tests/kfdtest.cpp @@ -0,0 +1,34 @@ +#include "kfdtest.h" + +#include <qstringlist.h> +#include <kfiledialog.h> +#include <kapplication.h> +#include <kmessagebox.h> +#include <qtimer.h> + +KFDTest::KFDTest( const QString& startDir, QObject *parent, const char *name ) + : QObject( parent, name ), + m_startDir( startDir ) +{ + QTimer::singleShot( 1000, this, SLOT( doit() )); +} + +void KFDTest::doit() +{ + KFileDialog *dlg = new KFileDialog( m_startDir, QString::null, 0L, + "file dialog", true ); + dlg->setMode( KFile::File); + dlg->setOperationMode( KFileDialog::Saving ); + QStringList filter; + filter << "all/allfiles" << "text/plain"; + dlg->setMimeFilter( filter, "all/allfiles" ); + + if ( dlg->exec() == KDialog::Accepted ) + { + KMessageBox::information(0, QString::fromLatin1("You selected the file: %1").arg( dlg->selectedURL().prettyURL() )); + } + +// qApp->quit(); +} + +#include "kfdtest.moc" diff --git a/kio/kfile/tests/kfdtest.h b/kio/kfile/tests/kfdtest.h new file mode 100644 index 000000000..0743bb96c --- /dev/null +++ b/kio/kfile/tests/kfdtest.h @@ -0,0 +1,28 @@ +/**************************************************************************** +** $Id$ +** +** Copyright (C) 2003 Carsten Pfeiffer <[email protected]> +** +****************************************************************************/ + +#ifndef KFDTEST_H +#define KFDTEST_H + +#include <qobject.h> + +class KFDTest : public QObject +{ + Q_OBJECT + +public: + KFDTest( const QString& startDir, QObject *parent = 0, const char *name = 0); + +public slots: + void doit(); + +private: + QString m_startDir; +}; + + +#endif // KFDTEST_H diff --git a/kio/kfile/tests/kfiletreeviewtest.cpp b/kio/kfile/tests/kfiletreeviewtest.cpp new file mode 100644 index 000000000..6a4ea719f --- /dev/null +++ b/kio/kfile/tests/kfiletreeviewtest.cpp @@ -0,0 +1,165 @@ +/* This file is part of the KDE libraries + Copyright (C) 2001 Klaas Freitag <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qdir.h> + +#include <kglobal.h> +#include <kiconloader.h> +#include <kmainwindow.h> +#include <kapplication.h> +#include <kurl.h> +#include <kdebug.h> +#include <kstatusbar.h> + +#include <kfiletreeview.h> +#include "kfiletreeviewtest.h" + + +#include "kfiletreeviewtest.moc" + +testFrame::testFrame():KMainWindow(0,"Test FileTreeView"), + dirOnlyMode(false) + +{ + treeView = new KFileTreeView( this ); + treeView->setDragEnabled( true ); + treeView->setAcceptDrops( true ); + treeView->setDropVisualizer( true ); + + + /* Connect to see the status bar */ + KStatusBar* sta = statusBar(); + connect( treeView, SIGNAL( onItem( const QString& )), + sta, SLOT( message( const QString& ))); + + connect( treeView, SIGNAL( dropped( QWidget*, QDropEvent*, KURL::List& )), + this, SLOT( urlsDropped( QWidget*, QDropEvent*, KURL::List& ))); + + connect( treeView, SIGNAL( dropped( KURL::List&, KURL& )), this, + SLOT( copyURLs( KURL::List&, KURL& ))); + + treeView->addColumn( "File" ); + treeView->addColumn( "ChildCount" ); + setCentralWidget( treeView ); + resize( 600, 400 ); + + showPath( KURL::fromPathOrURL( QDir::homeDirPath() )); +} + +void testFrame::showPath( const KURL &url ) +{ + QString fname = "TestBranch"; // url.fileName (); + /* try a user icon */ + KIconLoader *loader = KGlobal::iconLoader(); + QPixmap pix = loader->loadIcon( "contents2", KIcon::Small ); + QPixmap pixOpen = loader->loadIcon( "contents", KIcon::Small ); + + KFileTreeBranch *nb = treeView->addBranch( url, fname, pix ); + + if( nb ) + { + if( dirOnlyMode ) treeView->setDirOnlyMode( nb, true ); + nb->setOpenPixmap( pixOpen ); + + connect( nb, SIGNAL(populateFinished(KFileTreeViewItem*)), + this, SLOT(slotPopulateFinished(KFileTreeViewItem*))); + connect( nb, SIGNAL( directoryChildCount( KFileTreeViewItem *, int )), + this, SLOT( slotSetChildCount( KFileTreeViewItem*, int ))); + // nb->setChildRecurse(false ); + + nb->setOpen(true); + } + + +} + +void testFrame::urlsDropped( QWidget* , QDropEvent* , KURL::List& list ) +{ + KURL::List::ConstIterator it = list.begin(); + for ( ; it != list.end(); ++it ) { + kdDebug() << "Url dropped: " << (*it).prettyURL() << endl; + } +} + +void testFrame::copyURLs( KURL::List& list, KURL& to ) +{ + KURL::List::ConstIterator it = list.begin(); + kdDebug() << "Copy to " << to.prettyURL() << endl; + for ( ; it != list.end(); ++it ) { + kdDebug() << "Url: " << (*it).prettyURL() << endl; + } + +} + + +void testFrame::slotPopulateFinished(KFileTreeViewItem *item ) +{ + if( item ) + { +#if 0 + int cc = item->childCount(); + + kdDebug() << "setting column 2 of treeview with count " << cc << endl; + + item->setText( 1, QString::number( cc )); +#endif + } + else + { + kdDebug() << "slotPopFinished for uninitalised item" << endl; + } +} + +void testFrame::slotSetChildCount( KFileTreeViewItem *item, int c ) +{ + if( item ) + item->setText(1, QString::number( c )); +} + +int main(int argc, char **argv) +{ + KApplication a(argc, argv, "kfiletreeviewtest"); + QString name1; + QStringList names; + + QString argv1; + testFrame *tf; + + tf = new testFrame(); + a.setMainWidget( tf ); + + if (argc > 1) + { + for( int i = 1; i < argc; i++ ) + { + argv1 = QString::fromLatin1(argv[i]); + kdDebug() << "Opening " << argv1 << endl; + if( argv1 == "-d" ) + tf->setDirOnly(); + else + { + KURL u( argv1 ); + tf->showPath( u ); + } + } + } + tf->show(); + int ret = a.exec(); + return( ret ); +} diff --git a/kio/kfile/tests/kfiletreeviewtest.h b/kio/kfile/tests/kfiletreeviewtest.h new file mode 100644 index 000000000..1286e1be2 --- /dev/null +++ b/kio/kfile/tests/kfiletreeviewtest.h @@ -0,0 +1,42 @@ +/* This file is part of the KDE libraries + Copyright (C) 2001 Klaas Freitag <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KFILETREEVIEWTEST +#define KFILETREEVIEWTEST + +class testFrame: public KMainWindow +{ + Q_OBJECT +public: + testFrame(); + void showPath( const KURL & ); + void setDirOnly( ) { dirOnlyMode = true; } +public slots: + void slotPopulateFinished(KFileTreeViewItem *); + void slotSetChildCount( KFileTreeViewItem *item, int c ); + + void urlsDropped( QWidget*, QDropEvent*, KURL::List& ); + void copyURLs( KURL::List& list, KURL& to ); +private: + KFileTreeView *treeView; + bool dirOnlyMode; +}; + + +#endif diff --git a/kio/kfile/tests/kfstest.cpp b/kio/kfile/tests/kfstest.cpp new file mode 100644 index 000000000..16d74cb0c --- /dev/null +++ b/kio/kfile/tests/kfstest.cpp @@ -0,0 +1,183 @@ +/* This file is part of the KDE libraries + Copyright (C) 1997, 1998 Richard Moore <[email protected]> + 1998 Stephan Kulow <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <unistd.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <qdir.h> +#include <qlayout.h> +#include <qstringlist.h> +#include <qwidget.h> + +#include <kfiledialog.h> +#include <kfileiconview.h> +#include <kmessagebox.h> +#include <kconfig.h> +#include <kapplication.h> +#include <kurl.h> +#include <kurlbar.h> +#include <kdiroperator.h> +#include <kfile.h> +#include <kdebug.h> +#include <kicondialog.h> + +#include "kfdtest.h" + +int main(int argc, char **argv) +{ + KApplication a(argc, argv, "kfstest"); + QString name1; + QStringList names; + + QString argv1; + QString startDir; + if (argc > 1) + argv1 = QString::fromLatin1(argv[1]); + if ( argc > 2 ) + startDir = QString::fromLatin1( argv[2]); + + if (argv1 == QString::fromLatin1("diroperator")) { + KDirOperator *op = new KDirOperator(startDir, 0, "operator"); + op->setViewConfig( KGlobal::config(), "TestGroup" ); + op->setView(KFile::Simple); + op->show(); + a.setMainWidget(op); + a.exec(); + } + + else if (argv1 == QString::fromLatin1("justone")) { + QString name = KFileDialog::getOpenFileName(startDir); + qDebug("filename=%s",name.latin1()); + } + + else if (argv1 == QString::fromLatin1("existingURL")) { + KURL url = KFileDialog::getExistingURL(); + qDebug("URL=%s",url.url().latin1()); + name1 = url.url(); + } + + else if (argv1 == QString::fromLatin1("preview")) { + KURL u = KFileDialog::getImageOpenURL(); + qDebug("filename=%s", u.url().latin1()); + } + + else if (argv1 == QString::fromLatin1("preselect")) { + names = KFileDialog::getOpenFileNames(QString::fromLatin1("/etc/passwd")); + QStringList::Iterator it = names.begin(); + while ( it != names.end() ) { + qDebug("selected file: %s", (*it).latin1()); + ++it; + } + } + + else if (argv1 == QString::fromLatin1("dirs")) + name1 = KFileDialog::getExistingDirectory(); + + else if (argv1 == QString::fromLatin1("heap")) { + KFileDialog *dlg = new KFileDialog( startDir, QString::null, 0L, + "file dialog", true ); + dlg->setMode( KFile::File); + dlg->setOperationMode( KFileDialog::Saving ); + QStringList filter; + filter << "all/allfiles" << "text/plain"; + dlg->setMimeFilter( filter, "all/allfiles" ); + KURLBar *urlBar = dlg->speedBar(); + if ( urlBar ) + { + urlBar->insertDynamicItem( KURL("ftp://ftp.kde.org"), + QString::fromLatin1("KDE FTP Server") ); + } + + if ( dlg->exec() == KDialog::Accepted ) + name1 = dlg->selectedURL().url(); + } + + else if ( argv1 == QString::fromLatin1("eventloop") ) + { + KFDTest *test = new KFDTest( startDir ); + return a.exec(); + } + + else if (argv1 == QString::fromLatin1("save")) { + KURL u = KFileDialog::getSaveURL(); +// QString(QDir::homeDirPath() + QString::fromLatin1("/testfile")), +// QString::null, 0L); + name1 = u.url(); + } + + else if (argv1 == QString::fromLatin1("icon")) { + KIconDialog dlg; + QString icon = dlg.selectIcon(); + kdDebug() << icon << endl; + } + +// else if ( argv1 == QString::fromLatin1("dirselect") ) { +// KURL url; +// url.setPath( "/" ); +// KURL selected = KDirSelectDialog::selectDirectory( url ); +// name1 = selected.url(); +// qDebug("*** selected: %s", selected.url().latin1()); +// } + + else { + KFileDialog dlg(startDir, + QString::fromLatin1("*|All Files\n" + "*.lo *.o *.la|All libtool Files"), + 0, 0, true); +// dlg.setFilter( "*.kdevelop" ); + dlg.setMode( (KFile::Mode) (KFile::Files | + KFile::Directory | + KFile::ExistingOnly | + KFile::LocalOnly) ); +// QStringList filter; +// filter << "text/plain" << "text/html" << "image/png"; +// dlg.setMimeFilter( filter ); +// KMimeType::List types; +// types.append( KMimeType::mimeType( "text/plain" ) ); +// types.append( KMimeType::mimeType( "text/html" ) ); +// dlg.setFilterMimeType( "Filetypes:", types, types.first() ); + if ( dlg.exec() == QDialog::Accepted ) { + KURL::List list = dlg.selectedURLs(); + KURL::List::ConstIterator it = list.begin(); + qDebug("*** selectedURLs(): "); + while ( it != list.end() ) { + name1 = (*it).url(); + qDebug(" -> %s", name1.latin1()); + ++it; + } + qDebug("*** selectedFile: %s", dlg.selectedFile().latin1()); + qDebug("*** selectedURL: %s", dlg.selectedURL().url().latin1()); + qDebug("*** selectedFiles: "); + QStringList l = dlg.selectedFiles(); + QStringList::Iterator it2 = l.begin(); + while ( it2 != l.end() ) { + qDebug(" -> %s", (*it2).latin1()); + ++it2; + } + } + } + + if (!(name1.isNull())) + KMessageBox::information(0, QString::fromLatin1("You selected the file " ) + name1, + QString::fromLatin1("Your Choice")); + return 0; +} diff --git a/kio/kfile/tests/kicondialogtest.cpp b/kio/kfile/tests/kicondialogtest.cpp new file mode 100644 index 000000000..23301b752 --- /dev/null +++ b/kio/kfile/tests/kicondialogtest.cpp @@ -0,0 +1,19 @@ +#include <kapplication.h> +#include <kicondialog.h> + +int main( int argc, char **argv ) +{ + KApplication app( argc, argv, "kicondialogtest" ); + +// KIconDialog::getIcon(); + + KIconButton button; + app.setMainWidget( &button ); + button.show(); + + + return app.exec(); +} + +/* vim: et sw=4 + */ diff --git a/kio/kfile/tests/knotifytest.cpp b/kio/kfile/tests/knotifytest.cpp new file mode 100644 index 000000000..e27893589 --- /dev/null +++ b/kio/kfile/tests/knotifytest.cpp @@ -0,0 +1,10 @@ +#include <kapplication.h> +#include <knotifydialog.h> + +int main( int argc, char **argv ) +{ + KApplication app( argc, argv, "knotifytest" ); + KNotifyDialog *dlg = new KNotifyDialog(); + dlg->addApplicationEvents( "kwin" ); + return dlg->exec(); +} diff --git a/kio/kfile/tests/kopenwithtest.cpp b/kio/kfile/tests/kopenwithtest.cpp new file mode 100644 index 000000000..96e18bcc2 --- /dev/null +++ b/kio/kfile/tests/kopenwithtest.cpp @@ -0,0 +1,67 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002 Dirk Mueller <[email protected]> + Copyright (C) 2003 David Faure <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <kapplication.h> +#include <qwidget.h> +#include <qstringlist.h> +#include <qdir.h> +#include <kopenwith.h> +#include <kurl.h> +#include <kdebug.h> + +int main(int argc, char **argv) +{ + KApplication app(argc, argv, "kopenwithtest"); + KURL::List list; + + list += KURL("file:///tmp/testfile.txt"); + + // Test with one URL + KOpenWithDlg* dlg = new KOpenWithDlg(list, "OpenWith_Text", "OpenWith_Value", 0); + if(dlg->exec()) { + kdDebug() << "Dialog ended successfully\ntext: " << dlg->text() << endl; + } + else + kdDebug() << "Dialog was canceled." << endl; + delete dlg; + + // Test with two URLs + list += KURL("http://www.kde.org/index.html"); + dlg = new KOpenWithDlg(list, "OpenWith_Text", "OpenWith_Value", 0); + if(dlg->exec()) { + kdDebug() << "Dialog ended successfully\ntext: " << dlg->text() << endl; + } + else + kdDebug() << "Dialog was canceled." << endl; + delete dlg; + + // Test with a mimetype + QString mimetype = "text/plain"; + dlg = new KOpenWithDlg( mimetype, "kedit", 0); + if(dlg->exec()) { + kdDebug() << "Dialog ended successfully\ntext: " << dlg->text() << endl; + } + else + kdDebug() << "Dialog was canceled." << endl; + delete dlg; + + return 0; +} + diff --git a/kio/kfile/tests/kurlrequestertest.cpp b/kio/kfile/tests/kurlrequestertest.cpp new file mode 100644 index 000000000..5601f4922 --- /dev/null +++ b/kio/kfile/tests/kurlrequestertest.cpp @@ -0,0 +1,16 @@ +#include <kapplication.h> +#include <keditlistbox.h> +#include <kurlrequester.h> +#include <kurlrequesterdlg.h> + +int main( int argc, char **argv ) +{ + KApplication app( argc, argv, "kurlrequestertest" ); + KURL url = KURLRequesterDlg::getURL( "ftp://ftp.kde.org" ); + qDebug( "Selected url: %s", url.url().latin1()); + + KURLRequester *req = new KURLRequester(); + KEditListBox *el = new KEditListBox( QString::fromLatin1("Test"), req->customEditor() ); + el->show(); + return app.exec(); +} |