summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Faure <[email protected]>2015-08-12 20:59:51 +0200
committerDavid Faure <[email protected]>2015-08-12 22:11:21 +0200
commite2a7f953b155acfc8d1d6684784622f24dd11b8d (patch)
tree5e66ceeea6308ccacc94ee49680d8e9996047f0a
parentbb7b6fa3315d0e1e5eaa8260d81dec8a230639ac (diff)
Revert "Remove Dolphin from kde-baseapps"
It provides a part for konqueror, and removing symbols from libkonq is not allowed either. This reverts commit 6a70d903c56b5fc1cf2e6da997e11bffc76832cc. (except for the doc subdir)
-rw-r--r--CMakeLists.txt1
-rw-r--r--dolphin/AUTHORS6
-rw-r--r--dolphin/CMakeLists.txt2
-rw-r--r--dolphin/COPYING346
-rw-r--r--dolphin/COPYING.DOC397
-rw-r--r--dolphin/README2
-rw-r--r--dolphin/src/CMakeLists.txt368
-rwxr-xr-xdolphin/src/Messages.sh4
-rw-r--r--dolphin/src/config-X11.h.cmake1
-rw-r--r--dolphin/src/config-baloo.h.cmake1
-rw-r--r--dolphin/src/dolphin.appdata.xml431
-rwxr-xr-xdolphin/src/dolphin.desktop188
-rw-r--r--dolphin/src/dolphinapplication.cpp106
-rw-r--r--dolphin/src/dolphinapplication.h44
-rw-r--r--dolphin/src/dolphincontextmenu.cpp532
-rw-r--r--dolphin/src/dolphincontextmenu.h183
-rw-r--r--dolphin/src/dolphindockwidget.cpp94
-rw-r--r--dolphin/src/dolphindockwidget.h49
-rw-r--r--dolphin/src/dolphinmainwindow.cpp1525
-rw-r--r--dolphin/src/dolphinmainwindow.h530
-rw-r--r--dolphin/src/dolphinnewfilemenu.cpp48
-rw-r--r--dolphin/src/dolphinnewfilemenu.h54
-rw-r--r--dolphin/src/dolphinpart.cpp619
-rw-r--r--dolphin/src/dolphinpart.desktop341
-rw-r--r--dolphin/src/dolphinpart.h253
-rw-r--r--dolphin/src/dolphinpart.rc66
-rw-r--r--dolphin/src/dolphinpart_ext.cpp193
-rw-r--r--dolphin/src/dolphinpart_ext.h92
-rw-r--r--dolphin/src/dolphinrecenttabsmenu.cpp97
-rw-r--r--dolphin/src/dolphinrecenttabsmenu.h51
-rw-r--r--dolphin/src/dolphinremoveaction.cpp61
-rw-r--r--dolphin/src/dolphinremoveaction.h55
-rw-r--r--dolphin/src/dolphintabbar.cpp174
-rw-r--r--dolphin/src/dolphintabbar.h65
-rw-r--r--dolphin/src/dolphintabpage.cpp335
-rw-r--r--dolphin/src/dolphintabpage.h168
-rw-r--r--dolphin/src/dolphintabwidget.cpp361
-rw-r--r--dolphin/src/dolphintabwidget.h190
-rw-r--r--dolphin/src/dolphinui.rc116
-rw-r--r--dolphin/src/dolphinviewcontainer.cpp718
-rw-r--r--dolphin/src/dolphinviewcontainer.h354
-rw-r--r--dolphin/src/filterbar/filterbar.cpp143
-rw-r--r--dolphin/src/filterbar/filterbar.h85
-rw-r--r--dolphin/src/kitemviews/kfileitemlistview.cpp424
-rw-r--r--dolphin/src/kitemviews/kfileitemlistview.h131
-rw-r--r--dolphin/src/kitemviews/kfileitemlistwidget.cpp164
-rw-r--r--dolphin/src/kitemviews/kfileitemlistwidget.h63
-rw-r--r--dolphin/src/kitemviews/kfileitemmodel.cpp2248
-rw-r--r--dolphin/src/kitemviews/kfileitemmodel.h523
-rw-r--r--dolphin/src/kitemviews/kfileitemmodelrolesupdater.cpp1171
-rw-r--r--dolphin/src/kitemviews/kfileitemmodelrolesupdater.h341
-rw-r--r--dolphin/src/kitemviews/kitemlistcontainer.cpp406
-rw-r--r--dolphin/src/kitemviews/kitemlistcontainer.h97
-rw-r--r--dolphin/src/kitemviews/kitemlistcontroller.cpp1313
-rw-r--r--dolphin/src/kitemviews/kitemlistcontroller.h352
-rw-r--r--dolphin/src/kitemviews/kitemlistgroupheader.cpp236
-rw-r--r--dolphin/src/kitemviews/kitemlistgroupheader.h132
-rw-r--r--dolphin/src/kitemviews/kitemlistheader.cpp88
-rw-r--r--dolphin/src/kitemviews/kitemlistheader.h94
-rw-r--r--dolphin/src/kitemviews/kitemlistselectionmanager.cpp399
-rw-r--r--dolphin/src/kitemviews/kitemlistselectionmanager.h104
-rw-r--r--dolphin/src/kitemviews/kitemliststyleoption.cpp56
-rw-r--r--dolphin/src/kitemviews/kitemliststyleoption.h51
-rw-r--r--dolphin/src/kitemviews/kitemlistview.cpp2722
-rw-r--r--dolphin/src/kitemviews/kitemlistview.h915
-rw-r--r--dolphin/src/kitemviews/kitemlistviewaccessible.cpp545
-rw-r--r--dolphin/src/kitemviews/kitemlistviewaccessible.h163
-rw-r--r--dolphin/src/kitemviews/kitemlistwidget.cpp529
-rw-r--r--dolphin/src/kitemviews/kitemlistwidget.h257
-rw-r--r--dolphin/src/kitemviews/kitemmodelbase.cpp162
-rw-r--r--dolphin/src/kitemviews/kitemmodelbase.h271
-rw-r--r--dolphin/src/kitemviews/kitemrange.h106
-rw-r--r--dolphin/src/kitemviews/kitemset.cpp348
-rw-r--r--dolphin/src/kitemviews/kitemset.h413
-rw-r--r--dolphin/src/kitemviews/kstandarditem.cpp171
-rw-r--r--dolphin/src/kitemviews/kstandarditem.h102
-rw-r--r--dolphin/src/kitemviews/kstandarditemlistgroupheader.cpp125
-rw-r--r--dolphin/src/kitemviews/kstandarditemlistgroupheader.h57
-rw-r--r--dolphin/src/kitemviews/kstandarditemlistview.cpp182
-rw-r--r--dolphin/src/kitemviews/kstandarditemlistview.h82
-rw-r--r--dolphin/src/kitemviews/kstandarditemlistwidget.cpp1500
-rw-r--r--dolphin/src/kitemviews/kstandarditemlistwidget.h278
-rw-r--r--dolphin/src/kitemviews/kstandarditemmodel.cpp241
-rw-r--r--dolphin/src/kitemviews/kstandarditemmodel.h113
-rw-r--r--dolphin/src/kitemviews/private/kbaloorolesprovider.cpp184
-rw-r--r--dolphin/src/kitemviews/private/kbaloorolesprovider.h90
-rw-r--r--dolphin/src/kitemviews/private/kdirectorycontentscounter.cpp184
-rw-r--r--dolphin/src/kitemviews/private/kdirectorycontentscounter.h92
-rw-r--r--dolphin/src/kitemviews/private/kdirectorycontentscounterworker.cpp95
-rw-r--r--dolphin/src/kitemviews/private/kdirectorycontentscounterworker.h71
-rw-r--r--dolphin/src/kitemviews/private/kfileitemclipboard.cpp86
-rw-r--r--dolphin/src/kitemviews/private/kfileitemclipboard.h62
-rw-r--r--dolphin/src/kitemviews/private/kfileitemmodeldirlister.cpp48
-rw-r--r--dolphin/src/kitemviews/private/kfileitemmodeldirlister.h53
-rw-r--r--dolphin/src/kitemviews/private/kfileitemmodelfilter.cpp122
-rw-r--r--dolphin/src/kitemviews/private/kfileitemmodelfilter.h93
-rw-r--r--dolphin/src/kitemviews/private/kfileitemmodelsortalgorithm.h143
-rw-r--r--dolphin/src/kitemviews/private/kitemlistheaderwidget.cpp572
-rw-r--r--dolphin/src/kitemviews/private/kitemlistheaderwidget.h172
-rw-r--r--dolphin/src/kitemviews/private/kitemlistkeyboardsearchmanager.cpp96
-rw-r--r--dolphin/src/kitemviews/private/kitemlistkeyboardsearchmanager.h87
-rw-r--r--dolphin/src/kitemviews/private/kitemlistroleeditor.cpp151
-rw-r--r--dolphin/src/kitemviews/private/kitemlistroleeditor.h76
-rw-r--r--dolphin/src/kitemviews/private/kitemlistrubberband.cpp91
-rw-r--r--dolphin/src/kitemviews/private/kitemlistrubberband.h60
-rw-r--r--dolphin/src/kitemviews/private/kitemlistselectiontoggle.cpp118
-rw-r--r--dolphin/src/kitemviews/private/kitemlistselectiontoggle.h63
-rw-r--r--dolphin/src/kitemviews/private/kitemlistsizehintresolver.cpp154
-rw-r--r--dolphin/src/kitemviews/private/kitemlistsizehintresolver.h56
-rw-r--r--dolphin/src/kitemviews/private/kitemlistsmoothscroller.cpp212
-rw-r--r--dolphin/src/kitemviews/private/kitemlistsmoothscroller.h103
-rw-r--r--dolphin/src/kitemviews/private/kitemlistviewanimation.cpp245
-rw-r--r--dolphin/src/kitemviews/private/kitemlistviewanimation.h106
-rw-r--r--dolphin/src/kitemviews/private/kitemlistviewlayouter.cpp624
-rw-r--r--dolphin/src/kitemviews/private/kitemlistviewlayouter.h240
-rw-r--r--dolphin/src/kitemviews/private/kpixmapmodifier.cpp400
-rw-r--r--dolphin/src/kitemviews/private/kpixmapmodifier.h38
-rw-r--r--dolphin/src/libdolphin_export.h38
-rw-r--r--dolphin/src/main.cpp97
-rw-r--r--dolphin/src/panels/folders/dolphin_folderspanelsettings.kcfg18
-rw-r--r--dolphin/src/panels/folders/dolphin_folderspanelsettings.kcfgc4
-rw-r--r--dolphin/src/panels/folders/foldersitemlistwidget.cpp36
-rw-r--r--dolphin/src/panels/folders/foldersitemlistwidget.h42
-rw-r--r--dolphin/src/panels/folders/folderspanel.cpp331
-rw-r--r--dolphin/src/panels/folders/folderspanel.h104
-rw-r--r--dolphin/src/panels/folders/treeviewcontextmenu.cpp209
-rw-r--r--dolphin/src/panels/folders/treeviewcontextmenu.h94
-rw-r--r--dolphin/src/panels/information/dolphin_informationpanelsettings.kcfg14
-rw-r--r--dolphin/src/panels/information/dolphin_informationpanelsettings.kcfgc4
-rw-r--r--dolphin/src/panels/information/filemetadataconfigurationdialog.cpp102
-rw-r--r--dolphin/src/panels/information/filemetadataconfigurationdialog.h81
-rw-r--r--dolphin/src/panels/information/informationpanel.cpp362
-rw-r--r--dolphin/src/panels/information/informationpanel.h159
-rw-r--r--dolphin/src/panels/information/informationpanelcontent.cpp438
-rw-r--r--dolphin/src/panels/information/informationpanelcontent.h160
-rw-r--r--dolphin/src/panels/information/phononwidget.cpp233
-rw-r--r--dolphin/src/panels/information/phononwidget.h93
-rw-r--r--dolphin/src/panels/information/pixmapviewer.cpp132
-rw-r--r--dolphin/src/panels/information/pixmapviewer.h98
-rw-r--r--dolphin/src/panels/panel.cpp79
-rw-r--r--dolphin/src/panels/panel.h83
-rw-r--r--dolphin/src/panels/places/dolphin_placespanelsettings.kcfg14
-rw-r--r--dolphin/src/panels/places/dolphin_placespanelsettings.kcfgc4
-rw-r--r--dolphin/src/panels/places/placesitem.cpp338
-rw-r--r--dolphin/src/panels/places/placesitem.h129
-rw-r--r--dolphin/src/panels/places/placesitemeditdialog.cpp170
-rw-r--r--dolphin/src/panels/places/placesitemeditdialog.h76
-rw-r--r--dolphin/src/panels/places/placesitemlistgroupheader.cpp45
-rw-r--r--dolphin/src/panels/places/placesitemlistgroupheader.h40
-rw-r--r--dolphin/src/panels/places/placesitemlistwidget.cpp43
-rw-r--r--dolphin/src/panels/places/placesitemlistwidget.h44
-rw-r--r--dolphin/src/panels/places/placesitemmodel.cpp1214
-rw-r--r--dolphin/src/panels/places/placesitemmodel.h293
-rw-r--r--dolphin/src/panels/places/placesitemsignalhandler.cpp50
-rw-r--r--dolphin/src/panels/places/placesitemsignalhandler.h68
-rw-r--r--dolphin/src/panels/places/placespanel.cpp551
-rw-r--r--dolphin/src/panels/places/placespanel.h95
-rw-r--r--dolphin/src/panels/places/placesview.cpp52
-rw-r--r--dolphin/src/panels/places/placesview.h41
-rw-r--r--dolphin/src/panels/terminal/terminalpanel.cpp204
-rw-r--r--dolphin/src/panels/terminal/terminalpanel.h90
-rw-r--r--dolphin/src/search/dolphin_searchsettings.kcfg22
-rw-r--r--dolphin/src/search/dolphin_searchsettings.kcfgc4
-rw-r--r--dolphin/src/search/dolphinfacetswidget.cpp303
-rw-r--r--dolphin/src/search/dolphinfacetswidget.h106
-rw-r--r--dolphin/src/search/dolphinsearchbox.cpp515
-rw-r--r--dolphin/src/search/dolphinsearchbox.h182
-rw-r--r--dolphin/src/search/filenamesearch.protocol17
-rw-r--r--dolphin/src/search/filenamesearchprotocol.cpp191
-rw-r--r--dolphin/src/search/filenamesearchprotocol.h60
-rw-r--r--dolphin/src/settings/additionalinfodialog.cpp113
-rw-r--r--dolphin/src/settings/additionalinfodialog.h49
-rw-r--r--dolphin/src/settings/applyviewpropsjob.cpp79
-rw-r--r--dolphin/src/settings/applyviewpropsjob.h83
-rw-r--r--dolphin/src/settings/dolphin_compactmodesettings.kcfg44
-rw-r--r--dolphin/src/settings/dolphin_compactmodesettings.kcfgc4
-rw-r--r--dolphin/src/settings/dolphin_detailsmodesettings.kcfg48
-rw-r--r--dolphin/src/settings/dolphin_detailsmodesettings.kcfgc4
-rw-r--r--dolphin/src/settings/dolphin_directoryviewpropertysettings.kcfg83
-rw-r--r--dolphin/src/settings/dolphin_directoryviewpropertysettings.kcfgc6
-rw-r--r--dolphin/src/settings/dolphin_generalsettings.kcfg96
-rw-r--r--dolphin/src/settings/dolphin_generalsettings.kcfgc4
-rw-r--r--dolphin/src/settings/dolphin_iconsmodesettings.kcfg48
-rw-r--r--dolphin/src/settings/dolphin_iconsmodesettings.kcfgc4
-rw-r--r--dolphin/src/settings/dolphin_versioncontrolsettings.kcfg14
-rw-r--r--dolphin/src/settings/dolphin_versioncontrolsettings.kcfgc4
-rw-r--r--dolphin/src/settings/dolphinsettingsdialog.cpp152
-rw-r--r--dolphin/src/settings/dolphinsettingsdialog.h61
-rw-r--r--dolphin/src/settings/general/behaviorsettingspage.cpp144
-rw-r--r--dolphin/src/settings/general/behaviorsettingspage.h64
-rw-r--r--dolphin/src/settings/general/configurepreviewplugindialog.cpp81
-rw-r--r--dolphin/src/settings/general/configurepreviewplugindialog.h55
-rw-r--r--dolphin/src/settings/general/confirmationssettingspage.cpp114
-rw-r--r--dolphin/src/settings/general/confirmationssettingspage.h52
-rw-r--r--dolphin/src/settings/general/generalsettingspage.cpp92
-rw-r--r--dolphin/src/settings/general/generalsettingspage.h56
-rw-r--r--dolphin/src/settings/general/previewssettingspage.cpp204
-rw-r--r--dolphin/src/settings/general/previewssettingspage.h67
-rw-r--r--dolphin/src/settings/general/statusbarsettingspage.cpp76
-rw-r--r--dolphin/src/settings/general/statusbarsettingspage.h51
-rw-r--r--dolphin/src/settings/kcm/kcmdolphingeneral.cpp94
-rw-r--r--dolphin/src/settings/kcm/kcmdolphingeneral.desktop343
-rw-r--r--dolphin/src/settings/kcm/kcmdolphingeneral.h46
-rw-r--r--dolphin/src/settings/kcm/kcmdolphinnavigation.cpp68
-rw-r--r--dolphin/src/settings/kcm/kcmdolphinnavigation.desktop346
-rw-r--r--dolphin/src/settings/kcm/kcmdolphinnavigation.h45
-rw-r--r--dolphin/src/settings/kcm/kcmdolphinservices.cpp68
-rw-r--r--dolphin/src/settings/kcm/kcmdolphinservices.desktop293
-rw-r--r--dolphin/src/settings/kcm/kcmdolphinservices.h45
-rw-r--r--dolphin/src/settings/kcm/kcmdolphinviewmodes.cpp109
-rw-r--r--dolphin/src/settings/kcm/kcmdolphinviewmodes.desktop345
-rw-r--r--dolphin/src/settings/kcm/kcmdolphinviewmodes.h51
-rw-r--r--dolphin/src/settings/navigation/navigationsettingspage.cpp118
-rw-r--r--dolphin/src/settings/navigation/navigationsettingspage.h54
-rw-r--r--dolphin/src/settings/serviceitemdelegate.cpp132
-rw-r--r--dolphin/src/settings/serviceitemdelegate.h59
-rw-r--r--dolphin/src/settings/servicemodel.cpp108
-rw-r--r--dolphin/src/settings/servicemodel.h68
-rw-r--r--dolphin/src/settings/services/servicemenu.knsrc8
-rwxr-xr-xdolphin/src/settings/services/servicemenudeinstallation37
-rwxr-xr-xdolphin/src/settings/services/servicemenuinstallation89
-rw-r--r--dolphin/src/settings/services/servicessettingspage.cpp276
-rw-r--r--dolphin/src/settings/services/servicessettingspage.h83
-rw-r--r--dolphin/src/settings/settingspagebase.cpp31
-rw-r--r--dolphin/src/settings/settingspagebase.h54
-rw-r--r--dolphin/src/settings/startup/startupsettingspage.cpp184
-rw-r--r--dolphin/src/settings/startup/startupsettingspage.h67
-rw-r--r--dolphin/src/settings/trash/trashsettingspage.cpp70
-rw-r--r--dolphin/src/settings/trash/trashsettingspage.h47
-rw-r--r--dolphin/src/settings/viewmodes/dolphinfontrequester.cpp107
-rw-r--r--dolphin/src/settings/viewmodes/dolphinfontrequester.h75
-rw-r--r--dolphin/src/settings/viewmodes/viewmodesettings.cpp143
-rw-r--r--dolphin/src/settings/viewmodes/viewmodesettings.h70
-rw-r--r--dolphin/src/settings/viewmodes/viewsettingspage.cpp83
-rw-r--r--dolphin/src/settings/viewmodes/viewsettingspage.h52
-rw-r--r--dolphin/src/settings/viewmodes/viewsettingstab.cpp292
-rw-r--r--dolphin/src/settings/viewmodes/viewsettingstab.h76
-rw-r--r--dolphin/src/settings/viewpropertiesdialog.cpp411
-rw-r--r--dolphin/src/settings/viewpropertiesdialog.h86
-rw-r--r--dolphin/src/settings/viewpropsprogressinfo.cpp148
-rw-r--r--dolphin/src/settings/viewpropsprogressinfo.h78
-rw-r--r--dolphin/src/statusbar/dolphinstatusbar.cpp360
-rw-r--r--dolphin/src/statusbar/dolphinstatusbar.h158
-rw-r--r--dolphin/src/statusbar/mountpointobserver.cpp48
-rw-r--r--dolphin/src/statusbar/mountpointobserver.h109
-rw-r--r--dolphin/src/statusbar/mountpointobservercache.cpp99
-rw-r--r--dolphin/src/statusbar/mountpointobservercache.h58
-rw-r--r--dolphin/src/statusbar/spaceinfoobserver.cpp89
-rw-r--r--dolphin/src/statusbar/spaceinfoobserver.h51
-rw-r--r--dolphin/src/statusbar/statusbarspaceinfo.cpp90
-rw-r--r--dolphin/src/statusbar/statusbarspaceinfo.h63
-rw-r--r--dolphin/src/tests/CMakeLists.txt133
-rw-r--r--dolphin/src/tests/dolphinsearchboxtest.cpp70
-rw-r--r--dolphin/src/tests/kfileitemlistviewtest.cpp117
-rw-r--r--dolphin/src/tests/kfileitemmodelbenchmark.cpp334
-rw-r--r--dolphin/src/tests/kfileitemmodeltest.cpp1733
-rw-r--r--dolphin/src/tests/kitemlistcontrollertest.cpp675
-rw-r--r--dolphin/src/tests/kitemlistkeyboardsearchmanagertest.cpp152
-rw-r--r--dolphin/src/tests/kitemlistselectionmanagertest.cpp576
-rw-r--r--dolphin/src/tests/kitemrangetest.cpp75
-rw-r--r--dolphin/src/tests/kitemsettest.cpp612
-rw-r--r--dolphin/src/tests/kstandarditemmodeltest.cpp117
-rw-r--r--dolphin/src/tests/testdir.cpp121
-rw-r--r--dolphin/src/tests/testdir.h59
-rw-r--r--dolphin/src/tests/viewpropertiestest.cpp101
-rw-r--r--dolphin/src/views/dolphinfileitemlistwidget.cpp129
-rw-r--r--dolphin/src/views/dolphinfileitemlistwidget.h52
-rw-r--r--dolphin/src/views/dolphinitemlistview.cpp265
-rw-r--r--dolphin/src/views/dolphinitemlistview.h70
-rw-r--r--dolphin/src/views/dolphinnewfilemenuobserver.cpp66
-rw-r--r--dolphin/src/views/dolphinnewfilemenuobserver.h57
-rw-r--r--dolphin/src/views/dolphinremoteencoding.cpp238
-rw-r--r--dolphin/src/views/dolphinremoteencoding.h69
-rw-r--r--dolphin/src/views/dolphinview.cpp1691
-rw-r--r--dolphin/src/views/dolphinview.h790
-rw-r--r--dolphin/src/views/dolphinviewactionhandler.cpp598
-rw-r--r--dolphin/src/views/dolphinviewactionhandler.h260
-rw-r--r--dolphin/src/views/draganddrophelper.cpp67
-rw-r--r--dolphin/src/views/draganddrophelper.h61
-rw-r--r--dolphin/src/views/renamedialog.cpp221
-rw-r--r--dolphin/src/views/renamedialog.h71
-rw-r--r--dolphin/src/views/tooltips/filemetadatatooltip.cpp181
-rw-r--r--dolphin/src/views/tooltips/filemetadatatooltip.h86
-rw-r--r--dolphin/src/views/tooltips/tooltipmanager.cpp269
-rw-r--r--dolphin/src/views/tooltips/tooltipmanager.h89
-rw-r--r--dolphin/src/views/versioncontrol/fileviewversioncontrolplugin.desktop69
-rw-r--r--dolphin/src/views/versioncontrol/updateitemstatesthread.cpp78
-rw-r--r--dolphin/src/views/versioncontrol/updateitemstatesthread.h65
-rw-r--r--dolphin/src/views/versioncontrol/versioncontrolobserver.cpp368
-rw-r--r--dolphin/src/views/versioncontrol/versioncontrolobserver.h155
-rw-r--r--dolphin/src/views/viewmodecontroller.cpp88
-rw-r--r--dolphin/src/views/viewmodecontroller.h124
-rw-r--r--dolphin/src/views/viewproperties.cpp478
-rw-r--r--dolphin/src/views/viewproperties.h185
-rw-r--r--dolphin/src/views/zoomlevelinfo.cpp60
-rw-r--r--dolphin/src/views/zoomlevelinfo.h51
-rw-r--r--lib/konq/CMakeLists.txt4
-rw-r--r--lib/konq/kversioncontrolplugin.cpp30
-rw-r--r--lib/konq/kversioncontrolplugin.h182
-rw-r--r--lib/konq/kversioncontrolplugin2.cpp51
-rw-r--r--lib/konq/kversioncontrolplugin2.h229
301 files changed, 60541 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index cc36679..b06ba01 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -37,6 +37,7 @@ if ( KActivities_FOUND )
endif ( KActivities_FOUND )
add_subdirectory( lib )
+add_subdirectory( dolphin )
add_subdirectory( kdialog )
add_subdirectory( keditbookmarks )
add_subdirectory( konqueror )
diff --git a/dolphin/AUTHORS b/dolphin/AUTHORS
new file mode 100644
index 0000000..9019e9f
--- /dev/null
+++ b/dolphin/AUTHORS
@@ -0,0 +1,6 @@
+Peter Penz <peter.penz[email protected]>
+David Faure <[email protected]>
+Aaron J. Seigo <[email protected]>
+Rafael Fernández López <[email protected]>
+Kevin Ottens <[email protected]>
+Holger Freyther <[email protected]> \ No newline at end of file
diff --git a/dolphin/CMakeLists.txt b/dolphin/CMakeLists.txt
new file mode 100644
index 0000000..4b43d67
--- /dev/null
+++ b/dolphin/CMakeLists.txt
@@ -0,0 +1,2 @@
+
+add_subdirectory(src)
diff --git a/dolphin/COPYING b/dolphin/COPYING
new file mode 100644
index 0000000..5185fd3
--- /dev/null
+++ b/dolphin/COPYING
@@ -0,0 +1,346 @@
+NOTE! The GPL below is copyrighted by the Free Software Foundation, but
+the instance of code that it refers to (the kde programs) are copyrighted
+by the authors who actually wrote it.
+
+---------------------------------------------------------------------------
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/dolphin/COPYING.DOC b/dolphin/COPYING.DOC
new file mode 100644
index 0000000..4a0fe1c
--- /dev/null
+++ b/dolphin/COPYING.DOC
@@ -0,0 +1,397 @@
+ GNU Free Documentation License
+ Version 1.2, November 2002
+
+
+ Copyright (C) 2000,2001,2002 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+0. PREAMBLE
+
+The purpose of this License is to make a manual, textbook, or other
+functional and useful document "free" in the sense of freedom: to
+assure everyone the effective freedom to copy and redistribute it,
+with or without modifying it, either commercially or noncommercially.
+Secondarily, this License preserves for the author and publisher a way
+to get credit for their work, while not being considered responsible
+for modifications made by others.
+
+This License is a kind of "copyleft", which means that derivative
+works of the document must themselves be free in the same sense. It
+complements the GNU General Public License, which is a copyleft
+license designed for free software.
+
+We have designed this License in order to use it for manuals for free
+software, because free software needs free documentation: a free
+program should come with manuals providing the same freedoms that the
+software does. But this License is not limited to software manuals;
+it can be used for any textual work, regardless of subject matter or
+whether it is published as a printed book. We recommend this License
+principally for works whose purpose is instruction or reference.
+
+
+1. APPLICABILITY AND DEFINITIONS
+
+This License applies to any manual or other work, in any medium, that
+contains a notice placed by the copyright holder saying it can be
+distributed under the terms of this License. Such a notice grants a
+world-wide, royalty-free license, unlimited in duration, to use that
+work under the conditions stated herein. The "Document", below,
+refers to any such manual or work. Any member of the public is a
+licensee, and is addressed as "you". You accept the license if you
+copy, modify or distribute the work in a way requiring permission
+under copyright law.
+
+A "Modified Version" of the Document means any work containing the
+Document or a portion of it, either copied verbatim, or with
+modifications and/or translated into another language.
+
+A "Secondary Section" is a named appendix or a front-matter section of
+the Document that deals exclusively with the relationship of the
+publishers or authors of the Document to the Document's overall subject
+(or to related matters) and contains nothing that could fall directly
+within that overall subject. (Thus, if the Document is in part a
+textbook of mathematics, a Secondary Section may not explain any
+mathematics.) The relationship could be a matter of historical
+connection with the subject or with related matters, or of legal,
+commercial, philosophical, ethical or political position regarding
+them.
+
+The "Invariant Sections" are certain Secondary Sections whose titles
+are designated, as being those of Invariant Sections, in the notice
+that says that the Document is released under this License. If a
+section does not fit the above definition of Secondary then it is not
+allowed to be designated as Invariant. The Document may contain zero
+Invariant Sections. If the Document does not identify any Invariant
+Sections then there are none.
+
+The "Cover Texts" are certain short passages of text that are listed,
+as Front-Cover Texts or Back-Cover Texts, in the notice that says that
+the Document is released under this License. A Front-Cover Text may
+be at most 5 words, and a Back-Cover Text may be at most 25 words.
+
+A "Transparent" copy of the Document means a machine-readable copy,
+represented in a format whose specification is available to the
+general public, that is suitable for revising the document
+straightforwardly with generic text editors or (for images composed of
+pixels) generic paint programs or (for drawings) some widely available
+drawing editor, and that is suitable for input to text formatters or
+for automatic translation to a variety of formats suitable for input
+to text formatters. A copy made in an otherwise Transparent file
+format whose markup, or absence of markup, has been arranged to thwart
+or discourage subsequent modification by readers is not Transparent.
+An image format is not Transparent if used for any substantial amount
+of text. A copy that is not "Transparent" is called "Opaque".
+
+Examples of suitable formats for Transparent copies include plain
+ASCII without markup, Texinfo input format, LaTeX input format, SGML
+or XML using a publicly available DTD, and standard-conforming simple
+HTML, PostScript or PDF designed for human modification. Examples of
+transparent image formats include PNG, XCF and JPG. Opaque formats
+include proprietary formats that can be read and edited only by
+proprietary word processors, SGML or XML for which the DTD and/or
+processing tools are not generally available, and the
+machine-generated HTML, PostScript or PDF produced by some word
+processors for output purposes only.
+
+The "Title Page" means, for a printed book, the title page itself,
+plus such following pages as are needed to hold, legibly, the material
+this License requires to appear in the title page. For works in
+formats which do not have any title page as such, "Title Page" means
+the text near the most prominent appearance of the work's title,
+preceding the beginning of the body of the text.
+
+A section "Entitled XYZ" means a named subunit of the Document whose
+title either is precisely XYZ or contains XYZ in parentheses following
+text that translates XYZ in another language. (Here XYZ stands for a
+specific section name mentioned below, such as "Acknowledgements",
+"Dedications", "Endorsements", or "History".) To "Preserve the Title"
+of such a section when you modify the Document means that it remains a
+section "Entitled XYZ" according to this definition.
+
+The Document may include Warranty Disclaimers next to the notice which
+states that this License applies to the Document. These Warranty
+Disclaimers are considered to be included by reference in this
+License, but only as regards disclaiming warranties: any other
+implication that these Warranty Disclaimers may have is void and has
+no effect on the meaning of this License.
+
+
+2. VERBATIM COPYING
+
+You may copy and distribute the Document in any medium, either
+commercially or noncommercially, provided that this License, the
+copyright notices, and the license notice saying this License applies
+to the Document are reproduced in all copies, and that you add no other
+conditions whatsoever to those of this License. You may not use
+technical measures to obstruct or control the reading or further
+copying of the copies you make or distribute. However, you may accept
+compensation in exchange for copies. If you distribute a large enough
+number of copies you must also follow the conditions in section 3.
+
+You may also lend copies, under the same conditions stated above, and
+you may publicly display copies.
+
+
+3. COPYING IN QUANTITY
+
+If you publish printed copies (or copies in media that commonly have
+printed covers) of the Document, numbering more than 100, and the
+Document's license notice requires Cover Texts, you must enclose the
+copies in covers that carry, clearly and legibly, all these Cover
+Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
+the back cover. Both covers must also clearly and legibly identify
+you as the publisher of these copies. The front cover must present
+the full title with all words of the title equally prominent and
+visible. You may add other material on the covers in addition.
+Copying with changes limited to the covers, as long as they preserve
+the title of the Document and satisfy these conditions, can be treated
+as verbatim copying in other respects.
+
+If the required texts for either cover are too voluminous to fit
+legibly, you should put the first ones listed (as many as fit
+reasonably) on the actual cover, and continue the rest onto adjacent
+pages.
+
+If you publish or distribute Opaque copies of the Document numbering
+more than 100, you must either include a machine-readable Transparent
+copy along with each Opaque copy, or state in or with each Opaque copy
+a computer-network location from which the general network-using
+public has access to download using public-standard network protocols
+a complete Transparent copy of the Document, free of added material.
+If you use the latter option, you must take reasonably prudent steps,
+when you begin distribution of Opaque copies in quantity, to ensure
+that this Transparent copy will remain thus accessible at the stated
+location until at least one year after the last time you distribute an
+Opaque copy (directly or through your agents or retailers) of that
+edition to the public.
+
+It is requested, but not required, that you contact the authors of the
+Document well before redistributing any large number of copies, to give
+them a chance to provide you with an updated version of the Document.
+
+
+4. MODIFICATIONS
+
+You may copy and distribute a Modified Version of the Document under
+the conditions of sections 2 and 3 above, provided that you release
+the Modified Version under precisely this License, with the Modified
+Version filling the role of the Document, thus licensing distribution
+and modification of the Modified Version to whoever possesses a copy
+of it. In addition, you must do these things in the Modified Version:
+
+A. Use in the Title Page (and on the covers, if any) a title distinct
+ from that of the Document, and from those of previous versions
+ (which should, if there were any, be listed in the History section
+ of the Document). You may use the same title as a previous version
+ if the original publisher of that version gives permission.
+B. List on the Title Page, as authors, one or more persons or entities
+ responsible for authorship of the modifications in the Modified
+ Version, together with at least five of the principal authors of the
+ Document (all of its principal authors, if it has fewer than five),
+ unless they release you from this requirement.
+C. State on the Title page the name of the publisher of the
+ Modified Version, as the publisher.
+D. Preserve all the copyright notices of the Document.
+E. Add an appropriate copyright notice for your modifications
+ adjacent to the other copyright notices.
+F. Include, immediately after the copyright notices, a license notice
+ giving the public permission to use the Modified Version under the
+ terms of this License, in the form shown in the Addendum below.
+G. Preserve in that license notice the full lists of Invariant Sections
+ and required Cover Texts given in the Document's license notice.
+H. Include an unaltered copy of this License.
+I. Preserve the section Entitled "History", Preserve its Title, and add
+ to it an item stating at least the title, year, new authors, and
+ publisher of the Modified Version as given on the Title Page. If
+ there is no section Entitled "History" in the Document, create one
+ stating the title, year, authors, and publisher of the Document as
+ given on its Title Page, then add an item describing the Modified
+ Version as stated in the previous sentence.
+J. Preserve the network location, if any, given in the Document for
+ public access to a Transparent copy of the Document, and likewise
+ the network locations given in the Document for previous versions
+ it was based on. These may be placed in the "History" section.
+ You may omit a network location for a work that was published at
+ least four years before the Document itself, or if the original
+ publisher of the version it refers to gives permission.
+K. For any section Entitled "Acknowledgements" or "Dedications",
+ Preserve the Title of the section, and preserve in the section all
+ the substance and tone of each of the contributor acknowledgements
+ and/or dedications given therein.
+L. Preserve all the Invariant Sections of the Document,
+ unaltered in their text and in their titles. Section numbers
+ or the equivalent are not considered part of the section titles.
+M. Delete any section Entitled "Endorsements". Such a section
+ may not be included in the Modified Version.
+N. Do not retitle any existing section to be Entitled "Endorsements"
+ or to conflict in title with any Invariant Section.
+O. Preserve any Warranty Disclaimers.
+
+If the Modified Version includes new front-matter sections or
+appendices that qualify as Secondary Sections and contain no material
+copied from the Document, you may at your option designate some or all
+of these sections as invariant. To do this, add their titles to the
+list of Invariant Sections in the Modified Version's license notice.
+These titles must be distinct from any other section titles.
+
+You may add a section Entitled "Endorsements", provided it contains
+nothing but endorsements of your Modified Version by various
+parties--for example, statements of peer review or that the text has
+been approved by an organization as the authoritative definition of a
+standard.
+
+You may add a passage of up to five words as a Front-Cover Text, and a
+passage of up to 25 words as a Back-Cover Text, to the end of the list
+of Cover Texts in the Modified Version. Only one passage of
+Front-Cover Text and one of Back-Cover Text may be added by (or
+through arrangements made by) any one entity. If the Document already
+includes a cover text for the same cover, previously added by you or
+by arrangement made by the same entity you are acting on behalf of,
+you may not add another; but you may replace the old one, on explicit
+permission from the previous publisher that added the old one.
+
+The author(s) and publisher(s) of the Document do not by this License
+give permission to use their names for publicity for or to assert or
+imply endorsement of any Modified Version.
+
+
+5. COMBINING DOCUMENTS
+
+You may combine the Document with other documents released under this
+License, under the terms defined in section 4 above for modified
+versions, provided that you include in the combination all of the
+Invariant Sections of all of the original documents, unmodified, and
+list them all as Invariant Sections of your combined work in its
+license notice, and that you preserve all their Warranty Disclaimers.
+
+The combined work need only contain one copy of this License, and
+multiple identical Invariant Sections may be replaced with a single
+copy. If there are multiple Invariant Sections with the same name but
+different contents, make the title of each such section unique by
+adding at the end of it, in parentheses, the name of the original
+author or publisher of that section if known, or else a unique number.
+Make the same adjustment to the section titles in the list of
+Invariant Sections in the license notice of the combined work.
+
+In the combination, you must combine any sections Entitled "History"
+in the various original documents, forming one section Entitled
+"History"; likewise combine any sections Entitled "Acknowledgements",
+and any sections Entitled "Dedications". You must delete all sections
+Entitled "Endorsements".
+
+
+6. COLLECTIONS OF DOCUMENTS
+
+You may make a collection consisting of the Document and other documents
+released under this License, and replace the individual copies of this
+License in the various documents with a single copy that is included in
+the collection, provided that you follow the rules of this License for
+verbatim copying of each of the documents in all other respects.
+
+You may extract a single document from such a collection, and distribute
+it individually under this License, provided you insert a copy of this
+License into the extracted document, and follow this License in all
+other respects regarding verbatim copying of that document.
+
+
+7. AGGREGATION WITH INDEPENDENT WORKS
+
+A compilation of the Document or its derivatives with other separate
+and independent documents or works, in or on a volume of a storage or
+distribution medium, is called an "aggregate" if the copyright
+resulting from the compilation is not used to limit the legal rights
+of the compilation's users beyond what the individual works permit.
+When the Document is included in an aggregate, this License does not
+apply to the other works in the aggregate which are not themselves
+derivative works of the Document.
+
+If the Cover Text requirement of section 3 is applicable to these
+copies of the Document, then if the Document is less than one half of
+the entire aggregate, the Document's Cover Texts may be placed on
+covers that bracket the Document within the aggregate, or the
+electronic equivalent of covers if the Document is in electronic form.
+Otherwise they must appear on printed covers that bracket the whole
+aggregate.
+
+
+8. TRANSLATION
+
+Translation is considered a kind of modification, so you may
+distribute translations of the Document under the terms of section 4.
+Replacing Invariant Sections with translations requires special
+permission from their copyright holders, but you may include
+translations of some or all Invariant Sections in addition to the
+original versions of these Invariant Sections. You may include a
+translation of this License, and all the license notices in the
+Document, and any Warranty Disclaimers, provided that you also include
+the original English version of this License and the original versions
+of those notices and disclaimers. In case of a disagreement between
+the translation and the original version of this License or a notice
+or disclaimer, the original version will prevail.
+
+If a section in the Document is Entitled "Acknowledgements",
+"Dedications", or "History", the requirement (section 4) to Preserve
+its Title (section 1) will typically require changing the actual
+title.
+
+
+9. TERMINATION
+
+You may not copy, modify, sublicense, or distribute the Document except
+as expressly provided for under this License. Any other attempt to
+copy, modify, sublicense or distribute the Document is void, and will
+automatically terminate your rights under this License. However,
+parties who have received copies, or rights, from you under this
+License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+
+10. FUTURE REVISIONS OF THIS LICENSE
+
+The Free Software Foundation may publish new, revised versions
+of the GNU Free Documentation License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns. See
+http://www.gnu.org/copyleft/.
+
+Each version of the License is given a distinguishing version number.
+If the Document specifies that a particular numbered version of this
+License "or any later version" applies to it, you have the option of
+following the terms and conditions either of that specified version or
+of any later version that has been published (not as a draft) by the
+Free Software Foundation. If the Document does not specify a version
+number of this License, you may choose any version ever published (not
+as a draft) by the Free Software Foundation.
+
+
+ADDENDUM: How to use this License for your documents
+
+To use this License in a document you have written, include a copy of
+the License in the document and put the following copyright and
+license notices just after the title page:
+
+ Copyright (c) YEAR YOUR NAME.
+ Permission is granted to copy, distribute and/or modify this document
+ under the terms of the GNU Free Documentation License, Version 1.2
+ or any later version published by the Free Software Foundation;
+ with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
+ A copy of the license is included in the section entitled "GNU
+ Free Documentation License".
+
+If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
+replace the "with...Texts." line with this:
+
+ with the Invariant Sections being LIST THEIR TITLES, with the
+ Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
+
+If you have Invariant Sections without Cover Texts, or some other
+combination of the three, merge those two alternatives to suit the
+situation.
+
+If your document contains nontrivial examples of program code, we
+recommend releasing these examples in parallel under your choice of
+free software license, such as the GNU General Public License,
+to permit their use in free software.
diff --git a/dolphin/README b/dolphin/README
new file mode 100644
index 0000000..9c2c913
--- /dev/null
+++ b/dolphin/README
@@ -0,0 +1,2 @@
+See http://dolphin.kde.org for information about Dolphin.
+
diff --git a/dolphin/src/CMakeLists.txt b/dolphin/src/CMakeLists.txt
new file mode 100644
index 0000000..6f256a2
--- /dev/null
+++ b/dolphin/src/CMakeLists.txt
@@ -0,0 +1,368 @@
+macro_optional_find_package(Baloo)
+set_package_properties(Baloo PROPERTIES DESCRIPTION "Baloo Core libraries"
+ URL "http://www.kde.org"
+ TYPE OPTIONAL
+ PURPOSE "For adding desktop-wide search and tagging support to dolphin"
+ )
+
+macro_optional_find_package(BalooWidgets)
+set_package_properties(BalooWidgets PROPERTIES DESCRIPTION "Baloos Widgets"
+ URL "http://www.kde.org"
+ TYPE OPTIONAL
+ )
+
+macro_optional_find_package(KFileMetaData)
+set_package_properties(KFileMetaData PROPERTIES
+ URL "https://projects.kde.org/kfilemetadata"
+ TYPE OPTIONAL
+ PURPOSE "For accessing file metadata labels"
+ )
+
+if (Baloo_FOUND AND BalooWidgets_FOUND AND KFileMetaData_FOUND)
+ set(HAVE_BALOO TRUE)
+endif()
+
+configure_file(config-baloo.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-baloo.h )
+
+macro_bool_to_01(X11_Xrender_FOUND HAVE_XRENDER)
+configure_file(config-X11.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-X11.h )
+
+include_directories( ${KACTIVITIES_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR} )
+
+if(HAVE_BALOO)
+ include_directories(${BALOO_INCLUDE_DIR} ${BALOO_WIDGETS_INCLUDE_DIR} ${KFILEMETADATA_INCLUDE_DIR})
+endif()
+
+add_subdirectory(tests)
+
+########### next target ###############
+
+set(dolphinprivate_LIB_SRCS
+ kitemviews/kfileitemlistview.cpp
+ kitemviews/kfileitemlistwidget.cpp
+ kitemviews/kfileitemmodel.cpp
+ kitemviews/kfileitemmodelrolesupdater.cpp
+ kitemviews/kitemlistcontainer.cpp
+ kitemviews/kitemlistcontroller.cpp
+ kitemviews/kitemlistgroupheader.cpp
+ kitemviews/kitemlistheader.cpp
+ kitemviews/kitemlistselectionmanager.cpp
+ kitemviews/kitemliststyleoption.cpp
+ kitemviews/kitemlistview.cpp
+ kitemviews/kitemlistviewaccessible.cpp
+ kitemviews/kitemlistwidget.cpp
+ kitemviews/kitemmodelbase.cpp
+ kitemviews/kitemset.cpp
+ kitemviews/kstandarditem.cpp
+ kitemviews/kstandarditemlistgroupheader.cpp
+ kitemviews/kstandarditemlistwidget.cpp
+ kitemviews/kstandarditemlistview.cpp
+ kitemviews/kstandarditemmodel.cpp
+ kitemviews/private/kdirectorycontentscounter.cpp
+ kitemviews/private/kdirectorycontentscounterworker.cpp
+ kitemviews/private/kfileitemclipboard.cpp
+ kitemviews/private/kfileitemmodeldirlister.cpp
+ kitemviews/private/kfileitemmodelfilter.cpp
+ kitemviews/private/kitemlistheaderwidget.cpp
+ kitemviews/private/kitemlistkeyboardsearchmanager.cpp
+ kitemviews/private/kitemlistroleeditor.cpp
+ kitemviews/private/kitemlistrubberband.cpp
+ kitemviews/private/kitemlistselectiontoggle.cpp
+ kitemviews/private/kitemlistsizehintresolver.cpp
+ kitemviews/private/kitemlistsmoothscroller.cpp
+ kitemviews/private/kitemlistviewanimation.cpp
+ kitemviews/private/kitemlistviewlayouter.cpp
+ kitemviews/private/kpixmapmodifier.cpp
+ settings/additionalinfodialog.cpp
+ settings/applyviewpropsjob.cpp
+ settings/viewmodes/viewmodesettings.cpp
+ settings/viewpropertiesdialog.cpp
+ settings/viewpropsprogressinfo.cpp
+ views/dolphinfileitemlistwidget.cpp
+ views/dolphinitemlistview.cpp
+ views/dolphinnewfilemenuobserver.cpp
+ views/dolphinremoteencoding.cpp
+ views/dolphinview.cpp
+ views/dolphinviewactionhandler.cpp
+ views/draganddrophelper.cpp
+ views/renamedialog.cpp
+ views/tooltips/filemetadatatooltip.cpp
+ views/tooltips/tooltipmanager.cpp
+ views/versioncontrol/updateitemstatesthread.cpp
+ views/versioncontrol/versioncontrolobserver.cpp
+ views/viewmodecontroller.cpp
+ views/viewproperties.cpp
+ views/zoomlevelinfo.cpp
+ dolphinremoveaction.cpp
+ dolphinnewfilemenu.cpp
+)
+
+if(HAVE_BALOO)
+ set(dolphinprivate_LIB_SRCS
+ ${dolphinprivate_LIB_SRCS}
+ kitemviews/private/kbaloorolesprovider.cpp
+ )
+endif()
+
+kde4_add_kcfg_files(dolphinprivate_LIB_SRCS
+ settings/dolphin_compactmodesettings.kcfgc
+ settings/dolphin_directoryviewpropertysettings.kcfgc
+ settings/dolphin_detailsmodesettings.kcfgc
+ settings/dolphin_iconsmodesettings.kcfgc
+ settings/dolphin_generalsettings.kcfgc
+ settings/dolphin_versioncontrolsettings.kcfgc
+)
+
+kde4_add_library(dolphinprivate SHARED ${dolphinprivate_LIB_SRCS})
+
+target_link_libraries(
+ dolphinprivate
+ ${KDE4_KFILE_LIBS}
+ konq
+ ${KDE4_KNEWSTUFF3_LIBS}
+)
+
+if(HAVE_BALOO)
+ target_link_libraries(
+ dolphinprivate
+ ${BALOO_LIBRARIES}
+ ${BALOO_WIDGETS_LIBRARY}
+ ${KFILEMETADATA_LIBRARY}
+ )
+endif()
+
+if(X11_Xrender_FOUND)
+ target_link_libraries(dolphinprivate ${X11_Xrender_LIB})
+endif(X11_Xrender_FOUND)
+
+target_link_libraries(dolphinprivate ${KDE4_PLASMA_LIBS})
+
+set_target_properties(dolphinprivate PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION} )
+install(TARGETS dolphinprivate ${INSTALL_TARGETS_DEFAULT_ARGS})
+
+##########################################
+
+set(dolphinpart_SRCS
+ dolphinpart.cpp
+)
+
+# Add dolphinpart_ext.cpp conditionally, only with KDE > 4.9.1.
+if (${KDE_VERSION} VERSION_GREATER "4.9.1")
+set(dolphinpart_SRCS
+ ${dolphinpart_SRCS}
+ dolphinpart_ext.cpp)
+endif (${KDE_VERSION} VERSION_GREATER "4.9.1")
+
+kde4_add_plugin(dolphinpart ${dolphinpart_SRCS})
+
+target_link_libraries(dolphinpart dolphinprivate konq ${KDE4_KPARTS_LIBS} ${KDE4_KFILE_LIBS})
+
+install(TARGETS dolphinpart DESTINATION ${PLUGIN_INSTALL_DIR})
+
+install(FILES dolphinpart.rc DESTINATION ${DATA_INSTALL_DIR}/dolphinpart)
+install(FILES dolphinpart.desktop DESTINATION ${SERVICES_INSTALL_DIR} )
+install(FILES views/versioncontrol/fileviewversioncontrolplugin.desktop DESTINATION ${SERVICETYPES_INSTALL_DIR})
+
+##########################################
+
+set(dolphin_SRCS
+ dolphinapplication.cpp
+ dolphindockwidget.cpp
+ dolphinmainwindow.cpp
+ dolphinviewcontainer.cpp
+ dolphincontextmenu.cpp
+ dolphintabbar.cpp
+ dolphinrecenttabsmenu.cpp
+ dolphintabpage.cpp
+ dolphintabwidget.cpp
+ filterbar/filterbar.cpp
+ main.cpp
+ panels/information/filemetadataconfigurationdialog.cpp
+ panels/information/informationpanel.cpp
+ panels/information/informationpanelcontent.cpp
+ panels/information/pixmapviewer.cpp
+ panels/information/phononwidget.cpp
+ panels/places/placespanel.cpp
+ panels/places/placesitem.cpp
+ panels/places/placesitemeditdialog.cpp
+ panels/places/placesitemlistgroupheader.cpp
+ panels/places/placesitemlistwidget.cpp
+ panels/places/placesitemmodel.cpp
+ panels/places/placesitemsignalhandler.cpp
+ panels/places/placesview.cpp
+ panels/panel.cpp
+ panels/folders/foldersitemlistwidget.cpp
+ panels/folders/treeviewcontextmenu.cpp
+ panels/folders/folderspanel.cpp
+ search/dolphinfacetswidget.cpp
+ search/dolphinsearchbox.cpp
+ settings/general/behaviorsettingspage.cpp
+ settings/general/configurepreviewplugindialog.cpp
+ settings/general/confirmationssettingspage.cpp
+ settings/general/generalsettingspage.cpp
+ settings/general/previewssettingspage.cpp
+ settings/general/statusbarsettingspage.cpp
+ settings/dolphinsettingsdialog.cpp
+ settings/navigation/navigationsettingspage.cpp
+ settings/services/servicessettingspage.cpp
+ settings/settingspagebase.cpp
+ settings/serviceitemdelegate.cpp
+ settings/servicemodel.cpp
+ settings/startup/startupsettingspage.cpp
+ settings/trash/trashsettingspage.cpp
+ settings/viewmodes/dolphinfontrequester.cpp
+ settings/viewmodes/viewsettingspage.cpp
+ settings/viewmodes/viewmodesettings.cpp
+ settings/viewmodes/viewsettingstab.cpp
+ statusbar/dolphinstatusbar.cpp
+ statusbar/mountpointobserver.cpp
+ statusbar/mountpointobservercache.cpp
+ statusbar/spaceinfoobserver.cpp
+ statusbar/statusbarspaceinfo.cpp
+ views/zoomlevelinfo.cpp
+)
+
+kde4_add_kcfg_files(dolphin_SRCS
+ panels/folders/dolphin_folderspanelsettings.kcfgc
+ panels/information/dolphin_informationpanelsettings.kcfgc
+ panels/places/dolphin_placespanelsettings.kcfgc
+ settings/dolphin_compactmodesettings.kcfgc
+ settings/dolphin_detailsmodesettings.kcfgc
+ settings/dolphin_generalsettings.kcfgc
+ settings/dolphin_iconsmodesettings.kcfgc
+ search/dolphin_searchsettings.kcfgc
+ settings/dolphin_versioncontrolsettings.kcfgc
+)
+
+if(NOT WIN32)
+ set(dolphin_SRCS ${dolphin_SRCS} panels/terminal/terminalpanel.cpp)
+endif(NOT WIN32)
+
+kde4_add_app_icon(dolphin_SRCS "${KDE4_ICON_INSTALL_DIR}/oxygen/*/apps/system-file-manager.png")
+
+kde4_add_kdeinit_executable(dolphin ${dolphin_SRCS})
+
+target_link_libraries(kdeinit_dolphin
+ ${KDE4_KDEPRINT_LIBS}
+ ${KDE4_KFILE_LIBS}
+ ${KDE4_KPARTS_LIBS}
+ ${KDE4_KCMUTILS_LIBRARY}
+ konq
+ dolphinprivate
+ knewstuff3
+ ${KDE4_SOLID_LIBS}
+ ${KDE4_PHONON_LIBS}
+)
+
+if(HAVE_BALOO)
+ target_link_libraries(kdeinit_dolphin
+ ${BALOO_LIBRARIES}
+ ${BALOO_WIDGETS_LIBRARY}
+ )
+endif()
+
+if (KActivities_FOUND)
+ target_link_libraries(
+ kdeinit_dolphin
+ ${KACTIVITIES_LIBRARY}
+ )
+endif (KActivities_FOUND)
+
+install(TARGETS kdeinit_dolphin ${INSTALL_TARGETS_DEFAULT_ARGS})
+install(TARGETS dolphin ${INSTALL_TARGETS_DEFAULT_ARGS})
+
+##########################################
+
+set(kcm_dolphinviewmodes_PART_SRCS
+ settings/kcm/kcmdolphinviewmodes.cpp
+ settings/viewmodes/dolphinfontrequester.cpp
+ settings/viewmodes/viewmodesettings.cpp
+ settings/viewmodes/viewsettingstab.cpp
+ views/zoomlevelinfo.cpp)
+
+set(kcm_dolphinnavigation_PART_SRCS
+ settings/kcm/kcmdolphinnavigation.cpp
+ settings/navigation/navigationsettingspage.cpp
+ settings/settingspagebase.cpp)
+
+set(kcm_dolphinservices_PART_SRCS
+ settings/kcm/kcmdolphinservices.cpp
+ settings/services/servicessettingspage.cpp
+ settings/settingspagebase.cpp
+ settings/serviceitemdelegate.cpp
+ settings/servicemodel.cpp)
+
+set(kcm_dolphingeneral_PART_SRCS
+ settings/kcm/kcmdolphingeneral.cpp
+ settings/general/behaviorsettingspage.cpp
+ settings/general/previewssettingspage.cpp
+ settings/general/configurepreviewplugindialog.cpp
+ settings/general/confirmationssettingspage.cpp
+ settings/settingspagebase.cpp
+ settings/serviceitemdelegate.cpp
+ settings/servicemodel.cpp)
+
+kde4_add_kcfg_files(kcm_dolphinviewmodes_PART_SRCS
+ settings/dolphin_compactmodesettings.kcfgc
+ settings/dolphin_directoryviewpropertysettings.kcfgc
+ settings/dolphin_detailsmodesettings.kcfgc
+ settings/dolphin_iconsmodesettings.kcfgc
+ settings/dolphin_generalsettings.kcfgc
+ settings/dolphin_versioncontrolsettings.kcfgc
+)
+
+kde4_add_kcfg_files(kcm_dolphinnavigation_PART_SRCS
+ settings/dolphin_generalsettings.kcfgc)
+
+kde4_add_kcfg_files(kcm_dolphinservices_PART_SRCS
+ settings/dolphin_generalsettings.kcfgc
+ settings/dolphin_versioncontrolsettings.kcfgc)
+
+kde4_add_kcfg_files(kcm_dolphingeneral_PART_SRCS
+ settings/dolphin_generalsettings.kcfgc)
+
+kde4_add_plugin(kcm_dolphinviewmodes ${kcm_dolphinviewmodes_PART_SRCS})
+kde4_add_plugin(kcm_dolphinnavigation ${kcm_dolphinnavigation_PART_SRCS})
+kde4_add_plugin(kcm_dolphinservices ${kcm_dolphinservices_PART_SRCS})
+kde4_add_plugin(kcm_dolphingeneral ${kcm_dolphingeneral_PART_SRCS})
+
+target_link_libraries(kcm_dolphinviewmodes ${KDE4_KDEUI_LIBS} ${KDE4_KFILE_LIBS} dolphinprivate)
+target_link_libraries(kcm_dolphinnavigation ${KDE4_KDEUI_LIBS} ${KDE4_KFILE_LIBS} dolphinprivate)
+target_link_libraries(kcm_dolphinservices ${KDE4_KDEUI_LIBS} ${KDE4_KFILE_LIBS} ${KDE4_KIO_LIBS} ${KDE4_KNEWSTUFF3_LIBRARY} dolphinprivate)
+target_link_libraries(kcm_dolphingeneral ${KDE4_KDEUI_LIBS} ${KDE4_KFILE_LIBS} ${KDE4_KIO_LIBS} dolphinprivate)
+
+install(TARGETS kcm_dolphinviewmodes DESTINATION ${PLUGIN_INSTALL_DIR} )
+install(TARGETS kcm_dolphinnavigation DESTINATION ${PLUGIN_INSTALL_DIR} )
+install(TARGETS kcm_dolphinservices DESTINATION ${PLUGIN_INSTALL_DIR} )
+install(TARGETS kcm_dolphingeneral DESTINATION ${PLUGIN_INSTALL_DIR} )
+
+#########################################
+
+set(kio_search_PART_SRCS
+ search/filenamesearchprotocol.cpp)
+kde4_add_plugin(kio_filenamesearch ${kio_search_PART_SRCS})
+target_link_libraries(kio_filenamesearch ${KDE4_KIO_LIBS})
+install(TARGETS kio_filenamesearch DESTINATION ${PLUGIN_INSTALL_DIR})
+
+########### install files ###############
+
+install( PROGRAMS dolphin.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} )
+install( FILES settings/dolphin_directoryviewpropertysettings.kcfg
+ settings/dolphin_generalsettings.kcfg
+ settings/dolphin_compactmodesettings.kcfg
+ settings/dolphin_iconsmodesettings.kcfg
+ settings/dolphin_detailsmodesettings.kcfg
+ settings/dolphin_versioncontrolsettings.kcfg
+ DESTINATION ${KCFG_INSTALL_DIR} )
+install( FILES dolphinui.rc DESTINATION ${DATA_INSTALL_DIR}/dolphin )
+install( FILES dolphin.appdata.xml DESTINATION ${SHARE_INSTALL_PREFIX}/appdata )
+install( FILES search/filenamesearch.protocol DESTINATION ${SERVICES_INSTALL_DIR} )
+install( FILES settings/kcm/kcmdolphinviewmodes.desktop DESTINATION
+${SERVICES_INSTALL_DIR} )
+install( FILES settings/kcm/kcmdolphinnavigation.desktop DESTINATION ${SERVICES_INSTALL_DIR} )
+install( FILES settings/kcm/kcmdolphinservices.desktop DESTINATION ${SERVICES_INSTALL_DIR} )
+install( FILES settings/kcm/kcmdolphingeneral.desktop DESTINATION ${SERVICES_INSTALL_DIR} )
+install( FILES settings/services/servicemenu.knsrc DESTINATION ${CONFIG_INSTALL_DIR} )
+install( PROGRAMS settings/services/servicemenuinstallation DESTINATION ${BIN_INSTALL_DIR} )
+install( PROGRAMS settings/services/servicemenudeinstallation DESTINATION ${BIN_INSTALL_DIR} )
+
diff --git a/dolphin/src/Messages.sh b/dolphin/src/Messages.sh
new file mode 100755
index 0000000..1b46bbb
--- /dev/null
+++ b/dolphin/src/Messages.sh
@@ -0,0 +1,4 @@
+#! /usr/bin/env bash
+$EXTRACTRC `find . -name \*.ui -o -name \*.rc -o -name \*.kcfg` >> rc.cpp
+$XGETTEXT `find . -name \*.cpp` -o $podir/dolphin.pot
+rm -f rc.cpp
diff --git a/dolphin/src/config-X11.h.cmake b/dolphin/src/config-X11.h.cmake
new file mode 100644
index 0000000..d15b985
--- /dev/null
+++ b/dolphin/src/config-X11.h.cmake
@@ -0,0 +1 @@
+#cmakedefine HAVE_XRENDER 1
diff --git a/dolphin/src/config-baloo.h.cmake b/dolphin/src/config-baloo.h.cmake
new file mode 100644
index 0000000..ad95b2f
--- /dev/null
+++ b/dolphin/src/config-baloo.h.cmake
@@ -0,0 +1 @@
+#cmakedefine HAVE_BALOO
diff --git a/dolphin/src/dolphin.appdata.xml b/dolphin/src/dolphin.appdata.xml
new file mode 100644
index 0000000..1d894a5
--- /dev/null
+++ b/dolphin/src/dolphin.appdata.xml
@@ -0,0 +1,431 @@
+<?xml version="1.0" encoding="utf-8"?>
+<component type="desktop">
+ <id>dolphin.desktop</id>
+ <metadata_license>CC0-1.0</metadata_license>
+ <project_license>GPL-2.0+</project_license>
+ <name>Dolphin</name>
+ <name xml:lang="ar">دولفين</name>
+ <name xml:lang="bs">Dolphin</name>
+ <name xml:lang="ca">Dolphin</name>
+ <name xml:lang="cs">Dolphin</name>
+ <name xml:lang="da">Dolphin</name>
+ <name xml:lang="de">Dolphin</name>
+ <name xml:lang="el">Dolphin</name>
+ <name xml:lang="en-GB">Dolphin</name>
+ <name xml:lang="es">Dolphin</name>
+ <name xml:lang="et">Dolphin</name>
+ <name xml:lang="fi">Dolphin</name>
+ <name xml:lang="fr">Dolphin</name>
+ <name xml:lang="hu">Dolphin</name>
+ <name xml:lang="ia">Dolphin</name>
+ <name xml:lang="it">Dolphin</name>
+ <name xml:lang="ko">Dolphin</name>
+ <name xml:lang="lt">Dolphin</name>
+ <name xml:lang="nb">Dolphin</name>
+ <name xml:lang="nds">Dolphin</name>
+ <name xml:lang="nl">Dolphin</name>
+ <name xml:lang="pa">ਡਾਲਫਿਨ</name>
+ <name xml:lang="pl">Dolphin</name>
+ <name xml:lang="pt">Dolphin</name>
+ <name xml:lang="pt-BR">Dolphin</name>
+ <name xml:lang="ro">Dolphin</name>
+ <name xml:lang="sk">Dolphin</name>
+ <name xml:lang="sl">Dolphin</name>
+ <name xml:lang="sr">Делфин</name>
+ <name xml:lang="sr-Latn">Dolphin</name>
+ <name xml:lang="sr-ijekavian">Делфин</name>
+ <name xml:lang="sr-ijekavianlatin">Dolphin</name>
+ <name xml:lang="sv">Dolphin</name>
+ <name xml:lang="tr">Dolphin</name>
+ <name xml:lang="uk">Dolphin</name>
+ <name xml:lang="x-test">xxDolphinxx</name>
+ <name xml:lang="zh-TW">Dolphin</name>
+ <summary>File Manager</summary>
+ <summary xml:lang="ar">مدير ملفات</summary>
+ <summary xml:lang="bs">Upravitelj datoteka</summary>
+ <summary xml:lang="ca">Gestor de fitxers</summary>
+ <summary xml:lang="cs">Správce souborů</summary>
+ <summary xml:lang="da">Filhåndtering</summary>
+ <summary xml:lang="de">Dateiverwaltung</summary>
+ <summary xml:lang="el">Διαχειριστής αρχείων</summary>
+ <summary xml:lang="en-GB">File Manager</summary>
+ <summary xml:lang="es">Administrador de archivos</summary>
+ <summary xml:lang="et">Failihaldur</summary>
+ <summary xml:lang="fi">Tiedostonhallinta</summary>
+ <summary xml:lang="fr">Gestionnaire de fichier</summary>
+ <summary xml:lang="hu">Fájlkezelő</summary>
+ <summary xml:lang="ia">Gerente de File</summary>
+ <summary xml:lang="it">Gestore file</summary>
+ <summary xml:lang="ko">파일 관리자</summary>
+ <summary xml:lang="lt">Failų tvarkyklė</summary>
+ <summary xml:lang="nb">Filbehandler</summary>
+ <summary xml:lang="nds">Dateipleger</summary>
+ <summary xml:lang="nl">Bestandsbeheerder</summary>
+ <summary xml:lang="pa">ਫਾਇਲ ਮੈਨੇਜਰ</summary>
+ <summary xml:lang="pl">Zarządzanie plikami</summary>
+ <summary xml:lang="pt">Gestor de Ficheiros</summary>
+ <summary xml:lang="pt-BR">Gerenciador de arquivos</summary>
+ <summary xml:lang="ro">Gestionar de fișiere</summary>
+ <summary xml:lang="sk">Správca súborov</summary>
+ <summary xml:lang="sl">Upravljalnik datotek</summary>
+ <summary xml:lang="sr">Менаџер фајлова</summary>
+ <summary xml:lang="sr-Latn">Menadžer fajlova</summary>
+ <summary xml:lang="sr-ijekavian">Менаџер фајлова</summary>
+ <summary xml:lang="sr-ijekavianlatin">Menadžer fajlova</summary>
+ <summary xml:lang="sv">Filhanterare</summary>
+ <summary xml:lang="tr">Dosya Yöneticisi</summary>
+ <summary xml:lang="uk">Програма для керування файлами</summary>
+ <summary xml:lang="x-test">xxFile Managerxx</summary>
+ <summary xml:lang="zh-TW">檔案管理員</summary>
+ <description>
+ <p>Dolphin is a lightweight file manager. It has been designed with ease of use and simplicity in mind, while still allowing flexibility and customisation. This means that you can do your file management exactly the way you want to do it.</p>
+ <p xml:lang="ar">دولفين هو مدير ملفات خفيف. صُمِّم دولفين مع أخذ سهولة الاستخدام والبساطة بعين الاعتبار، مع السماح بالمرونة والتخصيص. يعني هذا أنه يمكنك إدارة ملفاتك كما تريد تمامًا.</p>
+ <p xml:lang="bs">Dolphinje lagan file manager. On je bio dizajniran sa lakoćom korišćenja i jednostavnosti u vidu, još omogućavajući fleksibilnost i prilagođavanje. To znači da možete da radite svoje upravljanje datotekama onako kako želite da to uradi.</p>
+ <p xml:lang="ca">El Dolphin és un gestor de fitxers lleuger. S'ha dissenyat pensant en facilitar el seu ús i que sigui simple, permetent la flexibilitat i la personalització. Això vol dir que podeu fer la gestió dels vostres fitxers de la manera exacta com ho vulgueu fer.</p>
+ <p xml:lang="da">Dolphin er letvægtsfilhåndtering. Den er blevet designet med henblik på brugervenlighed og simpelhed, mens fleksibilitet og tilpasning stadig er muligt. Det betyder at du klare din filhåndtering nøjagtig på den måde du vil gøre det.</p>
+ <p xml:lang="el">Το Dolphin είναι ένας ελαφρύς διαχειριστής αρχείων. Έχει σχεδιαστεί με φιλοσοφία την απλότητα για ευκολία στη χρήση, ενώ επιτρέπει ευελιξία και προσαρμογές. Αυτό σημαίνει ότι μπορείτε να διαχειριστείτε τα αρχεία σας με τον τρόπο που εσείς θέλετε.</p>
+ <p xml:lang="en-GB">Dolphin is a lightweight file manager. It has been designed with ease of use and simplicity in mind, while still allowing flexibility and customisation. This means that you can do your file management exactly the way you want to do it.</p>
+ <p xml:lang="es">Dolphin es un administrador de archivos ligero. Se ha diseñado teniendo en cuenta la facilidad de uso y la simplicidad, así como la flexibilidad y la personalización. Esto significa que el usuario puede administrar los archivos exactamente de la manera que prefiera.</p>
+ <p xml:lang="et">Dolphin on vähest koormust tekitav failihaldur. Selle loomisel on peetud silmas kasutamise hõlpsust ja lihtsust, ometi pakub see suurt paindlikkust ja oma käe järgi kohandamise võimalusi. Sel moel saab faile hallata just, nagu ise soovid.</p>
+ <p xml:lang="fi">Dolphin on kevyt tiedostonhallinta. Se on suunniteltu helppokäyttöiseksi ja yksinkertaiseksi, mutta silti joustavaksi ja mukautettavaksi. Voit siis hallita tiedostojasi juuri niin kuin haluat.</p>
+ <p xml:lang="fr">Dolphin est un gestionnaire de fichier léger. Il a été conçu en gardant à l'esprit la simplicité et l'aisance à l'usage, tout en permettant flexibilité et personnalisation. Cela signifie que vous pouvez gérer vos fichiers de la manière exacte que vous voulez.</p>
+ <p xml:lang="hu">A Dolphin egy pehelysúlyú fájlkezelő. Az egyszerű használatot és az egyszerűséget szem előtt tartva tervezték, miközben továbbra is lehetővé teszi a rugalmasságot és a testre szabhatóságot. Ez azt jelenti, hogy pontosan oly módon végezheti a fájlkezelést, ahogy csak akarja.</p>
+ <p xml:lang="ia">Dolphin es un gerente de file legier. Il ha essite designate con facilitate de uso e simplicitate in le mente, mentre il permitte ancora flexibilitate e personalisation. Isto significa que tu pote facer le gerente de file exactemente como tu lo vole.</p>
+ <p xml:lang="it">Dolphin è un gestore file leggero. È stato progettato per essere facile da utilizzare e pensando alla semplicità, garantendo al contempo flessibilità e personalizzazione. Ciò significa che puoi gestire i tuoi file come meglio desideri.</p>
+ <p xml:lang="ko">Dolphin은 가벼운 파일 관리자입니다. 사용 편의성과 간단함을 위주로 설계되었으며, 유연성과 사용자 정의 가능성을 배제하지 않았습니다. 원하는 대로 파일을 관리할 수 있습니다.</p>
+ <p xml:lang="nb">Dolphin er en lettvekts filbehandler. Den er laget for å være enkel og lett å bruke, samtidig som den er fleksibel og kan tilpasses. Det betyr at du kan utføre dine filbehandlingsoppgaver akkurat slik du vil gjøre det.</p>
+ <p xml:lang="nds">Dolphin is en slank Dateipleger. Dat wöör buut mit de Idee vun't eenfache Bedenen vör Ogen, bides dat liekers flexibel un topassbor wesen schull. Du kannst Dien Dateien also jüst so plegen, as Du dat wullt.</p>
+ <p xml:lang="nl">Dolphin is een lichtgewicht bestandsbeheerder. Het is ontworpen met gebruiksgemak en eenvoud in gedachte en staat toch flexibiliteit en aan te passen toe. Dit betekent dat u uw bestandsbeheer kunt doen precies op de manier zoals u dat wilt.</p>
+ <p xml:lang="pl">Dolphin jest lekkim programem do zarządzania plikami. Został on opracowany mając na uwadze łatwość i prostotę obsługi, zapewniając jednocześnie elastyczność i możliwość dostosowania. Oznacza to, że można urządzić zarządzanie plikami w dokładnie taki sposób w jaki jest to pożądane.</p>
+ <p xml:lang="pt">O Dolphin é um gestor de ficheiros leve. Foi desenhado com a facilidade de uso e simplicidade em mente, permitindo à mesma a flexibilidade e personalização. Isto significa que poderá fazer a sua gestão de ficheiros exactamente da forma que deseja.</p>
+ <p xml:lang="pt-BR">Dolphin é um gerenciador de arquivos leve e fácil de usar. Foi projetado para ser simples e ao mesmo tempo manter a flexibilidade e personalização. Isso significa que você poderá gerenciar seus arquivos da forma que desejar.</p>
+ <p xml:lang="sk">Dolphin je odľahčený správca súborov. Bol navrhnutý na jednoduché použitie a jednoduchosť, ale s možnosťami flexibility a prispôsobenia. To znamená, že môžete vykonávať správu súborov presne tak, ako chcete.</p>
+ <p xml:lang="sl">Dolphin je enostaven upravljalnik datotek. Bil je zasnovan kot enostaven in preprost, vseeno pa ostaja prilagodljiv. To pomeni, da lahko upravljanje datotek izvajate točno tako kot želite.</p>
+ <p xml:lang="sr">Делфин је лагани менаџер фајлова. Пројектован је да буде лак за употребу и једноставан, а да ипак омогућава флексибилност и прилагођавање. То значи да ће моћи да баратате фајловима тачно онако како бисте желели.</p>
+ <p xml:lang="sr-Latn">Dolphin je lagani menadžer fajlova. Projektovan je da bude lak za upotrebu i jednostavan, a da ipak omogućava fleksibilnost i prilagođavanje. To znači da će moći da baratate fajlovima tačno onako kako biste želeli.</p>
+ <p xml:lang="sr-ijekavian">Делфин је лагани менаџер фајлова. Пројектован је да буде лак за употребу и једноставан, а да ипак омогућава флексибилност и прилагођавање. То значи да ће моћи да баратате фајловима тачно онако како бисте желели.</p>
+ <p xml:lang="sr-ijekavianlatin">Dolphin je lagani menadžer fajlova. Projektovan je da bude lak za upotrebu i jednostavan, a da ipak omogućava fleksibilnost i prilagođavanje. To znači da će moći da baratate fajlovima tačno onako kako biste želeli.</p>
+ <p xml:lang="sv">Dolphin är en lättviktig filhanterare. Den har konstruerats med användbarhet och enkelhet i åtanke, men ändå tillåta flexibilitet och anpassning. Det betyder att du kan hantera filer exakt på det sätt som du vill göra det.</p>
+ <p xml:lang="tr">Dolphin hafif bir dosya yöneticisidir. Kolay kullanım ve basitliğin yanı sıra esneklik ve özelleştirilebilme de akılda tutularak geliştirilmiştir. Bu da dosya yöneticisini tam da istediğiniz gibi kullanabileceğiniz anlamına gelir.</p>
+ <p xml:lang="uk">Dolphin — невибаглива до ресурсів програма для керування файлами. Її створено простою у користуванні і гнучкою у налаштовуванні. Це означає, що ви можете зробити керування файлами саме таким, як вам потрібно.</p>
+ <p xml:lang="x-test">xxDolphin is a lightweight file manager. It has been designed with ease of use and simplicity in mind, while still allowing flexibility and customisation. This means that you can do your file management exactly the way you want to do it.xx</p>
+ <p xml:lang="zh-TW">Dolphin 是一套輕量級的檔案管理員。它設計的理念是易用與簡單,但仍然保持足夠的彈性。這表示您可以用您想要使用的方式來管理您的檔案。</p>
+ <p>Features:</p>
+ <p xml:lang="ar">المزايا:</p>
+ <p xml:lang="bs">Svojstva:</p>
+ <p xml:lang="ca">Característiques:</p>
+ <p xml:lang="cs">Vlastnosti:</p>
+ <p xml:lang="da">Funktioner:</p>
+ <p xml:lang="de">Funktionen:</p>
+ <p xml:lang="el">Χαρακτηριστικά:</p>
+ <p xml:lang="en-GB">Features:</p>
+ <p xml:lang="es">Características:</p>
+ <p xml:lang="et">Omadused:</p>
+ <p xml:lang="fi">Ominaisuudet:</p>
+ <p xml:lang="fr">Fonctionnalités :</p>
+ <p xml:lang="hu">Szolgáltatások:</p>
+ <p xml:lang="ia">Characteristicas:</p>
+ <p xml:lang="it">Funzionalità:</p>
+ <p xml:lang="ko">기능:</p>
+ <p xml:lang="lt">Galimybės</p>
+ <p xml:lang="nb">Egenskaper:</p>
+ <p xml:lang="nds">Markmalen:</p>
+ <p xml:lang="nl">Mogelijkheden:</p>
+ <p xml:lang="pa">ਲੱਛਣ:</p>
+ <p xml:lang="pl">Możliwości:</p>
+ <p xml:lang="pt">Características:</p>
+ <p xml:lang="pt-BR">Funcionalidades:</p>
+ <p xml:lang="ro">Caracteristici:</p>
+ <p xml:lang="sk">Funkcie:</p>
+ <p xml:lang="sl">Zmožnosti:</p>
+ <p xml:lang="sr">Могућности:</p>
+ <p xml:lang="sr-Latn">Mogućnosti:</p>
+ <p xml:lang="sr-ijekavian">Могућности:</p>
+ <p xml:lang="sr-ijekavianlatin">Mogućnosti:</p>
+ <p xml:lang="sv">Funktioner:</p>
+ <p xml:lang="tr">Özellikler:</p>
+ <p xml:lang="uk">Можливості:</p>
+ <p xml:lang="x-test">xxFeatures:xx</p>
+ <p xml:lang="zh-TW">功能:</p>
+ <ul>
+ <li>Navigation (or breadcrumb) bar for URLs, allowing you to quickly navigate through the hierarchy of files and folders.</li>
+ <li xml:lang="bs">Navigacijska (ili mrvična) traka za URL, dopušta vam da se brzo krećete kroz hijerarhiju datoteka i direktorija.</li>
+ <li xml:lang="ca">Barra de navegació (o fil d'Ariadna) per els URL, permetent una navegació ràpida per la jerarquia de fitxers i carpetes.</li>
+ <li xml:lang="da">Navigationsbjælke (eller brødkrumme-bjælke) til URL'er, lader dig navigere hurtigt igennem hierarkiet af filer og mapper.</li>
+ <li xml:lang="el">Η γραμμή πλοήγησης (ή ιχνηλάτησης) για URL, σας επιτρέπει να πλοηγηθείτε γρήγορα μέσα από την ιεραρχία αρχείων και φακέλων.</li>
+ <li xml:lang="en-GB">Navigation (or breadcrumb) bar for URLs, allowing you to quickly navigate through the hierarchy of files and folders.</li>
+ <li xml:lang="es">barra de navegación (o de ruta completa) para URL que permite navegar rápidamente a través de la jerarquía de archivos y carpetas.</li>
+ <li xml:lang="et">Liikumisriba IRL-idele, mis lubab kiiresti liigelda failide ja kataloogide hierarhias.</li>
+ <li xml:lang="fi">Osoiterivi, jonka avulla siirtyminen tiedostojen ja kansioiden hierarkiassa on nopeaa.</li>
+ <li xml:lang="fr">Barre de navigation (ou fil d'Ariane) permettant de naviguer rapidement dans la hiérarchie de fichiers et de dossiers.</li>
+ <li xml:lang="hu">Navigációs (vagy webmorzsa) sáv az URL-ekhez, amely lehetővé teszi a fájlok és mappák hierarchiáján keresztüli gyors navigációt.</li>
+ <li xml:lang="ia">Barra de navigation (o "breadcrumb") pro URLs, que il permitte te navigar rapidemente a transverso del hierarchia de files e dossieres.</li>
+ <li xml:lang="it">La barra di navigazione per gli URL, che ti consente di navigare rapidamente attraverso la struttura di file e cartelle.</li>
+ <li xml:lang="ko">파일과 폴더 구조를 빠르게 탐색할 수 있도록 URL 탐색 표시줄을 사용할 수 있습니다.</li>
+ <li xml:lang="nb">Navigasjonslinje (brødsmulelinje) for URL-er slik at du raskt kan navigere gjennom hierarkiet av filer og mapper.</li>
+ <li xml:lang="nds">Steed- (oder Krömelspoor-)Balken för URLs, mit de Du Di fix dör de Hierarchie ut Dateien un Ornern bewegen kannst</li>
+ <li xml:lang="nl">Navigatie- (of broodkruimel)balk voor URL's, waarmee u snel kunt navigeren door de hiërarchie van bestanden en mappen.</li>
+ <li xml:lang="pl">Pasek nawigacji (lub okruchy chleba) dla adresów URL, umożliwiające szybkie przechodzenie w hierarchii plików i katalogów.</li>
+ <li xml:lang="pt">Barra de navegação dos URL's, que lhe permite navegar rapidamente pela hierarquia de ficheiros e pastas.</li>
+ <li xml:lang="pt-BR">Barra de navegação de URLs, permitindo-lhe navegar rapidamente pela hierarquia de arquivos e pastas.</li>
+ <li xml:lang="sk">Navigačná lišta pre URL, umožňujúca vám rýchlu navigáciu cez hierarchiu súborov a priečinkov.</li>
+ <li xml:lang="sl">Vrstica za krmarjenje po naslovih URL, ki omogoča hitro krmarjenje po hierarhiji datotek in map.</li>
+ <li xml:lang="sr">Навигациона трака (или мрвице) за УРЛ‑ове, преко које се можете брзо кретати кроз стабло фајлова и фасцикли.</li>
+ <li xml:lang="sr-Latn">Navigaciona traka (ili mrvice) za URL‑ove, preko koje se možete brzo kretati kroz stablo fajlova i fascikli.</li>
+ <li xml:lang="sr-ijekavian">Навигациона трака (или мрвице) за УРЛ‑ове, преко које се можете брзо кретати кроз стабло фајлова и фасцикли.</li>
+ <li xml:lang="sr-ijekavianlatin">Navigaciona traka (ili mrvice) za URL‑ove, preko koje se možete brzo kretati kroz stablo fajlova i fascikli.</li>
+ <li xml:lang="sv">Navigeringsrad (eller länkstig) för webbadresser, som låter dig snabbt navigera igenom hierarkin av filer och kataloger.</li>
+ <li xml:lang="tr">Dosya ve dizinlerin sıralı dizilerinde hızlıca gezinmenize imkan veren adresler için gezinti (veya işaret) çubuğu.</li>
+ <li xml:lang="uk">Панель навігації (звичайний режим і режим послідовної навігації) для адрес надає вам змогу швидко пересуватися ієрархією файлів та каталогів.</li>
+ <li xml:lang="x-test">xxNavigation (or breadcrumb) bar for URLs, allowing you to quickly navigate through the hierarchy of files and folders.xx</li>
+ <li xml:lang="zh-TW">網址導覽列讓您可以快速瀏覽檔案與資料夾。</li>
+ <li>Supports several different kinds of view styles and properties and allows you to configure the view exactly how you want it.</li>
+ <li xml:lang="ar">يدعم العديد من الأنواع المختلفة من الخصائص وأنماط العرض ويسمح لك بضبط العرض كما تريد تمامًا.</li>
+ <li xml:lang="bs">Dopušta vište vrsta stilova pogleda i svojstava i dopšta vam da konfigurišete pogled baš kako želite.</li>
+ <li xml:lang="ca">Accepta diferents classes diverses d'estils de visualització i propietats i us permet configurar la visualització exactament com la vulgueu.</li>
+ <li xml:lang="da">Understøtter flere forskellige slags visninger og egenskaber og lader dig konfigurere visningen nøjagtig som du vil have den.</li>
+ <li xml:lang="el">Υποστηρίζει πολλά διαφορετικά είδη στιλ και ιδιότητες επισκόπησης και σας επιτρέπει να διαμορφώσετε την επισκόπηση ακριβώς όπως τη θέλετε.</li>
+ <li xml:lang="en-GB">Supports several different kinds of view styles and properties and allows you to configure the view exactly how you want it.</li>
+ <li xml:lang="es">Admite varios tipos diferentes de estilos de vista y propiedades y permite configurar la vista exactamente como prefiera el usuario.</li>
+ <li xml:lang="et">Võimalus kasutada mitut laadi vaatestiile ja -omadusi ning neid igati enda käe järgi seadistada.</li>
+ <li xml:lang="fi">Tukee useita erilaisia näkymätyylejä ja -ominaisuuksia, ja mahdollistaa näkymän muokkaamisen mieleisekseen.</li>
+ <li xml:lang="fr">Prend en charge plusieurs types de styles d'affichage et de propriété et vous permet de configurer l'affichage de la manière exacte que vous voulez.</li>
+ <li xml:lang="hu">Számos különféle nézetstílus fajtát és tulajdonságot támogat, valamint lehetővé teszi a nézet beállítását pontosan olyanra, ahogy azt látni szeretné.</li>
+ <li xml:lang="ia">Il supporta multe differente typos de stilos de vista e proprietates e il permitte te configurar le vista exactemente como tu vole.</li>
+ <li xml:lang="it">Supporta diversi stili di visualizzazione e proprietà e ti consente di configurare la vista come desideri.</li>
+ <li xml:lang="ko">여러 종류의 보기 형식을 지원하여 원하는 대로 항목을 볼 수 있습니다.</li>
+ <li xml:lang="nb">Støtter flere forskjellige visningsstiler og kan sette opp visningen akkurat slik du vil ha den.</li>
+ <li xml:lang="nds">Ünnerstütt en Reeg verscheden Ansichtstilen un -egenschappen un lett Di de Ansicht jüst so topassen, as Du dat bruukst.</li>
+ <li xml:lang="nl">Ondersteunt een aantal verschillende soorten stijlen van weergave en eigenschappen en biedt u de mogelijkheid de weergave in te stellen precies zoals u dat wilt.</li>
+ <li xml:lang="pl">Obsługa wielu różnych rodzajów stylów widoków i właściwości oraz możliwość ustawienia widoku dopasowanego do potrzeb.</li>
+ <li xml:lang="pt">Suposta diferentes tipos de vistas e propriedades e permite-lhe configurar cada vista exactamente como a deseja.</li>
+ <li xml:lang="pt-BR">Suporte a diferentes tipos de visualização, permitindo-lhe configurar cada modo de exibição da forma que desejar.</li>
+ <li xml:lang="sk">Podporuje niekoľko rôznych typov štýlov zobrazenia a vlastností a umožňuje vám nastaviť pohľad presne tak, ako chcete.</li>
+ <li xml:lang="sl">Podpira številne vrste slogov in lastnosti pogledov ter omogoča, da pogled nastavite točno tako kot vam ustreza.</li>
+ <li xml:lang="sr">Неколико начина приказа, са својствима које можете подесити по жељи.</li>
+ <li xml:lang="sr-Latn">Nekoliko načina prikaza, sa svojstvima koje možete podesiti po želji.</li>
+ <li xml:lang="sr-ijekavian">Неколико начина приказа, са својствима које можете подесити по жељи.</li>
+ <li xml:lang="sr-ijekavianlatin">Nekoliko načina prikaza, sa svojstvima koje možete podesiti po želji.</li>
+ <li xml:lang="sv">Stöder flera olika sorters visningsstilar och egenskaper och låter dig anpassa visningen exakt som du vill ha den.</li>
+ <li xml:lang="tr">Bir çok farklı görünüm tipini ve özelliğini destekler ve görünümü tam istediğiniz gibi yapılandırmanıza izin verir.</li>
+ <li xml:lang="uk">Підтримка декількох різних типів та параметрів перегляду надає вам змогу налаштувати перегляд каталогів саме так, як вам це потрібно.</li>
+ <li xml:lang="x-test">xxSupports several different kinds of view styles and properties and allows you to configure the view exactly how you want it.xx</li>
+ <li xml:lang="zh-TW">網址導覽列讓您可以快速瀏覽檔案與資料夾。</li>
+ <li>Split view, allowing you to easily copy or move files between locations.</li>
+ <li xml:lang="ar">العرض المقسوم، يسمح لك بنسخ ونقل الملفات بين مكانين بسهولة.</li>
+ <li xml:lang="bs">Razdvaja pogled, dopuštajući lako kopiranje ili pomijeranje datoteka između lokacija</li>
+ <li xml:lang="ca">Divisió de visualització, permetent copiar o moure fitxers fàcilment entre les ubicacions.</li>
+ <li xml:lang="da">Opdelt visning lader dig kopiere filer mellem placeringer på en nem måde.</li>
+ <li xml:lang="de">Geteilte Ansichten, damit können Sie einfach Daten zwischen Orten kopieren oder verschieben.</li>
+ <li xml:lang="el">Η διαίρεση επισκόπησης, σάς επιτρέπει με ευκολία να αντιγράφετε ή να μετακινείτε αρχεία μεταξύ διαφορετικών θέσεων.</li>
+ <li xml:lang="en-GB">Split view, allowing you to easily copy or move files between locations.</li>
+ <li xml:lang="es">Dividir vista, para poder copiar o mover archivos fácilmente entre distintas ubicaciones.</li>
+ <li xml:lang="et">Vaate poolitamise võimalus, mis muudab väga lihtsaks failide ühest kohast teise kopeerimise või liigutamise.</li>
+ <li xml:lang="fi">Näkymän puolitus, joka helpottaa tiedostojen kopiointia ja siirtämistä paikasta toiseen.</li>
+ <li xml:lang="fr">Affichage divisé, permettant de facilement copier ou déplacer des fichiers dans les différents emplacements.</li>
+ <li xml:lang="hu">Osztott nézet, amely lehetővé teszi a fájlok könnyű másolását és áthelyezését a helyek között.</li>
+ <li xml:lang="ia">Scinde vista, il permitte te copiar o mover facilemente files inter locationes.</li>
+ <li xml:lang="it">La vista divisa, che ti consente di copiare o spostare i file tra le diverse posizioni in maniera semplice.</li>
+ <li xml:lang="ko">화면을 나누어서 서로 다른 위치 간 파일을 쉽게 이동하거나 복사할 수 있도록 합니다.</li>
+ <li xml:lang="nb">Delt visning, så du lett kan kopiere eller flytte filer mellom steder.</li>
+ <li xml:lang="nds">Deelt Ansicht, mit De Du Dateien eenfach twischen Steden koperen oder bewegen kannst.</li>
+ <li xml:lang="nl">Gesplitst beeld, waarmee u gemakkelijk bestanden kunt kopiëren of verplaatsen tussen locaties.</li>
+ <li xml:lang="pl">Widok podzielony, umożliwiający łatwe kopiowane lub przenoszenie plików pomiędzy położeniami.</li>
+ <li xml:lang="pt">Uma vista dividida, que lhe permite facilmente copiar ou mover os ficheiros entre locais.</li>
+ <li xml:lang="pt-BR">Um modo de exibição dividido, permitindo-lhe copiar ou mover arquivos facilmente entre locais.</li>
+ <li xml:lang="sk">Rozdelený pohľad, umožňuje vám jednoducho kopírovať alebo presúvať súbory medzi umiestneniami.</li>
+ <li xml:lang="sl">Razdeljeni pogled vam omogoča enostavno kopiranje ali premikanje datotek med mesti.</li>
+ <li xml:lang="sr">Подељени приказ, за лако копирање и премештање фајлова између локација.</li>
+ <li xml:lang="sr-Latn">Podeljeni prikaz, za lako kopiranje i premeštanje fajlova između lokacija.</li>
+ <li xml:lang="sr-ijekavian">Подељени приказ, за лако копирање и премештање фајлова између локација.</li>
+ <li xml:lang="sr-ijekavianlatin">Podeljeni prikaz, za lako kopiranje i premeštanje fajlova između lokacija.</li>
+ <li xml:lang="sv">Delad visning, som låter dig enkelt kopiera eller flytta filer mellan platser.</li>
+ <li xml:lang="tr">Dosyaları farklı konumlar arasında kolayca kopyalamaya ve taşımaya izin veren ayrık görünüm.</li>
+ <li xml:lang="uk">За допомогою режиму двопанельного розділеного перегляду ви зможе без проблем копіювати або пересувати файли між каталогами.</li>
+ <li xml:lang="x-test">xxSplit view, allowing you to easily copy or move files between locations.xx</li>
+ <li xml:lang="zh-TW">支援數個檢視模式,您也可以調整檢視模式的屬性。</li>
+ <li>Additional information and shortcuts are available as dock-able panels, allowing you to move them around freely and display exactly what you want.</li>
+ <li xml:lang="ar">تتوفر معلومات واختصارات إضافية كلوحات قابلة للرصف، مما يسمح لك بنقلها بحريّة وعرضها بالضبط كما تريد.</li>
+ <li xml:lang="bs">Dodatne informacije i kratice su dostupne kao usidreni paneli, dopuštajući vam da se krećete slobodno i prikažete šta želite.</li>
+ <li xml:lang="ca">Hi ha informació addicional i dreceres disponibles com a plafons acoblables, permetent moure'ls lliurement i mostrar exactament el què vulgueu.</li>
+ <li xml:lang="da">Yderligere information og genveje er tilgængelige som dokbare paneler, som lader dig flytte dem frit omkring og vise nøjagtigt det du vil have.</li>
+ <li xml:lang="el">Πρόσθετες πληροφορίες και συντομεύσεις είναι διαθέσιμα ως προσαρτήσιμοι πίνακες, που σας επιτρέπουν να τα μετακινείτε ελεύθερα και να παρουσιάζετε ακριβώς αυτό που θέλετε.</li>
+ <li xml:lang="en-GB">Additional information and shortcuts are available as dock-able panels, allowing you to move them around freely and display exactly what you want.</li>
+ <li xml:lang="es">Hay disponible información adicional y accesos rápidos en forma de paneles separables para que se puedan mover libremente a la vez que muestran exactamente lo que prefiera el usuario.</li>
+ <li xml:lang="et">Lisateave ja otseteed dokitavate paneelidena, mida saab vabalt vajalikku kohta tõsta ja panna näitama just vajalikku teavet.</li>
+ <li xml:lang="fi">Lisätiedoille ja oikoteille on paneelit, joita voi siirtää sekä näyttää ja piilottaa vapaasti.</li>
+ <li xml:lang="fr">Des informations supplémentaires et des raccourcis sont disponible comme panneaux ancrable librement déplaçable et affichant exactement ce que vous voulez.</li>
+ <li xml:lang="hu">További információk és gyorsbillentyűk érhetők el dokkolható panelekként, lehetővé téve azok szabad mozgatását, illetve pontosan úgy megjelenítve, ahogy szeretné.</li>
+ <li xml:lang="ia">Information additional e vias breve es disponibile como pannellos de basin (dock-panels), il permitte mover los liberemente e monstrar los exactemente como tu vole.</li>
+ <li xml:lang="it">Informazioni aggiuntive e scorciatoie sono disponibili come pannelli agganciabili, che possono essere spostati liberamente e visualizzare esattamente ciò che desideri.</li>
+ <li xml:lang="ko">추가 정보 표시 창과 바로 가기는 도킹 가능한 패널 형태로 사용할 수 있으며, 원하는 곳으로 이동하여 표시할 수 있습니다.</li>
+ <li xml:lang="nb">Mer informasjon og snarveier er tilgjengelige som dokkbare ruter, som du kan flytte fritt rundt og bruke til å vise akkurat hva du vil.</li>
+ <li xml:lang="nds">Bito-Infos un Leestekens laat sik as Paneels andocken, Du kannst ehr verschuven un se jüst dat wiesen laten, wat Du weten wullt.</li>
+ <li xml:lang="nl">Extra informatie en sneltoetsen zijn beschikbaar als vast te zetten panelen, die u vrij kunt verplaatsen en precies kunt tonen wat u wilt.</li>
+ <li xml:lang="pl">Dodatkowe szczegóły i skróty dostępne jako dokowalne panele, umożliwiające ich dowolne przenoszenie i wyświetlanie dopasowane do potrzeb.</li>
+ <li xml:lang="pt">Estão disponíveis informações e atalhos adicionais como painéis acopláveis, permitindo-lhe movê-los à vontade e apresentar como desejar.</li>
+ <li xml:lang="pt-BR">As informações e atalhos adicionais estão disponíveis na forma de painéis acopláveis, permitindo-lhe movê-los à vontade e apresentar como desejar.</li>
+ <li xml:lang="sk">Dodatočné informácie a skratky sú dostupné ako dokovateľné panely, umožňujúce vám ich voľný presun a zobrazenie presne tak, ako chcete.</li>
+ <li xml:lang="sl">Dodatne podrobnosti in bližnjice lahko vklopite kot sidrne pulte, ki jih lahko poljubno premikate in prikazujete.</li>
+ <li xml:lang="sr">Допунски подаци и пречице доступни су као усидриви панели, које можете поставити где вам одговара и подесити да приказују тачно оно што желите.</li>
+ <li xml:lang="sr-Latn">Dopunski podaci i prečice dostupni su kao usidrivi paneli, koje možete postaviti gde vam odgovara i podesiti da prikazuju tačno ono što želite.</li>
+ <li xml:lang="sr-ijekavian">Допунски подаци и пречице доступни су као усидриви панели, које можете поставити где вам одговара и подесити да приказују тачно оно што желите.</li>
+ <li xml:lang="sr-ijekavianlatin">Dopunski podaci i prečice dostupni su kao usidrivi paneli, koje možete postaviti gde vam odgovara i podesiti da prikazuju tačno ono što želite.</li>
+ <li xml:lang="sv">Ytterligare information och genvägar är tillgängliga som dockningsbara paneler, vilket låter dig flytta omkring dem fritt och visa exakt vad du vill.</li>
+ <li xml:lang="tr">Ek bilgi ve kısayollar kilitlenebilen panolar olarak kullanılabilirler, bu sayede onları istediğiniz gibi taşıyabilir ve tam istediğiniz gibi görüntülenmelerini sağlayabilirsiniz.</li>
+ <li xml:lang="uk">За допомогою бічних пересувних панелей ви зможете отримувати додаткову інформацію та пересуватися каталогами. Ви можете розташувати ці панелі так, як вам це зручно, і наказати програмі показувати на них саме те, що вам потрібно.</li>
+ <li xml:lang="x-test">xxAdditional information and shortcuts are available as dock-able panels, allowing you to move them around freely and display exactly what you want.xx</li>
+ <li xml:lang="zh-TW">分割檢視讓您可以輕鬆複製或移動檔案。</li>
+ <li>Multiple tab support</li>
+ <li xml:lang="ar">دعم تعدّد الألسنة</li>
+ <li xml:lang="bs">Podrška za više kartica</li>
+ <li xml:lang="ca">Implementació de pestanyes múltiples</li>
+ <li xml:lang="da">Understøttelse af flere faneblade</li>
+ <li xml:lang="de">Unterstützung für Unterfenster</li>
+ <li xml:lang="el">Υποστήριξη πολλαπλών καρτελών</li>
+ <li xml:lang="en-GB">Multiple tab support</li>
+ <li xml:lang="es">Admite varias pestañas</li>
+ <li xml:lang="et">Mitme kaardi kasutamise toetus.</li>
+ <li xml:lang="fi">Useiden välilehtien tuki</li>
+ <li xml:lang="fr">Prise en charge des onglets multiples</li>
+ <li xml:lang="hu">Több lap támogatása</li>
+ <li xml:lang="ia">Supporto de scheda multiple</li>
+ <li xml:lang="it">Supporto di schede multiple</li>
+ <li xml:lang="ko">다중 탭 지원</li>
+ <li xml:lang="lt">Daugelio kortelių palaikymas</li>
+ <li xml:lang="nb">Støtte for flere faner</li>
+ <li xml:lang="nds">Ünnerstütten för Paneels</li>
+ <li xml:lang="nl">Ondersteuning voor meerdere tabbladen</li>
+ <li xml:lang="pa">ਬਹੁ ਟੈਬ ਸਹਿਯੋਗ</li>
+ <li xml:lang="pl">Obsługa wielu kart</li>
+ <li xml:lang="pt">Suporte para várias páginas</li>
+ <li xml:lang="pt-BR">Suporte a várias abas</li>
+ <li xml:lang="ro">Suport pentru file multiple</li>
+ <li xml:lang="sk">Podpora viacerých kariet</li>
+ <li xml:lang="sl">Podpora več zavihkom</li>
+ <li xml:lang="sr">Вишеструки језичци.</li>
+ <li xml:lang="sr-Latn">Višestruki jezičci.</li>
+ <li xml:lang="sr-ijekavian">Вишеструки језичци.</li>
+ <li xml:lang="sr-ijekavianlatin">Višestruki jezičci.</li>
+ <li xml:lang="sv">Stöd för flera flikar</li>
+ <li xml:lang="tr">Çoklu sekme desteği</li>
+ <li xml:lang="uk">Підтримка роботи з вкладками.</li>
+ <li xml:lang="x-test">xxMultiple tab supportxx</li>
+ <li xml:lang="zh-TW">額外資訊與嵌入式面板捷徑讓您可以輕易顯示您常用的項目。</li>
+ <li>Informational dialogues are displayed in an unobtrusive way.</li>
+ <li xml:lang="ar">حواريات المعلومات تُعرَض بطريقة غير مُزعجة.</li>
+ <li xml:lang="bs">Informativni dijalozi su prikazani na nenametljiv način.</li>
+ <li xml:lang="ca">El diàlegs informatius es mostren de manera no molesta.</li>
+ <li xml:lang="da">Informationsdialoger vises på en ikke-forstyrrende måde.</li>
+ <li xml:lang="de">Informationen werden unaufdringlich angezeigt.</li>
+ <li xml:lang="el">Ενημερωτικοί διάλογοι εμφανίζονται με μη παρεμβατικό τρόπο.</li>
+ <li xml:lang="en-GB">Informational dialogs are displayed in an unobtrusive way.</li>
+ <li xml:lang="es">Los diálogos informativos se muestran de manera discreta.</li>
+ <li xml:lang="et">Teavitavate dialoogide näitamine kasutajat liigselt ärritamata.</li>
+ <li xml:lang="fi">Informatiiviset valintaikkunat näytetään niin, että ne eivät keskeytä kaikkea muuta toimintaa.</li>
+ <li xml:lang="fr">Les dialogues d'information sont affiché de manière discrète.</li>
+ <li xml:lang="hu">Az információs párbeszédablakok szerény módon vannak megjelenítve.</li>
+ <li xml:lang="ia">Dialogos de information es monstrate de modo non importun.</li>
+ <li xml:lang="it">Le finestre informative sono visualizzate in modo molto discreto.</li>
+ <li xml:lang="ko">정보 대화 상자를 방해되지 않는 형태로 표시합니다.</li>
+ <li xml:lang="nb">Informasjonsdialoger vises på en lite påtrengende måte.</li>
+ <li xml:lang="nds">Informatschoondialogen kaamt Di nich in'n Weg.</li>
+ <li xml:lang="nl">Informatiedialogen worden op een prettige manier getoond.</li>
+ <li xml:lang="pl">Pokazywanie informacyjnych okien dialogowych w nienatrętny sposób.</li>
+ <li xml:lang="pt">As janelas informativas são apresentadas de forma não-intrusiva.</li>
+ <li xml:lang="pt-BR">As janelas informativas são apresentadas de forma não-intrusiva.</li>
+ <li xml:lang="sk">Informačné dialógy sú zobrazené nevtieravým spôsobom.</li>
+ <li xml:lang="sl">Pogovorna okna s podrobnostmi so prikazana na nevsiljiv način.</li>
+ <li xml:lang="sr">Информативни дијалози који се ненаметљиво појављују.</li>
+ <li xml:lang="sr-Latn">Informativni dijalozi koji se nenametljivo pojavljuju.</li>
+ <li xml:lang="sr-ijekavian">Информативни дијалози који се ненаметљиво појављују.</li>
+ <li xml:lang="sr-ijekavianlatin">Informativni dijalozi koji se nenametljivo pojavljuju.</li>
+ <li xml:lang="sv">Dialogrutor med information visas på ett diskret sätt.</li>
+ <li xml:lang="tr">Bilgi pencereleri rahatsız etmeyecek şekilde görüntülenir.</li>
+ <li xml:lang="uk">Показ інформаційних панелей у зручний спосіб, що не заважає роботі.</li>
+ <li xml:lang="x-test">xxInformational dialogues are displayed in an unobtrusive way.xx</li>
+ <li xml:lang="zh-TW">支援多分頁</li>
+ <li>Undo/redo support</li>
+ <li xml:lang="ar">دعم التراجع والإعادة</li>
+ <li xml:lang="bs">Podrška za poništavanje/ponavljanje akcija</li>
+ <li xml:lang="ca">Implementació de desfer/refer</li>
+ <li xml:lang="da">Understøttelse af fortryd/gendan</li>
+ <li xml:lang="de">Unterstützung für Rückgängig/Wiederherstellen</li>
+ <li xml:lang="el">Υποστήριξη αναίρεσης/επανάληψης</li>
+ <li xml:lang="en-GB">Undo/redo support</li>
+ <li xml:lang="es">Admite las operaciones de deshacer y rehacer</li>
+ <li xml:lang="et">Tagasivõtmise ja uuestitegemise toetus.</li>
+ <li xml:lang="fi">Tuki muutosten kumoamiselle ja tekemiselle uudelleen</li>
+ <li xml:lang="fr">Prise en charge d'annulation et recommencement</li>
+ <li xml:lang="hu">Visszavonás/ismétlés támogatás</li>
+ <li xml:lang="ia">Supporto de annulla/reface</li>
+ <li xml:lang="it">Supporto di annulla/rifai</li>
+ <li xml:lang="ko">실행 취소/다시 실행 지원</li>
+ <li xml:lang="nb">Støtte for angring/omgjøring</li>
+ <li xml:lang="nds">Ünnerstütten för Torüchnehmen un Wedderherstellen</li>
+ <li xml:lang="nl">Ondersteuning ongedaan maken/opnieuw</li>
+ <li xml:lang="pa">ਵਾਪਿਸ ਕਰੋ/ਪਰਤਾਉਣ ਸਹਿਯੋਗ</li>
+ <li xml:lang="pl">Obsługa cofnij/ponów</li>
+ <li xml:lang="pt">Suporte para desfazer/refazer</li>
+ <li xml:lang="pt-BR">Suporte para desfazer/refazer</li>
+ <li xml:lang="ro">Suport pentru desfacere/refacere</li>
+ <li xml:lang="sk">Podpora Späť/Znova</li>
+ <li xml:lang="sl">Podpora razveljavitvam/uveljavitvam</li>
+ <li xml:lang="sr">Опозивање и понављање.</li>
+ <li xml:lang="sr-Latn">Opozivanje i ponavljanje.</li>
+ <li xml:lang="sr-ijekavian">Опозивање и понављање.</li>
+ <li xml:lang="sr-ijekavianlatin">Opozivanje i ponavljanje.</li>
+ <li xml:lang="sv">Stöd för ångra och gör om</li>
+ <li xml:lang="tr">Geri alma/tekrarlama desteği</li>
+ <li xml:lang="uk">Підтримка скасовування та повторення дій.</li>
+ <li xml:lang="x-test">xxUndo/redo supportxx</li>
+ <li xml:lang="zh-TW">以不唐突的方式顯示資訊對話框。</li>
+ <li>Transparent network access through the KIO system.</li>
+ <li xml:lang="ar">اتصال شبكيّ مباشر باستخدام نظام KIO.</li>
+ <li xml:lang="bs">Transparentni mrežni pristup kroz KIO sistem.</li>
+ <li xml:lang="ca">Accés transparent a la xarxa a través del sistema KIO.</li>
+ <li xml:lang="da">Transparent netværksadgang igennem KIO-systemet</li>
+ <li xml:lang="de">Transparenter Netzwerkzugriff durch das KIO-System.</li>
+ <li xml:lang="el">Διαφανής δικτυακή πρόσβαση με το σύστημα KIO.</li>
+ <li xml:lang="en-GB">Transparent network access through the KIO system.</li>
+ <li xml:lang="es">Acceso transparente a la red a través del sistema KIO.</li>
+ <li xml:lang="et">Võrgu läbipaistev kasutamine KIO-moodulite süsteemi vahendusel.</li>
+ <li xml:lang="fi">Läpinäkyvä verkon käyttö KIO-järjestelmän välityksellä.</li>
+ <li xml:lang="fr">Accès au réseau transparent grâce au système des KIO.</li>
+ <li xml:lang="hu">Átlátszó hálózati hozzáférés a KIO rendszeren keresztül.</li>
+ <li xml:lang="ia">Accesso de rete transparente a transverso del systema KIO.</li>
+ <li xml:lang="it">Accesso trasparente alla rete tramite il sistema KIO.</li>
+ <li xml:lang="ko">KIO 시스템을 통하여 네트워크 자원에 접근합니다.</li>
+ <li xml:lang="nb">Gjennomsiktig nettverkstilgang via KIO-systemet.</li>
+ <li xml:lang="nds">Direkt Nettwarktogriep över dat KDE-In-/Utgaav-(KIO-)Moduulsysteem</li>
+ <li xml:lang="nl">Transparante toegang tot het netwerk via het KIO systeem.</li>
+ <li xml:lang="pl">Przezroczysty dostęp do sieci przez system KIO.</li>
+ <li xml:lang="pt">Acesso transparente à rede através do sistema KIO.</li>
+ <li xml:lang="pt-BR">Acesso transparente à rede através do sistema KIO.</li>
+ <li xml:lang="ro">Acces transparent la rețea prin sistemul KIO.</li>
+ <li xml:lang="sk">Transparentný prístup na sieť cez KIO systém.</li>
+ <li xml:lang="sl">Enostaven dostop do omrežja preko sistema KIO.</li>
+ <li xml:lang="sr">Прозиран мрежни приступ кроз систем К‑У/И.</li>
+ <li xml:lang="sr-Latn">Proziran mrežni pristup kroz sistem K‑U/I.</li>
+ <li xml:lang="sr-ijekavian">Прозиран мрежни приступ кроз систем К‑У/И.</li>
+ <li xml:lang="sr-ijekavianlatin">Proziran mrežni pristup kroz sistem K‑U/I.</li>
+ <li xml:lang="sv">Transparent nätverksåtkomst via I/O-slavsystemet.</li>
+ <li xml:lang="tr">KIO sistemi üzerinden şeffaf ağ erişimi.</li>
+ <li xml:lang="uk">Прозорий доступ до ресурсів у мережі за допомогою системи KIO.</li>
+ <li xml:lang="x-test">xxTransparent network access through the KIO system.xx</li>
+ <li xml:lang="zh-TW">復原支援</li>
+ </ul>
+ </description>
+ <url type="homepage">http://dolphin.kde.org/</url>
+ <url type="bugtracker">https://bugs.kde.org/enter_bug.cgi?format=guided&amp;product=dolphin</url>
+ <url type="help">http://docs.kde.org/stable/en/applications/dolphin/index.html</url>
+ <screenshots>
+ <screenshot type="default">
+ <image>http://kde.org/images/screenshots/dolphin.png</image>
+ </screenshot>
+ </screenshots>
+ <project_group>KDE</project_group>
+ <provides>
+ <binary>dolphin</binary>
+ </provides>
+</component>
diff --git a/dolphin/src/dolphin.desktop b/dolphin/src/dolphin.desktop
new file mode 100755
index 0000000..364bf79
--- /dev/null
+++ b/dolphin/src/dolphin.desktop
@@ -0,0 +1,188 @@
+[Desktop Entry]
+Name=Dolphin
+Name[af]=Dolphin
+Name[ar]=دولفين
+Name[as]=Dolphin
+Name[ast]=Dolphin
+Name[be]=Dolphin
+Name[[email protected]]=Dolphin
+Name[bg]=Dolphin
+Name[bn]=ডলফিন
+Name[bn_IN]=Dolphin
+Name[bs]=Delfin
+Name[ca]=Dolphin
+Name[[email protected]]=Dolphin
+Name[cs]=Dolphin
+Name[csb]=Dolphin
+Name[da]=Dolphin
+Name[de]=Dolphin
+Name[el]=Dolphin
+Name[en_GB]=Dolphin
+Name[eo]=Dolphin
+Name[es]=Dolphin
+Name[et]=Dolphin
+Name[eu]=Dolphin
+Name[fi]=Dolphin
+Name[fr]=Dolphin
+Name[fy]=Dolfyn
+Name[ga]=Dolphin
+Name[gl]=Dolphin
+Name[gu]=ડોલ્ફિન
+Name[he]=Dolphin
+Name[hi]=डॉल्फ़िन
+Name[hne]=डाल्फिन
+Name[hr]=Dolphin
+Name[hsb]=Dolphin
+Name[hu]=Dolphin
+Name[ia]=Dolphin
+Name[id]=Dolphin
+Name[is]=Dolphin
+Name[it]=Dolphin
+Name[ja]=Dolphin
+Name[ka]=Dolphin
+Name[kk]=Dolphin
+Name[km]=Dolphin
+Name[kn]=ಡಾಲ್ಫಿನ್
+Name[ko]=Dolphin
+Name[ku]=Dolphin
+Name[lt]=Dolphin
+Name[lv]=Dolphin
+Name[mai]=डाल्फिन
+Name[mk]=Делфин
+Name[ml]=ഡോള്‍ഫിന്‍
+Name[mr]=डॉल्फिन
+Name[ms]=Dolphin
+Name[nb]=Dolphin
+Name[nds]=Dolphin
+Name[ne]=डल्फिन
+Name[nl]=Dolphin
+Name[nn]=Dolphin
+Name[oc]=Dolphin
+Name[or]=ଡଲଫିନ
+Name[pa]=ਡਾਲਫਿਨ
+Name[pl]=Dolphin
+Name[pt]=Dolphin
+Name[pt_BR]=Dolphin
+Name[ro]=Dolphin
+Name[ru]=Dolphin
+Name[se]=Dolphin
+Name[si]=ඩොල්ෆින්
+Name[sk]=Dolphin
+Name[sl]=Dolphin
+Name[sr]=Делфин
+Name[[email protected]]=Делфин
+Name[[email protected]]=Dolphin
+Name[[email protected]]=Dolphin
+Name[sv]=Dolphin
+Name[ta]=டால்பின்
+Name[te]=డాల్ఫిన్
+Name[tg]=Dolphin
+Name[th]=ดอลฟิน
+Name[tr]=Dolphin
+Name[ug]=Dolphin
+Name[uk]=Dolphin
+Name[uz]=Dolphin
+Name[[email protected]]=Dolphin
+Name[vi]=Dolphin
+Name[wa]=Dolphin
+Name[x-test]=xxDolphinxx
+Name[zh_CN]=Dolphin
+Name[zh_TW]=Dolphin
+Exec=dolphin %i -caption %c %u
+Icon=system-file-manager
+Type=Application
+X-DocPath=dolphin/index.html
+Categories=Qt;KDE;System;FileTools;FileManager;
+GenericName=File Manager
+GenericName[af]=Lêerbestuurder
+GenericName[ar]=مدير ملفات
+GenericName[as]=নথিপত্ৰৰ পৰিচালক
+GenericName[ast]=Xestor de ficheros
+GenericName[be]=Кіраўнік файлаў
+GenericName[[email protected]]=Kiraŭnik fajłaŭ
+GenericName[bg]=Файлов мениджър
+GenericName[bn]=ফাইল ম্যানেজার
+GenericName[bn_IN]=ফাইল পরিচালন ব্যবস্থা
+GenericName[bs]=Menadžer datoteka
+GenericName[ca]=Gestor de fitxers
+GenericName[[email protected]]=Gestor de fitxers
+GenericName[cs]=Správce souborů
+GenericName[csb]=Menadżera lopków
+GenericName[da]=Filhåndtering
+GenericName[de]=Dateimanager
+GenericName[el]=Διαχειριστής αρχείων
+GenericName[en_GB]=File Manager
+GenericName[eo]=Dosieradministrilo
+GenericName[es]=Gestor de archivos
+GenericName[et]=Failihaldur
+GenericName[eu]=Fitxategi-kudeatzailea
+GenericName[fa]=مدیر پرونده
+GenericName[fi]=Tiedostonhallinta
+GenericName[fr]=Gestionnaire de fichiers
+GenericName[fy]=Triembehearder
+GenericName[ga]=Bainisteoir Comhad
+GenericName[gl]=Xestor de ficheiros
+GenericName[gu]=ફાઇલ વ્યવસ્થાપક
+GenericName[he]=מנהל קבצים
+GenericName[hi]=फ़ाइल प्रबंधक
+GenericName[hne]=फाइल प्रबंधक
+GenericName[hr]=Upravitelj datoteka
+GenericName[hsb]=Datajowy manager
+GenericName[hu]=Fájlkezelő
+GenericName[ia]=Gerente de file
+GenericName[id]=Manajer Berkas
+GenericName[is]=Skráastjóri
+GenericName[it]=Gestore dei file
+GenericName[ja]=ファイルマネージャ
+GenericName[ka]=ფაილების მმართველი
+GenericName[kk]=Файл менеджері
+GenericName[km]=កម្មវិធី​គ្រប់គ្រង​ឯកសារ
+GenericName[kn]=ಕಡತ ವ್ಯವಸ್ಥಾಪಕ
+GenericName[ko]=파일 관리자
+GenericName[ku]=Rêveberê Pelan
+GenericName[lt]=Failų tvarkyklė
+GenericName[lv]=Failu pārvaldnieks
+GenericName[mai]=फाइल प्रबंधक
+GenericName[mk]=Менаџер на датотеки
+GenericName[ml]=ഫയല്‍ നടത്തിപ്പുകാരന്‍
+GenericName[mr]=फाईल व्यवस्थापक
+GenericName[ms]=Pengurus Fail
+GenericName[nb]=Filbehandler
+GenericName[nds]=Dateipleger
+GenericName[ne]=फाइल प्रबन्धक
+GenericName[nl]=Bestandsbeheerder
+GenericName[nn]=Filhandsamar
+GenericName[oc]=Gestionari de fichièrs
+GenericName[or]=ଫାଇଲ ପରିଚାଳକ
+GenericName[pa]=ਫਾਇਲ ਮੈਨੇਜਰ
+GenericName[pl]=Zarządzanie plikami
+GenericName[pt]=Gestor de Ficheiros
+GenericName[pt_BR]=Gerenciador de arquivos
+GenericName[ro]=Gestionar de fișiere
+GenericName[ru]=Диспетчер файлов
+GenericName[se]=Fiilagieđahalli
+GenericName[si]=ගොනු කළමනාකරු
+GenericName[sk]=Správca súborov
+GenericName[sl]=Upravljalnik datotek
+GenericName[sr]=Менаџер фајлова
+GenericName[[email protected]]=Менаџер фајлова
+GenericName[[email protected]]=Menadžer fajlova
+GenericName[[email protected]]=Menadžer fajlova
+GenericName[sv]=Filhanterare
+GenericName[ta]=கோப்பு மேலாளர்
+GenericName[te]=దస్త్రాల అభికర్త
+GenericName[tg]=Мудири файлҳо
+GenericName[th]=เครื่องมือจัดการแฟ้ม
+GenericName[tr]=Dosya Yönetici
+GenericName[ug]=ھۆججەت باشقۇرغۇ
+GenericName[uk]=Менеджер файлів
+GenericName[uz]=Fayl boshqaruvchisi
+GenericName[[email protected]]=Файл бошқарувчиси
+GenericName[vi]=Bộ quản lý tập tin
+GenericName[wa]=Manaedjeu di fitchîs
+GenericName[x-test]=xxFile Managerxx
+GenericName[zh_CN]=文件管理器
+GenericName[zh_TW]=檔案管理員
+Terminal=false
+MimeType=inode/directory;
+InitialPreference=10
diff --git a/dolphin/src/dolphinapplication.cpp b/dolphin/src/dolphinapplication.cpp
new file mode 100644
index 0000000..a4b105b
--- /dev/null
+++ b/dolphin/src/dolphinapplication.cpp
@@ -0,0 +1,106 @@
+/***************************************************************************
+ * Copyright (C) 2006-2011 by Peter Penz <[email protected]> *
+ * Copyright (C) 2006 by Holger 'zecke' Freyther <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "dolphinapplication.h"
+#include "dolphinmainwindow.h"
+#include "dolphin_generalsettings.h"
+
+#include <KCmdLineArgs>
+#include <KDebug>
+#include <KRun>
+#include <KUrl>
+
+DolphinApplication::DolphinApplication() :
+ m_mainWindow(0)
+{
+ KGlobal::locale()->insertCatalog("libkonq"); // Needed for applications using libkonq
+
+ m_mainWindow = new DolphinMainWindow();
+ m_mainWindow->setAttribute(Qt::WA_DeleteOnClose);
+
+ KCmdLineArgs* args = KCmdLineArgs::parsedArgs();
+
+ const int argsCount = args->count();
+
+ QList<KUrl> urls;
+ for (int i = 0; i < argsCount; ++i) {
+ const KUrl url = args->url(i);
+ if (url.isValid()) {
+ urls.append(url);
+ }
+ }
+
+ bool resetSplitSettings = false;
+ if (args->isSet("split") && !GeneralSettings::splitView()) {
+ // Dolphin should be opened with a split view although this is not
+ // set in the GeneralSettings. Temporary adjust the setting until
+ // all passed URLs have been opened.
+ GeneralSettings::setSplitView(true);
+ resetSplitSettings = true;
+
+ // We need 2 URLs to open Dolphin in split view mode
+ if (urls.isEmpty()) { // No URL given - Open home URL in all two views
+ urls.append(GeneralSettings::homeUrl());
+ urls.append(GeneralSettings::homeUrl());
+ } else if (urls.length() == 1) { // Only 1 URL given - Open given URL in all two views
+ urls.append(urls.at(0));
+ }
+ }
+
+ if (!urls.isEmpty()) {
+ if (args->isSet("select")) {
+ m_mainWindow->openFiles(urls);
+ } else {
+ m_mainWindow->openDirectories(urls);
+ }
+ } else {
+ const KUrl homeUrl(GeneralSettings::homeUrl());
+ m_mainWindow->openNewActivatedTab(homeUrl);
+ }
+
+ if (resetSplitSettings) {
+ GeneralSettings::setSplitView(false);
+ }
+
+ args->clear();
+
+ m_mainWindow->show();
+}
+
+DolphinApplication::~DolphinApplication()
+{
+}
+
+DolphinApplication* DolphinApplication::app()
+{
+ return qobject_cast<DolphinApplication*>(qApp);
+}
+
+void DolphinApplication::restoreSession()
+{
+ const QString className = KXmlGuiWindow::classNameOfToplevel(1);
+ if (className == QLatin1String("DolphinMainWindow")) {
+ m_mainWindow->restore(1);
+ } else {
+ kWarning() << "Unknown class " << className << " in session saved data!";
+ }
+}
+
+#include "dolphinapplication.moc"
diff --git a/dolphin/src/dolphinapplication.h b/dolphin/src/dolphinapplication.h
new file mode 100644
index 0000000..69d07c3
--- /dev/null
+++ b/dolphin/src/dolphinapplication.h
@@ -0,0 +1,44 @@
+/***************************************************************************
+ * Copyright (C) 2006-2011 by Peter Penz <[email protected]> *
+ * Copyright (C) 2006 by Holger 'zecke' Freyther <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef DOLPHIN_APPLICATION_H
+#define DOLPHIN_APPLICATION_H
+
+#include <KApplication>
+
+class DolphinMainWindow;
+
+class DolphinApplication : public KApplication
+{
+ Q_OBJECT
+
+public:
+ DolphinApplication();
+ virtual ~DolphinApplication();
+
+ static DolphinApplication* app();
+
+ void restoreSession();
+
+private:
+ DolphinMainWindow* m_mainWindow;
+};
+
+#endif
diff --git a/dolphin/src/dolphincontextmenu.cpp b/dolphin/src/dolphincontextmenu.cpp
new file mode 100644
index 0000000..e692c8f
--- /dev/null
+++ b/dolphin/src/dolphincontextmenu.cpp
@@ -0,0 +1,532 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Peter Penz ([email protected]) and *
+ * Cvetoslav Ludmiloff *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "dolphincontextmenu.h"
+
+#include "dolphinmainwindow.h"
+#include "dolphinnewfilemenu.h"
+#include "dolphinviewcontainer.h"
+#include "dolphin_generalsettings.h"
+#include "dolphinremoveaction.h"
+
+#include <KActionCollection>
+#include <KDesktopFile>
+#include <kfileitemactionplugin.h>
+#include <kabstractfileitemactionplugin.h>
+#include <KFileItemActions>
+#include <KFileItemListProperties>
+#include <KGlobal>
+#include <KIconLoader>
+#include <KIO/NetAccess>
+#include <KMenu>
+#include <KMenuBar>
+#include <KMessageBox>
+#include <KMimeTypeTrader>
+#include <KNewFileMenu>
+#include <konqmimedata.h>
+#include <konq_operations.h>
+#include <KService>
+#include <KLocale>
+#include <KPropertiesDialog>
+#include <KStandardAction>
+#include <KStandardDirs>
+#include <KToolBar>
+
+#include <panels/places/placesitem.h>
+#include <panels/places/placesitemmodel.h>
+
+#include <QApplication>
+#include <QClipboard>
+#include <QDir>
+
+#include "views/dolphinview.h"
+#include "views/viewmodecontroller.h"
+
+DolphinContextMenu::DolphinContextMenu(DolphinMainWindow* parent,
+ const QPoint& pos,
+ const KFileItem& fileInfo,
+ const KUrl& baseUrl) :
+ KMenu(parent),
+ m_pos(pos),
+ m_mainWindow(parent),
+ m_fileInfo(fileInfo),
+ m_baseUrl(baseUrl),
+ m_baseFileItem(0),
+ m_selectedItems(),
+ m_selectedItemsProperties(0),
+ m_context(NoContext),
+ m_copyToMenu(parent),
+ m_customActions(),
+ m_command(None),
+ m_removeAction(0)
+{
+ // The context menu either accesses the URLs of the selected items
+ // or the items itself. To increase the performance both lists are cached.
+ const DolphinView* view = m_mainWindow->activeViewContainer()->view();
+ m_selectedItems = view->selectedItems();
+}
+
+DolphinContextMenu::~DolphinContextMenu()
+{
+ delete m_selectedItemsProperties;
+ m_selectedItemsProperties = 0;
+}
+
+void DolphinContextMenu::setCustomActions(const QList<QAction*>& actions)
+{
+ m_customActions = actions;
+}
+
+DolphinContextMenu::Command DolphinContextMenu::open()
+{
+ // get the context information
+ if (m_baseUrl.protocol() == QLatin1String("trash")) {
+ m_context |= TrashContext;
+ }
+
+ if (!m_fileInfo.isNull() && !m_selectedItems.isEmpty()) {
+ m_context |= ItemContext;
+ // TODO: handle other use cases like devices + desktop files
+ }
+
+ // open the corresponding popup for the context
+ if (m_context & TrashContext) {
+ if (m_context & ItemContext) {
+ openTrashItemContextMenu();
+ } else {
+ openTrashContextMenu();
+ }
+ } else if (m_context & ItemContext) {
+ openItemContextMenu();
+ } else {
+ Q_ASSERT(m_context == NoContext);
+ openViewportContextMenu();
+ }
+
+ return m_command;
+}
+
+void DolphinContextMenu::keyPressEvent(QKeyEvent *ev)
+{
+ if (m_removeAction && ev->key() == Qt::Key_Shift) {
+ m_removeAction->update();
+ }
+ KMenu::keyPressEvent(ev);
+}
+
+void DolphinContextMenu::keyReleaseEvent(QKeyEvent *ev)
+{
+ if (m_removeAction && ev->key() == Qt::Key_Shift) {
+ m_removeAction->update();
+ }
+ KMenu::keyReleaseEvent(ev);
+}
+
+void DolphinContextMenu::openTrashContextMenu()
+{
+ Q_ASSERT(m_context & TrashContext);
+
+ QAction* emptyTrashAction = new QAction(KIcon("trash-empty"), i18nc("@action:inmenu", "Empty Trash"), this);
+ KConfig trashConfig("trashrc", KConfig::SimpleConfig);
+ emptyTrashAction->setEnabled(!trashConfig.group("Status").readEntry("Empty", true));
+ addAction(emptyTrashAction);
+
+ addCustomActions();
+
+ QAction* propertiesAction = m_mainWindow->actionCollection()->action("properties");
+ addAction(propertiesAction);
+
+ addShowMenuBarAction();
+
+ if (exec(m_pos) == emptyTrashAction) {
+ KonqOperations::emptyTrash(m_mainWindow);
+ }
+}
+
+void DolphinContextMenu::openTrashItemContextMenu()
+{
+ Q_ASSERT(m_context & TrashContext);
+ Q_ASSERT(m_context & ItemContext);
+
+ QAction* restoreAction = new QAction(i18nc("@action:inmenu", "Restore"), m_mainWindow);
+ addAction(restoreAction);
+
+ QAction* deleteAction = m_mainWindow->actionCollection()->action("delete");
+ addAction(deleteAction);
+
+ QAction* propertiesAction = m_mainWindow->actionCollection()->action("properties");
+ addAction(propertiesAction);
+
+ if (exec(m_pos) == restoreAction) {
+ KUrl::List selectedUrls;
+ foreach (const KFileItem &item, m_selectedItems) {
+ selectedUrls.append(item.url());
+ }
+
+ KonqOperations::restoreTrashedItems(selectedUrls, m_mainWindow);
+ }
+}
+
+void DolphinContextMenu::openItemContextMenu()
+{
+ Q_ASSERT(!m_fileInfo.isNull());
+
+ QAction* openParentInNewWindowAction = 0;
+ QAction* openParentInNewTabAction = 0;
+ QAction* addToPlacesAction = 0;
+ const KFileItemListProperties& selectedItemsProps = selectedItemsProperties();
+
+ if (m_selectedItems.count() == 1) {
+ if (m_fileInfo.isDir()) {
+ // setup 'Create New' menu
+ DolphinNewFileMenu* newFileMenu = new DolphinNewFileMenu(m_mainWindow->actionCollection(), m_mainWindow);
+ const DolphinView* view = m_mainWindow->activeViewContainer()->view();
+ newFileMenu->setViewShowsHiddenFiles(view->hiddenFilesShown());
+ newFileMenu->checkUpToDate();
+ newFileMenu->setPopupFiles(m_fileInfo.url());
+ newFileMenu->setEnabled(selectedItemsProps.supportsWriting());
+ connect(newFileMenu, SIGNAL(fileCreated(KUrl)), newFileMenu, SLOT(deleteLater()));
+ connect(newFileMenu, SIGNAL(directoryCreated(KUrl)), newFileMenu, SLOT(deleteLater()));
+
+ KMenu* menu = newFileMenu->menu();
+ menu->setTitle(i18nc("@title:menu Create new folder, file, link, etc.", "Create New"));
+ menu->setIcon(KIcon("document-new"));
+ addMenu(menu);
+ addSeparator();
+
+ // insert 'Open in new window' and 'Open in new tab' entries
+ addAction(m_mainWindow->actionCollection()->action("open_in_new_window"));
+ addAction(m_mainWindow->actionCollection()->action("open_in_new_tab"));
+
+ // insert 'Add to Places' entry
+ if (!placeExists(m_fileInfo.url())) {
+ addToPlacesAction = addAction(KIcon("bookmark-new"),
+ i18nc("@action:inmenu Add selected folder to places",
+ "Add to Places"));
+ }
+
+ addSeparator();
+ } else if (m_baseUrl.protocol().contains("search")) {
+ openParentInNewWindowAction = new QAction(KIcon("window-new"),
+ i18nc("@action:inmenu",
+ "Open Path in New Window"),
+ this);
+ addAction(openParentInNewWindowAction);
+
+ openParentInNewTabAction = new QAction(KIcon("tab-new"),
+ i18nc("@action:inmenu",
+ "Open Path in New Tab"),
+ this);
+ addAction(openParentInNewTabAction);
+
+ addSeparator();
+ } else if (!DolphinView::openItemAsFolderUrl(m_fileInfo).isEmpty()) {
+ // insert 'Open in new window' and 'Open in new tab' entries
+ addAction(m_mainWindow->actionCollection()->action("open_in_new_window"));
+ addAction(m_mainWindow->actionCollection()->action("open_in_new_tab"));
+
+ addSeparator();
+ }
+ } else {
+ bool selectionHasOnlyDirs = true;
+ foreach (const KFileItem& item, m_selectedItems) {
+ const KUrl& url = DolphinView::openItemAsFolderUrl(item);
+ if (url.isEmpty()) {
+ selectionHasOnlyDirs = false;
+ break;
+ }
+ }
+
+ if (selectionHasOnlyDirs) {
+ // insert 'Open in new tab' entry
+ addAction(m_mainWindow->actionCollection()->action("open_in_new_tabs"));
+ addSeparator();
+ }
+ }
+
+ insertDefaultItemActions(selectedItemsProps);
+
+ addSeparator();
+
+ KFileItemActions fileItemActions;
+ fileItemActions.setItemListProperties(selectedItemsProps);
+ addServiceActions(fileItemActions);
+
+ addFileItemPluginActions();
+
+ addVersionControlPluginActions();
+
+ // insert 'Copy To' and 'Move To' sub menus
+ if (GeneralSettings::showCopyMoveMenu()) {
+ m_copyToMenu.setItems(m_selectedItems);
+ m_copyToMenu.setReadOnly(!selectedItemsProps.supportsWriting());
+ m_copyToMenu.addActionsTo(this);
+ }
+
+ // insert 'Properties...' entry
+ QAction* propertiesAction = m_mainWindow->actionCollection()->action("properties");
+ addAction(propertiesAction);
+
+ QAction* activatedAction = exec(m_pos);
+ if (activatedAction) {
+ if (activatedAction == addToPlacesAction) {
+ const KUrl selectedUrl(m_fileInfo.url());
+ if (selectedUrl.isValid()) {
+ PlacesItemModel model;
+ const QString text = selectedUrl.fileName();
+ PlacesItem* item = model.createPlacesItem(text, selectedUrl);
+ model.appendItemToGroup(item);
+ }
+ } else if (activatedAction == openParentInNewWindowAction) {
+ m_command = OpenParentFolderInNewWindow;
+ } else if (activatedAction == openParentInNewTabAction) {
+ m_command = OpenParentFolderInNewTab;
+ }
+ }
+}
+
+void DolphinContextMenu::openViewportContextMenu()
+{
+ // setup 'Create New' menu
+ KNewFileMenu* newFileMenu = m_mainWindow->newFileMenu();
+ const DolphinView* view = m_mainWindow->activeViewContainer()->view();
+ newFileMenu->setViewShowsHiddenFiles(view->hiddenFilesShown());
+ newFileMenu->checkUpToDate();
+ newFileMenu->setPopupFiles(m_baseUrl);
+ addMenu(newFileMenu->menu());
+ addSeparator();
+
+ // Insert 'New Window' and 'New Tab' entries. Don't use "open_in_new_window" and
+ // "open_in_new_tab" here, as the current selection should get ignored.
+ addAction(m_mainWindow->actionCollection()->action("new_window"));
+ addAction(m_mainWindow->actionCollection()->action("new_tab"));
+
+ // Insert 'Add to Places' entry if exactly one item is selected
+ QAction* addToPlacesAction = 0;
+ if (!placeExists(m_mainWindow->activeViewContainer()->url())) {
+ addToPlacesAction = addAction(KIcon("bookmark-new"),
+ i18nc("@action:inmenu Add current folder to places", "Add to Places"));
+ }
+
+ addSeparator();
+
+ QAction* pasteAction = createPasteAction();
+ addAction(pasteAction);
+ addSeparator();
+
+ // Insert service actions
+ const KFileItemListProperties baseUrlProperties(KFileItemList() << baseFileItem());
+ KFileItemActions fileItemActions;
+ fileItemActions.setItemListProperties(baseUrlProperties);
+ addServiceActions(fileItemActions);
+
+ addFileItemPluginActions();
+
+ addVersionControlPluginActions();
+
+ addCustomActions();
+
+ QAction* propertiesAction = m_mainWindow->actionCollection()->action("properties");
+ addAction(propertiesAction);
+
+ addShowMenuBarAction();
+
+ QAction* action = exec(m_pos);
+ if (addToPlacesAction && (action == addToPlacesAction)) {
+ const DolphinViewContainer* container = m_mainWindow->activeViewContainer();
+ if (container->url().isValid()) {
+ PlacesItemModel model;
+ PlacesItem* item = model.createPlacesItem(container->placesText(),
+ container->url());
+ model.appendItemToGroup(item);
+ }
+ }
+}
+
+void DolphinContextMenu::insertDefaultItemActions(const KFileItemListProperties& properties)
+{
+ const KActionCollection* collection = m_mainWindow->actionCollection();
+
+ // Insert 'Cut', 'Copy' and 'Paste'
+ addAction(collection->action(KStandardAction::name(KStandardAction::Cut)));
+ addAction(collection->action(KStandardAction::name(KStandardAction::Copy)));
+ addAction(createPasteAction());
+
+ addSeparator();
+
+ // Insert 'Rename'
+ QAction* renameAction = collection->action("rename");
+ addAction(renameAction);
+
+ // Insert 'Move to Trash' and/or 'Delete'
+ if (properties.supportsDeleting()) {
+ const bool showDeleteAction = (KGlobal::config()->group("KDE").readEntry("ShowDeleteCommand", false) ||
+ !properties.isLocal());
+ const bool showMoveToTrashAction = (properties.isLocal() &&
+ properties.supportsMoving());
+
+ if (showDeleteAction && showMoveToTrashAction) {
+ delete m_removeAction;
+ m_removeAction = 0;
+ addAction(m_mainWindow->actionCollection()->action("move_to_trash"));
+ addAction(m_mainWindow->actionCollection()->action("delete"));
+ } else if (showDeleteAction && !showMoveToTrashAction) {
+ addAction(m_mainWindow->actionCollection()->action("delete"));
+ } else {
+ if (!m_removeAction) {
+ m_removeAction = new DolphinRemoveAction(this, m_mainWindow->actionCollection());
+ }
+ addAction(m_removeAction);
+ m_removeAction->update();
+ }
+ }
+}
+
+void DolphinContextMenu::addShowMenuBarAction()
+{
+ const KActionCollection* ac = m_mainWindow->actionCollection();
+ QAction* showMenuBar = ac->action(KStandardAction::name(KStandardAction::ShowMenubar));
+ if (!m_mainWindow->menuBar()->isVisible() && !m_mainWindow->toolBar()->isVisible()) {
+ addSeparator();
+ addAction(showMenuBar);
+ }
+}
+
+bool DolphinContextMenu::placeExists(const KUrl& url) const
+{
+ PlacesItemModel model;
+
+ const int count = model.count();
+ for (int i = 0; i < count; ++i) {
+ const KUrl placeUrl = model.placesItem(i)->url();
+ if (placeUrl.equals(url, KUrl::CompareWithoutTrailingSlash)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+QAction* DolphinContextMenu::createPasteAction()
+{
+ QAction* action = 0;
+ const bool isDir = !m_fileInfo.isNull() && m_fileInfo.isDir();
+ if (isDir && (m_selectedItems.count() == 1)) {
+ const QPair<bool, QString> pasteInfo = KonqOperations::pasteInfo(m_fileInfo.url());
+ action = new QAction(KIcon("edit-paste"), i18nc("@action:inmenu", "Paste Into Folder"), this);
+ action->setEnabled(pasteInfo.first);
+ connect(action, SIGNAL(triggered()), m_mainWindow, SLOT(pasteIntoFolder()));
+ } else {
+ action = m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::Paste));
+ }
+
+ return action;
+}
+
+KFileItemListProperties& DolphinContextMenu::selectedItemsProperties() const
+{
+ if (!m_selectedItemsProperties) {
+ m_selectedItemsProperties = new KFileItemListProperties(m_selectedItems);
+ }
+ return *m_selectedItemsProperties;
+}
+
+KFileItem DolphinContextMenu::baseFileItem()
+{
+ if (!m_baseFileItem) {
+ m_baseFileItem = new KFileItem(KFileItem::Unknown, KFileItem::Unknown, m_baseUrl);
+ }
+ return *m_baseFileItem;
+}
+
+void DolphinContextMenu::addServiceActions(KFileItemActions& fileItemActions)
+{
+ fileItemActions.setParentWidget(m_mainWindow);
+
+ // insert 'Open With...' action or sub menu
+ fileItemActions.addOpenWithActionsTo(this, "DesktopEntryName != 'dolphin'");
+
+ // insert 'Actions' sub menu
+ fileItemActions.addServiceActionsTo(this);
+}
+
+void DolphinContextMenu::addFileItemPluginActions()
+{
+ KFileItemListProperties props;
+ if (m_selectedItems.isEmpty()) {
+ props.setItems(KFileItemList() << baseFileItem());
+ } else {
+ props = selectedItemsProperties();
+ }
+
+ QString commonMimeType = props.mimeType();
+ if (commonMimeType.isEmpty()) {
+ commonMimeType = QLatin1String("application/octet-stream");
+ }
+
+ const KService::List pluginServices = KMimeTypeTrader::self()->query(commonMimeType, "KFileItemAction/Plugin", "exist Library");
+ if (pluginServices.isEmpty()) {
+ return;
+ }
+
+ const KConfig config("kservicemenurc", KConfig::NoGlobals);
+ const KConfigGroup showGroup = config.group("Show");
+
+ foreach (const KSharedPtr<KService>& service, pluginServices) {
+ if (!showGroup.readEntry(service->desktopEntryName(), true)) {
+ // The plugin has been disabled
+ continue;
+ }
+
+ // Old API (kdelibs-4.6.0 only)
+ KFileItemActionPlugin* plugin = service->createInstance<KFileItemActionPlugin>();
+ if (plugin) {
+ plugin->setParent(this);
+ addActions(plugin->actions(props, m_mainWindow));
+ }
+ // New API (kdelibs >= 4.6.1)
+ KAbstractFileItemActionPlugin* abstractPlugin = service->createInstance<KAbstractFileItemActionPlugin>();
+ if (abstractPlugin) {
+ abstractPlugin->setParent(this);
+ addActions(abstractPlugin->actions(props, m_mainWindow));
+ }
+ }
+}
+
+void DolphinContextMenu::addVersionControlPluginActions()
+{
+ const DolphinView* view = m_mainWindow->activeViewContainer()->view();
+ const QList<QAction*> versionControlActions = view->versionControlActions(m_selectedItems);
+ if (!versionControlActions.isEmpty()) {
+ foreach (QAction* action, versionControlActions) {
+ addAction(action);
+ }
+ addSeparator();
+ }
+}
+
+void DolphinContextMenu::addCustomActions()
+{
+ foreach (QAction* action, m_customActions) {
+ addAction(action);
+ }
+}
+
+#include "dolphincontextmenu.moc"
diff --git a/dolphin/src/dolphincontextmenu.h b/dolphin/src/dolphincontextmenu.h
new file mode 100644
index 0000000..180f917
--- /dev/null
+++ b/dolphin/src/dolphincontextmenu.h
@@ -0,0 +1,183 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef DOLPHINCONTEXTMENU_H
+#define DOLPHINCONTEXTMENU_H
+
+#include <KFileItem>
+#include <KService>
+#include <KUrl>
+#include <konq_copytomenu.h>
+#include <KMenu>
+
+#include <QObject>
+
+#include <QVector>
+
+#include <QScopedPointer>
+
+class QAction;
+class DolphinMainWindow;
+class KFileItemActions;
+class KFileItemListProperties;
+class DolphinRemoveAction;
+
+/**
+ * @brief Represents the context menu which appears when doing a right
+ * click on an item or the viewport of the file manager.
+ *
+ * Beside static menu entries (e. g. 'Paste' or 'Properties') two
+ * dynamic sub menus are shown when opening a context menu above
+ * an item:
+ * - 'Open With': Contains all applications which are registered to
+ * open items of the given MIME type.
+ * - 'Actions': Contains all actions which can be applied to the
+ * given item.
+ */
+class DolphinContextMenu : public KMenu
+{
+ Q_OBJECT
+
+public:
+ enum Command
+ {
+ None,
+ OpenParentFolderInNewWindow,
+ OpenParentFolderInNewTab
+ };
+
+ /**
+ * @parent Pointer to the main window the context menu
+ * belongs to.
+ * @pos Position in screen coordinates.
+ * @fileInfo Pointer to the file item the context menu
+ * is applied. If 0 is passed, the context menu
+ * is above the viewport.
+ * @baseUrl Base URL of the viewport where the context menu
+ * should be opened.
+ */
+ DolphinContextMenu(DolphinMainWindow* parent,
+ const QPoint& pos,
+ const KFileItem& fileInfo,
+ const KUrl& baseUrl);
+
+ virtual ~DolphinContextMenu();
+
+ void setCustomActions(const QList<QAction*>& actions);
+
+ /**
+ * Opens the context menu model and returns the requested
+ * command, that should be triggered by the caller. If
+ * Command::None has been returned, either the context-menu
+ * had been closed without executing an action or an
+ * already available action from the main-window has been
+ * executed.
+ */
+ Command open();
+
+protected:
+ virtual void keyPressEvent(QKeyEvent *ev);
+ virtual void keyReleaseEvent(QKeyEvent *ev);
+
+private:
+ void openTrashContextMenu();
+ void openTrashItemContextMenu();
+ void openItemContextMenu();
+ void openViewportContextMenu();
+
+ void insertDefaultItemActions(const KFileItemListProperties&);
+
+ /**
+ * Adds the "Show menubar" action to the menu if the
+ * menubar is hidden.
+ */
+ void addShowMenuBarAction();
+
+ bool placeExists(const KUrl& url) const;
+
+ QAction* createPasteAction();
+
+ KFileItemListProperties& selectedItemsProperties() const;
+
+ /**
+ * Returns the file item for m_baseUrl.
+ */
+ KFileItem baseFileItem();
+
+ /**
+ * Adds actions that have been installed as service-menu.
+ * (see http://techbase.kde.org/index.php?title=Development/Tutorials/Creating_Konqueror_Service_Menus)
+ */
+ void addServiceActions(KFileItemActions& fileItemActions);
+
+ /**
+ * Adds actions that are provided by a KFileItemActionPlugin.
+ */
+ void addFileItemPluginActions();
+
+ /**
+ * Adds actions that are provided by a KVersionControlPlugin.
+ */
+ void addVersionControlPluginActions();
+
+ /**
+ * Adds custom actions e.g. like the "[x] Expandable Folders"-action
+ * provided in the details view.
+ */
+ void addCustomActions();
+
+private:
+ struct Entry
+ {
+ int type;
+ QString name;
+ QString filePath; // empty for separator
+ QString templatePath; // same as filePath for template
+ QString icon;
+ QString comment;
+ };
+
+ enum ContextType
+ {
+ NoContext = 0,
+ ItemContext = 1,
+ TrashContext = 2
+ };
+
+ QPoint m_pos;
+ DolphinMainWindow* m_mainWindow;
+
+ KFileItem m_fileInfo;
+
+ KUrl m_baseUrl;
+ KFileItem* m_baseFileItem; /// File item for m_baseUrl
+
+ KFileItemList m_selectedItems;
+ mutable KFileItemListProperties* m_selectedItemsProperties;
+
+ int m_context;
+ KonqCopyToMenu m_copyToMenu;
+ QList<QAction*> m_customActions;
+
+ Command m_command;
+
+ DolphinRemoveAction* m_removeAction; // Action that represents either 'Move To Trash' or 'Delete'
+};
+
+#endif
diff --git a/dolphin/src/dolphindockwidget.cpp b/dolphin/src/dolphindockwidget.cpp
new file mode 100644
index 0000000..6495c8d
--- /dev/null
+++ b/dolphin/src/dolphindockwidget.cpp
@@ -0,0 +1,94 @@
+/***************************************************************************
+ * Copyright (C) 2010 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "dolphindockwidget.h"
+
+#include <QStyle>
+
+namespace {
+ // Disable the 'Floatable' feature, i.e., the possibility to drag the
+ // dock widget out of the main window. This works around problems like
+ // https://bugs.kde.org/show_bug.cgi?id=288629
+ // https://bugs.kde.org/show_bug.cgi?id=322299
+ const QDockWidget::DockWidgetFeatures DefaultDockWidgetFeatures = QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable;
+}
+
+ // Empty titlebar for the dock widgets when "Lock Layout" has been activated.
+class DolphinDockTitleBar : public QWidget
+{
+public:
+ DolphinDockTitleBar(QWidget* parent = 0) : QWidget(parent) {}
+ virtual ~DolphinDockTitleBar() {}
+
+ virtual QSize minimumSizeHint() const
+ {
+ const int border = style()->pixelMetric(QStyle::PM_DockWidgetTitleBarButtonMargin);
+ return QSize(border, border);
+ }
+
+ virtual QSize sizeHint() const
+ {
+ return minimumSizeHint();
+ }
+};
+
+DolphinDockWidget::DolphinDockWidget(const QString& title, QWidget* parent, Qt::WindowFlags flags) :
+ QDockWidget(title, parent, flags),
+ m_locked(false),
+ m_dockTitleBar(0)
+{
+ setFeatures(DefaultDockWidgetFeatures);
+}
+
+DolphinDockWidget::DolphinDockWidget(QWidget* parent, Qt::WindowFlags flags) :
+ QDockWidget(parent, flags),
+ m_locked(false),
+ m_dockTitleBar(0)
+{
+ setFeatures(DefaultDockWidgetFeatures);
+}
+
+DolphinDockWidget::~DolphinDockWidget()
+{
+}
+
+void DolphinDockWidget::setLocked(bool lock)
+{
+ if (lock != m_locked) {
+ m_locked = lock;
+
+ if (lock) {
+ if (!m_dockTitleBar) {
+ m_dockTitleBar = new DolphinDockTitleBar(this);
+ }
+ setTitleBarWidget(m_dockTitleBar);
+ setFeatures(QDockWidget::NoDockWidgetFeatures);
+ } else {
+ setTitleBarWidget(0);
+ setFeatures(DefaultDockWidgetFeatures);
+ }
+ }
+}
+
+bool DolphinDockWidget::isLocked() const
+{
+ return m_locked;
+}
+
+#include "dolphindockwidget.moc"
diff --git a/dolphin/src/dolphindockwidget.h b/dolphin/src/dolphindockwidget.h
new file mode 100644
index 0000000..8f49129
--- /dev/null
+++ b/dolphin/src/dolphindockwidget.h
@@ -0,0 +1,49 @@
+/***************************************************************************
+ * Copyright (C) 2010 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef DOLPHIN_DOCK_WIDGET_H
+#define DOLPHIN_DOCK_WIDGET_H
+
+#include <QDockWidget>
+
+/**
+ * @brief Extends QDockWidget to be able to get locked.
+ */
+class DolphinDockWidget : public QDockWidget
+{
+ Q_OBJECT
+
+public:
+ explicit DolphinDockWidget(const QString& title, QWidget* parent = 0, Qt::WindowFlags flags = 0);
+ explicit DolphinDockWidget(QWidget* parent = 0, Qt::WindowFlags flags = 0);
+ virtual ~DolphinDockWidget();
+
+ /**
+ * @param lock If \a lock is true, the title bar of the dock-widget will get hidden so
+ * that it is not possible for the user anymore to move or undock the dock-widget.
+ */
+ void setLocked(bool lock);
+ bool isLocked() const;
+
+private:
+ bool m_locked;
+ QWidget* m_dockTitleBar;
+};
+
+#endif
diff --git a/dolphin/src/dolphinmainwindow.cpp b/dolphin/src/dolphinmainwindow.cpp
new file mode 100644
index 0000000..95b08af
--- /dev/null
+++ b/dolphin/src/dolphinmainwindow.cpp
@@ -0,0 +1,1525 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Peter Penz <[email protected]> *
+ * Copyright (C) 2006 by Stefan Monov <[email protected]> *
+ * Copyright (C) 2006 by Cvetoslav Ludmiloff <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "dolphinmainwindow.h"
+
+#include "dolphinapplication.h"
+#include "dolphindockwidget.h"
+#include "dolphincontextmenu.h"
+#include "dolphinnewfilemenu.h"
+#include "dolphinrecenttabsmenu.h"
+#include "dolphintabwidget.h"
+#include "dolphinviewcontainer.h"
+#include "dolphintabpage.h"
+#include "panels/folders/folderspanel.h"
+#include "panels/places/placespanel.h"
+#include "panels/information/informationpanel.h"
+#include "settings/dolphinsettingsdialog.h"
+#include "statusbar/dolphinstatusbar.h"
+#include "views/dolphinviewactionhandler.h"
+#include "views/dolphinremoteencoding.h"
+#include "views/draganddrophelper.h"
+#include "views/viewproperties.h"
+#include "views/dolphinnewfilemenuobserver.h"
+
+#ifndef Q_OS_WIN
+#include "panels/terminal/terminalpanel.h"
+#endif
+
+#include "dolphin_generalsettings.h"
+
+#include <KAcceleratorManager>
+#include <KAction>
+#include <KActionCollection>
+#include <KActionMenu>
+#include <KConfig>
+#include <KDesktopFile>
+#include <kdeversion.h>
+#include <kdualaction.h>
+#include <KFileDialog>
+#include <KGlobal>
+#include <KLineEdit>
+#include <KToolBar>
+#include <KIcon>
+#include <KIconLoader>
+#include <KIO/NetAccess>
+#include <KIO/JobUiDelegate>
+#include <KInputDialog>
+#include <KLocale>
+#include <KProtocolManager>
+#include <KMenu>
+#include <KMenuBar>
+#include <KMessageBox>
+#include <KFileItemListProperties>
+#include <konqmimedata.h>
+#include <KProtocolInfo>
+#include <KRun>
+#include <KShell>
+#include <KStandardDirs>
+#include <kstatusbar.h>
+#include <KStandardAction>
+#include <KToggleAction>
+#include <KUrlNavigator>
+#include <KUrl>
+#include <KUrlComboBox>
+#include <KToolInvocation>
+
+#include <QDesktopWidget>
+#include <QDBusMessage>
+#include <QKeyEvent>
+#include <QClipboard>
+#include <QToolButton>
+
+namespace {
+ // Used for GeneralSettings::version() to determine whether
+ // an updated version of Dolphin is running.
+ const int CurrentDolphinVersion = 200;
+};
+
+DolphinMainWindow::DolphinMainWindow() :
+ KXmlGuiWindow(0),
+ m_newFileMenu(0),
+ m_tabWidget(0),
+ m_activeViewContainer(0),
+ m_actionHandler(0),
+ m_remoteEncoding(0),
+ m_settingsDialog(),
+ m_controlButton(0),
+ m_updateToolBarTimer(0),
+ m_lastHandleUrlStatJob(0)
+{
+ setObjectName("Dolphin#");
+
+ connect(&DolphinNewFileMenuObserver::instance(), SIGNAL(errorMessage(QString)),
+ this, SLOT(showErrorMessage(QString)));
+
+ KIO::FileUndoManager* undoManager = KIO::FileUndoManager::self();
+ undoManager->setUiInterface(new UndoUiInterface());
+
+ connect(undoManager, SIGNAL(undoAvailable(bool)),
+ this, SLOT(slotUndoAvailable(bool)));
+ connect(undoManager, SIGNAL(undoTextChanged(QString)),
+ this, SLOT(slotUndoTextChanged(QString)));
+ connect(undoManager, SIGNAL(jobRecordingStarted(CommandType)),
+ this, SLOT(clearStatusBar()));
+ connect(undoManager, SIGNAL(jobRecordingFinished(CommandType)),
+ this, SLOT(showCommand(CommandType)));
+
+ GeneralSettings* generalSettings = GeneralSettings::self();
+ const bool firstRun = (generalSettings->version() < 200);
+ if (firstRun) {
+ generalSettings->setViewPropsTimestamp(QDateTime::currentDateTime());
+ }
+
+ setAcceptDrops(true);
+
+ m_tabWidget = new DolphinTabWidget(this);
+ connect(m_tabWidget, SIGNAL(activeViewChanged(DolphinViewContainer*)),
+ this, SLOT(activeViewChanged(DolphinViewContainer*)));
+ connect(m_tabWidget, SIGNAL(tabCountChanged(int)),
+ this, SLOT(tabCountChanged(int)));
+ connect(m_tabWidget, SIGNAL(currentUrlChanged(KUrl)),
+ this, SLOT(setUrlAsCaption(KUrl)));
+ setCentralWidget(m_tabWidget);
+
+ setupActions();
+
+ m_actionHandler = new DolphinViewActionHandler(actionCollection(), this);
+ connect(m_actionHandler, SIGNAL(actionBeingHandled()), SLOT(clearStatusBar()));
+ connect(m_actionHandler, SIGNAL(createDirectory()), SLOT(createDirectory()));
+
+ m_remoteEncoding = new DolphinRemoteEncoding(this, m_actionHandler);
+ connect(this, SIGNAL(urlChanged(KUrl)),
+ m_remoteEncoding, SLOT(slotAboutToOpenUrl()));
+
+ setupDockWidgets();
+
+ setupGUI(Keys | Save | Create | ToolBar);
+ stateChanged("new_file");
+
+ QClipboard* clipboard = QApplication::clipboard();
+ connect(clipboard, SIGNAL(dataChanged()),
+ this, SLOT(updatePasteAction()));
+
+ QAction* showFilterBarAction = actionCollection()->action("show_filter_bar");
+ showFilterBarAction->setChecked(generalSettings->filterBar());
+
+ if (firstRun) {
+ menuBar()->setVisible(false);
+ // Assure a proper default size if Dolphin runs the first time
+ resize(750, 500);
+ }
+
+ const bool showMenu = !menuBar()->isHidden();
+ QAction* showMenuBarAction = actionCollection()->action(KStandardAction::name(KStandardAction::ShowMenubar));
+ showMenuBarAction->setChecked(showMenu); // workaround for bug #171080
+ if (!showMenu) {
+ createControlButton();
+ }
+}
+
+DolphinMainWindow::~DolphinMainWindow()
+{
+}
+
+void DolphinMainWindow::openDirectories(const QList<KUrl>& dirs)
+{
+ m_tabWidget->openDirectories(dirs);
+}
+
+void DolphinMainWindow::openFiles(const QList<KUrl>& files)
+{
+ m_tabWidget->openFiles(files);
+}
+
+void DolphinMainWindow::showCommand(CommandType command)
+{
+ DolphinStatusBar* statusBar = m_activeViewContainer->statusBar();
+ switch (command) {
+ case KIO::FileUndoManager::Copy:
+ statusBar->setText(i18nc("@info:status", "Successfully copied."));
+ break;
+ case KIO::FileUndoManager::Move:
+ statusBar->setText(i18nc("@info:status", "Successfully moved."));
+ break;
+ case KIO::FileUndoManager::Link:
+ statusBar->setText(i18nc("@info:status", "Successfully linked."));
+ break;
+ case KIO::FileUndoManager::Trash:
+ statusBar->setText(i18nc("@info:status", "Successfully moved to trash."));
+ break;
+ case KIO::FileUndoManager::Rename:
+ statusBar->setText(i18nc("@info:status", "Successfully renamed."));
+ break;
+
+ case KIO::FileUndoManager::Mkdir:
+ statusBar->setText(i18nc("@info:status", "Created folder."));
+ break;
+
+ default:
+ break;
+ }
+}
+
+void DolphinMainWindow::pasteIntoFolder()
+{
+ m_activeViewContainer->view()->pasteIntoFolder();
+}
+
+void DolphinMainWindow::changeUrl(const KUrl& url)
+{
+ if (!KProtocolManager::supportsListing(url)) {
+ // The URL navigator only checks for validity, not
+ // if the URL can be listed. An error message is
+ // shown due to DolphinViewContainer::restoreView().
+ return;
+ }
+
+ m_activeViewContainer->setUrl(url);
+ updateEditActions();
+ updatePasteAction();
+ updateViewActions();
+ updateGoActions();
+
+ emit urlChanged(url);
+}
+
+void DolphinMainWindow::slotTerminalDirectoryChanged(const KUrl& url)
+{
+ m_activeViewContainer->setAutoGrabFocus(false);
+ changeUrl(url);
+ m_activeViewContainer->setAutoGrabFocus(true);
+}
+
+void DolphinMainWindow::slotEditableStateChanged(bool editable)
+{
+ KToggleAction* editableLocationAction =
+ static_cast<KToggleAction*>(actionCollection()->action("editable_location"));
+ editableLocationAction->setChecked(editable);
+}
+
+void DolphinMainWindow::slotSelectionChanged(const KFileItemList& selection)
+{
+ updateEditActions();
+
+ const int selectedUrlsCount = m_tabWidget->currentTabPage()->selectedItemsCount();
+
+ QAction* compareFilesAction = actionCollection()->action("compare_files");
+ if (selectedUrlsCount == 2) {
+ compareFilesAction->setEnabled(isKompareInstalled());
+ } else {
+ compareFilesAction->setEnabled(false);
+ }
+
+ emit selectionChanged(selection);
+}
+
+void DolphinMainWindow::slotRequestItemInfo(const KFileItem& item)
+{
+ emit requestItemInfo(item);
+}
+
+void DolphinMainWindow::updateHistory()
+{
+ const KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator();
+ const int index = urlNavigator->historyIndex();
+
+ QAction* backAction = actionCollection()->action("go_back");
+ if (backAction) {
+ backAction->setToolTip(i18nc("@info", "Go back"));
+ backAction->setEnabled(index < urlNavigator->historySize() - 1);
+ }
+
+ QAction* forwardAction = actionCollection()->action("go_forward");
+ if (forwardAction) {
+ forwardAction->setToolTip(i18nc("@info", "Go forward"));
+ forwardAction->setEnabled(index > 0);
+ }
+}
+
+void DolphinMainWindow::updateFilterBarAction(bool show)
+{
+ QAction* showFilterBarAction = actionCollection()->action("show_filter_bar");
+ showFilterBarAction->setChecked(show);
+}
+
+void DolphinMainWindow::openNewMainWindow()
+{
+ KRun::run("dolphin %u", KUrl::List(), this);
+}
+
+void DolphinMainWindow::openNewActivatedTab()
+{
+ m_tabWidget->openNewActivatedTab();
+}
+
+void DolphinMainWindow::openNewTab(const KUrl& primaryUrl, const KUrl& secondaryUrl)
+{
+ m_tabWidget->openNewTab(primaryUrl, secondaryUrl);
+}
+
+void DolphinMainWindow::openNewActivatedTab(const KUrl& primaryUrl, const KUrl& secondaryUrl)
+{
+ m_tabWidget->openNewActivatedTab(primaryUrl, secondaryUrl);
+}
+
+void DolphinMainWindow::openInNewTab()
+{
+ const KFileItemList& list = m_activeViewContainer->view()->selectedItems();
+ if (list.isEmpty()) {
+ openNewTab(m_activeViewContainer->url());
+ } else {
+ foreach (const KFileItem& item, list) {
+ const KUrl& url = DolphinView::openItemAsFolderUrl(item);
+ if (!url.isEmpty()) {
+ openNewTab(url);
+ }
+ }
+ }
+}
+
+void DolphinMainWindow::openInNewWindow()
+{
+ KUrl newWindowUrl;
+
+ const KFileItemList list = m_activeViewContainer->view()->selectedItems();
+ if (list.isEmpty()) {
+ newWindowUrl = m_activeViewContainer->url();
+ } else if (list.count() == 1) {
+ const KFileItem& item = list.first();
+ newWindowUrl = DolphinView::openItemAsFolderUrl(item);
+ }
+
+ if (!newWindowUrl.isEmpty()) {
+ KRun::run("dolphin %u", KUrl::List() << newWindowUrl, this);
+ }
+}
+
+void DolphinMainWindow::showEvent(QShowEvent* event)
+{
+ KXmlGuiWindow::showEvent(event);
+
+ if (!event->spontaneous()) {
+ m_activeViewContainer->view()->setFocus();
+ }
+}
+
+void DolphinMainWindow::closeEvent(QCloseEvent* event)
+{
+ // Find out if Dolphin is closed directly by the user or
+ // by the session manager because the session is closed
+ bool closedByUser = true;
+ DolphinApplication *application = qobject_cast<DolphinApplication*>(qApp);
+ if (application && application->sessionSaving()) {
+ closedByUser = false;
+ }
+
+ if (m_tabWidget->count() > 1 && GeneralSettings::confirmClosingMultipleTabs() && closedByUser) {
+ // Ask the user if he really wants to quit and close all tabs.
+ // Open a confirmation dialog with 3 buttons:
+ // KDialog::Yes -> Quit
+ // KDialog::No -> Close only the current tab
+ // KDialog::Cancel -> do nothing
+ KDialog *dialog = new KDialog(this, Qt::Dialog);
+ dialog->setCaption(i18nc("@title:window", "Confirmation"));
+ dialog->setButtons(KDialog::Yes | KDialog::No | KDialog::Cancel);
+ dialog->setModal(true);
+ dialog->setButtonGuiItem(KDialog::Yes, KStandardGuiItem::quit());
+ dialog->setButtonGuiItem(KDialog::No, KGuiItem(i18n("C&lose Current Tab"), KIcon("tab-close")));
+ dialog->setButtonGuiItem(KDialog::Cancel, KStandardGuiItem::cancel());
+ dialog->setDefaultButton(KDialog::Yes);
+
+ bool doNotAskAgainCheckboxResult = false;
+
+ const int result = KMessageBox::createKMessageBox(dialog,
+ QMessageBox::Warning,
+ i18n("You have multiple tabs open in this window, are you sure you want to quit?"),
+ QStringList(),
+ i18n("Do not ask again"),
+ &doNotAskAgainCheckboxResult,
+ KMessageBox::Notify);
+
+ if (doNotAskAgainCheckboxResult) {
+ GeneralSettings::setConfirmClosingMultipleTabs(false);
+ }
+
+ switch (result) {
+ case KDialog::Yes:
+ // Quit
+ break;
+ case KDialog::No:
+ // Close only the current tab
+ m_tabWidget->closeTab();
+ default:
+ event->ignore();
+ return;
+ }
+ }
+
+ GeneralSettings::setVersion(CurrentDolphinVersion);
+ GeneralSettings::self()->writeConfig();
+
+ KXmlGuiWindow::closeEvent(event);
+}
+
+void DolphinMainWindow::saveProperties(KConfigGroup& group)
+{
+ m_tabWidget->saveProperties(group);
+}
+
+void DolphinMainWindow::readProperties(const KConfigGroup& group)
+{
+ m_tabWidget->readProperties(group);
+}
+
+void DolphinMainWindow::updateNewMenu()
+{
+ m_newFileMenu->setViewShowsHiddenFiles(activeViewContainer()->view()->hiddenFilesShown());
+ m_newFileMenu->checkUpToDate();
+ m_newFileMenu->setPopupFiles(activeViewContainer()->url());
+}
+
+void DolphinMainWindow::createDirectory()
+{
+ m_newFileMenu->setViewShowsHiddenFiles(activeViewContainer()->view()->hiddenFilesShown());
+ m_newFileMenu->setPopupFiles(activeViewContainer()->url());
+ m_newFileMenu->createDirectory();
+}
+
+void DolphinMainWindow::quit()
+{
+ close();
+}
+
+void DolphinMainWindow::showErrorMessage(const QString& message)
+{
+ m_activeViewContainer->showMessage(message, DolphinViewContainer::Error);
+}
+
+void DolphinMainWindow::slotUndoAvailable(bool available)
+{
+ QAction* undoAction = actionCollection()->action(KStandardAction::name(KStandardAction::Undo));
+ if (undoAction) {
+ undoAction->setEnabled(available);
+ }
+}
+
+void DolphinMainWindow::slotUndoTextChanged(const QString& text)
+{
+ QAction* undoAction = actionCollection()->action(KStandardAction::name(KStandardAction::Undo));
+ if (undoAction) {
+ undoAction->setText(text);
+ }
+}
+
+void DolphinMainWindow::undo()
+{
+ clearStatusBar();
+ KIO::FileUndoManager::self()->uiInterface()->setParentWidget(this);
+ KIO::FileUndoManager::self()->undo();
+}
+
+void DolphinMainWindow::cut()
+{
+ m_activeViewContainer->view()->cutSelectedItems();
+}
+
+void DolphinMainWindow::copy()
+{
+ m_activeViewContainer->view()->copySelectedItems();
+}
+
+void DolphinMainWindow::paste()
+{
+ m_activeViewContainer->view()->paste();
+}
+
+void DolphinMainWindow::find()
+{
+ m_activeViewContainer->setSearchModeEnabled(true);
+}
+
+void DolphinMainWindow::updatePasteAction()
+{
+ QAction* pasteAction = actionCollection()->action(KStandardAction::name(KStandardAction::Paste));
+ QPair<bool, QString> pasteInfo = m_activeViewContainer->view()->pasteInfo();
+ pasteAction->setEnabled(pasteInfo.first);
+ pasteAction->setText(pasteInfo.second);
+}
+
+void DolphinMainWindow::selectAll()
+{
+ clearStatusBar();
+
+ // if the URL navigator is editable and focused, select the whole
+ // URL instead of all items of the view
+
+ KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator();
+ QLineEdit* lineEdit = urlNavigator->editor()->lineEdit(); // krazy:exclude=qclasses
+ const bool selectUrl = urlNavigator->isUrlEditable() &&
+ lineEdit->hasFocus();
+ if (selectUrl) {
+ lineEdit->selectAll();
+ } else {
+ m_activeViewContainer->view()->selectAll();
+ }
+}
+
+void DolphinMainWindow::invertSelection()
+{
+ clearStatusBar();
+ m_activeViewContainer->view()->invertSelection();
+}
+
+void DolphinMainWindow::toggleSplitView()
+{
+ DolphinTabPage* tabPage = m_tabWidget->currentTabPage();
+ tabPage->setSplitViewEnabled(!tabPage->splitViewEnabled());
+
+ updateViewActions();
+}
+
+void DolphinMainWindow::reloadView()
+{
+ clearStatusBar();
+ m_activeViewContainer->view()->reload();
+}
+
+void DolphinMainWindow::stopLoading()
+{
+ m_activeViewContainer->view()->stopLoading();
+}
+
+void DolphinMainWindow::enableStopAction()
+{
+ actionCollection()->action("stop")->setEnabled(true);
+}
+
+void DolphinMainWindow::disableStopAction()
+{
+ actionCollection()->action("stop")->setEnabled(false);
+}
+
+void DolphinMainWindow::showFilterBar()
+{
+ m_activeViewContainer->setFilterBarVisible(true);
+}
+
+void DolphinMainWindow::toggleEditLocation()
+{
+ clearStatusBar();
+
+ QAction* action = actionCollection()->action("editable_location");
+ KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator();
+ urlNavigator->setUrlEditable(action->isChecked());
+}
+
+void DolphinMainWindow::replaceLocation()
+{
+ KUrlNavigator* navigator = m_activeViewContainer->urlNavigator();
+ navigator->setUrlEditable(true);
+ navigator->setFocus();
+
+ // select the whole text of the combo box editor
+ QLineEdit* lineEdit = navigator->editor()->lineEdit(); // krazy:exclude=qclasses
+ lineEdit->selectAll();
+}
+
+void DolphinMainWindow::togglePanelLockState()
+{
+ const bool newLockState = !GeneralSettings::lockPanels();
+ foreach (QObject* child, children()) {
+ DolphinDockWidget* dock = qobject_cast<DolphinDockWidget*>(child);
+ if (dock) {
+ dock->setLocked(newLockState);
+ }
+ }
+
+ GeneralSettings::setLockPanels(newLockState);
+}
+
+void DolphinMainWindow::goBack()
+{
+ KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator();
+ urlNavigator->goBack();
+
+ if (urlNavigator->locationState().isEmpty()) {
+ // An empty location state indicates a redirection URL,
+ // which must be skipped too
+ urlNavigator->goBack();
+ }
+}
+
+void DolphinMainWindow::goForward()
+{
+ m_activeViewContainer->urlNavigator()->goForward();
+}
+
+void DolphinMainWindow::goUp()
+{
+ m_activeViewContainer->urlNavigator()->goUp();
+}
+
+void DolphinMainWindow::goHome()
+{
+ m_activeViewContainer->urlNavigator()->goHome();
+}
+
+void DolphinMainWindow::goBack(Qt::MouseButtons buttons)
+{
+ // The default case (left button pressed) is handled in goBack().
+ if (buttons == Qt::MidButton) {
+ KUrlNavigator* urlNavigator = activeViewContainer()->urlNavigator();
+ const int index = urlNavigator->historyIndex() + 1;
+ openNewTab(urlNavigator->locationUrl(index));
+ }
+}
+
+void DolphinMainWindow::goForward(Qt::MouseButtons buttons)
+{
+ // The default case (left button pressed) is handled in goForward().
+ if (buttons == Qt::MidButton) {
+ KUrlNavigator* urlNavigator = activeViewContainer()->urlNavigator();
+ const int index = urlNavigator->historyIndex() - 1;
+ openNewTab(urlNavigator->locationUrl(index));
+ }
+}
+
+void DolphinMainWindow::goUp(Qt::MouseButtons buttons)
+{
+ // The default case (left button pressed) is handled in goUp().
+ if (buttons == Qt::MidButton) {
+ openNewTab(activeViewContainer()->url().upUrl());
+ }
+}
+
+void DolphinMainWindow::goHome(Qt::MouseButtons buttons)
+{
+ // The default case (left button pressed) is handled in goHome().
+ if (buttons == Qt::MidButton) {
+ openNewTab(GeneralSettings::self()->homeUrl());
+ }
+}
+
+void DolphinMainWindow::compareFiles()
+{
+ const KFileItemList items = m_tabWidget->currentTabPage()->selectedItems();
+ if (items.count() != 2) {
+ // The action is disabled in this case, but it could have been triggered
+ // via D-Bus, see https://bugs.kde.org/show_bug.cgi?id=325517
+ return;
+ }
+
+ KUrl urlA = items.at(0).url();
+ KUrl urlB = items.at(1).url();
+
+ QString command("kompare -c \"");
+ command.append(urlA.pathOrUrl());
+ command.append("\" \"");
+ command.append(urlB.pathOrUrl());
+ command.append('\"');
+ KRun::runCommand(command, "Kompare", "kompare", this);
+}
+
+void DolphinMainWindow::toggleShowMenuBar()
+{
+ const bool visible = menuBar()->isVisible();
+ menuBar()->setVisible(!visible);
+ if (visible) {
+ createControlButton();
+ } else {
+ deleteControlButton();
+ }
+}
+
+void DolphinMainWindow::openTerminal()
+{
+ QString dir(QDir::homePath());
+
+ // If the given directory is not local, it can still be the URL of an
+ // ioslave using UDS_LOCAL_PATH which to be converted first.
+ KUrl url = KIO::NetAccess::mostLocalUrl(m_activeViewContainer->url(), this);
+
+ //If the URL is local after the above conversion, set the directory.
+ if (url.isLocalFile()) {
+ dir = url.toLocalFile();
+ }
+
+ KToolInvocation::invokeTerminal(QString(), dir);
+}
+
+void DolphinMainWindow::editSettings()
+{
+ if (!m_settingsDialog) {
+ DolphinViewContainer* container = activeViewContainer();
+ container->view()->writeSettings();
+
+ const KUrl url = container->url();
+ DolphinSettingsDialog* settingsDialog = new DolphinSettingsDialog(url, this);
+ connect(settingsDialog, SIGNAL(settingsChanged()), this, SLOT(refreshViews()));
+ settingsDialog->setAttribute(Qt::WA_DeleteOnClose);
+ settingsDialog->show();
+ m_settingsDialog = settingsDialog;
+ } else {
+ m_settingsDialog.data()->raise();
+ }
+}
+
+void DolphinMainWindow::handleUrl(const KUrl& url)
+{
+ delete m_lastHandleUrlStatJob;
+ m_lastHandleUrlStatJob = 0;
+
+ if (url.isLocalFile() && QFileInfo(url.toLocalFile()).isDir()) {
+ activeViewContainer()->setUrl(url);
+ } else if (KProtocolManager::supportsListing(url)) {
+ // stat the URL to see if it is a dir or not
+ m_lastHandleUrlStatJob = KIO::stat(url, KIO::HideProgressInfo);
+ if (m_lastHandleUrlStatJob->ui()) {
+ m_lastHandleUrlStatJob->ui()->setWindow(this);
+ }
+ connect(m_lastHandleUrlStatJob, SIGNAL(result(KJob*)),
+ this, SLOT(slotHandleUrlStatFinished(KJob*)));
+
+ } else {
+ new KRun(url, this); // Automatically deletes itself after being finished
+ }
+}
+
+void DolphinMainWindow::slotHandleUrlStatFinished(KJob* job)
+{
+ m_lastHandleUrlStatJob = 0;
+ const KIO::UDSEntry entry = static_cast<KIO::StatJob*>(job)->statResult();
+ const KUrl url = static_cast<KIO::StatJob*>(job)->url();
+ if (entry.isDir()) {
+ activeViewContainer()->setUrl(url);
+ } else {
+ new KRun(url, this); // Automatically deletes itself after being finished
+ }
+}
+
+void DolphinMainWindow::slotWriteStateChanged(bool isFolderWritable)
+{
+ newFileMenu()->setEnabled(isFolderWritable);
+}
+
+void DolphinMainWindow::openContextMenu(const QPoint& pos,
+ const KFileItem& item,
+ const KUrl& url,
+ const QList<QAction*>& customActions)
+{
+ QWeakPointer<DolphinContextMenu> contextMenu = new DolphinContextMenu(this, pos, item, url);
+ contextMenu.data()->setCustomActions(customActions);
+ const DolphinContextMenu::Command command = contextMenu.data()->open();
+
+ switch (command) {
+ case DolphinContextMenu::OpenParentFolderInNewWindow: {
+ KRun::run("dolphin %u", KUrl::List() << item.url().upUrl(), this);
+ break;
+ }
+
+ case DolphinContextMenu::OpenParentFolderInNewTab:
+ openNewTab(item.url().upUrl());
+ break;
+
+ case DolphinContextMenu::None:
+ default:
+ break;
+ }
+
+ delete contextMenu.data();
+}
+
+void DolphinMainWindow::updateControlMenu()
+{
+ KMenu* menu = qobject_cast<KMenu*>(sender());
+ Q_ASSERT(menu);
+
+ // All actions get cleared by KMenu::clear(). The sub-menus are deleted
+ // by connecting to the aboutToHide() signal from the parent-menu.
+ menu->clear();
+
+ KActionCollection* ac = actionCollection();
+
+ // Add "Edit" actions
+ bool added = addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Undo)), menu) |
+ addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Find)), menu) |
+ addActionToMenu(ac->action("select_all"), menu) |
+ addActionToMenu(ac->action("invert_selection"), menu);
+
+ if (added) {
+ menu->addSeparator();
+ }
+
+ // Add "View" actions
+ if (!GeneralSettings::showZoomSlider()) {
+ addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ZoomIn)), menu);
+ addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ZoomOut)), menu);
+ menu->addSeparator();
+ }
+
+ added = addActionToMenu(ac->action("view_mode"), menu) |
+ addActionToMenu(ac->action("sort"), menu) |
+ addActionToMenu(ac->action("additional_info"), menu) |
+ addActionToMenu(ac->action("show_preview"), menu) |
+ addActionToMenu(ac->action("show_in_groups"), menu) |
+ addActionToMenu(ac->action("show_hidden_files"), menu);
+
+ if (added) {
+ menu->addSeparator();
+ }
+
+ added = addActionToMenu(ac->action("split_view"), menu) |
+ addActionToMenu(ac->action("reload"), menu) |
+ addActionToMenu(ac->action("view_properties"), menu);
+ if (added) {
+ menu->addSeparator();
+ }
+
+ addActionToMenu(ac->action("panels"), menu);
+ KMenu* locationBarMenu = new KMenu(i18nc("@action:inmenu", "Location Bar"), menu);
+ locationBarMenu->addAction(ac->action("editable_location"));
+ locationBarMenu->addAction(ac->action("replace_location"));
+ menu->addMenu(locationBarMenu);
+
+ menu->addSeparator();
+
+ // Add "Go" menu
+ KMenu* goMenu = new KMenu(i18nc("@action:inmenu", "Go"), menu);
+ connect(menu, SIGNAL(aboutToHide()), goMenu, SLOT(deleteLater()));
+ goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Back)));
+ goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Forward)));
+ goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Up)));
+ goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Home)));
+ goMenu->addAction(ac->action("closed_tabs"));
+ menu->addMenu(goMenu);
+
+ // Add "Tool" menu
+ KMenu* toolsMenu = new KMenu(i18nc("@action:inmenu", "Tools"), menu);
+ connect(menu, SIGNAL(aboutToHide()), toolsMenu, SLOT(deleteLater()));
+ toolsMenu->addAction(ac->action("show_filter_bar"));
+ toolsMenu->addAction(ac->action("compare_files"));
+ toolsMenu->addAction(ac->action("open_terminal"));
+ toolsMenu->addAction(ac->action("change_remote_encoding"));
+ menu->addMenu(toolsMenu);
+
+ // Add "Settings" menu entries
+ addActionToMenu(ac->action(KStandardAction::name(KStandardAction::KeyBindings)), menu);
+ addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ConfigureToolbars)), menu);
+ addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Preferences)), menu);
+
+ // Add "Help" menu
+ KMenu* helpMenu = new KMenu(i18nc("@action:inmenu", "Help"), menu);
+ connect(menu, SIGNAL(aboutToHide()), helpMenu, SLOT(deleteLater()));
+ helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::HelpContents)));
+ helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::WhatsThis)));
+ helpMenu->addSeparator();
+ helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::ReportBug)));
+ helpMenu->addSeparator();
+ helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::SwitchApplicationLanguage)));
+ helpMenu->addSeparator();
+ helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::AboutApp)));
+ helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::AboutKDE)));
+ menu->addMenu(helpMenu);
+
+ menu->addSeparator();
+ addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ShowMenubar)), menu);
+}
+
+void DolphinMainWindow::updateToolBar()
+{
+ if (!menuBar()->isVisible()) {
+ createControlButton();
+ }
+}
+
+void DolphinMainWindow::slotControlButtonDeleted()
+{
+ m_controlButton = 0;
+ m_updateToolBarTimer->start();
+}
+
+void DolphinMainWindow::slotPanelErrorMessage(const QString& error)
+{
+ activeViewContainer()->showMessage(error, DolphinViewContainer::Error);
+}
+
+void DolphinMainWindow::slotPlaceActivated(const KUrl& url)
+{
+ DolphinViewContainer* view = activeViewContainer();
+
+ if (view->url() == url) {
+ // We can end up here if the user clicked a device in the Places Panel
+ // which had been unmounted earlier, see https://bugs.kde.org/show_bug.cgi?id=161385.
+ reloadView();
+ } else {
+ changeUrl(url);
+ }
+}
+
+void DolphinMainWindow::closedTabsCountChanged(unsigned int count)
+{
+ actionCollection()->action("undo_close_tab")->setEnabled(count > 0);
+}
+
+void DolphinMainWindow::activeViewChanged(DolphinViewContainer* viewContainer)
+{
+ DolphinViewContainer* oldViewContainer = m_activeViewContainer;
+ Q_ASSERT(viewContainer);
+
+ m_activeViewContainer = viewContainer;
+
+ if (oldViewContainer) {
+ // Disconnect all signals between the old view container (container,
+ // view and url navigator) and main window.
+ oldViewContainer->disconnect(this);
+ oldViewContainer->view()->disconnect(this);
+ oldViewContainer->urlNavigator()->disconnect(this);
+ }
+
+ connectViewSignals(viewContainer);
+
+ m_actionHandler->setCurrentView(viewContainer->view());
+
+ updateHistory();
+ updateEditActions();
+ updatePasteAction();
+ updateViewActions();
+ updateGoActions();
+
+ const KUrl url = viewContainer->url();
+ emit urlChanged(url);
+}
+
+void DolphinMainWindow::tabCountChanged(int count)
+{
+ const bool enableTabActions = (count > 1);
+ actionCollection()->action("close_tab")->setEnabled(enableTabActions);
+ actionCollection()->action("activate_next_tab")->setEnabled(enableTabActions);
+ actionCollection()->action("activate_prev_tab")->setEnabled(enableTabActions);
+}
+
+void DolphinMainWindow::setUrlAsCaption(const KUrl& url)
+{
+ QString caption;
+ if (!url.isLocalFile()) {
+ caption.append(url.protocol() + " - ");
+ if (url.hasHost()) {
+ caption.append(url.host() + " - ");
+ }
+ }
+
+ const QString fileName = url.fileName().isEmpty() ? "/" : url.fileName();
+ caption.append(fileName);
+
+ setCaption(caption);
+}
+
+void DolphinMainWindow::setupActions()
+{
+ // setup 'File' menu
+ m_newFileMenu = new DolphinNewFileMenu(actionCollection(), this);
+ KMenu* menu = m_newFileMenu->menu();
+ menu->setTitle(i18nc("@title:menu Create new folder, file, link, etc.", "Create New"));
+ menu->setIcon(KIcon("document-new"));
+ m_newFileMenu->setDelayed(false);
+ connect(menu, SIGNAL(aboutToShow()),
+ this, SLOT(updateNewMenu()));
+
+ KAction* newWindow = actionCollection()->addAction("new_window");
+ newWindow->setIcon(KIcon("window-new"));
+ newWindow->setText(i18nc("@action:inmenu File", "New &Window"));
+ newWindow->setShortcut(Qt::CTRL | Qt::Key_N);
+ connect(newWindow, SIGNAL(triggered()), this, SLOT(openNewMainWindow()));
+
+ KAction* newTab = actionCollection()->addAction("new_tab");
+ newTab->setIcon(KIcon("tab-new"));
+ newTab->setText(i18nc("@action:inmenu File", "New Tab"));
+ newTab->setShortcut(KShortcut(Qt::CTRL | Qt::Key_T, Qt::CTRL | Qt::SHIFT | Qt::Key_N));
+ connect(newTab, SIGNAL(triggered()), this, SLOT(openNewActivatedTab()));
+
+ KAction* closeTab = actionCollection()->addAction("close_tab");
+ closeTab->setIcon(KIcon("tab-close"));
+ closeTab->setText(i18nc("@action:inmenu File", "Close Tab"));
+ closeTab->setShortcut(Qt::CTRL | Qt::Key_W);
+ closeTab->setEnabled(false);
+ connect(closeTab, SIGNAL(triggered()), m_tabWidget, SLOT(closeTab()));
+
+ KStandardAction::quit(this, SLOT(quit()), actionCollection());
+
+ // setup 'Edit' menu
+ KStandardAction::undo(this,
+ SLOT(undo()),
+ actionCollection());
+
+ // need to remove shift+del from cut action, else the shortcut for deletejob
+ // doesn't work
+ KAction* cut = KStandardAction::cut(this, SLOT(cut()), actionCollection());
+ KShortcut cutShortcut = cut->shortcut();
+ cutShortcut.remove(Qt::SHIFT | Qt::Key_Delete, KShortcut::KeepEmpty);
+ cut->setShortcut(cutShortcut);
+ KStandardAction::copy(this, SLOT(copy()), actionCollection());
+ KAction* paste = KStandardAction::paste(this, SLOT(paste()), actionCollection());
+ // The text of the paste-action is modified dynamically by Dolphin
+ // (e. g. to "Paste One Folder"). To prevent that the size of the toolbar changes
+ // due to the long text, the text "Paste" is used:
+ paste->setIconText(i18nc("@action:inmenu Edit", "Paste"));
+
+ KStandardAction::find(this, SLOT(find()), actionCollection());
+
+ KAction* selectAll = actionCollection()->addAction("select_all");
+ selectAll->setText(i18nc("@action:inmenu Edit", "Select All"));
+ selectAll->setShortcut(Qt::CTRL | Qt::Key_A);
+ connect(selectAll, SIGNAL(triggered()), this, SLOT(selectAll()));
+
+ KAction* invertSelection = actionCollection()->addAction("invert_selection");
+ invertSelection->setText(i18nc("@action:inmenu Edit", "Invert Selection"));
+ invertSelection->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_A);
+ connect(invertSelection, SIGNAL(triggered()), this, SLOT(invertSelection()));
+
+ // setup 'View' menu
+ // (note that most of it is set up in DolphinViewActionHandler)
+
+ KAction* split = actionCollection()->addAction("split_view");
+ split->setShortcut(Qt::Key_F3);
+ connect(split, SIGNAL(triggered()), this, SLOT(toggleSplitView()));
+
+ KAction* reload = actionCollection()->addAction("reload");
+ reload->setText(i18nc("@action:inmenu View", "Reload"));
+ reload->setShortcut(Qt::Key_F5);
+ reload->setIcon(KIcon("view-refresh"));
+ connect(reload, SIGNAL(triggered()), this, SLOT(reloadView()));
+
+ KAction* stop = actionCollection()->addAction("stop");
+ stop->setText(i18nc("@action:inmenu View", "Stop"));
+ stop->setToolTip(i18nc("@info", "Stop loading"));
+ stop->setIcon(KIcon("process-stop"));
+ connect(stop, SIGNAL(triggered()), this, SLOT(stopLoading()));
+
+ KToggleAction* editableLocation = actionCollection()->add<KToggleAction>("editable_location");
+ editableLocation->setText(i18nc("@action:inmenu Navigation Bar", "Editable Location"));
+ editableLocation->setShortcut(Qt::Key_F6);
+ connect(editableLocation, SIGNAL(triggered()), this, SLOT(toggleEditLocation()));
+
+ KAction* replaceLocation = actionCollection()->addAction("replace_location");
+ replaceLocation->setText(i18nc("@action:inmenu Navigation Bar", "Replace Location"));
+ replaceLocation->setShortcut(Qt::CTRL | Qt::Key_L);
+ connect(replaceLocation, SIGNAL(triggered()), this, SLOT(replaceLocation()));
+
+ // setup 'Go' menu
+ KAction* backAction = KStandardAction::back(this, SLOT(goBack()), actionCollection());
+ connect(backAction, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), this, SLOT(goBack(Qt::MouseButtons)));
+ KShortcut backShortcut = backAction->shortcut();
+ backShortcut.setAlternate(Qt::Key_Backspace);
+ backAction->setShortcut(backShortcut);
+
+ DolphinRecentTabsMenu* recentTabsMenu = new DolphinRecentTabsMenu(this);
+ actionCollection()->addAction("closed_tabs", recentTabsMenu);
+ connect(m_tabWidget, SIGNAL(rememberClosedTab(KUrl,QByteArray)),
+ recentTabsMenu, SLOT(rememberClosedTab(KUrl,QByteArray)));
+ connect(recentTabsMenu, SIGNAL(restoreClosedTab(QByteArray)),
+ m_tabWidget, SLOT(restoreClosedTab(QByteArray)));
+ connect(recentTabsMenu, SIGNAL(closedTabsCountChanged(uint)),
+ this, SLOT(closedTabsCountChanged(uint)));
+
+ KAction* undoCloseTab = actionCollection()->addAction("undo_close_tab");
+ undoCloseTab->setText(i18nc("@action:inmenu File", "Undo close tab"));
+ undoCloseTab->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_T);
+ undoCloseTab->setIcon(KIcon("edit-undo"));
+ undoCloseTab->setEnabled(false);
+ connect(undoCloseTab, SIGNAL(triggered()), recentTabsMenu, SLOT(undoCloseTab()));
+
+ KAction* forwardAction = KStandardAction::forward(this, SLOT(goForward()), actionCollection());
+ connect(forwardAction, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), this, SLOT(goForward(Qt::MouseButtons)));
+
+ KAction* upAction = KStandardAction::up(this, SLOT(goUp()), actionCollection());
+ connect(upAction, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), this, SLOT(goUp(Qt::MouseButtons)));
+
+ KAction* homeAction = KStandardAction::home(this, SLOT(goHome()), actionCollection());
+ connect(homeAction, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), this, SLOT(goHome(Qt::MouseButtons)));
+
+ // setup 'Tools' menu
+ KAction* showFilterBar = actionCollection()->addAction("show_filter_bar");
+ showFilterBar->setText(i18nc("@action:inmenu Tools", "Show Filter Bar"));
+ showFilterBar->setIcon(KIcon("view-filter"));
+ showFilterBar->setShortcut(Qt::CTRL | Qt::Key_I);
+ connect(showFilterBar, SIGNAL(triggered()), this, SLOT(showFilterBar()));
+
+ KAction* compareFiles = actionCollection()->addAction("compare_files");
+ compareFiles->setText(i18nc("@action:inmenu Tools", "Compare Files"));
+ compareFiles->setIcon(KIcon("kompare"));
+ compareFiles->setEnabled(false);
+ connect(compareFiles, SIGNAL(triggered()), this, SLOT(compareFiles()));
+
+ KAction* openTerminal = actionCollection()->addAction("open_terminal");
+ openTerminal->setText(i18nc("@action:inmenu Tools", "Open Terminal"));
+ openTerminal->setIcon(KIcon("utilities-terminal"));
+ openTerminal->setShortcut(Qt::SHIFT | Qt::Key_F4);
+ connect(openTerminal, SIGNAL(triggered()), this, SLOT(openTerminal()));
+
+ // setup 'Settings' menu
+ KToggleAction* showMenuBar = KStandardAction::showMenubar(0, 0, actionCollection());
+ connect(showMenuBar, SIGNAL(triggered(bool)), // Fixes #286822
+ this, SLOT(toggleShowMenuBar()), Qt::QueuedConnection);
+ KStandardAction::preferences(this, SLOT(editSettings()), actionCollection());
+
+ // not in menu actions
+ QList<QKeySequence> nextTabKeys;
+ nextTabKeys.append(KStandardShortcut::tabNext().primary());
+ nextTabKeys.append(QKeySequence(Qt::CTRL | Qt::Key_Tab));
+
+ QList<QKeySequence> prevTabKeys;
+ prevTabKeys.append(KStandardShortcut::tabPrev().primary());
+ prevTabKeys.append(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Tab));
+
+ KAction* activateNextTab = actionCollection()->addAction("activate_next_tab");
+ activateNextTab->setIconText(i18nc("@action:inmenu", "Next Tab"));
+ activateNextTab->setText(i18nc("@action:inmenu", "Activate Next Tab"));
+ activateNextTab->setEnabled(false);
+ connect(activateNextTab, SIGNAL(triggered()), m_tabWidget, SLOT(activateNextTab()));
+ activateNextTab->setShortcuts(QApplication::isRightToLeft() ? prevTabKeys : nextTabKeys);
+
+ KAction* activatePrevTab = actionCollection()->addAction("activate_prev_tab");
+ activatePrevTab->setIconText(i18nc("@action:inmenu", "Previous Tab"));
+ activatePrevTab->setText(i18nc("@action:inmenu", "Activate Previous Tab"));
+ activatePrevTab->setEnabled(false);
+ connect(activatePrevTab, SIGNAL(triggered()), m_tabWidget, SLOT(activatePrevTab()));
+ activatePrevTab->setShortcuts(QApplication::isRightToLeft() ? nextTabKeys : prevTabKeys);
+
+ // for context menu
+ KAction* openInNewTab = actionCollection()->addAction("open_in_new_tab");
+ openInNewTab->setText(i18nc("@action:inmenu", "Open in New Tab"));
+ openInNewTab->setIcon(KIcon("tab-new"));
+ connect(openInNewTab, SIGNAL(triggered()), this, SLOT(openInNewTab()));
+
+ KAction* openInNewTabs = actionCollection()->addAction("open_in_new_tabs");
+ openInNewTabs->setText(i18nc("@action:inmenu", "Open in New Tabs"));
+ openInNewTabs->setIcon(KIcon("tab-new"));
+ connect(openInNewTabs, SIGNAL(triggered()), this, SLOT(openInNewTab()));
+
+ KAction* openInNewWindow = actionCollection()->addAction("open_in_new_window");
+ openInNewWindow->setText(i18nc("@action:inmenu", "Open in New Window"));
+ openInNewWindow->setIcon(KIcon("window-new"));
+ connect(openInNewWindow, SIGNAL(triggered()), this, SLOT(openInNewWindow()));
+}
+
+void DolphinMainWindow::setupDockWidgets()
+{
+ const bool lock = GeneralSettings::lockPanels();
+
+ KDualAction* lockLayoutAction = actionCollection()->add<KDualAction>("lock_panels");
+ lockLayoutAction->setActiveText(i18nc("@action:inmenu Panels", "Unlock Panels"));
+ lockLayoutAction->setActiveIcon(KIcon("object-unlocked"));
+ lockLayoutAction->setInactiveText(i18nc("@action:inmenu Panels", "Lock Panels"));
+ lockLayoutAction->setInactiveIcon(KIcon("object-locked"));
+ lockLayoutAction->setActive(lock);
+ connect(lockLayoutAction, SIGNAL(triggered()), this, SLOT(togglePanelLockState()));
+
+ // Setup "Information"
+ DolphinDockWidget* infoDock = new DolphinDockWidget(i18nc("@title:window", "Information"));
+ infoDock->setLocked(lock);
+ infoDock->setObjectName("infoDock");
+ infoDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
+ Panel* infoPanel = new InformationPanel(infoDock);
+ infoPanel->setCustomContextMenuActions(QList<QAction*>() << lockLayoutAction);
+ connect(infoPanel, SIGNAL(urlActivated(KUrl)), this, SLOT(handleUrl(KUrl)));
+ infoDock->setWidget(infoPanel);
+
+ QAction* infoAction = infoDock->toggleViewAction();
+ createPanelAction(KIcon("dialog-information"), Qt::Key_F11, infoAction, "show_information_panel");
+
+ addDockWidget(Qt::RightDockWidgetArea, infoDock);
+ connect(this, SIGNAL(urlChanged(KUrl)),
+ infoPanel, SLOT(setUrl(KUrl)));
+ connect(this, SIGNAL(selectionChanged(KFileItemList)),
+ infoPanel, SLOT(setSelection(KFileItemList)));
+ connect(this, SIGNAL(requestItemInfo(KFileItem)),
+ infoPanel, SLOT(requestDelayedItemInfo(KFileItem)));
+
+ // Setup "Folders"
+ DolphinDockWidget* foldersDock = new DolphinDockWidget(i18nc("@title:window", "Folders"));
+ foldersDock->setLocked(lock);
+ foldersDock->setObjectName("foldersDock");
+ foldersDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
+ FoldersPanel* foldersPanel = new FoldersPanel(foldersDock);
+ foldersPanel->setCustomContextMenuActions(QList<QAction*>() << lockLayoutAction);
+ foldersDock->setWidget(foldersPanel);
+
+ QAction* foldersAction = foldersDock->toggleViewAction();
+ createPanelAction(KIcon("folder"), Qt::Key_F7, foldersAction, "show_folders_panel");
+
+ addDockWidget(Qt::LeftDockWidgetArea, foldersDock);
+ connect(this, SIGNAL(urlChanged(KUrl)),
+ foldersPanel, SLOT(setUrl(KUrl)));
+ connect(foldersPanel, SIGNAL(folderActivated(KUrl)),
+ this, SLOT(changeUrl(KUrl)));
+ connect(foldersPanel, SIGNAL(folderMiddleClicked(KUrl)),
+ this, SLOT(openNewTab(KUrl)));
+ connect(foldersPanel, SIGNAL(errorMessage(QString)),
+ this, SLOT(slotPanelErrorMessage(QString)));
+
+ // Setup "Terminal"
+#ifndef Q_OS_WIN
+ DolphinDockWidget* terminalDock = new DolphinDockWidget(i18nc("@title:window Shell terminal", "Terminal"));
+ terminalDock->setLocked(lock);
+ terminalDock->setObjectName("terminalDock");
+ terminalDock->setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);
+ Panel* terminalPanel = new TerminalPanel(terminalDock);
+ terminalPanel->setCustomContextMenuActions(QList<QAction*>() << lockLayoutAction);
+ terminalDock->setWidget(terminalPanel);
+
+ connect(terminalPanel, SIGNAL(hideTerminalPanel()), terminalDock, SLOT(hide()));
+ connect(terminalPanel, SIGNAL(changeUrl(KUrl)), this, SLOT(slotTerminalDirectoryChanged(KUrl)));
+ connect(terminalDock, SIGNAL(visibilityChanged(bool)),
+ terminalPanel, SLOT(dockVisibilityChanged()));
+
+ QAction* terminalAction = terminalDock->toggleViewAction();
+ createPanelAction(KIcon("utilities-terminal"), Qt::Key_F4, terminalAction, "show_terminal_panel");
+
+ addDockWidget(Qt::BottomDockWidgetArea, terminalDock);
+ connect(this, SIGNAL(urlChanged(KUrl)),
+ terminalPanel, SLOT(setUrl(KUrl)));
+#endif
+
+ if (GeneralSettings::version() < 200) {
+ infoDock->hide();
+ foldersDock->hide();
+#ifndef Q_OS_WIN
+ terminalDock->hide();
+#endif
+ }
+
+ // Setup "Places"
+ DolphinDockWidget* placesDock = new DolphinDockWidget(i18nc("@title:window", "Places"));
+ placesDock->setLocked(lock);
+ placesDock->setObjectName("placesDock");
+ placesDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
+
+ PlacesPanel* placesPanel = new PlacesPanel(placesDock);
+ placesPanel->setCustomContextMenuActions(QList<QAction*>() << lockLayoutAction);
+ placesDock->setWidget(placesPanel);
+
+ QAction* placesAction = placesDock->toggleViewAction();
+ createPanelAction(KIcon("bookmarks"), Qt::Key_F9, placesAction, "show_places_panel");
+
+ addDockWidget(Qt::LeftDockWidgetArea, placesDock);
+ connect(placesPanel, SIGNAL(placeActivated(KUrl)),
+ this, SLOT(slotPlaceActivated(KUrl)));
+ connect(placesPanel, SIGNAL(placeMiddleClicked(KUrl)),
+ this, SLOT(openNewTab(KUrl)));
+ connect(placesPanel, SIGNAL(errorMessage(QString)),
+ this, SLOT(slotPanelErrorMessage(QString)));
+ connect(this, SIGNAL(urlChanged(KUrl)),
+ placesPanel, SLOT(setUrl(KUrl)));
+ connect(placesDock, SIGNAL(visibilityChanged(bool)),
+ m_tabWidget, SLOT(slotPlacesPanelVisibilityChanged(bool)));
+ connect(this, SIGNAL(settingsChanged()),
+ placesPanel, SLOT(readSettings()));
+
+ m_tabWidget->slotPlacesPanelVisibilityChanged(placesPanel->isVisible());
+
+ // Add actions into the "Panels" menu
+ KActionMenu* panelsMenu = new KActionMenu(i18nc("@action:inmenu View", "Panels"), this);
+ actionCollection()->addAction("panels", panelsMenu);
+ panelsMenu->setDelayed(false);
+ const KActionCollection* ac = actionCollection();
+ panelsMenu->addAction(ac->action("show_places_panel"));
+ panelsMenu->addAction(ac->action("show_information_panel"));
+ panelsMenu->addAction(ac->action("show_folders_panel"));
+#ifndef Q_OS_WIN
+ panelsMenu->addAction(ac->action("show_terminal_panel"));
+#endif
+ panelsMenu->addSeparator();
+ panelsMenu->addAction(lockLayoutAction);
+}
+
+void DolphinMainWindow::updateEditActions()
+{
+ const KFileItemList list = m_activeViewContainer->view()->selectedItems();
+ if (list.isEmpty()) {
+ stateChanged("has_no_selection");
+ } else {
+ stateChanged("has_selection");
+
+ KActionCollection* col = actionCollection();
+ QAction* renameAction = col->action("rename");
+ QAction* moveToTrashAction = col->action("move_to_trash");
+ QAction* deleteAction = col->action("delete");
+ QAction* cutAction = col->action(KStandardAction::name(KStandardAction::Cut));
+ QAction* deleteWithTrashShortcut = col->action("delete_shortcut"); // see DolphinViewActionHandler
+
+ KFileItemListProperties capabilities(list);
+ const bool enableMoveToTrash = capabilities.isLocal() && capabilities.supportsMoving();
+
+ renameAction->setEnabled(capabilities.supportsMoving());
+ moveToTrashAction->setEnabled(enableMoveToTrash);
+ deleteAction->setEnabled(capabilities.supportsDeleting());
+ deleteWithTrashShortcut->setEnabled(capabilities.supportsDeleting() && !enableMoveToTrash);
+ cutAction->setEnabled(capabilities.supportsMoving());
+ }
+}
+
+void DolphinMainWindow::updateViewActions()
+{
+ m_actionHandler->updateViewActions();
+
+ QAction* showFilterBarAction = actionCollection()->action("show_filter_bar");
+ showFilterBarAction->setChecked(m_activeViewContainer->isFilterBarVisible());
+
+ updateSplitAction();
+
+ QAction* editableLocactionAction = actionCollection()->action("editable_location");
+ const KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator();
+ editableLocactionAction->setChecked(urlNavigator->isUrlEditable());
+}
+
+void DolphinMainWindow::updateGoActions()
+{
+ QAction* goUpAction = actionCollection()->action(KStandardAction::name(KStandardAction::Up));
+ const KUrl currentUrl = m_activeViewContainer->url();
+ goUpAction->setEnabled(currentUrl.upUrl() != currentUrl);
+}
+
+void DolphinMainWindow::createControlButton()
+{
+ if (m_controlButton) {
+ return;
+ }
+ Q_ASSERT(!m_controlButton);
+
+ m_controlButton = new QToolButton(this);
+ m_controlButton->setIcon(KIcon("applications-system"));
+ m_controlButton->setText(i18nc("@action", "Control"));
+ m_controlButton->setPopupMode(QToolButton::InstantPopup);
+ m_controlButton->setToolButtonStyle(toolBar()->toolButtonStyle());
+
+ KMenu* controlMenu = new KMenu(m_controlButton);
+ connect(controlMenu, SIGNAL(aboutToShow()), this, SLOT(updateControlMenu()));
+
+ m_controlButton->setMenu(controlMenu);
+
+ toolBar()->addWidget(m_controlButton);
+ connect(toolBar(), SIGNAL(iconSizeChanged(QSize)),
+ m_controlButton, SLOT(setIconSize(QSize)));
+ connect(toolBar(), SIGNAL(toolButtonStyleChanged(Qt::ToolButtonStyle)),
+ m_controlButton, SLOT(setToolButtonStyle(Qt::ToolButtonStyle)));
+
+ // The added widgets are owned by the toolbar and may get deleted when e.g. the toolbar
+ // gets edited. In this case we must add them again. The adding is done asynchronously by
+ // m_updateToolBarTimer.
+ connect(m_controlButton, SIGNAL(destroyed()), this, SLOT(slotControlButtonDeleted()));
+ m_updateToolBarTimer = new QTimer(this);
+ m_updateToolBarTimer->setInterval(500);
+ connect(m_updateToolBarTimer, SIGNAL(timeout()), this, SLOT(updateToolBar()));
+}
+
+void DolphinMainWindow::deleteControlButton()
+{
+ delete m_controlButton;
+ m_controlButton = 0;
+
+ delete m_updateToolBarTimer;
+ m_updateToolBarTimer = 0;
+}
+
+bool DolphinMainWindow::addActionToMenu(QAction* action, KMenu* menu)
+{
+ Q_ASSERT(action);
+ Q_ASSERT(menu);
+
+ const KToolBar* toolBarWidget = toolBar();
+ foreach (const QWidget* widget, action->associatedWidgets()) {
+ if (widget == toolBarWidget) {
+ return false;
+ }
+ }
+
+ menu->addAction(action);
+ return true;
+}
+
+void DolphinMainWindow::refreshViews()
+{
+ m_tabWidget->refreshViews();
+
+ if (GeneralSettings::modifiedStartupSettings()) {
+ // The startup settings have been changed by the user (see bug #254947).
+ // Synchronize the split-view setting with the active view:
+ const bool splitView = GeneralSettings::splitView();
+ m_tabWidget->currentTabPage()->setSplitViewEnabled(splitView);
+ updateSplitAction();
+ }
+
+ emit settingsChanged();
+}
+
+void DolphinMainWindow::clearStatusBar()
+{
+ m_activeViewContainer->statusBar()->resetToDefaultText();
+}
+
+void DolphinMainWindow::connectViewSignals(DolphinViewContainer* container)
+{
+ connect(container, SIGNAL(showFilterBarChanged(bool)),
+ this, SLOT(updateFilterBarAction(bool)));
+ connect(container, SIGNAL(writeStateChanged(bool)),
+ this, SLOT(slotWriteStateChanged(bool)));
+
+ const DolphinView* view = container->view();
+ connect(view, SIGNAL(selectionChanged(KFileItemList)),
+ this, SLOT(slotSelectionChanged(KFileItemList)));
+ connect(view, SIGNAL(requestItemInfo(KFileItem)),
+ this, SLOT(slotRequestItemInfo(KFileItem)));
+ connect(view, SIGNAL(tabRequested(KUrl)),
+ this, SLOT(openNewTab(KUrl)));
+ connect(view, SIGNAL(requestContextMenu(QPoint,KFileItem,KUrl,QList<QAction*>)),
+ this, SLOT(openContextMenu(QPoint,KFileItem,KUrl,QList<QAction*>)));
+ connect(view, SIGNAL(directoryLoadingStarted()),
+ this, SLOT(enableStopAction()));
+ connect(view, SIGNAL(directoryLoadingCompleted()),
+ this, SLOT(disableStopAction()));
+ connect(view, SIGNAL(goBackRequested()),
+ this, SLOT(goBack()));
+ connect(view, SIGNAL(goForwardRequested()),
+ this, SLOT(goForward()));
+
+ const KUrlNavigator* navigator = container->urlNavigator();
+ connect(navigator, SIGNAL(urlChanged(KUrl)),
+ this, SLOT(changeUrl(KUrl)));
+ connect(navigator, SIGNAL(historyChanged()),
+ this, SLOT(updateHistory()));
+ connect(navigator, SIGNAL(editableStateChanged(bool)),
+ this, SLOT(slotEditableStateChanged(bool)));
+ connect(navigator, SIGNAL(tabRequested(KUrl)),
+ this, SLOT(openNewTab(KUrl)));
+}
+
+void DolphinMainWindow::updateSplitAction()
+{
+ QAction* splitAction = actionCollection()->action("split_view");
+ const DolphinTabPage* tabPage = m_tabWidget->currentTabPage();
+ if (tabPage->splitViewEnabled()) {
+ if (tabPage->primaryViewActive()) {
+ splitAction->setText(i18nc("@action:intoolbar Close left view", "Close"));
+ splitAction->setToolTip(i18nc("@info", "Close left view"));
+ splitAction->setIcon(KIcon("view-left-close"));
+ } else {
+ splitAction->setText(i18nc("@action:intoolbar Close right view", "Close"));
+ splitAction->setToolTip(i18nc("@info", "Close right view"));
+ splitAction->setIcon(KIcon("view-right-close"));
+ }
+ } else {
+ splitAction->setText(i18nc("@action:intoolbar Split view", "Split"));
+ splitAction->setToolTip(i18nc("@info", "Split view"));
+ splitAction->setIcon(KIcon("view-right-new"));
+ }
+}
+
+bool DolphinMainWindow::isKompareInstalled() const
+{
+ static bool initialized = false;
+ static bool installed = false;
+ if (!initialized) {
+ // TODO: maybe replace this approach later by using a menu
+ // plugin like kdiff3plugin.cpp
+ installed = !KGlobal::dirs()->findExe("kompare").isEmpty();
+ initialized = true;
+ }
+ return installed;
+}
+
+void DolphinMainWindow::createPanelAction(const KIcon& icon,
+ const QKeySequence& shortcut,
+ QAction* dockAction,
+ const QString& actionName)
+{
+ KAction* panelAction = actionCollection()->addAction(actionName);
+ panelAction->setCheckable(true);
+ panelAction->setChecked(dockAction->isChecked());
+ panelAction->setText(dockAction->text());
+ panelAction->setIcon(icon);
+ panelAction->setShortcut(shortcut);
+
+ connect(panelAction, SIGNAL(triggered()), dockAction, SLOT(trigger()));
+ connect(dockAction, SIGNAL(toggled(bool)), panelAction, SLOT(setChecked(bool)));
+}
+
+DolphinMainWindow::UndoUiInterface::UndoUiInterface() :
+ KIO::FileUndoManager::UiInterface()
+{
+}
+
+DolphinMainWindow::UndoUiInterface::~UndoUiInterface()
+{
+}
+
+void DolphinMainWindow::UndoUiInterface::jobError(KIO::Job* job)
+{
+ DolphinMainWindow* mainWin= qobject_cast<DolphinMainWindow *>(parentWidget());
+ if (mainWin) {
+ DolphinViewContainer* container = mainWin->activeViewContainer();
+ container->showMessage(job->errorString(), DolphinViewContainer::Error);
+ } else {
+ KIO::FileUndoManager::UiInterface::jobError(job);
+ }
+}
+
+#include "dolphinmainwindow.moc"
diff --git a/dolphin/src/dolphinmainwindow.h b/dolphin/src/dolphinmainwindow.h
new file mode 100644
index 0000000..9d4c003
--- /dev/null
+++ b/dolphin/src/dolphinmainwindow.h
@@ -0,0 +1,530 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Peter Penz <[email protected]> *
+ * Copyright (C) 2006 by Stefan Monov <[email protected]> *
+ * Copyright (C) 2006 by Cvetoslav Ludmiloff <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef DOLPHIN_MAINWINDOW_H
+#define DOLPHIN_MAINWINDOW_H
+
+#include <config-baloo.h>
+
+#include <KFileItemDelegate>
+#include <kio/fileundomanager.h>
+#include <ksortablelist.h>
+#include <kxmlguiwindow.h>
+#include <KIcon>
+
+#include <QList>
+#include <QWeakPointer>
+
+typedef KIO::FileUndoManager::CommandType CommandType;
+
+class DolphinViewActionHandler;
+class DolphinApplication;
+class DolphinSettingsDialog;
+class DolphinViewContainer;
+class DolphinRemoteEncoding;
+class DolphinTabWidget;
+class KAction;
+class KFileItem;
+class KFileItemList;
+class KJob;
+class KNewFileMenu;
+class KUrl;
+class QToolButton;
+
+/**
+ * @short Main window for Dolphin.
+ *
+ * Handles the menus, toolbars and Dolphin views.
+ */
+class DolphinMainWindow: public KXmlGuiWindow
+{
+ Q_OBJECT
+ Q_CLASSINFO("D-Bus Interface", "org.kde.dolphin.MainWindow")
+ Q_PROPERTY(int id READ getId SCRIPTABLE true)
+ friend class DolphinApplication;
+
+public:
+ DolphinMainWindow();
+ virtual ~DolphinMainWindow();
+
+ /**
+ * Returns the currently active view.
+ * All menu actions are applied to this view. When
+ * having a split view setup, the nonactive view
+ * is usually shown in darker colors.
+ */
+ DolphinViewContainer* activeViewContainer() const;
+
+ /**
+ * Opens each directory in \p dirs in a separate tab. If the "split view"
+ * option is enabled, 2 directories are collected within one tab.
+ */
+ void openDirectories(const QList<KUrl>& dirs);
+
+ /**
+ * Opens the directory which contains the files \p files
+ * and selects all files (implements the --select option
+ * of Dolphin).
+ */
+ void openFiles(const QList<KUrl>& files);
+
+ /**
+ * Returns the 'Create New...' sub menu which also can be shared
+ * with other menus (e. g. a context menu).
+ */
+ KNewFileMenu* newFileMenu() const;
+
+public slots:
+ /**
+ * Pastes the clipboard data into the currently selected folder
+ * of the active view. If not exactly one folder is selected,
+ * no pasting is done at all.
+ */
+ void pasteIntoFolder();
+
+ /**
+ * Returns the main window ID used through DBus.
+ */
+ int getId() const;
+
+ /**
+ * Implementation of the MainWindowAdaptor/QDBusAbstractAdaptor interface.
+ * Inform all affected dolphin components (panels, views) of an URL
+ * change.
+ */
+ void changeUrl(const KUrl& url);
+
+ /**
+ * The current directory of the Terminal Panel has changed, probably because
+ * the user entered a 'cd' command. This slot calls changeUrl(url) and makes
+ * sure that the panel keeps the keyboard focus.
+ */
+ void slotTerminalDirectoryChanged(const KUrl& url);
+
+ /** Stores all settings and quits Dolphin. */
+ void quit();
+
+signals:
+ /**
+ * Is sent if the selection of the currently active view has
+ * been changed.
+ */
+ void selectionChanged(const KFileItemList& selection);
+
+ /**
+ * Is sent if the url of the currently active view has
+ * been changed.
+ */
+ void urlChanged(const KUrl& url);
+
+ /**
+ * Is emitted if information of an item is requested to be shown e. g. in the panel.
+ * If item is null, no item information request is pending.
+ */
+ void requestItemInfo(const KFileItem& item);
+
+ /**
+ * Is emitted if the settings have been changed.
+ */
+ void settingsChanged();
+
+protected:
+ /** @see QWidget::showEvent() */
+ virtual void showEvent(QShowEvent* event);
+
+ /** @see QMainWindow::closeEvent() */
+ virtual void closeEvent(QCloseEvent* event);
+
+ /** @see KMainWindow::saveProperties() */
+ virtual void saveProperties(KConfigGroup& group);
+
+ /** @see KMainWindow::readProperties() */
+ virtual void readProperties(const KConfigGroup& group);
+
+private slots:
+ /**
+ * Refreshes the views of the main window by recreating them according to
+ * the given Dolphin settings.
+ */
+ void refreshViews();
+
+ void clearStatusBar();
+
+ /** Updates the 'Create New...' sub menu. */
+ void updateNewMenu();
+
+ void createDirectory();
+
+ /** Shows the error message in the status bar of the active view. */
+ void showErrorMessage(const QString& message);
+
+ /**
+ * Updates the state of the 'Undo' menu action dependent
+ * on the parameter \a available.
+ */
+ void slotUndoAvailable(bool available);
+
+ /** Sets the text of the 'Undo' menu action to \a text. */
+ void slotUndoTextChanged(const QString& text);
+
+ /** Performs the current undo operation. */
+ void undo();
+
+ /**
+ * Copies all selected items to the clipboard and marks
+ * the items as cut.
+ */
+ void cut();
+
+ /** Copies all selected items to the clipboard. */
+ void copy();
+
+ /** Pastes the clipboard data to the active view. */
+ void paste();
+
+ /** Replaces the URL navigator by a search box to find files. */
+ void find();
+
+ /**
+ * Updates the text of the paste action dependent on
+ * the number of items which are in the clipboard.
+ */
+ void updatePasteAction();
+
+ /** Selects all items from the active view. */
+ void selectAll();
+
+ /**
+ * Inverts the selection of all items of the active view:
+ * Selected items get nonselected and nonselected items get
+ * selected.
+ */
+ void invertSelection();
+
+ /**
+ * Switches between one and two views:
+ * If one view is visible, it will get split into two views.
+ * If already two views are visible, the active view gets closed.
+ */
+ void toggleSplitView();
+
+ /** Reloads the currently active view. */
+ void reloadView();
+
+ /** Stops the loading process for the currently active view. */
+ void stopLoading();
+
+ void enableStopAction();
+ void disableStopAction();
+
+ void showFilterBar();
+
+ /**
+ * Toggles between edit and browse mode of the navigation bar.
+ */
+ void toggleEditLocation();
+
+ /**
+ * Switches to the edit mode of the navigation bar and selects
+ * the whole URL, so that it can be replaced by the user. If the edit mode is
+ * already active, it is assured that the navigation bar get focused.
+ */
+ void replaceLocation();
+
+ /**
+ * Toggles the state of the panels between a locked and unlocked layout.
+ */
+ void togglePanelLockState();
+
+ /** Goes back one step of the URL history. */
+ void goBack();
+
+ /** Goes forward one step of the URL history. */
+ void goForward();
+
+ /** Goes up one hierarchy of the current URL. */
+ void goUp();
+
+ /** Changes the location to the home URL. */
+ void goHome();
+
+ /**
+ * Open the previous URL in the URL history in a new tab
+ * if the middle mouse button is clicked.
+ */
+ void goBack(Qt::MouseButtons buttons);
+
+ /**
+ * Open the next URL in the URL history in a new tab
+ * if the middle mouse button is clicked.
+ */
+ void goForward(Qt::MouseButtons buttons);
+
+ /**
+ * Open the URL one hierarchy above the current URL in a new tab
+ * if the middle mouse button is clicked.
+ */
+ void goUp(Qt::MouseButtons buttons);
+
+ /**
+ * Open the home URL in a new tab
+ */
+ void goHome(Qt::MouseButtons buttons);
+
+ /** Opens Kompare for 2 selected files. */
+ void compareFiles();
+
+ /**
+ * Hides the menu bar if it is visible, makes the menu bar
+ * visible if it is hidden.
+ */
+ void toggleShowMenuBar();
+
+ /** Opens a terminal window for the current location. */
+ void openTerminal();
+
+ /** Opens the settings dialog for Dolphin. */
+ void editSettings();
+
+ /** Updates the state of the 'Show Full Location' action. */
+ void slotEditableStateChanged(bool editable);
+
+ /**
+ * Updates the state of the 'Edit' menu actions and emits
+ * the signal selectionChanged().
+ */
+ void slotSelectionChanged(const KFileItemList& selection);
+
+ /** Emits the signal requestItemInfo(). */
+ void slotRequestItemInfo(const KFileItem&);
+
+ /**
+ * Updates the state of the 'Back' and 'Forward' menu
+ * actions corresponding to the current history.
+ */
+ void updateHistory();
+
+ /** Updates the state of the 'Show filter bar' menu action. */
+ void updateFilterBarAction(bool show);
+
+ /** Open a new main window. */
+ void openNewMainWindow();
+
+ /**
+ * Opens a new view with the current URL that is part of a tab and
+ * activates it.
+ */
+ void openNewActivatedTab();
+
+ /**
+ * Opens a new tab in the background showing the URL \a primaryUrl and the
+ * optional URL \a secondaryUrl.
+ */
+ void openNewTab(const KUrl& primaryUrl, const KUrl& secondaryUrl = KUrl());
+
+ /**
+ * Opens a new tab showing the URL \a primaryUrl and the optional URL
+ * \a secondaryUrl and activates the tab.
+ */
+ void openNewActivatedTab(const KUrl& primaryUrl, const KUrl& secondaryUrl = KUrl());
+
+ /**
+ * Opens the selected folder in a new tab.
+ */
+ void openInNewTab();
+
+ /**
+ * Opens the selected folder in a new window.
+ */
+ void openInNewWindow();
+
+ /**
+ * Indicates in the statusbar that the execution of the command \a command
+ * has been finished.
+ */
+ void showCommand(CommandType command);
+
+ /**
+ * If the URL can be listed, open it in the current view, otherwise
+ * run it through KRun.
+ */
+ void handleUrl(const KUrl& url);
+
+ /**
+ * handleUrl() can trigger a stat job to see if the url can actually
+ * be listed.
+ */
+ void slotHandleUrlStatFinished(KJob* job);
+
+ /**
+ * Is invoked when the write state of a folder has been changed and
+ * enables/disables the "Create New..." menu entry.
+ */
+ void slotWriteStateChanged(bool isFolderWritable);
+
+ /**
+ * Opens the context menu on the current mouse position.
+ * @pos Position in screen coordinates.
+ * @item File item context. If item is null, the context menu
+ * should be applied to \a url.
+ * @url URL which contains \a item.
+ * @customActions Actions that should be added to the context menu,
+ * if the file item is null.
+ */
+ void openContextMenu(const QPoint& pos,
+ const KFileItem& item,
+ const KUrl& url,
+ const QList<QAction*>& customActions);
+
+ void updateControlMenu();
+ void updateToolBar();
+ void slotControlButtonDeleted();
+
+ /**
+ * Is called if a panel emits an error-message and shows
+ * the error-message in the active view-container.
+ */
+ void slotPanelErrorMessage(const QString& error);
+
+ /**
+ * Is called if the user clicked an item in the Places Panel.
+ * Reloads the view if \a url is the current URL already, and changes the
+ * current URL otherwise.
+ */
+ void slotPlaceActivated(const KUrl& url);
+
+ /**
+ * Is called if the another view has been activated by changing the current
+ * tab or activating another view in split-view mode.
+ *
+ * Activates the given view, which means that all menu actions are applied
+ * to this view. When having a split view setup, the nonactive view is
+ * usually shown in darker colors.
+ */
+ void activeViewChanged(DolphinViewContainer* viewContainer);
+
+ void closedTabsCountChanged(unsigned int count);
+
+ /**
+ * Is called if a new tab has been opened or a tab has been closed to
+ * enable/disable the tab actions.
+ */
+ void tabCountChanged(int count);
+
+ /**
+ * Sets the window caption to url.fileName() if this is non-empty,
+ * "/" if the URL is "file:///", and url.protocol() otherwise.
+ */
+ void setUrlAsCaption(const KUrl& url);
+
+private:
+ void setupActions();
+ void setupDockWidgets();
+ void updateEditActions();
+ void updateViewActions();
+ void updateGoActions();
+
+ void createControlButton();
+ void deleteControlButton();
+
+ /**
+ * Adds the action \p action to the menu \p menu in
+ * case if it has not added already to the toolbar.
+ * @return True if the action has been added to the menu.
+ */
+ bool addActionToMenu(QAction* action, KMenu* menu);
+
+ /**
+ * Connects the signals from the created DolphinView with
+ * the DolphinViewContainer \a container with the corresponding slots of
+ * the DolphinMainWindow. This method must be invoked each
+ * time a DolphinView has been created.
+ */
+ void connectViewSignals(DolphinViewContainer* container);
+
+ /**
+ * Updates the text of the split action:
+ * If two views are shown, the text is set to "Split",
+ * otherwise the text is set to "Join". The icon
+ * is updated to match with the text and the currently active view.
+ */
+ void updateSplitAction();
+
+ bool isKompareInstalled() const;
+
+ /**
+ * Creates an action for showing/hiding a panel, that is accessible
+ * in "Configure toolbars..." and "Configure shortcuts...". This is necessary
+ * as the action for toggling the dock visibility is done by Qt which
+ * is no KAction instance.
+ */
+ void createPanelAction(const KIcon& icon,
+ const QKeySequence& shortcut,
+ QAction* dockAction,
+ const QString& actionName);
+
+private:
+ /**
+ * Implements a custom error handling for the undo manager. This
+ * assures that all errors are shown in the status bar of Dolphin
+ * instead as modal error dialog with an OK button.
+ */
+ class UndoUiInterface : public KIO::FileUndoManager::UiInterface
+ {
+ public:
+ UndoUiInterface();
+ virtual ~UndoUiInterface();
+ virtual void jobError(KIO::Job* job);
+ };
+
+ KNewFileMenu* m_newFileMenu;
+ DolphinTabWidget* m_tabWidget;
+ DolphinViewContainer* m_activeViewContainer;
+ int m_id;
+
+ DolphinViewActionHandler* m_actionHandler;
+ DolphinRemoteEncoding* m_remoteEncoding;
+ QWeakPointer<DolphinSettingsDialog> m_settingsDialog;
+
+ // Members for the toolbar menu that is shown when the menubar is hidden:
+ QToolButton* m_controlButton;
+ QTimer* m_updateToolBarTimer;
+
+ KIO::Job* m_lastHandleUrlStatJob;
+};
+
+inline DolphinViewContainer* DolphinMainWindow::activeViewContainer() const
+{
+ return m_activeViewContainer;
+}
+
+inline KNewFileMenu* DolphinMainWindow::newFileMenu() const
+{
+ return m_newFileMenu;
+}
+
+inline int DolphinMainWindow::getId() const
+{
+ return m_id;
+}
+
+#endif // DOLPHIN_MAINWINDOW_H
+
diff --git a/dolphin/src/dolphinnewfilemenu.cpp b/dolphin/src/dolphinnewfilemenu.cpp
new file mode 100644
index 0000000..da57ca9
--- /dev/null
+++ b/dolphin/src/dolphinnewfilemenu.cpp
@@ -0,0 +1,48 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Peter Penz *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "dolphinnewfilemenu.h"
+
+#include "views/dolphinnewfilemenuobserver.h"
+
+#include <KActionCollection>
+#include <KIO/Job>
+
+DolphinNewFileMenu::DolphinNewFileMenu(KActionCollection* collection, QObject* parent) :
+ KNewFileMenu(collection, "new_menu", parent)
+{
+ DolphinNewFileMenuObserver::instance().attach(this);
+}
+
+DolphinNewFileMenu::~DolphinNewFileMenu()
+{
+ DolphinNewFileMenuObserver::instance().detach(this);
+}
+
+void DolphinNewFileMenu::slotResult(KJob* job)
+{
+ if (job->error()) {
+ emit errorMessage(job->errorString());
+ } else {
+ KNewFileMenu::slotResult(job);
+ }
+}
+
+#include "dolphinnewfilemenu.moc"
diff --git a/dolphin/src/dolphinnewfilemenu.h b/dolphin/src/dolphinnewfilemenu.h
new file mode 100644
index 0000000..e211dfd
--- /dev/null
+++ b/dolphin/src/dolphinnewfilemenu.h
@@ -0,0 +1,54 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Peter Penz *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef DOLPHINNEWFILEMENU_H
+#define DOLPHINNEWFILEMENU_H
+
+#include <KNewFileMenu>
+
+#include "libdolphin_export.h"
+
+class KJob;
+
+/**
+ * @brief Represents the 'Create New...' sub menu for the File menu
+ * and the context menu.
+ *
+ * The only difference to KNewFileMenu is the custom error handling.
+ * All errors are shown in the status bar of Dolphin
+ * instead as modal error dialog with an OK button.
+ */
+class LIBDOLPHINPRIVATE_EXPORT DolphinNewFileMenu : public KNewFileMenu
+{
+ Q_OBJECT
+
+public:
+ DolphinNewFileMenu(KActionCollection* collection, QObject* parent);
+ virtual ~DolphinNewFileMenu();
+
+signals:
+ void errorMessage(const QString& error);
+
+protected slots:
+ /** @see KNewFileMenu::slotResult() */
+ virtual void slotResult(KJob* job);
+};
+
+#endif
diff --git a/dolphin/src/dolphinpart.cpp b/dolphin/src/dolphinpart.cpp
new file mode 100644
index 0000000..9081731
--- /dev/null
+++ b/dolphin/src/dolphinpart.cpp
@@ -0,0 +1,619 @@
+/* This file is part of the KDE project
+ Copyright (c) 2007 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 "dolphinpart.h"
+#include "dolphinremoveaction.h"
+
+#include <KFileItemListProperties>
+#include <konq_operations.h>
+
+#include <KAboutData>
+#include <KActionCollection>
+#include <KConfigGroup>
+#include <KDebug>
+#include <KGlobalSettings>
+#include <KIconLoader>
+#include <KLocale>
+#include <KMessageBox>
+#include <KPluginFactory>
+#include <KRun>
+#include <KToggleAction>
+#include <KIO/NetAccess>
+#include <KToolInvocation>
+#include <kauthorized.h>
+#include <KMenu>
+#include <KInputDialog>
+#include <KProtocolInfo>
+#include <kdeversion.h>
+
+#if KDE_IS_VERSION(4, 9, 2)
+#include "dolphinpart_ext.h"
+#endif
+
+#include "dolphinnewfilemenu.h"
+#include "views/dolphinview.h"
+#include "views/dolphinviewactionhandler.h"
+#include "views/dolphinnewfilemenuobserver.h"
+#include "views/dolphinremoteencoding.h"
+#include "kitemviews/kfileitemmodel.h"
+#include "kitemviews/private/kfileitemmodeldirlister.h"
+
+#include <QActionGroup>
+#include <QApplication>
+#include <QClipboard>
+#include <QDir>
+#include <QTextDocument>
+
+K_PLUGIN_FACTORY(DolphinPartFactory, registerPlugin<DolphinPart>();)
+K_EXPORT_PLUGIN(DolphinPartFactory("dolphinpart", "dolphin"))
+
+DolphinPart::DolphinPart(QWidget* parentWidget, QObject* parent, const QVariantList& args)
+ : KParts::ReadOnlyPart(parent)
+ ,m_openTerminalAction(0)
+ ,m_removeAction(0)
+{
+ Q_UNUSED(args)
+ setComponentData(DolphinPartFactory::componentData(), false);
+ m_extension = new DolphinPartBrowserExtension(this);
+
+ // make sure that other apps using this part find Dolphin's view-file-columns icons
+ KIconLoader::global()->addAppDir("dolphin");
+
+ m_view = new DolphinView(KUrl(), parentWidget);
+ m_view->setTabsForFilesEnabled(true);
+ setWidget(m_view);
+
+ connect(&DolphinNewFileMenuObserver::instance(), SIGNAL(errorMessage(QString)),
+ this, SLOT(slotErrorMessage(QString)));
+
+ connect(m_view, SIGNAL(directoryLoadingCompleted()), this, SIGNAL(completed()));
+ connect(m_view, SIGNAL(directoryLoadingProgress(int)), this, SLOT(updateProgress(int)));
+ connect(m_view, SIGNAL(errorMessage(QString)), this, SLOT(slotErrorMessage(QString)));
+
+ setXMLFile("dolphinpart.rc");
+
+ connect(m_view, SIGNAL(infoMessage(QString)),
+ this, SLOT(slotMessage(QString)));
+ connect(m_view, SIGNAL(operationCompletedMessage(QString)),
+ this, SLOT(slotMessage(QString)));
+ connect(m_view, SIGNAL(errorMessage(QString)),
+ this, SLOT(slotErrorMessage(QString)));
+ connect(m_view, SIGNAL(itemActivated(KFileItem)),
+ this, SLOT(slotItemActivated(KFileItem)));
+ connect(m_view, SIGNAL(itemsActivated(KFileItemList)),
+ this, SLOT(slotItemsActivated(KFileItemList)));
+ connect(m_view, SIGNAL(tabRequested(KUrl)),
+ this, SLOT(createNewWindow(KUrl)));
+ connect(m_view, SIGNAL(requestContextMenu(QPoint,KFileItem,KUrl,QList<QAction*>)),
+ this, SLOT(slotOpenContextMenu(QPoint,KFileItem,KUrl,QList<QAction*>)));
+ connect(m_view, SIGNAL(selectionChanged(KFileItemList)),
+ m_extension, SIGNAL(selectionInfo(KFileItemList)));
+ connect(m_view, SIGNAL(selectionChanged(KFileItemList)),
+ this, SLOT(slotSelectionChanged(KFileItemList)));
+ connect(m_view, SIGNAL(requestItemInfo(KFileItem)),
+ this, SLOT(slotRequestItemInfo(KFileItem)));
+ connect(m_view, SIGNAL(modeChanged(DolphinView::Mode,DolphinView::Mode)),
+ this, SIGNAL(viewModeChanged())); // relay signal
+ connect(m_view, SIGNAL(redirection(KUrl,KUrl)),
+ this, SLOT(slotDirectoryRedirection(KUrl,KUrl)));
+
+ // Watch for changes that should result in updates to the
+ // status bar text.
+ connect(m_view, SIGNAL(itemCountChanged()), this, SLOT(updateStatusBar()));
+ connect(m_view, SIGNAL(selectionChanged(KFileItemList)), this, SLOT(updateStatusBar()));
+
+ m_actionHandler = new DolphinViewActionHandler(actionCollection(), this);
+ m_actionHandler->setCurrentView(m_view);
+ connect(m_actionHandler, SIGNAL(createDirectory()), SLOT(createDirectory()));
+
+ m_remoteEncoding = new DolphinRemoteEncoding(this, m_actionHandler);
+ connect(this, SIGNAL(aboutToOpenURL()),
+ m_remoteEncoding, SLOT(slotAboutToOpenUrl()));
+
+ QClipboard* clipboard = QApplication::clipboard();
+ connect(clipboard, SIGNAL(dataChanged()),
+ this, SLOT(updatePasteAction()));
+
+ // Create file info and listing filter extensions.
+ // NOTE: Listing filter needs to be instantiated after the creation of the view.
+ new DolphinPartFileInfoExtension(this);
+
+#if KDE_IS_VERSION(4, 9, 2)
+ new DolphinPartListingFilterExtension(this);
+
+ KDirLister* lister = m_view->m_model->m_dirLister;
+ if (lister) {
+ DolphinPartListingNotificationExtension* notifyExt = new DolphinPartListingNotificationExtension(this);
+ connect(lister, SIGNAL(newItems(KFileItemList)), notifyExt, SLOT(slotNewItems(KFileItemList)));
+ connect(lister, SIGNAL(itemsDeleted(KFileItemList)), notifyExt, SLOT(slotItemsDeleted(KFileItemList)));
+ } else {
+ kWarning() << "NULL KDirLister object! KParts::ListingNotificationExtension will NOT be supported";
+ }
+#endif
+
+ createActions();
+ m_actionHandler->updateViewActions();
+ slotSelectionChanged(KFileItemList()); // initially disable selection-dependent actions
+
+ // Listen to events from the app so we can update the remove key by
+ // checking for a Shift key press.
+ qApp->installEventFilter(this);
+
+ // TODO there was a "always open a new window" (when clicking on a directory) setting in konqueror
+ // (sort of spacial navigation)
+
+ loadPlugins(this, this, componentData());
+}
+
+DolphinPart::~DolphinPart()
+{
+}
+
+void DolphinPart::createActions()
+{
+ // Edit menu
+
+ m_newFileMenu = new DolphinNewFileMenu(actionCollection(), this);
+ m_newFileMenu->setParentWidget(widget());
+ connect(m_newFileMenu->menu(), SIGNAL(aboutToShow()),
+ this, SLOT(updateNewMenu()));
+
+ KAction *editMimeTypeAction = actionCollection()->addAction( "editMimeType" );
+ editMimeTypeAction->setText( i18nc("@action:inmenu Edit", "&Edit File Type..." ) );
+ connect(editMimeTypeAction, SIGNAL(triggered()), SLOT(slotEditMimeType()));
+
+ KAction* selectItemsMatching = actionCollection()->addAction("select_items_matching");
+ selectItemsMatching->setText(i18nc("@action:inmenu Edit", "Select Items Matching..."));
+ selectItemsMatching->setShortcut(Qt::CTRL | Qt::Key_S);
+ connect(selectItemsMatching, SIGNAL(triggered()), this, SLOT(slotSelectItemsMatchingPattern()));
+
+ KAction* unselectItemsMatching = actionCollection()->addAction("unselect_items_matching");
+ unselectItemsMatching->setText(i18nc("@action:inmenu Edit", "Unselect Items Matching..."));
+ connect(unselectItemsMatching, SIGNAL(triggered()), this, SLOT(slotUnselectItemsMatchingPattern()));
+
+ actionCollection()->addAction(KStandardAction::SelectAll, "select_all", m_view, SLOT(selectAll()));
+
+ KAction* unselectAll = actionCollection()->addAction("unselect_all");
+ unselectAll->setText(i18nc("@action:inmenu Edit", "Unselect All"));
+ connect(unselectAll, SIGNAL(triggered()), m_view, SLOT(clearSelection()));
+
+ KAction* invertSelection = actionCollection()->addAction("invert_selection");
+ invertSelection->setText(i18nc("@action:inmenu Edit", "Invert Selection"));
+ invertSelection->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_A);
+ connect(invertSelection, SIGNAL(triggered()), m_view, SLOT(invertSelection()));
+
+ // View menu: all done by DolphinViewActionHandler
+
+ // Go menu
+
+ QActionGroup* goActionGroup = new QActionGroup(this);
+ connect(goActionGroup, SIGNAL(triggered(QAction*)),
+ this, SLOT(slotGoTriggered(QAction*)));
+
+ createGoAction("go_applications", "start-here-kde",
+ i18nc("@action:inmenu Go", "App&lications"), QString("programs:/"),
+ goActionGroup);
+ createGoAction("go_network_folders", "folder-remote",
+ i18nc("@action:inmenu Go", "&Network Folders"), QString("remote:/"),
+ goActionGroup);
+ createGoAction("go_settings", "preferences-system",
+ i18nc("@action:inmenu Go", "Sett&ings"), QString("settings:/"),
+ goActionGroup);
+ createGoAction("go_trash", "user-trash",
+ i18nc("@action:inmenu Go", "Trash"), QString("trash:/"),
+ goActionGroup);
+ createGoAction("go_autostart", "",
+ i18nc("@action:inmenu Go", "Autostart"), KGlobalSettings::autostartPath(),
+ goActionGroup);
+
+ // Tools menu
+ m_findFileAction = actionCollection()->addAction("find_file");
+ m_findFileAction->setText(i18nc("@action:inmenu Tools", "Find File..."));
+ m_findFileAction->setShortcut(Qt::CTRL | Qt::Key_F);
+ m_findFileAction->setIcon(KIcon("edit-find"));
+ connect(m_findFileAction, SIGNAL(triggered()), this, SLOT(slotFindFile()));
+
+ if (KAuthorized::authorizeKAction("shell_access")) {
+ m_openTerminalAction = actionCollection()->addAction("open_terminal");
+ m_openTerminalAction->setIcon(KIcon("utilities-terminal"));
+ m_openTerminalAction->setText(i18nc("@action:inmenu Tools", "Open &Terminal"));
+ connect(m_openTerminalAction, SIGNAL(triggered()), SLOT(slotOpenTerminal()));
+ m_openTerminalAction->setShortcut(Qt::Key_F4);
+ }
+}
+
+void DolphinPart::createGoAction(const char* name, const char* iconName,
+ const QString& text, const QString& url,
+ QActionGroup* actionGroup)
+{
+ KAction* action = actionCollection()->addAction(name);
+ action->setIcon(KIcon(iconName));
+ action->setText(text);
+ action->setData(url);
+ action->setActionGroup(actionGroup);
+}
+
+void DolphinPart::slotGoTriggered(QAction* action)
+{
+ const QString url = action->data().toString();
+ emit m_extension->openUrlRequest(KUrl(url));
+}
+
+void DolphinPart::slotSelectionChanged(const KFileItemList& selection)
+{
+ const bool hasSelection = !selection.isEmpty();
+
+ QAction* renameAction = actionCollection()->action("rename");
+ QAction* moveToTrashAction = actionCollection()->action("move_to_trash");
+ QAction* deleteAction = actionCollection()->action("delete");
+ QAction* editMimeTypeAction = actionCollection()->action("editMimeType");
+ QAction* propertiesAction = actionCollection()->action("properties");
+ QAction* deleteWithTrashShortcut = actionCollection()->action("delete_shortcut"); // see DolphinViewActionHandler
+
+ if (!hasSelection) {
+ stateChanged("has_no_selection");
+
+ emit m_extension->enableAction("cut", false);
+ emit m_extension->enableAction("copy", false);
+ deleteWithTrashShortcut->setEnabled(false);
+ editMimeTypeAction->setEnabled(false);
+ } else {
+ stateChanged("has_selection");
+
+ // TODO share this code with DolphinMainWindow::updateEditActions (and the desktop code)
+ // in libkonq
+ KFileItemListProperties capabilities(selection);
+ const bool enableMoveToTrash = capabilities.isLocal() && capabilities.supportsMoving();
+
+ renameAction->setEnabled(capabilities.supportsMoving());
+ moveToTrashAction->setEnabled(enableMoveToTrash);
+ deleteAction->setEnabled(capabilities.supportsDeleting());
+ deleteWithTrashShortcut->setEnabled(capabilities.supportsDeleting() && !enableMoveToTrash);
+ editMimeTypeAction->setEnabled(true);
+ propertiesAction->setEnabled(true);
+ emit m_extension->enableAction("cut", capabilities.supportsMoving());
+ emit m_extension->enableAction("copy", true);
+ }
+}
+
+void DolphinPart::updatePasteAction()
+{
+ QPair<bool, QString> pasteInfo = m_view->pasteInfo();
+ emit m_extension->enableAction( "paste", pasteInfo.first );
+ emit m_extension->setActionText( "paste", pasteInfo.second );
+}
+
+KAboutData* DolphinPart::createAboutData()
+{
+ return new KAboutData("dolphinpart", "dolphin", ki18nc("@title", "Dolphin Part"), "0.1");
+}
+
+bool DolphinPart::openUrl(const KUrl& url)
+{
+ bool reload = arguments().reload();
+ // A bit of a workaround so that changing the namefilter works: force reload.
+ // Otherwise DolphinView wouldn't relist the URL, so nothing would happen.
+ if (m_nameFilter != m_view->nameFilter())
+ reload = true;
+ if (m_view->url() == url && !reload) { // DolphinView won't do anything in that case, so don't emit started
+ return true;
+ }
+ setUrl(url); // remember it at the KParts level
+ KUrl visibleUrl(url);
+ if (!m_nameFilter.isEmpty()) {
+ visibleUrl.addPath(m_nameFilter);
+ }
+ QString prettyUrl = visibleUrl.pathOrUrl();
+ emit setWindowCaption(prettyUrl);
+ emit m_extension->setLocationBarUrl(prettyUrl);
+ emit started(0); // get the wheel to spin
+ m_view->setNameFilter(m_nameFilter);
+ m_view->setUrl(url);
+ updatePasteAction();
+ emit aboutToOpenURL();
+ if (reload)
+ m_view->reload();
+ // Disable "Find File" and "Open Terminal" actions for non-file URLs,
+ // e.g. ftp, smb, etc. #279283
+ const bool isLocalUrl = url.isLocalFile();
+ m_findFileAction->setEnabled(isLocalUrl);
+ if (m_openTerminalAction) {
+ m_openTerminalAction->setEnabled(isLocalUrl);
+ }
+ return true;
+}
+
+void DolphinPart::slotMessage(const QString& msg)
+{
+ emit setStatusBarText(msg);
+}
+
+void DolphinPart::slotErrorMessage(const QString& msg)
+{
+ kDebug() << msg;
+ emit canceled(msg);
+ //KMessageBox::error(m_view, msg);
+}
+
+void DolphinPart::slotRequestItemInfo(const KFileItem& item)
+{
+ emit m_extension->mouseOverInfo(item);
+ if (item.isNull()) {
+ updateStatusBar();
+ } else {
+ const QString escapedText = Qt::convertFromPlainText(item.getStatusBarInfo());
+ ReadOnlyPart::setStatusBarText(QString("<qt>%1</qt>").arg(escapedText));
+ }
+}
+
+void DolphinPart::slotItemActivated(const KFileItem& item)
+{
+ KParts::OpenUrlArguments args;
+ // Forget about the known mimetype if a target URL is used.
+ // Testcase: network:/ with a item (mimetype "inode/some-foo-service") pointing to a http URL (html)
+ if (item.targetUrl() == item.url()) {
+ args.setMimeType(item.mimetype());
+ }
+
+ // Ideally, konqueror should be changed to not require trustedSource for directory views,
+ // since the idea was not to need BrowserArguments for non-browser stuff...
+ KParts::BrowserArguments browserArgs;
+ browserArgs.trustedSource = true;
+ emit m_extension->openUrlRequest(item.targetUrl(), args, browserArgs);
+}
+
+void DolphinPart::slotItemsActivated(const KFileItemList& items)
+{
+ foreach (const KFileItem& item, items) {
+ slotItemActivated(item);
+ }
+}
+
+void DolphinPart::createNewWindow(const KUrl& url)
+{
+ // TODO: Check issue N176832 for the missing QAIV signal; task 177399 - maybe this code
+ // should be moved into DolphinPart::slotItemActivated()
+ emit m_extension->createNewWindow(url);
+}
+
+void DolphinPart::slotOpenContextMenu(const QPoint& pos,
+ const KFileItem& _item,
+ const KUrl&,
+ const QList<QAction*>& customActions)
+{
+ KParts::BrowserExtension::PopupFlags popupFlags = KParts::BrowserExtension::DefaultPopupItems
+ | KParts::BrowserExtension::ShowProperties
+ | KParts::BrowserExtension::ShowUrlOperations;
+
+ KFileItem item(_item);
+
+ if (item.isNull()) { // viewport context menu
+ popupFlags |= KParts::BrowserExtension::ShowNavigationItems | KParts::BrowserExtension::ShowUp;
+ item = m_view->rootItem();
+ if (item.isNull())
+ item = KFileItem( S_IFDIR, (mode_t)-1, url() );
+ else
+ item.setUrl(url()); // ensure we use the view url, not the canonical path (#213799)
+ }
+
+ // TODO: We should change the signature of the slots (and signals) for being able
+ // to tell for which items we want a popup.
+ KFileItemList items;
+ if (m_view->selectedItems().isEmpty()) {
+ items.append(item);
+ } else {
+ items = m_view->selectedItems();
+ }
+
+ KFileItemListProperties capabilities(items);
+
+ KParts::BrowserExtension::ActionGroupMap actionGroups;
+ QList<QAction *> editActions;
+ editActions += m_view->versionControlActions(m_view->selectedItems());
+ editActions += customActions;
+
+ if (!_item.isNull()) { // only for context menu on one or more items
+ const bool supportsMoving = capabilities.supportsMoving();
+
+ if (capabilities.supportsDeleting()) {
+ const bool showDeleteAction = (KGlobal::config()->group("KDE").readEntry("ShowDeleteCommand", false) ||
+ !item.isLocalFile());
+ const bool showMoveToTrashAction = capabilities.isLocal() && supportsMoving;
+
+ if (showDeleteAction && showMoveToTrashAction) {
+ delete m_removeAction;
+ m_removeAction = 0;
+ editActions.append(actionCollection()->action("move_to_trash"));
+ editActions.append(actionCollection()->action("delete"));
+ } else if (showDeleteAction && !showMoveToTrashAction) {
+ editActions.append(actionCollection()->action("delete"));
+ } else {
+ if (!m_removeAction)
+ m_removeAction = new DolphinRemoveAction(this, actionCollection());
+ editActions.append(m_removeAction);
+ m_removeAction->update();
+ }
+ } else {
+ popupFlags |= KParts::BrowserExtension::NoDeletion;
+ }
+
+ if (supportsMoving) {
+ editActions.append(actionCollection()->action("rename"));
+ }
+
+ // Normally KonqPopupMenu only shows the "Create new" submenu in the current view
+ // since otherwise the created file would not be visible.
+ // But in treeview mode we should allow it.
+ if (m_view->itemsExpandable())
+ popupFlags |= KParts::BrowserExtension::ShowCreateDirectory;
+
+ }
+
+ actionGroups.insert("editactions", editActions);
+
+ emit m_extension->popupMenu(pos,
+ items,
+ KParts::OpenUrlArguments(),
+ KParts::BrowserArguments(),
+ popupFlags,
+ actionGroups);
+}
+
+void DolphinPart::slotDirectoryRedirection(const KUrl& oldUrl, const KUrl& newUrl)
+{
+ //kDebug() << oldUrl << newUrl << "currentUrl=" << url();
+ if (oldUrl.equals(url(), KUrl::CompareWithoutTrailingSlash /* #207572 */)) {
+ KParts::ReadOnlyPart::setUrl(newUrl);
+ const QString prettyUrl = newUrl.pathOrUrl();
+ emit m_extension->setLocationBarUrl(prettyUrl);
+ }
+}
+
+
+void DolphinPart::slotEditMimeType()
+{
+ const KFileItemList items = m_view->selectedItems();
+ if (!items.isEmpty()) {
+ KonqOperations::editMimeType(items.first().mimetype(), m_view);
+ }
+}
+
+void DolphinPart::slotSelectItemsMatchingPattern()
+{
+ openSelectionDialog(i18nc("@title:window", "Select"),
+ i18n("Select all items matching this pattern:"),
+ true);
+}
+
+void DolphinPart::slotUnselectItemsMatchingPattern()
+{
+ openSelectionDialog(i18nc("@title:window", "Unselect"),
+ i18n("Unselect all items matching this pattern:"),
+ false);
+}
+
+void DolphinPart::openSelectionDialog(const QString& title, const QString& text, bool selectItems)
+{
+ bool okClicked;
+ QString pattern = KInputDialog::getText(title, text, "*", &okClicked, m_view);
+
+ if (okClicked && !pattern.isEmpty()) {
+ QRegExp patternRegExp(pattern, Qt::CaseSensitive, QRegExp::Wildcard);
+ m_view->selectItems(patternRegExp, selectItems);
+ }
+}
+
+void DolphinPart::setCurrentViewMode(const QString& viewModeName)
+{
+ QAction* action = actionCollection()->action(viewModeName);
+ Q_ASSERT(action);
+ action->trigger();
+}
+
+QString DolphinPart::currentViewMode() const
+{
+ return m_actionHandler->currentViewModeActionName();
+}
+
+void DolphinPart::setNameFilter(const QString& nameFilter)
+{
+ // This is the "/home/dfaure/*.diff" kind of name filter (KDirLister::setNameFilter)
+ // which is unrelated to DolphinView::setNameFilter which is substring filtering in a proxy.
+ m_nameFilter = nameFilter;
+ // TODO save/restore name filter in saveState/restoreState like KonqDirPart did in kde3?
+}
+
+void DolphinPart::slotOpenTerminal()
+{
+ QString dir(QDir::homePath());
+
+ KUrl u(url());
+
+ // If the given directory is not local, it can still be the URL of an
+ // ioslave using UDS_LOCAL_PATH which to be converted first.
+ u = KIO::NetAccess::mostLocalUrl(u, widget());
+
+ //If the URL is local after the above conversion, set the directory.
+ if (u.isLocalFile()) {
+ dir = u.toLocalFile();
+ }
+
+ KToolInvocation::invokeTerminal(QString(), dir);
+}
+
+void DolphinPart::slotFindFile()
+{
+ KRun::run("kfind", url(), widget());
+}
+
+void DolphinPart::updateNewMenu()
+{
+ // As requested by KNewFileMenu :
+ m_newFileMenu->checkUpToDate();
+ m_newFileMenu->setViewShowsHiddenFiles(m_view->hiddenFilesShown());
+ // And set the files that the menu apply on :
+ m_newFileMenu->setPopupFiles(url());
+}
+
+void DolphinPart::updateStatusBar()
+{
+ const QString escapedText = Qt::convertFromPlainText(m_view->statusBarText());
+ emit ReadOnlyPart::setStatusBarText(QString("<qt>%1</qt>").arg(escapedText));
+}
+
+void DolphinPart::updateProgress(int percent)
+{
+ m_extension->loadingProgress(percent);
+}
+
+void DolphinPart::createDirectory()
+{
+ m_newFileMenu->setViewShowsHiddenFiles(m_view->hiddenFilesShown());
+ m_newFileMenu->setPopupFiles(url());
+ m_newFileMenu->createDirectory();
+}
+
+void DolphinPart::setFilesToSelect(const KUrl::List& files)
+{
+ if (files.isEmpty()) {
+ return;
+ }
+
+ m_view->markUrlsAsSelected(files);
+ m_view->markUrlAsCurrent(files.at(0));
+}
+
+bool DolphinPart::eventFilter(QObject* obj, QEvent* event)
+{
+ const int type = event->type();
+
+ if ((type == QEvent::KeyPress || type == QEvent::KeyRelease) && m_removeAction) {
+ QMenu* menu = qobject_cast<QMenu*>(obj);
+ if (menu && menu->parent() == m_view) {
+ QKeyEvent* ev = static_cast<QKeyEvent*>(event);
+ if (ev->key() == Qt::Key_Shift) {
+ m_removeAction->update();
+ }
+ }
+ }
+
+ return KParts::ReadOnlyPart::eventFilter(obj, event);
+}
+
+#include "dolphinpart.moc"
diff --git a/dolphin/src/dolphinpart.desktop b/dolphin/src/dolphinpart.desktop
new file mode 100644
index 0000000..ce04670
--- /dev/null
+++ b/dolphin/src/dolphinpart.desktop
@@ -0,0 +1,341 @@
+[Desktop Entry]
+Type=Service
+Name=Dolphin View
+Name[af]=Dolphin Deel
+Name[ar]=عرض دولفين
+Name[as]=Dolphin প্ৰদৰ্শন
+Name[ast]=Vista de Dolphin
+Name[[email protected]]=Vyhlad „Dolphin”
+Name[bg]=Преглед в Dolphin
+Name[bn]=ডলফিন ভিউ
+Name[bn_IN]=Dolphin প্রদর্শন
+Name[bs]=Delfinov prikaz
+Name[ca]=Vista del Dolphin
+Name[[email protected]]=Vista del Dolphin
+Name[cs]=Pohled Dolphin
+Name[csb]=Wëzdrzatk Dolphina
+Name[da]=Dolphin-visning
+Name[de]=Dolphin-Ansicht
+Name[el]=Προβολή του Dolphin
+Name[en_GB]=Dolphin View
+Name[eo]=Dolphin-rigardo
+Name[es]=Vista de Dolphin
+Name[et]=Dolphini vaade
+Name[eu]=Dolphin ikuspegia
+Name[fi]=Dolphin-näkymä
+Name[fr]=Vue de Dolphin
+Name[fy]=Dolfyn werjefte
+Name[ga]=Amharc Dolphin
+Name[gl]=Vista de Dolphin
+Name[gu]=ડોલ્ફિન દેખાવ
+Name[he]=הגדרות תצוגה ב־Dolphin
+Name[hi]=डॉल्फ़िन दृश्य
+Name[hne]=डाल्फिन दृस्य
+Name[hr]=Dolphinov prikaz
+Name[hsb]=Napohlad w Dolphinje
+Name[hu]=Dolphin-nézet
+Name[ia]=Vista de Dolphin
+Name[id]=Tampilan Dolphin
+Name[is]=Dolphin sýn
+Name[it]=Vista di Dolphin
+Name[ja]=Dolphin ビュー
+Name[kk]=Dolphin көрінісі
+Name[km]=ទិដ្ឋភាព Dolphin
+Name[kn]=ಡಾಲ್ಫಿನ್ ನೋಟ
+Name[ko]=Dolphin 보기
+Name[ku]=Bergehê Dolphin
+Name[lt]=Dolphin žiūryklė
+Name[lv]=Dolphin skats
+Name[mai]=डाल्फिन दृश्य
+Name[mk]=Преглед со Делфин
+Name[ml]=ഡോള്‍ഫിന്‍ അവതരണരീതി
+Name[mr]=डॉल्फिन दृश्य
+Name[ms]=Lihat Dolphin
+Name[nb]=Dolphin visning
+Name[nds]=Dolphin-Ansicht
+Name[nl]=Dolphin-weergave
+Name[nn]=Dolphin-vising
+Name[or]=ଡଲଫିନ ଦୃଶ୍ୟ
+Name[pa]=ਡਾਲਫਿਨ ਝਲਕ
+Name[pl]=Widok Dolphina
+Name[pt]=Área do Dolphin
+Name[pt_BR]=Visualização do Dolphin
+Name[ro]=Vizualizare Dolphin
+Name[ru]=Представление Dolphin
+Name[se]=Dolphinčájeheapmi
+Name[si]=ඩොල්ෆින් දසුන
+Name[sk]=Dolphin pohľad
+Name[sl]=Dolphin - pogled
+Name[sr]=Делфинов приказ
+Name[[email protected]]=Делфинов приказ
+Name[[email protected]]=Dolphinov prikaz
+Name[[email protected]]=Dolphinov prikaz
+Name[sv]=Vy i Dolphin
+Name[ta]=டால்பின் காட்சி
+Name[te]=డాల్ఫిన్ వీక్షణం
+Name[tg]=Намоиши Dolphin
+Name[th]=มุมมองของดอลฟิน
+Name[tr]=Dolphin Görünümü
+Name[ug]=Dolphin كۆرۈنۈش
+Name[uk]=Перегляд Dolphin
+Name[wa]=Vuwe di Dolphin
+Name[x-test]=xxDolphin Viewxx
+Name[zh_CN]=Dolphin 视图
+Name[zh_TW]=Dolphin 檢視
+MimeType=inode/directory;
+X-KDE-ServiceTypes=KParts/ReadOnlyPart,Browser/View
+X-KDE-Library=dolphinpart
+#X-KDE-BrowserView-Args=Icon
+X-KDE-BrowserView-HideFromMenus=true
+X-KDE-BrowserView-Built-Into=konqueror
+Icon=view-icon
+InitialPreference=7
+
+# Provide info about the view modes using the Actions mechanism so that KService parses it.
+# Konqueror then queries KService to get hold of the translated texts for the view modes
+Actions=icons;details;compact;
+
+[Desktop Action icons]
+Name=Icons
+Name[af]=Ikone
+Name[ar]=أيقونات
+Name[as]=আইকন
+Name[ast]=Iconos
+Name[be]=Значкі
+Name[[email protected]]=Ikony
+Name[bg]=Икони
+Name[bn]=আইকন
+Name[bn_IN]=আইকন
+Name[br]=Arlunioù
+Name[bs]=Ikone
+Name[ca]=Icones
+Name[[email protected]]=Icones
+Name[cs]=Ikony
+Name[csb]=Ikònë
+Name[cy]=Eicon
+Name[da]=Ikoner
+Name[de]=Symbole
+Name[el]=Εικονίδια
+Name[en_GB]=Icons
+Name[eo]=Piktogramoj
+Name[es]=Iconos
+Name[et]=Ikoonid
+Name[eu]=Ikonoak
+Name[fa]=شمایلها
+Name[fi]=Kuvakkeet
+Name[fr]=Icônes
+Name[fy]=Byldkaikes
+Name[ga]=Deilbhíní
+Name[gl]=Iconas
+Name[gu]=ચિહ્નો
+Name[he]=סמלים
+Name[hi]=प्रतीक
+Name[hne]=चिनहा
+Name[hr]=Ikone
+Name[hsb]=Piktogramy
+Name[hu]=Ikonok
+Name[ia]=Icones
+Name[id]=Ikon
+Name[is]=Táknmyndir
+Name[it]=Icone
+Name[ja]=アイコン
+Name[ka]=ხატულები
+Name[kk]=Таңбашалар
+Name[km]=រូប​តំណាង
+Name[kn]=ಚಿಹ್ನೆಗಳು
+Name[ko]=아이콘
+Name[ku]=Îkon
+Name[lt]=Ženkliukai
+Name[lv]=Ikonas
+Name[mai]=प्रतीक
+Name[mk]=Икони
+Name[ml]=ചിഹ്നങ്ങള്‍
+Name[mr]=चिन्ह
+Name[ms]=Ikon
+Name[nb]=Ikoner
+Name[nds]=Lüttbiller
+Name[ne]=प्रतिमा
+Name[nl]=Pictogrammen
+Name[nn]=Ikon
+Name[oc]=Icònas
+Name[or]=ଚିତ୍ର ସଂକେତଗୁଡ଼ିକ
+Name[pa]=ਆਈਕਾਨ
+Name[pl]=Ikony
+Name[pt]=Ícones
+Name[pt_BR]=Ícones
+Name[ro]=Pictograme
+Name[ru]=Значки
+Name[se]=Govažat
+Name[si]=අයිකන
+Name[sk]=Ikony
+Name[sl]=Ikone
+Name[sr]=Иконе
+Name[[email protected]]=Иконе
+Name[[email protected]]=Ikone
+Name[[email protected]]=Ikone
+Name[sv]=Ikoner
+Name[ta]=சின்னங்கள்
+Name[te]=ప్రతిమలు
+Name[tg]=Нишонаҳо
+Name[th]=ไอคอน
+Name[tr]=Simgeler
+Name[ug]=سىنبەلگىلەر
+Name[uk]=Піктограми
+Name[uz]=Nishonchalar
+Name[[email protected]]=Нишончалар
+Name[vi]=Biểu tượng
+Name[wa]=Imådjetes
+Name[xh]=Imphawu zemmifanekiso
+Name[x-test]=xxIconsxx
+Name[zh_CN]=图标
+Name[zh_TW]=圖示
+Icon=view-list-icons
+# Dummy
+Exec=dolphin
+
+[Desktop Action compact]
+Name=Compact
+Name[ar]=مُتضامّ
+Name[bg]=Компактно
+Name[bs]=Sabij
+Name[ca]=Compacte
+Name[[email protected]]=Compacte
+Name[cs]=Kompaktní
+Name[da]=Kompakt
+Name[de]=Kompakt
+Name[el]=Σύμπτυξη
+Name[en_GB]=Compact
+Name[es]=Compacta
+Name[et]=Kompaktne
+Name[fi]=Tiivis
+Name[fr]=Concis
+Name[ga]=Dlúth
+Name[gl]=Compacto
+Name[he]=מרוכז
+Name[hu]=Kompakt
+Name[ia]=Compacte
+Name[id]=Kompak
+Name[is]=Þjappað
+Name[it]=Compatta
+Name[kk]=Ықшамды
+Name[km]=តូច​ល្មម
+Name[ko]=축소됨
+Name[lt]=Kompaktiškas
+Name[lv]=Kompakts
+Name[mr]=संक्षिप्त
+Name[nb]=Kompakt
+Name[nds]=Drang
+Name[nl]=Compact
+Name[pa]=ਸੰਖੇਪ
+Name[pl]=Kompaktowo
+Name[pt]=Compacto
+Name[pt_BR]=Compacto
+Name[ro]=Compact
+Name[ru]=Столбцы
+Name[sk]=Kompaktný
+Name[sl]=Strnjeno
+Name[sr]=Сажето
+Name[[email protected]]=Сажето
+Name[[email protected]]=Sažeto
+Name[[email protected]]=Sažeto
+Name[sv]=Kompakt
+Name[tr]=Sıkışık
+Name[ug]=ئىخچام
+Name[uk]=Компактний
+Name[wa]=Rastrindou
+Name[x-test]=xxCompactxx
+Name[zh_CN]=简洁视图
+Name[zh_TW]=簡潔模式
+Icon=view-list-details
+# Dummy
+Exec=dolphin
+
+[Desktop Action details]
+Name=Details
+Name[af]=Besonderhede
+Name[ar]=تفاصيل
+Name[as]=বিৱৰণ
+Name[ast]=Detalles
+Name[[email protected]]=Detali
+Name[bg]=Подробности
+Name[bn]=বিস্তারিত
+Name[bn_IN]=বিবরণ
+Name[bs]=Detalji
+Name[ca]=Detalls
+Name[[email protected]]=Detalls
+Name[cs]=Podrobnosti
+Name[csb]=Detale
+Name[da]=Detaljer
+Name[de]=Details
+Name[el]=Λεπτομέρειες
+Name[en_GB]=Details
+Name[eo]=Detaloj
+Name[es]=Detalles
+Name[et]=Üksikasjad
+Name[eu]=Xehetasunak
+Name[fi]=Yksityiskohdat
+Name[fr]=Détails
+Name[fy]=Details
+Name[ga]=Mionsonraí
+Name[gl]=Detalles
+Name[gu]=વિગતો
+Name[he]=פרטים
+Name[hi]=विवरण
+Name[hne]=विवरन
+Name[hr]=Detalji
+Name[hsb]=Nadrobnosće
+Name[hu]=Részletek
+Name[ia]=Detalios
+Name[id]=Detail
+Name[is]=Smáatriði
+Name[it]=Dettagli
+Name[ja]=詳細
+Name[ka]=დეტალები
+Name[kk]=Егжей-тегжейі
+Name[km]=សេចក្ដី​លម្អិត
+Name[kn]=ವಿವರಗಳು
+Name[ko]=자세히
+Name[ku]=Kitekit
+Name[lt]=Detalės
+Name[lv]=Detaļas
+Name[mai]=विवरण
+Name[mk]=Детали
+Name[ml]=വിശദവിവരങ്ങള്‍
+Name[mr]=तपशील
+Name[ms]=Perincian
+Name[nb]=Detaljer
+Name[nds]=Enkelheiten
+Name[nl]=Details
+Name[nn]=Detaljar
+Name[oc]=Detalhs
+Name[or]=ବିସ୍ତୃତ ବିବରଣୀ
+Name[pa]=ਵੇਰਵਾ
+Name[pl]=Szczegóły
+Name[pt]=Detalhes
+Name[pt_BR]=Detalhes
+Name[ro]=Detalii
+Name[ru]=Таблица
+Name[si]=විස්තර
+Name[sk]=Podrobnosti
+Name[sl]=Podrobnosti
+Name[sr]=Детаљи
+Name[[email protected]]=Детаљи
+Name[[email protected]]=Detalji
+Name[[email protected]]=Detalji
+Name[sv]=Detaljinformation
+Name[ta]=விவரங்கள்
+Name[te]=వివరాలు
+Name[tg]=Тафсилотҳо
+Name[th]=รายละเอียด
+Name[tr]=Ayrıntılar
+Name[ug]=تەپسىلاتلار
+Name[uk]=Подробиці
+Name[uz]=Tafsilotlar
+Name[[email protected]]=Тафсилотлар
+Name[wa]=Detays
+Name[x-test]=xxDetailsxx
+Name[zh_CN]=细节
+Name[zh_TW]=詳細模式
+Icon=view-list-tree
+# Dummy
+Exec=dolphin
diff --git a/dolphin/src/dolphinpart.h b/dolphin/src/dolphinpart.h
new file mode 100644
index 0000000..7146b46
--- /dev/null
+++ b/dolphin/src/dolphinpart.h
@@ -0,0 +1,253 @@
+/* This file is part of the KDE project
+ Copyright (c) 2007 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 DOLPHINPART_H
+#define DOLPHINPART_H
+
+#include <kparts/part.h>
+
+#include <QItemSelectionModel>
+
+class DolphinNewFileMenu;
+class DolphinViewActionHandler;
+class QActionGroup;
+class KAction;
+class KFileItemList;
+class KFileItem;
+class DolphinPartBrowserExtension;
+class DolphinSortFilterProxyModel;
+class DolphinRemoteEncoding;
+class DolphinModel;
+class KDirLister;
+class DolphinView;
+class KAboutData;
+class DolphinRemoveAction;
+
+class DolphinPart : public KParts::ReadOnlyPart
+{
+ Q_OBJECT
+ // Used by konqueror. Technically it means "we want undo enabled if
+ // there are things in the undo history and the current part is a dolphin part".
+ // Even though it's konqueror doing the undo...
+ Q_PROPERTY( bool supportsUndo READ supportsUndo )
+
+ Q_PROPERTY( QString currentViewMode READ currentViewMode WRITE setCurrentViewMode )
+
+ // Used by konqueror when typing something like /home/dfaure/*.diff in the location bar
+ Q_PROPERTY( QString nameFilter READ nameFilter WRITE setNameFilter )
+
+ // Used by konqueror to implement the --select command-line option
+ Q_PROPERTY( KUrl::List filesToSelect READ filesToSelect WRITE setFilesToSelect )
+
+public:
+ explicit DolphinPart(QWidget* parentWidget, QObject* parent, const QVariantList& args);
+ ~DolphinPart();
+
+ static KAboutData* createAboutData();
+
+ /**
+ * Standard KParts::ReadOnlyPart openUrl method.
+ * Called by Konqueror to view a directory in DolphinPart.
+ */
+ virtual bool openUrl(const KUrl& url);
+
+ /// see the supportsUndo property
+ bool supportsUndo() const { return true; }
+
+ /**
+ * Used by konqueror for setting the view mode
+ * @param viewModeName internal name for the view mode, like "icons"
+ * Those names come from the Actions line in dolphinpart.desktop,
+ * and have to match the name of the KActions.
+ */
+ void setCurrentViewMode(const QString& viewModeName);
+
+ /**
+ * Used by konqueror for displaying the current view mode.
+ * @see setCurrentViewMode
+ */
+ QString currentViewMode() const;
+
+ /// Returns the view owned by this part; used by DolphinPartBrowserExtension
+ DolphinView* view() { return m_view; }
+
+ /**
+ * Sets a name filter, like *.diff
+ */
+ void setNameFilter(const QString& nameFilter);
+
+ /**
+ * Returns the current name filter. Used by konqueror to show it in the URL.
+ */
+ QString nameFilter() const { return m_nameFilter; }
+
+protected:
+ /**
+ * We reimplement openUrl so no need to implement openFile.
+ */
+ virtual bool openFile() { return true; }
+
+Q_SIGNALS:
+ /**
+ * Emitted when the view mode changes. Used by konqueror.
+ */
+ void viewModeChanged();
+
+
+ /**
+ * Emitted whenever the current URL is about to be changed.
+ */
+ void aboutToOpenURL();
+
+private Q_SLOTS:
+ void slotMessage(const QString& msg);
+ void slotErrorMessage(const QString& msg);
+ /**
+ * Shows the information for the item \a item inside the statusbar. If the
+ * item is null, the default statusbar information is shown.
+ */
+ void slotRequestItemInfo(const KFileItem& item);
+ /**
+ * Handles clicking on an item
+ */
+ void slotItemActivated(const KFileItem& item);
+ /**
+ * Handles activation of multiple items
+ */
+ void slotItemsActivated(const KFileItemList& items);
+ /**
+ * Creates a new window showing the content of \a url.
+ */
+ void createNewWindow(const KUrl& url);
+ /**
+ * Opens the context menu on the current mouse position.
+ * @pos Position in screen coordinates.
+ * @item File item context. If item is null, the context menu
+ * should be applied to \a url.
+ * @url URL which contains \a item.
+ * @customActions Actions that should be added to the context menu,
+ * if the file item is null.
+ */
+ void slotOpenContextMenu(const QPoint& pos,
+ const KFileItem& item,
+ const KUrl& url,
+ const QList<QAction*>& customActions);
+
+ /**
+ * Informs the host that we are opening \a url (e.g. after a redirection
+ * coming from KDirLister).
+ * Testcase 1: fish://localhost
+ * Testcase 2: showing a directory that is being renamed by another window (#180156)
+ */
+ void slotDirectoryRedirection(const KUrl& oldUrl, const KUrl& newUrl);
+
+ /**
+ * Updates the state of the 'Edit' menu actions and emits
+ * the signal selectionChanged().
+ */
+ void slotSelectionChanged(const KFileItemList& selection);
+
+ /**
+ * Updates the text of the paste action dependent from
+ * the number of items which are in the clipboard.
+ */
+ void updatePasteAction();
+
+ /**
+ * Connected to all "Go" menu actions provided by DolphinPart
+ */
+ void slotGoTriggered(QAction* action);
+
+ /**
+ * Connected to the "editMimeType" action
+ */
+ void slotEditMimeType();
+
+ /**
+ * Connected to the "select_items_matching" action.
+ * Opens a dialog which permits to select all items matching a pattern like "*.jpg".
+ */
+ void slotSelectItemsMatchingPattern();
+
+ /**
+ * Connected to the "unselect_items_matching" action.
+ * Opens a dialog which permits to unselect all items matching a pattern like "*.jpg".
+ */
+ void slotUnselectItemsMatchingPattern();
+
+ /**
+ * Open a terminal window, starting with the current directory.
+ */
+ void slotOpenTerminal();
+
+ /**
+ * Open KFind with the current path.
+ */
+ void slotFindFile();
+
+ /**
+ * Updates the 'Create New...' sub menu, just before it's shown.
+ */
+ void updateNewMenu();
+
+ /**
+ * Updates the number of items (= number of files + number of
+ * directories) in the statusbar. If files are selected, the number
+ * of selected files and the sum of the filesize is shown.
+ */
+ void updateStatusBar();
+
+ /**
+ * Notify container of folder loading progress.
+ */
+ void updateProgress(int percent);
+
+ void createDirectory();
+
+ /**
+ * Called by konqueror --select
+ */
+ void setFilesToSelect(const KUrl::List& files);
+ KUrl::List filesToSelect() const { return KUrl::List(); } // silence moc
+
+ virtual bool eventFilter(QObject*, QEvent*);
+
+private:
+ void createActions();
+ void createGoAction(const char* name, const char* iconName,
+ const QString& text, const QString& url,
+ QActionGroup* actionGroup);
+
+ void openSelectionDialog(const QString& title, const QString& text,
+ bool selectItems);
+
+private:
+ DolphinView* m_view;
+ DolphinViewActionHandler* m_actionHandler;
+ DolphinRemoteEncoding* m_remoteEncoding;
+ DolphinPartBrowserExtension* m_extension;
+ DolphinNewFileMenu* m_newFileMenu;
+ KAction* m_findFileAction;
+ KAction* m_openTerminalAction;
+ QString m_nameFilter;
+ DolphinRemoveAction* m_removeAction;
+ Q_DISABLE_COPY(DolphinPart)
+};
+
+#endif /* DOLPHINPART_H */
diff --git a/dolphin/src/dolphinpart.rc b/dolphin/src/dolphinpart.rc
new file mode 100644
index 0000000..893d6c8
--- /dev/null
+++ b/dolphin/src/dolphinpart.rc
@@ -0,0 +1,66 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+<kpartgui name="dolphinpart" version="10" >
+ <MenuBar>
+ <Menu name="edit"><text>&amp;Edit</text>
+ <Action name="new_menu"/>
+ <Separator/>
+ <Action name="rename"/>
+ <Action name="move_to_trash" />
+ <Action name="delete"/>
+ <Action name="editMimeType"/>
+ <Action name="properties"/>
+ <Separator/>
+ <Menu name="selection">
+ <text context="@title:menu">Selection</text>
+ <Action name="select_items_matching" />
+ <Action name="unselect_items_matching" />
+ <Separator/>
+ <Action name="select_all" />
+ <Action name="unselect_all" />
+ <Action name="invert_selection" />
+ </Menu>
+ </Menu>
+ <Menu name="view"><text>&amp;View</text>
+ <Action name="sort" />
+ <Action name="additional_info" />
+ <Action name="show_preview" />
+ <Action name="show_in_groups" />
+ <Action name="show_hidden_files" />
+ <Separator/>
+ <Action name="view_properties" />
+ </Menu>
+ <Menu name="go"><text>&amp;Go</text>
+ <Action name="go_applications"/>
+ <Action name="go_network_folders"/>
+ <Action name="go_settings"/>
+ <Action name="go_media"/>
+ <Action name="go_trash"/>
+ <Action name="go_autostart"/>
+ </Menu>
+ <Menu name="tools"><text context="@title:menu">Tools</text>
+ <Action name="open_terminal"/>
+ <Action name="find_file" />
+ <Action name="show_filter_bar" />
+ <Action name="compare_files" />
+ <Action name="change_remote_encoding" />
+ </Menu>
+</MenuBar>
+<ToolBar name="mainToolBar"><text context="@title:menu">Dolphin Toolbar</text>
+ <Action name="icons" />
+ <Action name="compact" />
+ <Action name="details" />
+</ToolBar>
+<State name="has_selection" >
+ <enable>
+ <Action name="move_to_trash" />
+ <Action name="delete" />
+ </enable>
+ </State>
+ <State name="has_no_selection" >
+ <disable>
+ <Action name="rename" />
+ <Action name="move_to_trash" />
+ <Action name="delete" />
+ </disable>
+</State>
+</kpartgui>
diff --git a/dolphin/src/dolphinpart_ext.cpp b/dolphin/src/dolphinpart_ext.cpp
new file mode 100644
index 0000000..fb7a4d2
--- /dev/null
+++ b/dolphin/src/dolphinpart_ext.cpp
@@ -0,0 +1,193 @@
+/* This file is part of the KDE project
+ * Copyright (c) 2012 Dawit Alemayehu <[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 "dolphinpart_ext.h"
+
+#include "dolphinpart.h"
+#include "views/dolphinview.h"
+
+#include <QVariant>
+
+#include <KFileItemList>
+
+
+DolphinPartBrowserExtension::DolphinPartBrowserExtension(DolphinPart* part)
+ :KParts::BrowserExtension( part )
+ ,m_part(part)
+{
+
+}
+
+void DolphinPartBrowserExtension::restoreState(QDataStream &stream)
+{
+ KParts::BrowserExtension::restoreState(stream);
+ m_part->view()->restoreState(stream);
+}
+
+void DolphinPartBrowserExtension::saveState(QDataStream &stream)
+{
+ KParts::BrowserExtension::saveState(stream);
+ m_part->view()->saveState(stream);
+}
+
+void DolphinPartBrowserExtension::cut()
+{
+ m_part->view()->cutSelectedItems();
+}
+
+void DolphinPartBrowserExtension::copy()
+{
+ m_part->view()->copySelectedItems();
+}
+
+void DolphinPartBrowserExtension::paste()
+{
+ m_part->view()->paste();
+}
+
+void DolphinPartBrowserExtension::pasteTo(const KUrl&)
+{
+ m_part->view()->pasteIntoFolder();
+}
+
+void DolphinPartBrowserExtension::reparseConfiguration()
+{
+ m_part->view()->readSettings();
+}
+
+
+DolphinPartFileInfoExtension::DolphinPartFileInfoExtension(DolphinPart* part)
+ :KParts::FileInfoExtension(part)
+ ,m_part(part)
+{
+}
+
+bool DolphinPartFileInfoExtension::hasSelection() const
+{
+ return m_part->view()->selectedItemsCount() > 0;
+}
+
+KParts::FileInfoExtension::QueryModes DolphinPartFileInfoExtension::supportedQueryModes() const
+{
+ return (KParts::FileInfoExtension::AllItems | KParts::FileInfoExtension::SelectedItems);
+}
+
+KFileItemList DolphinPartFileInfoExtension::queryFor(KParts::FileInfoExtension::QueryMode mode) const
+{
+ KFileItemList list;
+
+ if (mode == KParts::FileInfoExtension::None)
+ return list;
+
+ if (!(supportedQueryModes() & mode))
+ return list;
+
+ switch (mode) {
+ case KParts::FileInfoExtension::SelectedItems:
+ if (hasSelection())
+ return m_part->view()->selectedItems();
+ break;
+ case KParts::FileInfoExtension::AllItems:
+ return m_part->view()->items();
+ default:
+ break;
+ }
+
+ return list;
+}
+
+DolphinPartListingFilterExtension::DolphinPartListingFilterExtension(DolphinPart* part)
+ : KParts::ListingFilterExtension(part)
+ , m_part(part)
+{
+}
+
+KParts::ListingFilterExtension::FilterModes DolphinPartListingFilterExtension::supportedFilterModes() const
+{
+ return (KParts::ListingFilterExtension::MimeType |
+ KParts::ListingFilterExtension::SubString |
+ KParts::ListingFilterExtension::WildCard);
+}
+
+bool DolphinPartListingFilterExtension::supportsMultipleFilters(KParts::ListingFilterExtension::FilterMode mode) const
+{
+ if (mode == KParts::ListingFilterExtension::MimeType)
+ return true;
+
+ return false;
+}
+
+QVariant DolphinPartListingFilterExtension::filter(KParts::ListingFilterExtension::FilterMode mode) const
+{
+ QVariant result;
+
+ switch (mode) {
+ case KParts::ListingFilterExtension::MimeType:
+ result = m_part->view()->mimeTypeFilters();
+ break;
+ case KParts::ListingFilterExtension::SubString:
+ case KParts::ListingFilterExtension::WildCard:
+ result = m_part->view()->nameFilter();
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+void DolphinPartListingFilterExtension::setFilter(KParts::ListingFilterExtension::FilterMode mode, const QVariant& filter)
+{
+ switch (mode) {
+ case KParts::ListingFilterExtension::MimeType:
+ m_part->view()->setMimeTypeFilters(filter.toStringList());
+ break;
+ case KParts::ListingFilterExtension::SubString:
+ case KParts::ListingFilterExtension::WildCard:
+ m_part->view()->setNameFilter(filter.toString());
+ break;
+ default:
+ break;
+ }
+}
+
+////
+
+DolphinPartListingNotificationExtension::DolphinPartListingNotificationExtension(DolphinPart* part)
+ : KParts::ListingNotificationExtension(part)
+{
+}
+
+KParts::ListingNotificationExtension::NotificationEventTypes DolphinPartListingNotificationExtension::supportedNotificationEventTypes() const
+{
+ return (KParts::ListingNotificationExtension::ItemsAdded |
+ KParts::ListingNotificationExtension::ItemsDeleted);
+}
+
+void DolphinPartListingNotificationExtension::slotNewItems(const KFileItemList& items)
+{
+ emit listingEvent(KParts::ListingNotificationExtension::ItemsAdded, items);
+}
+
+void DolphinPartListingNotificationExtension::slotItemsDeleted(const KFileItemList& items)
+{
+ emit listingEvent(KParts::ListingNotificationExtension::ItemsDeleted, items);
+}
+
+#include "dolphinpart_ext.moc"
diff --git a/dolphin/src/dolphinpart_ext.h b/dolphin/src/dolphinpart_ext.h
new file mode 100644
index 0000000..c05962c
--- /dev/null
+++ b/dolphin/src/dolphinpart_ext.h
@@ -0,0 +1,92 @@
+/* This file is part of the KDE project
+ * Copyright (c) 2012 Dawit Alemayehu <[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 DOLPHINPART_EXT_H
+#define DOLPHINPART_EXT_H
+
+#include <kparts/browserextension.h>
+#include <kparts/fileinfoextension.h>
+#include <kparts/listingextension.h>
+
+class DolphinPart;
+
+class DolphinPartBrowserExtension : public KParts::BrowserExtension
+{
+ Q_OBJECT
+public:
+ DolphinPartBrowserExtension( DolphinPart* part );
+ virtual void restoreState(QDataStream &stream);
+ virtual void saveState(QDataStream &stream);
+
+public Q_SLOTS:
+ void cut();
+ void copy();
+ void paste();
+ void pasteTo(const KUrl&);
+ void reparseConfiguration();
+
+private:
+ DolphinPart* m_part;
+};
+
+class DolphinPartFileInfoExtension : public KParts::FileInfoExtension
+{
+ Q_OBJECT
+
+public:
+ DolphinPartFileInfoExtension(DolphinPart* part);
+
+ virtual QueryModes supportedQueryModes() const;
+ virtual bool hasSelection() const;
+
+ virtual KFileItemList queryFor(QueryMode mode) const;
+
+private:
+ DolphinPart* m_part;
+};
+
+class DolphinPartListingFilterExtension : public KParts::ListingFilterExtension
+{
+ Q_OBJECT
+
+public:
+ DolphinPartListingFilterExtension(DolphinPart* part);
+ virtual FilterModes supportedFilterModes() const;
+ virtual bool supportsMultipleFilters(FilterMode mode) const;
+ virtual QVariant filter(FilterMode mode) const;
+ virtual void setFilter(FilterMode mode, const QVariant& filter);
+
+private:
+ DolphinPart* m_part;
+};
+
+class DolphinPartListingNotificationExtension : public KParts::ListingNotificationExtension
+{
+ Q_OBJECT
+
+public:
+ DolphinPartListingNotificationExtension(DolphinPart* part);
+ virtual NotificationEventTypes supportedNotificationEventTypes() const;
+
+public Q_SLOTS:
+ void slotNewItems(const KFileItemList&);
+ void slotItemsDeleted(const KFileItemList&);
+};
+
+#endif
diff --git a/dolphin/src/dolphinrecenttabsmenu.cpp b/dolphin/src/dolphinrecenttabsmenu.cpp
new file mode 100644
index 0000000..fa3eaf1
--- /dev/null
+++ b/dolphin/src/dolphinrecenttabsmenu.cpp
@@ -0,0 +1,97 @@
+/***************************************************************************
+ * Copyright (C) 2014 by Emmanuel Pescosta <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "dolphinrecenttabsmenu.h"
+
+#include <KLocalizedString>
+#include <KAcceleratorManager>
+#include <KMimeType>
+#include <KMenu>
+
+DolphinRecentTabsMenu::DolphinRecentTabsMenu(QObject* parent) :
+ KActionMenu(KIcon("edit-undo"), i18n("Recently Closed Tabs"), parent)
+{
+ setDelayed(false);
+ setEnabled(false);
+
+ m_clearListAction = new QAction(i18n("Empty Recently Closed Tabs"), this);
+ m_clearListAction->setIcon(KIcon("edit-clear-list"));
+ addAction(m_clearListAction);
+
+ addSeparator();
+
+ connect(menu(), SIGNAL(triggered(QAction*)),
+ this, SLOT(handleAction(QAction*)));
+}
+
+void DolphinRecentTabsMenu::rememberClosedTab(const KUrl& url, const QByteArray& state)
+{
+ QAction* action = new QAction(menu());
+ action->setText(url.path());
+ action->setData(state);
+ const QString iconName = KMimeType::iconNameForUrl(url);
+ action->setIcon(KIcon(iconName));
+
+ // Add the closed tab menu entry after the separator and
+ // "Empty Recently Closed Tabs" entry
+ if (menu()->actions().size() == 2) {
+ addAction(action);
+ } else {
+ insertAction(menu()->actions().at(2), action);
+ }
+ emit closedTabsCountChanged(menu()->actions().size() - 2);
+ // Assure that only up to 6 closed tabs are shown in the menu.
+ // 8 because of clear action + separator + 6 closed tabs
+ if (menu()->actions().size() > 8) {
+ removeAction(menu()->actions().last());
+ }
+ setEnabled(true);
+ KAcceleratorManager::manage(menu());
+}
+
+void DolphinRecentTabsMenu::undoCloseTab()
+{
+ Q_ASSERT(menu()->actions().size() > 2);
+ handleAction(menu()->actions().at(2));
+}
+
+void DolphinRecentTabsMenu::handleAction(QAction* action)
+{
+ if (action == m_clearListAction) {
+ // Clear all actions except the "Empty Recently Closed Tabs"
+ // action and the separator
+ QList<QAction*> actions = menu()->actions();
+ const int count = actions.size();
+ for (int i = 2; i < count; ++i) {
+ removeAction(actions.at(i));
+ }
+ emit closedTabsCountChanged(0);
+ } else {
+ const QByteArray state = action->data().value<QByteArray>();
+ removeAction(action);
+ delete action;
+ action = 0;
+ emit restoreClosedTab(state);
+ emit closedTabsCountChanged(menu()->actions().size() - 2);
+ }
+
+ if (menu()->actions().count() <= 2) {
+ setEnabled(false);
+ }
+} \ No newline at end of file
diff --git a/dolphin/src/dolphinrecenttabsmenu.h b/dolphin/src/dolphinrecenttabsmenu.h
new file mode 100644
index 0000000..910e564
--- /dev/null
+++ b/dolphin/src/dolphinrecenttabsmenu.h
@@ -0,0 +1,51 @@
+/***************************************************************************
+ * Copyright (C) 2014 by Emmanuel Pescosta <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef DOLPHIN_RECENT_TABS_MENU_H
+#define DOLPHIN_RECENT_TABS_MENU_H
+
+#include <KActionMenu>
+#include <KUrl>
+
+class DolphinTabPage;
+class QAction;
+
+class DolphinRecentTabsMenu : public KActionMenu
+{
+ Q_OBJECT
+
+public:
+ explicit DolphinRecentTabsMenu(QObject* parent);
+
+public slots:
+ void rememberClosedTab(const KUrl& url, const QByteArray& state);
+ void undoCloseTab();
+
+signals:
+ void restoreClosedTab(const QByteArray& state);
+ void closedTabsCountChanged(unsigned int count);
+
+private slots:
+ void handleAction(QAction* action);
+
+private:
+ QAction* m_clearListAction;
+};
+
+#endif \ No newline at end of file
diff --git a/dolphin/src/dolphinremoveaction.cpp b/dolphin/src/dolphinremoveaction.cpp
new file mode 100644
index 0000000..7d7c2f0
--- /dev/null
+++ b/dolphin/src/dolphinremoveaction.cpp
@@ -0,0 +1,61 @@
+/***************************************************************************
+ * Copyright (C) 2013 by Dawit Alemayehu <[email protected] *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "dolphinremoveaction.h"
+
+#include <QApplication>
+
+#include <KLocalizedString>
+
+
+DolphinRemoveAction::DolphinRemoveAction(QObject* parent, KActionCollection* collection) :
+ QAction(parent),
+ m_collection(collection)
+{
+ update();
+ connect(this, SIGNAL(triggered()), this, SLOT(slotRemoveActionTriggered()));
+}
+
+void DolphinRemoveAction::slotRemoveActionTriggered()
+{
+ if (m_action) {
+ m_action->trigger();
+ }
+}
+
+void DolphinRemoveAction::update()
+{
+ Q_ASSERT(m_collection);
+ // Using setText(action->text()) does not apply the &-shortcut.
+ // This is only done until the original action has been shown at least once. To
+ // bypass this issue, the text and &-shortcut is applied manually.
+ if (qApp->keyboardModifiers() & Qt::ShiftModifier) {
+ m_action = m_collection ? m_collection->action("delete") : 0;
+ setText(i18nc("@action:inmenu", "&Delete"));
+ } else {
+ m_action = m_collection ? m_collection->action("move_to_trash") : 0;
+ setText(i18nc("@action:inmenu", "&Move to Trash"));
+ }
+
+ if (m_action) {
+ setIcon(m_action->icon());
+ setShortcuts(m_action->shortcuts());
+ setEnabled(m_action->isEnabled());
+ }
+}
diff --git a/dolphin/src/dolphinremoveaction.h b/dolphin/src/dolphinremoveaction.h
new file mode 100644
index 0000000..1a123ac
--- /dev/null
+++ b/dolphin/src/dolphinremoveaction.h
@@ -0,0 +1,55 @@
+/***************************************************************************
+ * Copyright (C) 2013 by Dawit Alemayehu <[email protected] *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef DOLPHINREMOVEACTION_H
+#define DOLPHINREMOVEACTION_H
+
+#include "libdolphin_export.h"
+
+#include <QAction>
+#include <QPointer>
+
+#include <KActionCollection>
+
+/**
+ * A QAction that manages the delete based on the current state of
+ * the Shift key or the parameter passed to update.
+ *
+ * This class expects the presence of both the "move_to_trash" and "delete"
+ * actions in @ref collection.
+ */
+class LIBDOLPHINPRIVATE_EXPORT DolphinRemoveAction : public QAction
+{
+ Q_OBJECT
+public:
+ DolphinRemoveAction(QObject* parent, KActionCollection* collection);
+ /**
+ * Updates this action key based on the state of the Shift key.
+ */
+ void update();
+
+private Q_SLOTS:
+ void slotRemoveActionTriggered();
+
+private:
+ QPointer<KActionCollection> m_collection;
+ QPointer<QAction> m_action;
+};
+
+#endif
diff --git a/dolphin/src/dolphintabbar.cpp b/dolphin/src/dolphintabbar.cpp
new file mode 100644
index 0000000..78bd5ed
--- /dev/null
+++ b/dolphin/src/dolphintabbar.cpp
@@ -0,0 +1,174 @@
+/***************************************************************************
+ * Copyright (C) 2014 by Emmanuel Pescosta <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "dolphintabbar.h"
+
+#include <QTimer>
+#include <QDragEnterEvent>
+#include <KLocalizedString>
+#include <KMenu>
+#include <KIcon>
+#include <KUrl>
+
+DolphinTabBar::DolphinTabBar(QWidget* parent) :
+ QTabBar(parent),
+ m_autoActivationIndex(-1)
+{
+ setAcceptDrops(true);
+ setSelectionBehaviorOnRemove(QTabBar::SelectPreviousTab);
+ setMovable(true);
+ setTabsClosable(true);
+
+ m_autoActivationTimer = new QTimer(this);
+ m_autoActivationTimer->setSingleShot(true);
+ m_autoActivationTimer->setInterval(800);
+ connect(m_autoActivationTimer, SIGNAL(timeout()),
+ this, SLOT(slotAutoActivationTimeout()));
+}
+
+void DolphinTabBar::dragEnterEvent(QDragEnterEvent* event)
+{
+ const QMimeData* mimeData = event->mimeData();
+ const int index = tabAt(event->pos());
+
+ if (KUrl::List::canDecode(mimeData)) {
+ event->acceptProposedAction();
+ updateAutoActivationTimer(index);
+ }
+
+ QTabBar::dragEnterEvent(event);
+}
+
+void DolphinTabBar::dragLeaveEvent(QDragLeaveEvent* event)
+{
+ updateAutoActivationTimer(-1);
+
+ QTabBar::dragLeaveEvent(event);
+}
+
+void DolphinTabBar::dragMoveEvent(QDragMoveEvent* event)
+{
+ const QMimeData* mimeData = event->mimeData();
+ const int index = tabAt(event->pos());
+
+ if (KUrl::List::canDecode(mimeData)) {
+ updateAutoActivationTimer(index);
+ }
+
+ QTabBar::dragMoveEvent(event);
+}
+
+void DolphinTabBar::dropEvent(QDropEvent* event)
+{
+ // Disable the auto activation timer
+ updateAutoActivationTimer(-1);
+
+ const QMimeData* mimeData = event->mimeData();
+ const int index = tabAt(event->pos());
+
+ if (index >= 0 && KUrl::List::canDecode(mimeData)) {
+ emit tabDropEvent(index, event);
+ }
+
+ QTabBar::dropEvent(event);
+}
+
+void DolphinTabBar::mousePressEvent(QMouseEvent* event)
+{
+ const int index = tabAt(event->pos());
+
+ if (index >= 0 && event->button() == Qt::MiddleButton) {
+ // Mouse middle click on a tab closes this tab.
+ emit tabCloseRequested(index);
+ return;
+ }
+
+ QTabBar::mousePressEvent(event);
+}
+
+void DolphinTabBar::mouseDoubleClickEvent(QMouseEvent* event)
+{
+ const int index = tabAt(event->pos());
+
+ if (index < 0) {
+ // Double click on the empty tabbar area opens a new activated tab
+ // with the url from the current tab.
+ emit openNewActivatedTab(currentIndex());
+ return;
+ }
+
+ QTabBar::mouseDoubleClickEvent(event);
+}
+
+void DolphinTabBar::contextMenuEvent(QContextMenuEvent* event)
+{
+ const int index = tabAt(event->pos());
+
+ if (index >= 0) {
+ // Tab context menu
+ KMenu menu(this);
+
+ QAction* newTabAction = menu.addAction(KIcon("tab-new"), i18nc("@action:inmenu", "New Tab"));
+ QAction* detachTabAction = menu.addAction(KIcon("tab-detach"), i18nc("@action:inmenu", "Detach Tab"));
+ QAction* closeOtherTabsAction = menu.addAction(KIcon("tab-close-other"), i18nc("@action:inmenu", "Close Other Tabs"));
+ QAction* closeTabAction = menu.addAction(KIcon("tab-close"), i18nc("@action:inmenu", "Close Tab"));
+
+ QAction* selectedAction = menu.exec(event->globalPos());
+ if (selectedAction == newTabAction) {
+ emit openNewActivatedTab(index);
+ } else if (selectedAction == detachTabAction) {
+ emit tabDetachRequested(index);
+ } else if (selectedAction == closeOtherTabsAction) {
+ const int tabCount = count();
+ for (int i = 0; i < index; i++) {
+ emit tabCloseRequested(0);
+ }
+ for (int i = index + 1; i < tabCount; i++) {
+ emit tabCloseRequested(1);
+ }
+ } else if (selectedAction == closeTabAction) {
+ emit tabCloseRequested(index);
+ }
+
+ return;
+ }
+
+ QTabBar::contextMenuEvent(event);
+}
+
+void DolphinTabBar::slotAutoActivationTimeout()
+{
+ if (m_autoActivationIndex >= 0) {
+ setCurrentIndex(m_autoActivationIndex);
+ updateAutoActivationTimer(-1);
+ }
+}
+
+void DolphinTabBar::updateAutoActivationTimer(const int index)
+{
+ if (m_autoActivationIndex != index) {
+ m_autoActivationIndex = index;
+
+ if (m_autoActivationIndex < 0) {
+ m_autoActivationTimer->stop();
+ } else {
+ m_autoActivationTimer->start();
+ }
+ }
+}
diff --git a/dolphin/src/dolphintabbar.h b/dolphin/src/dolphintabbar.h
new file mode 100644
index 0000000..d2b2e9e
--- /dev/null
+++ b/dolphin/src/dolphintabbar.h
@@ -0,0 +1,65 @@
+/***************************************************************************
+ * Copyright (C) 2014 by Emmanuel Pescosta <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef DOLPHIN_TAB_BAR_H
+#define DOLPHIN_TAB_BAR_H
+
+#include <QTabBar>
+
+class DolphinTabBar : public QTabBar
+{
+ Q_OBJECT
+
+public:
+ explicit DolphinTabBar(QWidget* parent);
+
+signals:
+ void openNewActivatedTab(int index);
+ void tabDropEvent(int index, QDropEvent* event);
+ void tabDetachRequested(int index);
+
+protected:
+ virtual void dragEnterEvent(QDragEnterEvent* event);
+ virtual void dragLeaveEvent(QDragLeaveEvent* event);
+ virtual void dragMoveEvent(QDragMoveEvent* event);
+ virtual void dropEvent(QDropEvent* event);
+ virtual void mousePressEvent(QMouseEvent* event);
+ virtual void mouseDoubleClickEvent(QMouseEvent* event);
+
+ /**
+ * Opens a context menu for the tab on the \a event position.
+ */
+ virtual void contextMenuEvent(QContextMenuEvent* event);
+
+private slots:
+ void slotAutoActivationTimeout();
+
+private:
+ /**
+ * If \a index is a valid index (>= 0), store the index and start the timer
+ * (if the interval >= 0 ms). If the index is not valid (< 0), stop the timer.
+ */
+ void updateAutoActivationTimer(const int index);
+
+private:
+ QTimer* m_autoActivationTimer;
+ int m_autoActivationIndex;
+};
+
+#endif // DOLPHIN_TAB_BAR_H
diff --git a/dolphin/src/dolphintabpage.cpp b/dolphin/src/dolphintabpage.cpp
new file mode 100644
index 0000000..f7000ea
--- /dev/null
+++ b/dolphin/src/dolphintabpage.cpp
@@ -0,0 +1,335 @@
+/***************************************************************************
+ * Copyright (C) 2014 by Emmanuel Pescosta <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "dolphintabpage.h"
+
+#include "dolphinviewcontainer.h"
+#include "dolphin_generalsettings.h"
+
+#include <QSplitter>
+
+DolphinTabPage::DolphinTabPage(const KUrl& primaryUrl, const KUrl& secondaryUrl, QWidget* parent) :
+ QWidget(parent),
+ m_primaryViewActive(true),
+ m_splitViewEnabled(false)
+{
+ QVBoxLayout* layout = new QVBoxLayout(this);
+ layout->setSpacing(0);
+ layout->setMargin(0);
+
+ m_splitter = new QSplitter(Qt::Horizontal, this);
+ m_splitter->setChildrenCollapsible(false);
+ layout->addWidget(m_splitter);
+
+ // Create a new primary view
+ m_primaryViewContainer = createViewContainer(primaryUrl);
+ connect(m_primaryViewContainer->view(), SIGNAL(urlChanged(KUrl)),
+ this, SIGNAL(activeViewUrlChanged(KUrl)));
+ connect(m_primaryViewContainer->view(), SIGNAL(redirection(KUrl,KUrl)),
+ this, SLOT(slotViewUrlRedirection(KUrl,KUrl)));
+
+ m_splitter->addWidget(m_primaryViewContainer);
+ m_primaryViewContainer->show();
+
+ if (secondaryUrl.isValid() || GeneralSettings::splitView()) {
+ // Provide a secondary view, if the given secondary url is valid or if the
+ // startup settings are set this way (use the url of the primary view).
+ m_splitViewEnabled = true;
+ const KUrl& url = secondaryUrl.isValid() ? secondaryUrl : primaryUrl;
+ m_secondaryViewContainer = createViewContainer(url);
+ m_splitter->addWidget(m_secondaryViewContainer);
+ m_secondaryViewContainer->show();
+ }
+
+ m_primaryViewContainer->setActive(true);
+}
+
+bool DolphinTabPage::primaryViewActive() const
+{
+ return m_primaryViewActive;
+}
+
+bool DolphinTabPage::splitViewEnabled() const
+{
+ return m_splitViewEnabled;
+}
+
+void DolphinTabPage::setSplitViewEnabled(bool enabled)
+{
+ if (m_splitViewEnabled != enabled) {
+ m_splitViewEnabled = enabled;
+
+ if (enabled) {
+ const KUrl& url = m_primaryViewContainer->url();
+ m_secondaryViewContainer = createViewContainer(url);
+
+ const bool placesSelectorVisible = m_primaryViewContainer->urlNavigator()->isPlacesSelectorVisible();
+ m_secondaryViewContainer->urlNavigator()->setPlacesSelectorVisible(placesSelectorVisible);
+
+ m_splitter->addWidget(m_secondaryViewContainer);
+ m_secondaryViewContainer->show();
+ m_secondaryViewContainer->setActive(true);
+ } else {
+ // Close the view which is active.
+ DolphinViewContainer* view = activeViewContainer();
+ if (m_primaryViewActive) {
+ // If the primary view is active, we have to swap the pointers
+ // because the secondary view will be the new primary view.
+ qSwap(m_primaryViewContainer, m_secondaryViewContainer);
+ }
+ m_primaryViewContainer->setActive(true);
+ view->close();
+ view->deleteLater();
+ }
+ }
+}
+
+DolphinViewContainer* DolphinTabPage::primaryViewContainer() const
+{
+ return m_primaryViewContainer;
+}
+
+DolphinViewContainer* DolphinTabPage::secondaryViewContainer() const
+{
+ return m_secondaryViewContainer;
+}
+
+DolphinViewContainer* DolphinTabPage::activeViewContainer() const
+{
+ return m_primaryViewActive ? m_primaryViewContainer :
+ m_secondaryViewContainer;
+}
+
+KFileItemList DolphinTabPage::selectedItems() const
+{
+ KFileItemList items = m_primaryViewContainer->view()->selectedItems();
+ if (m_splitViewEnabled) {
+ items += m_secondaryViewContainer->view()->selectedItems();
+ }
+ return items;
+}
+
+int DolphinTabPage::selectedItemsCount() const
+{
+ int selectedItemsCount = m_primaryViewContainer->view()->selectedItemsCount();
+ if (m_splitViewEnabled) {
+ selectedItemsCount += m_secondaryViewContainer->view()->selectedItemsCount();
+ }
+ return selectedItemsCount;
+}
+
+void DolphinTabPage::markUrlsAsSelected(const QList<KUrl>& urls)
+{
+ m_primaryViewContainer->view()->markUrlsAsSelected(urls);
+ if (m_splitViewEnabled) {
+ m_secondaryViewContainer->view()->markUrlsAsSelected(urls);
+ }
+}
+
+void DolphinTabPage::markUrlAsCurrent(const KUrl& url)
+{
+ m_primaryViewContainer->view()->markUrlAsCurrent(url);
+ if (m_splitViewEnabled) {
+ m_secondaryViewContainer->view()->markUrlAsCurrent(url);
+ }
+}
+
+void DolphinTabPage::setPlacesSelectorVisible(bool visible)
+{
+ m_primaryViewContainer->urlNavigator()->setPlacesSelectorVisible(visible);
+ if (m_splitViewEnabled) {
+ m_secondaryViewContainer->urlNavigator()->setPlacesSelectorVisible(visible);
+ }
+}
+
+void DolphinTabPage::refreshViews()
+{
+ m_primaryViewContainer->readSettings();
+ if (m_splitViewEnabled) {
+ m_secondaryViewContainer->readSettings();
+ }
+}
+
+QByteArray DolphinTabPage::saveState() const
+{
+ QByteArray state;
+ QDataStream stream(&state, QIODevice::WriteOnly);
+
+ stream << quint32(2); // Tab state version
+
+ stream << m_splitViewEnabled;
+
+ stream << m_primaryViewContainer->url();
+ stream << m_primaryViewContainer->urlNavigator()->isUrlEditable();
+ m_primaryViewContainer->view()->saveState(stream);
+
+ if (m_splitViewEnabled) {
+ stream << m_secondaryViewContainer->url();
+ stream << m_secondaryViewContainer->urlNavigator()->isUrlEditable();
+ m_secondaryViewContainer->view()->saveState(stream);
+ }
+
+ stream << m_primaryViewActive;
+ stream << m_splitter->saveState();
+
+ return state;
+}
+
+void DolphinTabPage::restoreState(const QByteArray& state)
+{
+ if (state.isEmpty()) {
+ return;
+ }
+
+ QByteArray sd = state;
+ QDataStream stream(&sd, QIODevice::ReadOnly);
+
+ // Read the version number of the tab state and check if the version is supported.
+ quint32 version = 0;
+ stream >> version;
+ if (version != 2) {
+ // The version of the tab state isn't supported, we can't restore it.
+ return;
+ }
+
+ bool isSplitViewEnabled = false;
+ stream >> isSplitViewEnabled;
+ setSplitViewEnabled(isSplitViewEnabled);
+
+ KUrl primaryUrl;
+ stream >> primaryUrl;
+ m_primaryViewContainer->setUrl(primaryUrl);
+ bool primaryUrlEditable;
+ stream >> primaryUrlEditable;
+ m_primaryViewContainer->urlNavigator()->setUrlEditable(primaryUrlEditable);
+ m_primaryViewContainer->view()->restoreState(stream);
+
+ if (isSplitViewEnabled) {
+ KUrl secondaryUrl;
+ stream >> secondaryUrl;
+ m_secondaryViewContainer->setUrl(secondaryUrl);
+ bool secondaryUrlEditable;
+ stream >> secondaryUrlEditable;
+ m_secondaryViewContainer->urlNavigator()->setUrlEditable(secondaryUrlEditable);
+ m_secondaryViewContainer->view()->restoreState(stream);
+ }
+
+ stream >> m_primaryViewActive;
+ if (m_primaryViewActive) {
+ m_primaryViewContainer->setActive(true);
+ } else {
+ Q_ASSERT(m_splitViewEnabled);
+ m_secondaryViewContainer->setActive(true);
+ }
+
+ QByteArray splitterState;
+ stream >> splitterState;
+ m_splitter->restoreState(splitterState);
+}
+
+void DolphinTabPage::restoreStateV1(const QByteArray& state)
+{
+ if (state.isEmpty()) {
+ return;
+ }
+
+ QByteArray sd = state;
+ QDataStream stream(&sd, QIODevice::ReadOnly);
+
+ bool isSplitViewEnabled = false;
+ stream >> isSplitViewEnabled;
+ setSplitViewEnabled(isSplitViewEnabled);
+
+ KUrl primaryUrl;
+ stream >> primaryUrl;
+ m_primaryViewContainer->setUrl(primaryUrl);
+ bool primaryUrlEditable;
+ stream >> primaryUrlEditable;
+ m_primaryViewContainer->urlNavigator()->setUrlEditable(primaryUrlEditable);
+
+ if (isSplitViewEnabled) {
+ KUrl secondaryUrl;
+ stream >> secondaryUrl;
+ m_secondaryViewContainer->setUrl(secondaryUrl);
+ bool secondaryUrlEditable;
+ stream >> secondaryUrlEditable;
+ m_secondaryViewContainer->urlNavigator()->setUrlEditable(secondaryUrlEditable);
+ }
+
+ stream >> m_primaryViewActive;
+ if (m_primaryViewActive) {
+ m_primaryViewContainer->setActive(true);
+ } else {
+ Q_ASSERT(m_splitViewEnabled);
+ m_secondaryViewContainer->setActive(true);
+ }
+
+ QByteArray splitterState;
+ stream >> splitterState;
+ m_splitter->restoreState(splitterState);
+}
+
+void DolphinTabPage::slotViewActivated()
+{
+ const DolphinView* oldActiveView = activeViewContainer()->view();
+
+ // Set the view, which was active before, to inactive
+ // and update the active view type.
+ if (m_splitViewEnabled) {
+ activeViewContainer()->setActive(false);
+ m_primaryViewActive = !m_primaryViewActive;
+ } else {
+ m_primaryViewActive = true;
+ }
+
+ const DolphinView* newActiveView = activeViewContainer()->view();
+
+ if (newActiveView != oldActiveView) {
+ disconnect(oldActiveView, SIGNAL(urlChanged(KUrl)),
+ this, SIGNAL(activeViewUrlChanged(KUrl)));
+ disconnect(oldActiveView, SIGNAL(redirection(KUrl,KUrl)),
+ this, SLOT(slotViewUrlRedirection(KUrl,KUrl)));
+ connect(newActiveView, SIGNAL(urlChanged(KUrl)),
+ this, SIGNAL(activeViewUrlChanged(KUrl)));
+ connect(newActiveView, SIGNAL(redirection(KUrl,KUrl)),
+ this, SLOT(slotViewUrlRedirection(KUrl,KUrl)));
+ }
+
+ emit activeViewUrlChanged(activeViewContainer()->url());
+ emit activeViewChanged(activeViewContainer());
+}
+
+void DolphinTabPage::slotViewUrlRedirection(const KUrl& oldUrl, const KUrl& newUrl)
+{
+ Q_UNUSED(oldUrl);
+
+ emit activeViewUrlChanged(newUrl);
+}
+
+DolphinViewContainer* DolphinTabPage::createViewContainer(const KUrl& url) const
+{
+ DolphinViewContainer* container = new DolphinViewContainer(url, m_splitter);
+ container->setActive(false);
+
+ const DolphinView* view = container->view();
+ connect(view, SIGNAL(activated()),
+ this, SLOT(slotViewActivated()));
+
+ return container;
+}
diff --git a/dolphin/src/dolphintabpage.h b/dolphin/src/dolphintabpage.h
new file mode 100644
index 0000000..2a406f4
--- /dev/null
+++ b/dolphin/src/dolphintabpage.h
@@ -0,0 +1,168 @@
+/***************************************************************************
+ * Copyright (C) 2014 by Emmanuel Pescosta <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef DOLPHIN_TAB_PAGE_H
+#define DOLPHIN_TAB_PAGE_H
+
+#include <QWidget>
+#include <QPointer>
+#include <KUrl>
+
+class QSplitter;
+class DolphinViewContainer;
+class KFileItemList;
+
+class DolphinTabPage : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit DolphinTabPage(const KUrl& primaryUrl, const KUrl& secondaryUrl = KUrl(), QWidget* parent = 0);
+
+ /**
+ * @return True if primary view is the active view in this tab.
+ */
+ bool primaryViewActive() const;
+
+ /**
+ * @return True if split view is enabled.
+ */
+ bool splitViewEnabled() const;
+
+ /**
+ * Enables or disables the split view mode.
+ *
+ * If \a enabled is true, it creates a secondary view with the url of the primary view.
+ */
+ void setSplitViewEnabled(bool enabled);
+
+ /**
+ * @return The primary view containter.
+ */
+ DolphinViewContainer* primaryViewContainer() const;
+
+ /**
+ * @return The secondary view containter, can be 0 if split view is disabled.
+ */
+ DolphinViewContainer* secondaryViewContainer() const;
+
+ /**
+ * @return DolphinViewContainer of the active view
+ */
+ DolphinViewContainer* activeViewContainer() const;
+
+ /**
+ * Returns the selected items. The list is empty if no item has been
+ * selected.
+ */
+ KFileItemList selectedItems() const;
+
+ /**
+ * Returns the number of selected items (this is faster than
+ * invoking selectedItems().count()).
+ */
+ int selectedItemsCount() const;
+
+ /**
+ * Marks the items indicated by \p urls to get selected after the
+ * directory DolphinView::url() has been loaded. Note that nothing
+ * gets selected if no loading of a directory has been triggered
+ * by DolphinView::setUrl() or DolphinView::reload().
+ */
+ void markUrlsAsSelected(const QList<KUrl>& urls);
+
+ /**
+ * Marks the item indicated by \p url to be scrolled to and as the
+ * current item after directory DolphinView::url() has been loaded.
+ */
+ void markUrlAsCurrent(const KUrl& url);
+
+ /**
+ * Sets the places selector visible, if \a visible is true.
+ * The places selector allows to select the places provided
+ * by the places model passed in the constructor. Per default
+ * the places selector is visible.
+ */
+ void setPlacesSelectorVisible(bool visible);
+
+ /**
+ * Refreshes the views of the main window by recreating them according to
+ * the given Dolphin settings.
+ */
+ void refreshViews();
+
+ /**
+ * Saves all tab related properties (urls, splitter layout, ...).
+ *
+ * @return A byte-array which contains all properties.
+ */
+ QByteArray saveState() const;
+
+ /**
+ * Restores all tab related properties (urls, splitter layout, ...) from
+ * the given \a state.
+ */
+ void restoreState(const QByteArray& state);
+
+ /**
+ * Restores all tab related properties (urls, splitter layout, ...) from
+ * the given \a state.
+ *
+ * @deprecated The first tab state version has no version number, we keep
+ * this method to restore old states (<= Dolphin 4.14.x).
+ */
+ void restoreStateV1(const QByteArray& state);
+
+signals:
+ void activeViewChanged(DolphinViewContainer* viewContainer);
+ void activeViewUrlChanged(const KUrl& url);
+
+private slots:
+ /**
+ * Handles the view activated event.
+ *
+ * It sets the previous active view to inactive, updates the current
+ * active view type and triggers the activeViewChanged event.
+ */
+ void slotViewActivated();
+
+ /**
+ * Handles the view url redirection event.
+ *
+ * It emits the activeViewUrlChanged signal with the url \a newUrl.
+ */
+ void slotViewUrlRedirection(const KUrl& oldUrl, const KUrl& newUrl);
+
+private:
+ /**
+ * Creates a new view container and does the default initialization.
+ */
+ DolphinViewContainer* createViewContainer(const KUrl& url) const;
+
+private:
+ QSplitter* m_splitter;
+
+ QPointer<DolphinViewContainer> m_primaryViewContainer;
+ QPointer<DolphinViewContainer> m_secondaryViewContainer;
+
+ bool m_primaryViewActive;
+ bool m_splitViewEnabled;
+};
+
+#endif // DOLPHIN_TAB_PAGE_H
diff --git a/dolphin/src/dolphintabwidget.cpp b/dolphin/src/dolphintabwidget.cpp
new file mode 100644
index 0000000..c6607e5
--- /dev/null
+++ b/dolphin/src/dolphintabwidget.cpp
@@ -0,0 +1,361 @@
+/***************************************************************************
+ * Copyright (C) 2014 by Emmanuel Pescosta <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "dolphintabwidget.h"
+
+#include "dolphintabbar.h"
+#include "dolphintabpage.h"
+#include "dolphinviewcontainer.h"
+#include "dolphin_generalsettings.h"
+#include "views/draganddrophelper.h"
+
+#include <QApplication>
+#include <KConfigGroup>
+#include <KIcon>
+#include <KRun>
+
+DolphinTabWidget::DolphinTabWidget(QWidget* parent) :
+ QTabWidget(parent),
+ m_placesSelectorVisible(true)
+{
+ connect(this, SIGNAL(tabCloseRequested(int)),
+ this, SLOT(closeTab(int)));
+ connect(this, SIGNAL(currentChanged(int)),
+ this, SLOT(currentTabChanged(int)));
+
+ DolphinTabBar* tabBar = new DolphinTabBar(this);
+ connect(tabBar, SIGNAL(openNewActivatedTab(int)),
+ this, SLOT(openNewActivatedTab(int)));
+ connect(tabBar, SIGNAL(tabDropEvent(int,QDropEvent*)),
+ this, SLOT(tabDropEvent(int,QDropEvent*)));
+ connect(tabBar, SIGNAL(tabDetachRequested(int)),
+ this, SLOT(detachTab(int)));
+ tabBar->hide();
+
+ setTabBar(tabBar);
+ setDocumentMode(true);
+ setElideMode(Qt::ElideRight);
+ setUsesScrollButtons(true);
+}
+
+DolphinTabPage* DolphinTabWidget::currentTabPage() const
+{
+ return tabPageAt(currentIndex());
+}
+
+DolphinTabPage* DolphinTabWidget::tabPageAt(const int index) const
+{
+ return static_cast<DolphinTabPage*>(widget(index));
+}
+
+void DolphinTabWidget::saveProperties(KConfigGroup& group) const
+{
+ const int tabCount = count();
+ group.writeEntry("Tab Count", tabCount);
+ group.writeEntry("Active Tab Index", currentIndex());
+
+ for (int i = 0; i < tabCount; ++i) {
+ const DolphinTabPage* tabPage = tabPageAt(i);
+ group.writeEntry("Tab Data " % QString::number(i), tabPage->saveState());
+ }
+}
+
+void DolphinTabWidget::readProperties(const KConfigGroup& group)
+{
+ const int tabCount = group.readEntry("Tab Count", 0);
+ for (int i = 0; i < tabCount; ++i) {
+ if (i >= count()) {
+ openNewActivatedTab();
+ }
+ if (group.hasKey("Tab Data " % QString::number(i))) {
+ // Tab state created with Dolphin > 4.14.x
+ const QByteArray state = group.readEntry("Tab Data " % QString::number(i), QByteArray());
+ tabPageAt(i)->restoreState(state);
+ } else {
+ // Tab state created with Dolphin <= 4.14.x
+ const QByteArray state = group.readEntry("Tab " % QString::number(i), QByteArray());
+ tabPageAt(i)->restoreStateV1(state);
+ }
+ }
+
+ const int index = group.readEntry("Active Tab Index", 0);
+ setCurrentIndex(index);
+}
+
+void DolphinTabWidget::refreshViews()
+{
+ const int tabCount = count();
+ for (int i = 0; i < tabCount; ++i) {
+ tabPageAt(i)->refreshViews();
+ }
+}
+
+void DolphinTabWidget::openNewActivatedTab()
+{
+ const DolphinViewContainer* oldActiveViewContainer = currentTabPage()->activeViewContainer();
+ Q_ASSERT(oldActiveViewContainer);
+
+ const bool isUrlEditable = oldActiveViewContainer->urlNavigator()->isUrlEditable();
+
+ openNewActivatedTab(oldActiveViewContainer->url());
+
+ DolphinViewContainer* newActiveViewContainer = currentTabPage()->activeViewContainer();
+ Q_ASSERT(newActiveViewContainer);
+
+ // The URL navigator of the new tab should have the same editable state
+ // as the current tab
+ KUrlNavigator* navigator = newActiveViewContainer->urlNavigator();
+ navigator->setUrlEditable(isUrlEditable);
+
+ if (isUrlEditable) {
+ // If a new tab is opened and the URL is editable, assure that
+ // the user can edit the URL without manually setting the focus
+ navigator->setFocus();
+ }
+}
+
+void DolphinTabWidget::openNewActivatedTab(const KUrl& primaryUrl, const KUrl& secondaryUrl)
+{
+ openNewTab(primaryUrl, secondaryUrl);
+ setCurrentIndex(count() - 1);
+}
+
+void DolphinTabWidget::openNewTab(const KUrl& primaryUrl, const KUrl& secondaryUrl)
+{
+ QWidget* focusWidget = QApplication::focusWidget();
+
+ DolphinTabPage* tabPage = new DolphinTabPage(primaryUrl, secondaryUrl, this);
+ tabPage->setPlacesSelectorVisible(m_placesSelectorVisible);
+ connect(tabPage, SIGNAL(activeViewChanged(DolphinViewContainer*)),
+ this, SIGNAL(activeViewChanged(DolphinViewContainer*)));
+ connect(tabPage, SIGNAL(activeViewUrlChanged(KUrl)),
+ this, SLOT(tabUrlChanged(KUrl)));
+ addTab(tabPage, KIcon(KMimeType::iconNameForUrl(primaryUrl)), tabName(primaryUrl));
+
+ if (focusWidget) {
+ // The DolphinViewContainer grabbed the keyboard focus. As the tab is opened
+ // in background, assure that the previous focused widget gets the focus back.
+ focusWidget->setFocus();
+ }
+}
+
+void DolphinTabWidget::openDirectories(const QList<KUrl>& dirs)
+{
+ const bool hasSplitView = GeneralSettings::splitView();
+
+ // Open each directory inside a new tab. If the "split view" option has been enabled,
+ // always show two directories within one tab.
+ QList<KUrl>::const_iterator it = dirs.constBegin();
+ while (it != dirs.constEnd()) {
+ const KUrl& primaryUrl = *(it++);
+ if (hasSplitView && (it != dirs.constEnd())) {
+ const KUrl& secondaryUrl = *(it++);
+ openNewTab(primaryUrl, secondaryUrl);
+ } else {
+ openNewTab(primaryUrl);
+ }
+ }
+}
+
+void DolphinTabWidget::openFiles(const QList<KUrl>& files)
+{
+ if (files.isEmpty()) {
+ return;
+ }
+
+ // Get all distinct directories from 'files' and open a tab
+ // for each directory. If the "split view" option is enabled, two
+ // directories are shown inside one tab (see openDirectories()).
+ QList<KUrl> dirs;
+ foreach (const KUrl& url, files) {
+ const KUrl dir(url.directory());
+ if (!dirs.contains(dir)) {
+ dirs.append(dir);
+ }
+ }
+
+ const int oldTabCount = count();
+ openDirectories(dirs);
+ const int tabCount = count();
+
+ // Select the files. Although the files can be split between several
+ // tabs, there is no need to split 'files' accordingly, as
+ // the DolphinView will just ignore invalid selections.
+ for (int i = oldTabCount; i < tabCount; ++i) {
+ DolphinTabPage* tabPage = tabPageAt(i);
+ tabPage->markUrlsAsSelected(files);
+ tabPage->markUrlAsCurrent(files.first());
+ }
+}
+
+void DolphinTabWidget::closeTab()
+{
+ closeTab(currentIndex());
+}
+
+void DolphinTabWidget::closeTab(const int index)
+{
+ Q_ASSERT(index >= 0);
+ Q_ASSERT(index < count());
+
+ if (count() < 2) {
+ // Never close the last tab.
+ return;
+ }
+
+ DolphinTabPage* tabPage = tabPageAt(index);
+ emit rememberClosedTab(tabPage->activeViewContainer()->url(), tabPage->saveState());
+
+ removeTab(index);
+ tabPage->deleteLater();
+}
+
+void DolphinTabWidget::activateNextTab()
+{
+ const int index = currentIndex() + 1;
+ setCurrentIndex(index < count() ? index : 0);
+}
+
+void DolphinTabWidget::activatePrevTab()
+{
+ const int index = currentIndex() - 1;
+ setCurrentIndex(index >= 0 ? index : (count() - 1));
+}
+
+void DolphinTabWidget::slotPlacesPanelVisibilityChanged(bool visible)
+{
+ // The places-selector from the URL navigator should only be shown
+ // if the places dock is invisible
+ m_placesSelectorVisible = !visible;
+
+ const int tabCount = count();
+ for (int i = 0; i < tabCount; ++i) {
+ DolphinTabPage* tabPage = tabPageAt(i);
+ tabPage->setPlacesSelectorVisible(m_placesSelectorVisible);
+ }
+}
+
+void DolphinTabWidget::restoreClosedTab(const QByteArray& state)
+{
+ openNewActivatedTab();
+ currentTabPage()->restoreState(state);
+}
+
+void DolphinTabWidget::detachTab(int index)
+{
+ Q_ASSERT(index >= 0);
+
+ const QString separator(QLatin1Char(' '));
+ QString command = QLatin1String("dolphin");
+
+ const DolphinTabPage* tabPage = tabPageAt(index);
+ command += separator + tabPage->primaryViewContainer()->url().url();
+ if (tabPage->splitViewEnabled()) {
+ command += separator + tabPage->secondaryViewContainer()->url().url();
+ command += separator + QLatin1String("-split");
+ }
+
+ KRun::runCommand(command, this);
+
+ closeTab(index);
+}
+
+void DolphinTabWidget::openNewActivatedTab(int index)
+{
+ Q_ASSERT(index >= 0);
+ const DolphinTabPage* tabPage = tabPageAt(index);
+ openNewActivatedTab(tabPage->activeViewContainer()->url());
+}
+
+void DolphinTabWidget::tabDropEvent(int index, QDropEvent* event)
+{
+ if (index >= 0) {
+ const DolphinView* view = tabPageAt(index)->activeViewContainer()->view();
+
+ QString error;
+ DragAndDropHelper::dropUrls(view->rootItem(), view->url(), event, error);
+ if (!error.isEmpty()) {
+ currentTabPage()->activeViewContainer()->showMessage(error, DolphinViewContainer::Error);
+ }
+ }
+}
+
+void DolphinTabWidget::tabUrlChanged(const KUrl& url)
+{
+ const int index = indexOf(qobject_cast<QWidget*>(sender()));
+ if (index >= 0) {
+ tabBar()->setTabText(index, tabName(url));
+ tabBar()->setTabIcon(index, KIcon(KMimeType::iconNameForUrl(url)));
+
+ // Emit the currentUrlChanged signal if the url of the current tab has been changed.
+ if (index == currentIndex()) {
+ emit currentUrlChanged(url);
+ }
+ }
+}
+
+void DolphinTabWidget::currentTabChanged(int index)
+{
+ DolphinViewContainer* viewContainer = tabPageAt(index)->activeViewContainer();
+ emit activeViewChanged(viewContainer);
+ emit currentUrlChanged(viewContainer->url());
+ viewContainer->view()->setFocus();
+}
+
+void DolphinTabWidget::tabInserted(int index)
+{
+ QTabWidget::tabInserted(index);
+
+ if (count() > 1) {
+ tabBar()->show();
+ }
+
+ emit tabCountChanged(count());
+}
+
+void DolphinTabWidget::tabRemoved(int index)
+{
+ QTabWidget::tabRemoved(index);
+
+ // If only one tab is left, then remove the tab entry so that
+ // closing the last tab is not possible.
+ if (count() < 2) {
+ tabBar()->hide();
+ }
+
+ emit tabCountChanged(count());
+}
+
+QString DolphinTabWidget::tabName(const KUrl& url) const
+{
+ QString name;
+ if (url.equals(KUrl("file:///"))) {
+ name = '/';
+ } else {
+ name = url.fileName();
+ if (name.isEmpty()) {
+ name = url.protocol();
+ } else {
+ // Make sure that a '&' inside the directory name is displayed correctly
+ // and not misinterpreted as a keyboard shortcut in QTabBar::setTabText()
+ name.replace('&', "&&");
+ }
+ }
+ return name;
+}
diff --git a/dolphin/src/dolphintabwidget.h b/dolphin/src/dolphintabwidget.h
new file mode 100644
index 0000000..98bcd98
--- /dev/null
+++ b/dolphin/src/dolphintabwidget.h
@@ -0,0 +1,190 @@
+/***************************************************************************
+ * Copyright (C) 2014 by Emmanuel Pescosta <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef DOLPHIN_TAB_WIDGET_H
+#define DOLPHIN_TAB_WIDGET_H
+
+#include <QTabWidget>
+#include <KUrl>
+
+class DolphinViewContainer;
+class DolphinTabPage;
+class KConfigGroup;
+
+class DolphinTabWidget : public QTabWidget
+{
+ Q_OBJECT
+
+public:
+ explicit DolphinTabWidget(QWidget* parent);
+
+ /**
+ * @return Tab page at the current index (can be 0 if tabs count is smaller than 1)
+ */
+ DolphinTabPage* currentTabPage() const;
+
+ /**
+ * @return Tab page at the given \a index (can be 0 if the index is out-of-range)
+ */
+ DolphinTabPage* tabPageAt(const int index) const;
+
+ void saveProperties(KConfigGroup& group) const;
+ void readProperties(const KConfigGroup& group);
+
+ /**
+ * Refreshes the views of the main window by recreating them according to
+ * the given Dolphin settings.
+ */
+ void refreshViews();
+
+signals:
+ /**
+ * Is emitted when the active view has been changed, by changing the current
+ * tab or by activating another view when split view is enabled in the current
+ * tab.
+ */
+ void activeViewChanged(DolphinViewContainer* viewContainer);
+
+ /**
+ * Is emitted when the number of open tabs has changed (e.g. by opening or
+ * closing a tab)
+ */
+ void tabCountChanged(int count);
+
+ /**
+ * Is emitted when a tab has been closed.
+ */
+ void rememberClosedTab(const KUrl& url, const QByteArray& state);
+
+ /**
+ * Is emitted when the url of the current tab has been changed. This signal
+ * is also emitted when the active view has been changed.
+ */
+ void currentUrlChanged(const KUrl& url);
+
+public slots:
+ /**
+ * Opens a new view with the current URL that is part of a tab and activates
+ * the tab.
+ */
+ void openNewActivatedTab();
+
+ /**
+ * Opens a new tab showing the URL \a primaryUrl and the optional URL
+ * \a secondaryUrl and activates the tab.
+ */
+ void openNewActivatedTab(const KUrl& primaryUrl, const KUrl& secondaryUrl = KUrl());
+
+ /**
+ * Opens a new tab in the background showing the URL \a primaryUrl and the
+ * optional URL \a secondaryUrl.
+ */
+ void openNewTab(const KUrl& primaryUrl, const KUrl& secondaryUrl = KUrl());
+
+ /**
+ * Opens each directory in \p dirs in a separate tab. If the "split view"
+ * option is enabled, 2 directories are collected within one tab.
+ */
+ void openDirectories(const QList<KUrl>& dirs);
+
+ /**
+ * Opens the directory which contains the files \p files
+ * and selects all files (implements the --select option
+ * of Dolphin).
+ */
+ void openFiles(const QList<KUrl>& files);
+
+ /**
+ * Closes the currently active tab.
+ */
+ void closeTab();
+
+ /**
+ * Closes the tab with the index \a index and activates the tab with index - 1.
+ */
+ void closeTab(const int index);
+
+ /**
+ * Activates the next tab in the tab bar.
+ * If the current active tab is the last tab, it activates the first tab.
+ */
+ void activateNextTab();
+
+ /**
+ * Activates the previous tab in the tab bar.
+ * If the current active tab is the first tab, it activates the last tab.
+ */
+ void activatePrevTab();
+
+ /**
+ * Is invoked if the Places panel got visible/invisible and takes care
+ * that the places-selector of all views is only shown if the Places panel
+ * is invisible.
+ */
+ void slotPlacesPanelVisibilityChanged(bool visible);
+
+ /**
+ * Is called when the user wants to reopen a previously closed tab from
+ * the recent tabs menu.
+ */
+ void restoreClosedTab(const QByteArray& state);
+
+private slots:
+ /**
+ * Opens the tab with the index \a index in a new Dolphin instance and closes
+ * this tab.
+ */
+ void detachTab(int index);
+
+ /**
+ * Opens a new tab showing the url from tab at the given \a index and
+ * activates the tab.
+ */
+ void openNewActivatedTab(int index);
+
+ /**
+ * Is connected to the KTabBar signal receivedDropEvent.
+ * Allows dragging and dropping files onto tabs.
+ */
+ void tabDropEvent(int tab, QDropEvent* event);
+
+ /**
+ * The active view url of a tab has been changed so update the text and the
+ * icon of the corresponding tab.
+ */
+ void tabUrlChanged(const KUrl& url);
+
+ void currentTabChanged(int index);
+
+protected:
+ virtual void tabInserted(int index);
+ virtual void tabRemoved(int index);
+
+private:
+ /**
+ * Returns the name of the tab for the URL \a url.
+ */
+ QString tabName(const KUrl& url) const;
+
+private:
+ /** Caches the (negated) places panel visibility */
+ bool m_placesSelectorVisible;
+};
+
+#endif \ No newline at end of file
diff --git a/dolphin/src/dolphinui.rc b/dolphin/src/dolphinui.rc
new file mode 100644
index 0000000..f197af4
--- /dev/null
+++ b/dolphin/src/dolphinui.rc
@@ -0,0 +1,116 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+<kpartgui name="dolphin" version="14">
+ <MenuBar>
+ <Menu name="file">
+ <Action name="new_menu" />
+ <Action name="new_window" />
+ <Action name="new_tab" />
+ <Action name="close_tab" />
+ <Action name="undo_close_tab" />
+ <Separator/>
+ <Action name="rename" />
+ <Action name="move_to_trash" />
+ <Action name="delete" />
+ <Separator/>
+ <Action name="properties" />
+ </Menu>
+ <Menu name="edit">
+ <Action name="select_all" />
+ <Action name="invert_selection" />
+ </Menu>
+ <Menu name="view">
+ <Action name="view_mode" />
+ <Action name="sort" />
+ <Action name="additional_info" />
+ <Action name="show_preview" />
+ <Action name="show_in_groups" />
+ <Action name="show_hidden_files" />
+ <Separator/>
+ <Action name="split_view" />
+ <Action name="reload" />
+ <Action name="stop" />
+ <Separator/>
+ <Action name="panels" />
+ <Menu name="location_bar">
+ <text context="@title:menu">Location Bar</text>
+ <Action name="editable_location" />
+ <Action name="replace_location" />
+ </Menu>
+ <Separator/>
+ <Action name="view_properties" />
+ </Menu>
+ <Menu name="go">
+ <Action name="closed_tabs" />
+ </Menu>
+ <Menu name="tools">
+ <Action name="show_filter_bar" />
+ <Action name="open_terminal" />
+ <Action name="compare_files" />
+ <Action name="change_remote_encoding" />
+ </Menu>
+ </MenuBar>
+ <State name="new_file" >
+ <disable>
+ <Action name="edit_undo" />
+ <Action name="edit_redo" />
+ <Action name="edit_cut" />
+ <Action name="edit_copy" />
+ <Action name="rename" />
+ <Action name="move_to_trash" />
+ <Action name="delete" />
+ <Action name="invert_selection" />
+ <Separator/>
+ <Action name="go_back" />
+ <Action name="go_forward" />
+ </disable>
+ </State>
+ <State name="has_selection" >
+ <enable>
+ <Action name="edit_cut" />
+ <Action name="edit_copy" />
+ <Action name="rename" />
+ <Action name="move_to_trash" />
+ <Action name="delete" />
+ <Action name="invert_selection" />
+ </enable>
+ </State>
+ <State name="has_no_selection" >
+ <disable>
+ <Action name="edit_cut" />
+ <Action name="edit_copy" />
+ <Action name="rename" />
+ <Action name="move_to_trash" />
+ <Action name="delete" />
+ <Action name="delete_shortcut" />
+ <Action name="invert_selection" />
+ </disable>
+ </State>
+ <ToolBar noMerge="1" name="mainToolBar" >
+ <text context="@title:menu">Main Toolbar</text>
+ <Action name="go_back" />
+ <Action name="go_forward" />
+ <Separator name="separator_1" />
+ <Action name="icons" />
+ <Action name="compact" />
+ <Action name="details" />
+ <Separator name="separator_0" />
+ <Action name="edit_find"/>
+ <Action name="show_preview" />
+ <Action name="split_view" />
+ </ToolBar>
+ <ActionProperties scheme="Default">
+ <Action priority="0" name="go_back"/>
+ <Action priority="0" name="go_forward"/>
+ <Action priority="0" name="go_up"/>
+ <Action priority="0" name="go_home"/>
+ <Action priority="0" name="stop"/>
+ <Action priority="0" name="icons"/>
+ <Action priority="0" name="compact"/>
+ <Action priority="0" name="details"/>
+ <Action priority="0" name="view_zoom_in"/>
+ <Action priority="0" name="view_zoom_out"/>
+ <Action priority="0" name="edit_cut"/>
+ <Action priority="0" name="edit_copy"/>
+ <Action priority="0" name="edit_paste"/>
+ </ActionProperties>
+</kpartgui>
diff --git a/dolphin/src/dolphinviewcontainer.cpp b/dolphin/src/dolphinviewcontainer.cpp
new file mode 100644
index 0000000..a11ba42
--- /dev/null
+++ b/dolphin/src/dolphinviewcontainer.cpp
@@ -0,0 +1,718 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "dolphinviewcontainer.h"
+#include <KProtocolManager>
+
+#include <QApplication>
+#include <QKeyEvent>
+#include <QItemSelection>
+#include <QBoxLayout>
+#include <QTimer>
+#include <QScrollBar>
+
+#include <KDesktopFile>
+#include <KFileItemDelegate>
+#include <KFileItemActions>
+#include <KFilePlacesModel>
+#include <KLocale>
+#include <KIconEffect>
+#include <KIO/NetAccess>
+#include <KIO/PreviewJob>
+#include <KMessageWidget>
+#include <KNewFileMenu>
+#include <konqmimedata.h>
+#include <konq_operations.h>
+#include <KShell>
+#include <KUrl>
+#include <KUrlComboBox>
+#include <KUrlNavigator>
+#include <KRun>
+
+#ifdef KActivities_FOUND
+#include <KActivities/ResourceInstance>
+#endif
+
+#include "dolphin_generalsettings.h"
+#include "filterbar/filterbar.h"
+#include "search/dolphinsearchbox.h"
+#include "statusbar/dolphinstatusbar.h"
+#include "views/draganddrophelper.h"
+#include "views/viewmodecontroller.h"
+#include "views/viewproperties.h"
+
+DolphinViewContainer::DolphinViewContainer(const KUrl& url, QWidget* parent) :
+ QWidget(parent),
+ m_topLayout(0),
+ m_urlNavigator(0),
+ m_searchBox(0),
+ m_messageWidget(0),
+ m_view(0),
+ m_filterBar(0),
+ m_statusBar(0),
+ m_statusBarTimer(0),
+ m_statusBarTimestamp(),
+ m_autoGrabFocus(true),
+ m_dropDestination(),
+ m_dropEvent(0)
+#ifdef KActivities_FOUND
+ , m_activityResourceInstance(0)
+#endif
+{
+ hide();
+
+ m_topLayout = new QVBoxLayout(this);
+ m_topLayout->setSpacing(0);
+ m_topLayout->setMargin(0);
+
+ m_urlNavigator = new KUrlNavigator(new KFilePlacesModel(this), url, this);
+ connect(m_urlNavigator, SIGNAL(urlsDropped(KUrl,QDropEvent*)),
+ this, SLOT(dropUrls(KUrl,QDropEvent*)));
+ connect(m_urlNavigator, SIGNAL(activated()),
+ this, SLOT(activate()));
+ connect(m_urlNavigator->editor(), SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
+ this, SLOT(saveUrlCompletionMode(KGlobalSettings::Completion)));
+
+ const GeneralSettings* settings = GeneralSettings::self();
+ m_urlNavigator->setUrlEditable(settings->editableUrl());
+ m_urlNavigator->setShowFullPath(settings->showFullPath());
+ m_urlNavigator->setHomeUrl(KUrl(settings->homeUrl()));
+ KUrlComboBox* editor = m_urlNavigator->editor();
+ editor->setCompletionMode(KGlobalSettings::Completion(settings->urlCompletionMode()));
+
+ m_searchBox = new DolphinSearchBox(this);
+ m_searchBox->hide();
+ connect(m_searchBox, SIGNAL(activated()), this, SLOT(activate()));
+ connect(m_searchBox, SIGNAL(closeRequest()), this, SLOT(closeSearchBox()));
+ connect(m_searchBox, SIGNAL(searchRequest()), this, SLOT(startSearching()));
+ connect(m_searchBox, SIGNAL(returnPressed(QString)), this, SLOT(requestFocus()));
+
+ m_messageWidget = new KMessageWidget(this);
+ m_messageWidget->setCloseButtonVisible(true);
+ m_messageWidget->hide();
+
+ m_view = new DolphinView(url, this);
+ connect(m_view, SIGNAL(urlChanged(KUrl)), m_urlNavigator, SLOT(setUrl(KUrl)));
+ connect(m_view, SIGNAL(urlChanged(KUrl)), m_messageWidget, SLOT(hide()));
+ connect(m_view, SIGNAL(directoryLoadingCompleted()), m_messageWidget, SLOT(hide()));
+ connect(m_view, SIGNAL(writeStateChanged(bool)), this, SIGNAL(writeStateChanged(bool)));
+ connect(m_view, SIGNAL(requestItemInfo(KFileItem)), this, SLOT(showItemInfo(KFileItem)));
+ connect(m_view, SIGNAL(itemActivated(KFileItem)), this, SLOT(slotItemActivated(KFileItem)));
+ connect(m_view, SIGNAL(itemsActivated(KFileItemList)), this, SLOT(slotItemsActivated(KFileItemList)));
+ connect(m_view, SIGNAL(redirection(KUrl,KUrl)), this, SLOT(redirect(KUrl,KUrl)));
+ connect(m_view, SIGNAL(directoryLoadingStarted()), this, SLOT(slotDirectoryLoadingStarted()));
+ connect(m_view, SIGNAL(directoryLoadingCompleted()), this, SLOT(slotDirectoryLoadingCompleted()));
+ connect(m_view, SIGNAL(directoryLoadingCanceled()), this, SLOT(slotDirectoryLoadingCanceled()));
+ connect(m_view, SIGNAL(itemCountChanged()), this, SLOT(delayedStatusBarUpdate()));
+ connect(m_view, SIGNAL(directoryLoadingProgress(int)), this, SLOT(updateDirectoryLoadingProgress(int)));
+ connect(m_view, SIGNAL(directorySortingProgress(int)), this, SLOT(updateDirectorySortingProgress(int)));
+ connect(m_view, SIGNAL(selectionChanged(KFileItemList)), this, SLOT(delayedStatusBarUpdate()));
+ connect(m_view, SIGNAL(urlAboutToBeChanged(KUrl)), this, SLOT(slotViewUrlAboutToBeChanged(KUrl)));
+ connect(m_view, SIGNAL(errorMessage(QString)), this, SLOT(showErrorMessage(QString)));
+ connect(m_view, SIGNAL(urlIsFileError(KUrl)), this, SLOT(slotUrlIsFileError(KUrl)));
+ connect(m_view, SIGNAL(activated()), this, SLOT(activate()));
+
+ connect(m_urlNavigator, SIGNAL(urlAboutToBeChanged(KUrl)),
+ this, SLOT(slotUrlNavigatorLocationAboutToBeChanged(KUrl)));
+ connect(m_urlNavigator, SIGNAL(urlChanged(KUrl)),
+ this, SLOT(slotUrlNavigatorLocationChanged(KUrl)));
+ connect(m_urlNavigator, SIGNAL(historyChanged()),
+ this, SLOT(slotHistoryChanged()));
+ connect(m_urlNavigator, SIGNAL(returnPressed()),
+ this, SLOT(slotReturnPressed()));
+
+ // Initialize status bar
+ m_statusBar = new DolphinStatusBar(this);
+ m_statusBar->setUrl(m_view->url());
+ m_statusBar->setZoomLevel(m_view->zoomLevel());
+ connect(m_view, SIGNAL(urlChanged(KUrl)), m_statusBar, SLOT(setUrl(KUrl)));
+ connect(m_view, SIGNAL(zoomLevelChanged(int,int)), m_statusBar, SLOT(setZoomLevel(int)));
+ connect(m_view, SIGNAL(infoMessage(QString)), m_statusBar, SLOT(setText(QString)));
+ connect(m_view, SIGNAL(operationCompletedMessage(QString)), m_statusBar, SLOT(setText(QString)));
+ connect(m_statusBar, SIGNAL(stopPressed()), this, SLOT(stopDirectoryLoading()));
+ connect(m_statusBar, SIGNAL(zoomLevelChanged(int)), this, SLOT(slotStatusBarZoomLevelChanged(int)));
+
+ m_statusBarTimer = new QTimer(this);
+ m_statusBarTimer->setSingleShot(true);
+ m_statusBarTimer->setInterval(300);
+ connect(m_statusBarTimer, SIGNAL(timeout()), this, SLOT(updateStatusBar()));
+
+ KIO::FileUndoManager* undoManager = KIO::FileUndoManager::self();
+ connect(undoManager, SIGNAL(jobRecordingFinished(CommandType)),
+ this, SLOT(delayedStatusBarUpdate()));
+
+ // Initialize filter bar
+ m_filterBar = new FilterBar(this);
+ m_filterBar->setVisible(settings->filterBar());
+ connect(m_filterBar, SIGNAL(filterChanged(QString)),
+ this, SLOT(setNameFilter(QString)));
+ connect(m_filterBar, SIGNAL(closeRequest()),
+ this, SLOT(closeFilterBar()));
+ connect(m_filterBar, SIGNAL(focusViewRequest()),
+ this, SLOT(requestFocus()));
+ connect(m_view, SIGNAL(urlChanged(KUrl)),
+ m_filterBar, SLOT(slotUrlChanged()));
+
+ m_topLayout->addWidget(m_urlNavigator);
+ m_topLayout->addWidget(m_searchBox);
+ m_topLayout->addWidget(m_messageWidget);
+ m_topLayout->addWidget(m_view);
+ m_topLayout->addWidget(m_filterBar);
+ m_topLayout->addWidget(m_statusBar);
+
+ setSearchModeEnabled(isSearchUrl(url));
+
+ // Initialize kactivities resource instance
+
+ #ifdef KActivities_FOUND
+ m_activityResourceInstance = new KActivities::ResourceInstance(
+ window()->winId(), url);
+ m_activityResourceInstance->setParent(this);
+ #endif
+}
+
+DolphinViewContainer::~DolphinViewContainer()
+{
+}
+
+KUrl DolphinViewContainer::url() const
+{
+ return m_view->url();
+}
+
+void DolphinViewContainer::setActive(bool active)
+{
+ m_searchBox->setActive(active);
+ m_urlNavigator->setActive(active);
+ m_view->setActive(active);
+
+ #ifdef KActivities_FOUND
+ if (active) {
+ m_activityResourceInstance->notifyFocusedIn();
+ } else {
+ m_activityResourceInstance->notifyFocusedOut();
+ }
+ #endif
+}
+
+bool DolphinViewContainer::isActive() const
+{
+ Q_ASSERT(m_view->isActive() == m_urlNavigator->isActive());
+ return m_view->isActive();
+}
+
+void DolphinViewContainer::setAutoGrabFocus(bool grab)
+{
+ m_autoGrabFocus = grab;
+}
+
+bool DolphinViewContainer::autoGrabFocus() const
+{
+ return m_autoGrabFocus;
+}
+
+const DolphinStatusBar* DolphinViewContainer::statusBar() const
+{
+ return m_statusBar;
+}
+
+DolphinStatusBar* DolphinViewContainer::statusBar()
+{
+ return m_statusBar;
+}
+
+const KUrlNavigator* DolphinViewContainer::urlNavigator() const
+{
+ return m_urlNavigator;
+}
+
+KUrlNavigator* DolphinViewContainer::urlNavigator()
+{
+ return m_urlNavigator;
+}
+
+const DolphinView* DolphinViewContainer::view() const
+{
+ return m_view;
+}
+
+DolphinView* DolphinViewContainer::view()
+{
+ return m_view;
+}
+
+void DolphinViewContainer::showMessage(const QString& msg, MessageType type)
+{
+ if (msg.isEmpty()) {
+ return;
+ }
+
+ m_messageWidget->setText(msg);
+
+ switch (type) {
+ case Information: m_messageWidget->setMessageType(KMessageWidget::Information); break;
+ case Warning: m_messageWidget->setMessageType(KMessageWidget::Warning); break;
+ case Error: m_messageWidget->setMessageType(KMessageWidget::Error); break;
+ default:
+ Q_ASSERT(false);
+ break;
+ }
+
+ m_messageWidget->setWordWrap(false);
+ const int unwrappedWidth = m_messageWidget->sizeHint().width();
+ m_messageWidget->setWordWrap(unwrappedWidth > size().width());
+
+ if (m_messageWidget->isVisible()) {
+ m_messageWidget->hide();
+ }
+ m_messageWidget->animatedShow();
+}
+
+void DolphinViewContainer::readSettings()
+{
+ if (GeneralSettings::modifiedStartupSettings()) {
+ // The startup settings should only get applied if they have been
+ // modified by the user. Otherwise keep the (possibly) different current
+ // settings of the URL navigator and the filterbar.
+ m_urlNavigator->setUrlEditable(GeneralSettings::editableUrl());
+ m_urlNavigator->setShowFullPath(GeneralSettings::showFullPath());
+ m_urlNavigator->setHomeUrl(KUrl(GeneralSettings::homeUrl()));
+ setFilterBarVisible(GeneralSettings::filterBar());
+ }
+
+ m_view->readSettings();
+ m_statusBar->readSettings();
+}
+
+bool DolphinViewContainer::isFilterBarVisible() const
+{
+ return m_filterBar->isVisible();
+}
+
+void DolphinViewContainer::setSearchModeEnabled(bool enabled)
+{
+ if (enabled == isSearchModeEnabled()) {
+ if (enabled && !m_searchBox->hasFocus()) {
+ m_searchBox->setFocus();
+ m_searchBox->selectAll();
+ }
+ return;
+ }
+
+ m_searchBox->setVisible(enabled);
+ m_urlNavigator->setVisible(!enabled);
+
+ if (enabled) {
+ const KUrl& locationUrl = m_urlNavigator->locationUrl();
+ m_searchBox->fromSearchUrl(locationUrl);
+ } else {
+ m_view->setViewPropertiesContext(QString());
+
+ // Restore the URL for the URL navigator. If Dolphin has been
+ // started with a search-URL, the home URL is used as fallback.
+ KUrl url = m_searchBox->searchPath();
+ if (url.isEmpty() || !url.isValid() || isSearchUrl(url)) {
+ url = GeneralSettings::self()->homeUrl();
+ }
+ m_urlNavigator->setLocationUrl(url);
+ }
+}
+
+bool DolphinViewContainer::isSearchModeEnabled() const
+{
+ return m_searchBox->isVisible();
+}
+
+QString DolphinViewContainer::placesText() const
+{
+ QString text;
+
+ if (isSearchModeEnabled()) {
+ text = m_searchBox->searchPath().fileName() + QLatin1String(": ") + m_searchBox->text();
+ } else {
+ text = url().fileName();
+ if (text.isEmpty()) {
+ text = url().host();
+ }
+ }
+
+ return text;
+}
+
+void DolphinViewContainer::setUrl(const KUrl& newUrl)
+{
+ if (newUrl != m_urlNavigator->locationUrl()) {
+ m_urlNavigator->setLocationUrl(newUrl);
+ }
+
+ #ifdef KActivities_FOUND
+ m_activityResourceInstance->setUri(newUrl);
+ #endif
+}
+
+void DolphinViewContainer::setFilterBarVisible(bool visible)
+{
+ Q_ASSERT(m_filterBar);
+ if (visible) {
+ m_filterBar->show();
+ m_filterBar->setFocus();
+ m_filterBar->selectAll();
+ } else {
+ closeFilterBar();
+ }
+}
+
+void DolphinViewContainer::delayedStatusBarUpdate()
+{
+ if (m_statusBarTimer->isActive() && (m_statusBarTimestamp.elapsed() > 2000)) {
+ // No update of the statusbar has been done during the last 2 seconds,
+ // although an update has been requested. Trigger an immediate update.
+ m_statusBarTimer->stop();
+ updateStatusBar();
+ } else {
+ // Invoke updateStatusBar() with a small delay. This assures that
+ // when a lot of delayedStatusBarUpdates() are done in a short time,
+ // no bottleneck is given.
+ m_statusBarTimer->start();
+ }
+}
+
+void DolphinViewContainer::updateStatusBar()
+{
+ m_statusBarTimestamp.start();
+
+ const QString text = m_view->statusBarText();
+ m_statusBar->setDefaultText(text);
+ m_statusBar->resetToDefaultText();
+}
+
+void DolphinViewContainer::updateDirectoryLoadingProgress(int percent)
+{
+ if (m_statusBar->progressText().isEmpty()) {
+ m_statusBar->setProgressText(i18nc("@info:progress", "Loading folder..."));
+ }
+ m_statusBar->setProgress(percent);
+}
+
+void DolphinViewContainer::updateDirectorySortingProgress(int percent)
+{
+ if (m_statusBar->progressText().isEmpty()) {
+ m_statusBar->setProgressText(i18nc("@info:progress", "Sorting..."));
+ }
+ m_statusBar->setProgress(percent);
+}
+
+void DolphinViewContainer::slotDirectoryLoadingStarted()
+{
+ if (isSearchUrl(url())) {
+ // Search KIO-slaves usually don't provide any progress information. Give
+ // a hint to the user that a searching is done:
+ updateStatusBar();
+ m_statusBar->setProgressText(i18nc("@info", "Searching..."));
+ m_statusBar->setProgress(-1);
+ } else {
+ // Trigger an undetermined progress indication. The progress
+ // information in percent will be triggered by the percent() signal
+ // of the directory lister later.
+ updateDirectoryLoadingProgress(-1);
+ }
+}
+
+void DolphinViewContainer::slotDirectoryLoadingCompleted()
+{
+ if (!m_statusBar->progressText().isEmpty()) {
+ m_statusBar->setProgressText(QString());
+ m_statusBar->setProgress(100);
+ }
+
+ if (isSearchUrl(url()) && m_view->itemsCount() == 0) {
+ // The dir lister has been completed on a Baloo-URI and no items have been found. Instead
+ // of showing the default status bar information ("0 items") a more helpful information is given:
+ m_statusBar->setText(i18nc("@info:status", "No items found."));
+ } else {
+ updateStatusBar();
+ }
+}
+
+void DolphinViewContainer::slotDirectoryLoadingCanceled()
+{
+ if (!m_statusBar->progressText().isEmpty()) {
+ m_statusBar->setProgressText(QString());
+ m_statusBar->setProgress(100);
+ }
+
+ m_statusBar->setText(QString());
+}
+
+void DolphinViewContainer::slotUrlIsFileError(const KUrl& url)
+{
+ const KFileItem item(KFileItem::Unknown, KFileItem::Unknown, url);
+
+ // Find out if the file can be opened in the view (for example, this is the
+ // case if the file is an archive). The mime type must be known for that.
+ item.determineMimeType();
+ const KUrl& folderUrl = DolphinView::openItemAsFolderUrl(item, true);
+ if (!folderUrl.isEmpty()) {
+ m_view->setUrl(folderUrl);
+ } else {
+ slotItemActivated(item);
+ }
+}
+
+void DolphinViewContainer::slotItemActivated(const KFileItem& item)
+{
+ // It is possible to activate items on inactive views by
+ // drag & drop operations. Assure that activating an item always
+ // results in an active view.
+ m_view->setActive(true);
+
+ const KUrl& url = DolphinView::openItemAsFolderUrl(item, GeneralSettings::browseThroughArchives());
+ if (!url.isEmpty()) {
+ m_view->setUrl(url);
+ return;
+ }
+
+ item.run();
+}
+
+void DolphinViewContainer::slotItemsActivated(const KFileItemList& items)
+{
+ Q_ASSERT(items.count() >= 2);
+
+ KFileItemActions fileItemActions(this);
+ fileItemActions.runPreferredApplications(items, QString());
+}
+
+void DolphinViewContainer::showItemInfo(const KFileItem& item)
+{
+ if (item.isNull()) {
+ m_statusBar->resetToDefaultText();
+ } else {
+ m_statusBar->setText(item.getStatusBarInfo());
+ }
+}
+
+void DolphinViewContainer::closeFilterBar()
+{
+ m_filterBar->closeFilterBar();
+ m_view->setFocus();
+ emit showFilterBarChanged(false);
+}
+
+void DolphinViewContainer::setNameFilter(const QString& nameFilter)
+{
+ m_view->setNameFilter(nameFilter);
+ delayedStatusBarUpdate();
+}
+
+void DolphinViewContainer::activate()
+{
+ setActive(true);
+}
+
+void DolphinViewContainer::slotViewUrlAboutToBeChanged(const KUrl& url)
+{
+ // URL changes of the view can happen in two ways:
+ // 1. The URL navigator gets changed and will trigger the view to update its URL
+ // 2. The URL of the view gets changed and will trigger the URL navigator to update
+ // its URL (e.g. by clicking on an item)
+ // In this scope the view-state may only get saved in case 2:
+ if (url != m_urlNavigator->locationUrl()) {
+ saveViewState();
+ }
+}
+
+void DolphinViewContainer::slotUrlNavigatorLocationAboutToBeChanged(const KUrl& url)
+{
+ // URL changes of the view can happen in two ways:
+ // 1. The URL navigator gets changed and will trigger the view to update its URL
+ // 2. The URL of the view gets changed and will trigger the URL navigator to update
+ // its URL (e.g. by clicking on an item)
+ // In this scope the view-state may only get saved in case 1:
+ if (url != m_view->url()) {
+ saveViewState();
+ }
+}
+
+void DolphinViewContainer::slotUrlNavigatorLocationChanged(const KUrl& url)
+{
+ slotReturnPressed();
+
+ if (KProtocolManager::supportsListing(url)) {
+ setSearchModeEnabled(isSearchUrl(url));
+ m_view->setUrl(url);
+
+ if (m_autoGrabFocus && isActive() && !isSearchUrl(url)) {
+ // When an URL has been entered, the view should get the focus.
+ // The focus must be requested asynchronously, as changing the URL might create
+ // a new view widget.
+ QTimer::singleShot(0, this, SLOT(requestFocus()));
+ }
+ } else if (KProtocolManager::isSourceProtocol(url)) {
+ QString app = "konqueror";
+ if (url.protocol().startsWith(QLatin1String("http"))) {
+ showMessage(i18nc("@info:status", // krazy:exclude=qmethods
+ "Dolphin does not support web pages, the web browser has been launched"),
+ Information);
+
+ const KConfigGroup config(KSharedConfig::openConfig("kdeglobals"), "General");
+ const QString browser = config.readEntry("BrowserApplication");
+ if (!browser.isEmpty()) {
+ app = browser;
+ if (app.startsWith('!')) {
+ // a literal command has been configured, remove the '!' prefix
+ app = app.mid(1);
+ }
+ }
+ } else {
+ showMessage(i18nc("@info:status",
+ "Protocol not supported by Dolphin, Konqueror has been launched"),
+ Information);
+ }
+
+ const QString secureUrl = KShell::quoteArg(url.pathOrUrl());
+ const QString command = app + ' ' + secureUrl;
+ KRun::runCommand(command, app, app, this);
+ } else {
+ showMessage(i18nc("@info:status", "Invalid protocol"), Error);
+ }
+}
+
+void DolphinViewContainer::dropUrls(const KUrl& destination, QDropEvent* event)
+{
+ m_dropDestination = destination;
+
+ const QMimeData* mimeData = event->mimeData();
+ QMimeData* mimeDataCopy = new QMimeData;
+ foreach (const QString& format, mimeData->formats()) {
+ mimeDataCopy->setData(format, mimeData->data(format));
+ }
+
+ m_dropEvent.reset(new QDropEvent(event->pos(),
+ event->possibleActions(),
+ mimeDataCopy,
+ event->mouseButtons(),
+ event->keyboardModifiers()));
+
+ QTimer::singleShot(0, this, SLOT(dropUrlsDelayed()));
+}
+
+void DolphinViewContainer::dropUrlsDelayed()
+{
+ if (m_dropEvent.isNull()) {
+ return;
+ }
+
+ QString error;
+ DragAndDropHelper::dropUrls(KFileItem(), m_dropDestination, m_dropEvent.data(), error);
+ if (!error.isEmpty()) {
+ showMessage(error, Error);
+ }
+
+ delete m_dropEvent->mimeData();
+ m_dropEvent.reset();
+}
+
+void DolphinViewContainer::redirect(const KUrl& oldUrl, const KUrl& newUrl)
+{
+ Q_UNUSED(oldUrl);
+ const bool block = m_urlNavigator->signalsBlocked();
+ m_urlNavigator->blockSignals(true);
+
+ // Assure that the location state is reset for redirection URLs. This
+ // allows to skip redirection URLs when going back or forward in the
+ // URL history.
+ m_urlNavigator->saveLocationState(QByteArray());
+ m_urlNavigator->setLocationUrl(newUrl);
+ setSearchModeEnabled(isSearchUrl(newUrl));
+
+ m_urlNavigator->blockSignals(block);
+}
+
+void DolphinViewContainer::requestFocus()
+{
+ m_view->setFocus();
+}
+
+void DolphinViewContainer::saveUrlCompletionMode(KGlobalSettings::Completion completion)
+{
+ GeneralSettings::setUrlCompletionMode(completion);
+}
+
+void DolphinViewContainer::slotHistoryChanged()
+{
+ QByteArray locationState = m_urlNavigator->locationState();
+ if (!locationState.isEmpty()) {
+ QDataStream stream(&locationState, QIODevice::ReadOnly);
+ m_view->restoreState(stream);
+ }
+}
+
+void DolphinViewContainer::slotReturnPressed()
+{
+ if (!GeneralSettings::editableUrl()) {
+ m_urlNavigator->setUrlEditable(false);
+ }
+}
+
+void DolphinViewContainer::startSearching()
+{
+ const KUrl url = m_searchBox->urlForSearching();
+ if (url.isValid() && !url.isEmpty()) {
+ m_view->setViewPropertiesContext("search");
+ m_urlNavigator->setLocationUrl(url);
+ }
+}
+
+void DolphinViewContainer::closeSearchBox()
+{
+ setSearchModeEnabled(false);
+}
+
+void DolphinViewContainer::stopDirectoryLoading()
+{
+ m_view->stopLoading();
+ m_statusBar->setProgress(100);
+}
+
+void DolphinViewContainer::slotStatusBarZoomLevelChanged(int zoomLevel)
+{
+ m_view->setZoomLevel(zoomLevel);
+}
+
+void DolphinViewContainer::showErrorMessage(const QString& msg)
+{
+ showMessage(msg, Error);
+}
+
+bool DolphinViewContainer::isSearchUrl(const KUrl& url) const
+{
+ const QString protocol = url.protocol();
+ return protocol.contains("search");
+}
+
+void DolphinViewContainer::saveViewState()
+{
+ QByteArray locationState;
+ QDataStream stream(&locationState, QIODevice::WriteOnly);
+ m_view->saveState(stream);
+ m_urlNavigator->saveLocationState(locationState);
+}
+
+#include "dolphinviewcontainer.moc"
diff --git a/dolphin/src/dolphinviewcontainer.h b/dolphin/src/dolphinviewcontainer.h
new file mode 100644
index 0000000..31612f1
--- /dev/null
+++ b/dolphin/src/dolphinviewcontainer.h
@@ -0,0 +1,354 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef DOLPHINVIEWCONTAINER_H
+#define DOLPHINVIEWCONTAINER_H
+
+#include <KFileItem>
+#include <KFileItemDelegate>
+#include <KGlobalSettings>
+#include <KIO/Job>
+
+#include <KUrlNavigator>
+
+#include <QElapsedTimer>
+#include <QWidget>
+
+#include <views/dolphinview.h>
+#include <config-apps.h>
+
+#ifdef KActivities_FOUND
+namespace KActivities {
+ class ResourceInstance;
+}
+#endif
+
+class FilterBar;
+class KMessageWidget;
+class KUrl;
+class KUrlNavigator;
+class DolphinSearchBox;
+class DolphinStatusBar;
+
+/**
+ * @short Represents a view for the directory content
+ * including the navigation bar, filter bar and status bar.
+ *
+ * View modes for icons, compact and details are supported. Currently
+ * Dolphin allows to have up to two views inside the main window.
+ *
+ * @see DolphinView
+ * @see FilterBar
+ * @see KUrlNavigator
+ * @see DolphinStatusBar
+ */
+class DolphinViewContainer : public QWidget
+{
+ Q_OBJECT
+
+public:
+ enum MessageType
+ {
+ Information,
+ Warning,
+ Error
+ };
+
+ DolphinViewContainer(const KUrl& url, QWidget* parent);
+ virtual ~DolphinViewContainer();
+
+ /**
+ * Returns the current active URL, where all actions are applied.
+ * The URL navigator is synchronized with this URL.
+ */
+ KUrl url() const;
+
+ /**
+ * If \a active is true, the view container will marked as active. The active
+ * view container is defined as view where all actions are applied to.
+ */
+ void setActive(bool active);
+ bool isActive() const;
+
+ /**
+ * If \a grab is set to true, the container automatically grabs the focus
+ * as soon as the URL has been changed. Per default the grabbing
+ * of the focus is enabled.
+ */
+ void setAutoGrabFocus(bool grab);
+ bool autoGrabFocus() const;
+
+ const DolphinStatusBar* statusBar() const;
+ DolphinStatusBar* statusBar();
+
+ const KUrlNavigator* urlNavigator() const;
+ KUrlNavigator* urlNavigator();
+
+ const DolphinView* view() const;
+ DolphinView* view();
+
+ /**
+ * Shows the message \msg with the given type non-modal above
+ * the view-content.
+ */
+ void showMessage(const QString& msg, MessageType type);
+
+ /**
+ * Refreshes the view container to get synchronized with the (updated) Dolphin settings.
+ */
+ void readSettings();
+
+ /** Returns true, if the filter bar is visible. */
+ bool isFilterBarVisible() const;
+
+ /**
+ * Enables the search mode, if \p enabled is true. In the search mode the URL navigator
+ * will be hidden and replaced by a line editor that allows to enter a search term.
+ */
+ void setSearchModeEnabled(bool enabled);
+ bool isSearchModeEnabled() const;
+
+ /**
+ * @return Text that should be used for the current URL when creating
+ * a new place.
+ */
+ QString placesText() const;
+
+public slots:
+ /**
+ * Sets the current active URL, where all actions are applied. The
+ * URL navigator is synchronized with this URL. The signals
+ * KUrlNavigator::urlChanged() and KUrlNavigator::historyChanged()
+ * are emitted.
+ * @see DolphinViewContainer::urlNavigator()
+ */
+ void setUrl(const KUrl& url);
+
+ /**
+ * Popups the filter bar above the status bar if \a visible is true.
+ * It \a visible is true, it is assured that the filter bar gains
+ * the keyboard focus.
+ */
+ void setFilterBarVisible(bool visible);
+
+signals:
+ /**
+ * Is emitted whenever the filter bar has changed its visibility state.
+ */
+ void showFilterBarChanged(bool shown);
+
+ /**
+ * Is emitted when the write state of the folder has been changed. The application
+ * should disable all actions like "Create New..." that depend on the write
+ * state.
+ */
+ void writeStateChanged(bool isFolderWritable);
+
+private slots:
+ /**
+ * Updates the number of items (= number of files + number of
+ * directories) in the statusbar. If files are selected, the number
+ * of selected files and the sum of the filesize is shown. The update
+ * is done asynchronously, as getting the sum of the
+ * filesizes can be an expensive operation.
+ * Unless a previous OperationCompletedMessage was set very shortly before
+ * calling this method, it will be overwritten (see DolphinStatusBar::setMessage).
+ * Previous ErrorMessages however are always preserved.
+ */
+ void delayedStatusBarUpdate();
+
+ /**
+ * Is invoked by DolphinViewContainer::delayedStatusBarUpdate() and
+ * updates the status bar synchronously.
+ */
+ void updateStatusBar();
+
+ void updateDirectoryLoadingProgress(int percent);
+
+ void updateDirectorySortingProgress(int percent);
+
+ /**
+ * Updates the statusbar to show an undetermined progress with the correct
+ * context information whether a searching or a directory loading is done.
+ */
+ void slotDirectoryLoadingStarted();
+
+ /**
+ * Assures that the viewport position is restored and updates the
+ * statusbar to reflect the current content.
+ */
+ void slotDirectoryLoadingCompleted();
+
+ /**
+ * Updates the statusbar to show, that the directory loading has
+ * been canceled.
+ */
+ void slotDirectoryLoadingCanceled();
+
+ /**
+ * Is called if the URL set by DolphinView::setUrl() represents
+ * a file and not a directory. Takes care to activate the file.
+ */
+ void slotUrlIsFileError(const KUrl& url);
+
+ /**
+ * Handles clicking on an item. If the item is a directory, the
+ * directory is opened in the view. If the item is a file, the file
+ * gets started by the corresponding application.
+ */
+ void slotItemActivated(const KFileItem& item);
+
+ /**
+ * Handles activation of multiple files. The files get started by
+ * the corresponding applications.
+ */
+ void slotItemsActivated(const KFileItemList& items);
+
+ /**
+ * Shows the information for the item \a item inside the statusbar. If the
+ * item is null, the default statusbar information is shown.
+ */
+ void showItemInfo(const KFileItem& item);
+
+ void closeFilterBar();
+
+ /**
+ * Filters the currently shown items by \a nameFilter. All items
+ * which contain the given filter string will be shown.
+ */
+ void setNameFilter(const QString& nameFilter);
+
+ /**
+ * Marks the view container as active
+ * (see DolphinViewContainer::setActive()).
+ */
+ void activate();
+
+ /**
+ * Is invoked if the signal urlAboutToBeChanged() from the DolphinView
+ * is emitted. Tries to save the view-state.
+ */
+ void slotViewUrlAboutToBeChanged(const KUrl& url);
+
+ /**
+ * Is invoked if the signal urlAboutToBeChanged() from the URL navigator
+ * is emitted. Tries to save the view-state.
+ */
+ void slotUrlNavigatorLocationAboutToBeChanged(const KUrl& url);
+
+ /**
+ * Restores the current view to show \a url and assures
+ * that the root URL of the view is respected.
+ */
+ void slotUrlNavigatorLocationChanged(const KUrl& url);
+
+ /**
+ * Is connected with the URL navigator and drops the URLs
+ * above the destination \a destination.
+ *
+ * Creates a copy of \a event and invokes \a dropUrlsDelayed with a
+ * queued connection.
+ */
+ void dropUrls(const KUrl& destination, QDropEvent* event);
+
+ /**
+ * Is invoked with a queued connection by \a dropUrls to prevent that the
+ * drop actions are executed in the URL navigator menu's nested event loop,
+ * which might cause a crash. Simply using a queued connection from the URL
+ * navigator to \a dropUrls would not work because the \a event pointer
+ * would be dangling then.
+ */
+ void dropUrlsDelayed();
+
+ /**
+ * Is invoked when a redirection is done and changes the
+ * URL of the URL navigator to \a newUrl without triggering
+ * a reloading of the directory.
+ */
+ void redirect(const KUrl& oldUrl, const KUrl& newUrl);
+
+ /** Requests the focus for the view \a m_view. */
+ void requestFocus();
+
+ /**
+ * Saves the currently used URL completion mode of
+ * the URL navigator.
+ */
+ void saveUrlCompletionMode(KGlobalSettings::Completion completion);
+
+ void slotHistoryChanged();
+
+ void slotReturnPressed();
+
+ /**
+ * Gets the search URL from the searchbox and starts searching.
+ */
+ void startSearching();
+ void closeSearchBox();
+
+ /**
+ * Stops the loading of a directory. Is connected with the "stopPressed" signal
+ * from the statusbar.
+ */
+ void stopDirectoryLoading();
+
+ void slotStatusBarZoomLevelChanged(int zoomLevel);
+
+ /**
+ * Slot that calls showMessage(msg, Error).
+ */
+ void showErrorMessage(const QString& msg);
+
+private:
+ /**
+ * @return True if the URL protocol is a search URL (e. g. baloosearch:// or filenamesearch://).
+ */
+ bool isSearchUrl(const KUrl& url) const;
+
+ /**
+ * Saves the state of the current view: contents position,
+ * root URL, ...
+ */
+ void saveViewState();
+
+private:
+ QVBoxLayout* m_topLayout;
+ KUrlNavigator* m_urlNavigator;
+ DolphinSearchBox* m_searchBox;
+ KMessageWidget* m_messageWidget;
+
+ DolphinView* m_view;
+
+ FilterBar* m_filterBar;
+
+ DolphinStatusBar* m_statusBar;
+ QTimer* m_statusBarTimer; // Triggers a delayed update
+ QElapsedTimer m_statusBarTimestamp; // Time in ms since last update
+ bool m_autoGrabFocus;
+
+ KUrl m_dropDestination;
+ QScopedPointer<QDropEvent> m_dropEvent;
+
+#ifdef KActivities_FOUND
+private:
+ KActivities::ResourceInstance * m_activityResourceInstance;
+#endif
+};
+
+#endif // DOLPHINVIEWCONTAINER_H
diff --git a/dolphin/src/filterbar/filterbar.cpp b/dolphin/src/filterbar/filterbar.cpp
new file mode 100644
index 0000000..6de6fbe
--- /dev/null
+++ b/dolphin/src/filterbar/filterbar.cpp
@@ -0,0 +1,143 @@
+/***************************************************************************
+ * Copyright (C) 2006-2010 by Peter Penz <[email protected]> *
+ * Copyright (C) 2006 by Gregor Kališnik <[email protected]> *
+ * Copyright (C) 2012 by Stuart Citrin <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+#include "filterbar.h"
+
+#include <QBoxLayout>
+#include <QKeyEvent>
+#include <QLabel>
+#include <QToolButton>
+
+#include <KIcon>
+#include <KLocale>
+#include <KLineEdit>
+#include <KIconLoader>
+
+FilterBar::FilterBar(QWidget* parent) :
+ QWidget(parent)
+{
+ // Create close button
+ QToolButton *closeButton = new QToolButton(this);
+ closeButton->setAutoRaise(true);
+ closeButton->setIcon(KIcon("dialog-close"));
+ closeButton->setToolTip(i18nc("@info:tooltip", "Hide Filter Bar"));
+ connect(closeButton, SIGNAL(clicked()), this, SIGNAL(closeRequest()));
+
+ // Create button to lock text when changing folders
+ m_lockButton = new QToolButton(this);
+ m_lockButton->setAutoRaise(true);
+ m_lockButton->setCheckable(true);
+ m_lockButton->setIcon(KIcon("object-unlocked"));
+ m_lockButton->setToolTip(i18nc("@info:tooltip", "Keep Filter When Changing Folders"));
+ connect(m_lockButton, SIGNAL(toggled(bool)), this, SLOT(slotToggleLockButton(bool)));
+
+ // Create label
+ QLabel* filterLabel = new QLabel(i18nc("@label:textbox", "Filter:"), this);
+
+ // Create filter editor
+ m_filterInput = new KLineEdit(this);
+ m_filterInput->setLayoutDirection(Qt::LeftToRight);
+ m_filterInput->setClearButtonShown(true);
+ connect(m_filterInput, SIGNAL(textChanged(QString)),
+ this, SIGNAL(filterChanged(QString)));
+ setFocusProxy(m_filterInput);
+
+ // Apply layout
+ QHBoxLayout* hLayout = new QHBoxLayout(this);
+ hLayout->setMargin(0);
+ hLayout->addWidget(closeButton);
+ hLayout->addWidget(filterLabel);
+ hLayout->addWidget(m_filterInput);
+ hLayout->addWidget(m_lockButton);
+
+ filterLabel->setBuddy(m_filterInput);
+}
+
+FilterBar::~FilterBar()
+{
+}
+
+void FilterBar::closeFilterBar()
+{
+ hide();
+ clear();
+ if (m_lockButton) {
+ m_lockButton->setChecked(false);
+ }
+}
+
+void FilterBar::selectAll()
+{
+ m_filterInput->selectAll();
+}
+
+void FilterBar::clear()
+{
+ m_filterInput->clear();
+}
+
+void FilterBar::slotUrlChanged()
+{
+ if (!m_lockButton || !(m_lockButton->isChecked())) {
+ clear();
+ }
+}
+
+void FilterBar::slotToggleLockButton(bool checked)
+{
+ if (checked) {
+ m_lockButton->setIcon(KIcon("object-locked"));
+ } else {
+ m_lockButton->setIcon(KIcon("object-unlocked"));
+ clear();
+ }
+}
+
+void FilterBar::showEvent(QShowEvent* event)
+{
+ if (!event->spontaneous()) {
+ m_filterInput->setFocus();
+ }
+}
+
+void FilterBar::keyReleaseEvent(QKeyEvent* event)
+{
+ QWidget::keyReleaseEvent(event);
+
+ switch (event->key()) {
+ case Qt::Key_Escape:
+ if (m_filterInput->text().isEmpty()) {
+ emit closeRequest();
+ } else {
+ m_filterInput->clear();
+ }
+ break;
+
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ emit focusViewRequest();
+ break;
+
+ default:
+ break;
+ }
+}
+
+#include "filterbar.moc"
diff --git a/dolphin/src/filterbar/filterbar.h b/dolphin/src/filterbar/filterbar.h
new file mode 100644
index 0000000..5d5463a
--- /dev/null
+++ b/dolphin/src/filterbar/filterbar.h
@@ -0,0 +1,85 @@
+/***************************************************************************
+ * Copyright (C) 2006-2010 by Peter Penz <[email protected]> *
+ * Copyright (C) 2006 by Gregor Kališnik <[email protected]> *
+ * Copyright (C) 2012 by Stuart Citrin <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef FILTERBAR_H
+#define FILTERBAR_H
+
+#include <QWidget>
+
+class KLineEdit;
+class QToolButton;
+
+/**
+ * @brief Provides an input field for filtering the currently shown items.
+ *
+ * @author Gregor Kališnik <[email protected]>
+ */
+class FilterBar : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit FilterBar(QWidget* parent = 0);
+ virtual ~FilterBar();
+
+ /** Called by view container to hide this **/
+ void closeFilterBar();
+
+ /**
+ * Selects the whole text of the filter bar.
+ */
+ void selectAll();
+
+public slots:
+ /** Clears the input field. */
+ void clear();
+ /** Clears the input field if the "lock button" is disabled. */
+ void slotUrlChanged();
+ /** The input field is cleared also if the "lock button" is released. */
+ void slotToggleLockButton(bool checked);
+
+signals:
+ /**
+ * Signal that reports the name filter has been
+ * changed to \a nameFilter.
+ */
+ void filterChanged(const QString& nameFilter);
+
+ /**
+ * Emitted as soon as the filterbar should get closed.
+ */
+ void closeRequest();
+
+ /*
+ * Emitted as soon as the focus should be returned back to the view.
+ */
+ void focusViewRequest();
+
+protected:
+ virtual void showEvent(QShowEvent* event);
+ virtual void keyReleaseEvent(QKeyEvent* event);
+
+private:
+ KLineEdit* m_filterInput;
+ QToolButton* m_lockButton;
+};
+
+#endif
diff --git a/dolphin/src/kitemviews/kfileitemlistview.cpp b/dolphin/src/kitemviews/kfileitemlistview.cpp
new file mode 100644
index 0000000..fd01f2c
--- /dev/null
+++ b/dolphin/src/kitemviews/kfileitemlistview.cpp
@@ -0,0 +1,424 @@
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "kfileitemlistview.h"
+
+#include "kfileitemmodelrolesupdater.h"
+#include "kfileitemlistwidget.h"
+#include "kfileitemmodel.h"
+#include <KLocale>
+#include <KStringHandler>
+#include "private/kpixmapmodifier.h"
+
+#include <KDebug>
+#include <KIcon>
+#include <KTextEdit>
+
+#include <QPainter>
+#include <QTextLine>
+#include <QTimer>
+
+// #define KFILEITEMLISTVIEW_DEBUG
+
+namespace {
+ // If the visible index range changes, KFileItemModelRolesUpdater is not
+ // informed immediatetly, but with a short delay. This ensures that scrolling
+ // always feels smooth and is not interrupted by icon loading (which can be
+ // quite expensive if a disk access is required to determine the final icon).
+ const int ShortInterval = 50;
+
+ // If the icon size changes, a longer delay is used. This prevents that
+ // the expensive re-generation of all previews is triggered repeatedly when
+ // chaning the zoom level.
+ const int LongInterval = 300;
+}
+
+KFileItemListView::KFileItemListView(QGraphicsWidget* parent) :
+ KStandardItemListView(parent),
+ m_modelRolesUpdater(0),
+ m_updateVisibleIndexRangeTimer(0),
+ m_updateIconSizeTimer(0)
+{
+ setAcceptDrops(true);
+
+ setScrollOrientation(Qt::Vertical);
+
+ m_updateVisibleIndexRangeTimer = new QTimer(this);
+ m_updateVisibleIndexRangeTimer->setSingleShot(true);
+ m_updateVisibleIndexRangeTimer->setInterval(ShortInterval);
+ connect(m_updateVisibleIndexRangeTimer, SIGNAL(timeout()), this, SLOT(updateVisibleIndexRange()));
+
+ m_updateIconSizeTimer = new QTimer(this);
+ m_updateIconSizeTimer->setSingleShot(true);
+ m_updateIconSizeTimer->setInterval(LongInterval);
+ connect(m_updateIconSizeTimer, SIGNAL(timeout()), this, SLOT(updateIconSize()));
+
+ setVisibleRoles(QList<QByteArray>() << "text");
+}
+
+KFileItemListView::~KFileItemListView()
+{
+}
+
+void KFileItemListView::setPreviewsShown(bool show)
+{
+ if (!m_modelRolesUpdater) {
+ return;
+ }
+
+ if (m_modelRolesUpdater->previewsShown() != show) {
+ beginTransaction();
+ m_modelRolesUpdater->setPreviewsShown(show);
+ onPreviewsShownChanged(show);
+ endTransaction();
+ }
+}
+
+bool KFileItemListView::previewsShown() const
+{
+ return m_modelRolesUpdater ? m_modelRolesUpdater->previewsShown() : false;
+}
+
+void KFileItemListView::setEnlargeSmallPreviews(bool enlarge)
+{
+ if (m_modelRolesUpdater) {
+ m_modelRolesUpdater->setEnlargeSmallPreviews(enlarge);
+ }
+}
+
+bool KFileItemListView::enlargeSmallPreviews() const
+{
+ return m_modelRolesUpdater ? m_modelRolesUpdater->enlargeSmallPreviews() : false;
+}
+
+void KFileItemListView::setEnabledPlugins(const QStringList& list)
+{
+ if (m_modelRolesUpdater) {
+ m_modelRolesUpdater->setEnabledPlugins(list);
+ }
+}
+
+QStringList KFileItemListView::enabledPlugins() const
+{
+ return m_modelRolesUpdater ? m_modelRolesUpdater->enabledPlugins() : QStringList();
+}
+
+QPixmap KFileItemListView::createDragPixmap(const KItemSet& indexes) const
+{
+ if (!model()) {
+ return QPixmap();
+ }
+
+ const int itemCount = indexes.count();
+ Q_ASSERT(itemCount > 0);
+ if (itemCount == 1) {
+ return KItemListView::createDragPixmap(indexes);
+ }
+
+ // If more than one item is dragged, align the items inside a
+ // rectangular grid. The maximum grid size is limited to 5 x 5 items.
+ int xCount;
+ int size;
+ if (itemCount > 16) {
+ xCount = 5;
+ size = KIconLoader::SizeSmall;
+ } else if (itemCount > 9) {
+ xCount = 4;
+ size = KIconLoader::SizeSmallMedium;
+ } else {
+ xCount = 3;
+ size = KIconLoader::SizeMedium;
+ }
+
+ if (itemCount < xCount) {
+ xCount = itemCount;
+ }
+
+ int yCount = itemCount / xCount;
+ if (itemCount % xCount != 0) {
+ ++yCount;
+ }
+ if (yCount > xCount) {
+ yCount = xCount;
+ }
+
+ // Draw the selected items into the grid cells.
+ QPixmap dragPixmap(xCount * size + xCount, yCount * size + yCount);
+ dragPixmap.fill(Qt::transparent);
+
+ QPainter painter(&dragPixmap);
+ int x = 0;
+ int y = 0;
+
+ foreach (int index, indexes) {
+ QPixmap pixmap = model()->data(index).value("iconPixmap").value<QPixmap>();
+ if (pixmap.isNull()) {
+ KIcon icon(model()->data(index).value("iconName").toString());
+ pixmap = icon.pixmap(size, size);
+ } else {
+ KPixmapModifier::scale(pixmap, QSize(size, size));
+ }
+
+ painter.drawPixmap(x, y, pixmap);
+
+ x += size + 1;
+ if (x >= dragPixmap.width()) {
+ x = 0;
+ y += size + 1;
+ }
+
+ if (y >= dragPixmap.height()) {
+ break;
+ }
+ }
+
+ return dragPixmap;
+}
+
+KItemListWidgetCreatorBase* KFileItemListView::defaultWidgetCreator() const
+{
+ return new KItemListWidgetCreator<KFileItemListWidget>();
+}
+
+void KFileItemListView::initializeItemListWidget(KItemListWidget* item)
+{
+ KStandardItemListView::initializeItemListWidget(item);
+
+ // Make sure that the item has an icon.
+ QHash<QByteArray, QVariant> data = item->data();
+ if (!data.contains("iconName") && data["iconPixmap"].value<QPixmap>().isNull()) {
+ Q_ASSERT(qobject_cast<KFileItemModel*>(model()));
+ KFileItemModel* fileItemModel = static_cast<KFileItemModel*>(model());
+
+ const KFileItem fileItem = fileItemModel->fileItem(item->index());
+ data.insert("iconName", fileItem.iconName());
+ item->setData(data, QSet<QByteArray>() << "iconName");
+ }
+}
+
+void KFileItemListView::onPreviewsShownChanged(bool shown)
+{
+ Q_UNUSED(shown);
+}
+
+void KFileItemListView::onItemLayoutChanged(ItemLayout current, ItemLayout previous)
+{
+ KStandardItemListView::onItemLayoutChanged(current, previous);
+ triggerVisibleIndexRangeUpdate();
+}
+
+void KFileItemListView::onModelChanged(KItemModelBase* current, KItemModelBase* previous)
+{
+ Q_ASSERT(qobject_cast<KFileItemModel*>(current));
+ KStandardItemListView::onModelChanged(current, previous);
+
+ delete m_modelRolesUpdater;
+ m_modelRolesUpdater = 0;
+
+ if (current) {
+ m_modelRolesUpdater = new KFileItemModelRolesUpdater(static_cast<KFileItemModel*>(current), this);
+ m_modelRolesUpdater->setIconSize(availableIconSize());
+
+ applyRolesToModel();
+ }
+}
+
+void KFileItemListView::onScrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous)
+{
+ KStandardItemListView::onScrollOrientationChanged(current, previous);
+ triggerVisibleIndexRangeUpdate();
+}
+
+void KFileItemListView::onItemSizeChanged(const QSizeF& current, const QSizeF& previous)
+{
+ Q_UNUSED(current);
+ Q_UNUSED(previous);
+ triggerVisibleIndexRangeUpdate();
+}
+
+void KFileItemListView::onScrollOffsetChanged(qreal current, qreal previous)
+{
+ KStandardItemListView::onScrollOffsetChanged(current, previous);
+ triggerVisibleIndexRangeUpdate();
+}
+
+void KFileItemListView::onVisibleRolesChanged(const QList<QByteArray>& current, const QList<QByteArray>& previous)
+{
+ KStandardItemListView::onVisibleRolesChanged(current, previous);
+ applyRolesToModel();
+}
+
+void KFileItemListView::onStyleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous)
+{
+ KStandardItemListView::onStyleOptionChanged(current, previous);
+ triggerIconSizeUpdate();
+}
+
+void KFileItemListView::onSupportsItemExpandingChanged(bool supportsExpanding)
+{
+ applyRolesToModel();
+ KStandardItemListView::onSupportsItemExpandingChanged(supportsExpanding);
+ triggerVisibleIndexRangeUpdate();
+}
+
+void KFileItemListView::onTransactionBegin()
+{
+ if (m_modelRolesUpdater) {
+ m_modelRolesUpdater->setPaused(true);
+ }
+}
+
+void KFileItemListView::onTransactionEnd()
+{
+ if (!m_modelRolesUpdater) {
+ return;
+ }
+
+ // Only unpause the model-roles-updater if no timer is active. If one
+ // timer is still active the model-roles-updater will be unpaused later as
+ // soon as the timer has been exceeded.
+ const bool timerActive = m_updateVisibleIndexRangeTimer->isActive() ||
+ m_updateIconSizeTimer->isActive();
+ if (!timerActive) {
+ m_modelRolesUpdater->setPaused(false);
+ }
+}
+
+void KFileItemListView::resizeEvent(QGraphicsSceneResizeEvent* event)
+{
+ KStandardItemListView::resizeEvent(event);
+ triggerVisibleIndexRangeUpdate();
+}
+
+void KFileItemListView::slotItemsRemoved(const KItemRangeList& itemRanges)
+{
+ KStandardItemListView::slotItemsRemoved(itemRanges);
+}
+
+void KFileItemListView::slotSortRoleChanged(const QByteArray& current, const QByteArray& previous)
+{
+ const QByteArray sortRole = model()->sortRole();
+ if (!visibleRoles().contains(sortRole)) {
+ applyRolesToModel();
+ }
+
+ KStandardItemListView::slotSortRoleChanged(current, previous);
+}
+
+void KFileItemListView::triggerVisibleIndexRangeUpdate()
+{
+ if (!model()) {
+ return;
+ }
+ m_modelRolesUpdater->setPaused(true);
+
+ // If the icon size has been changed recently, wait until
+ // m_updateIconSizeTimer expires.
+ if (!m_updateIconSizeTimer->isActive()) {
+ m_updateVisibleIndexRangeTimer->start();
+ }
+}
+
+void KFileItemListView::updateVisibleIndexRange()
+{
+ if (!m_modelRolesUpdater) {
+ return;
+ }
+
+ const int index = firstVisibleIndex();
+ const int count = lastVisibleIndex() - index + 1;
+ m_modelRolesUpdater->setMaximumVisibleItems(maximumVisibleItems());
+ m_modelRolesUpdater->setVisibleIndexRange(index, count);
+ m_modelRolesUpdater->setPaused(isTransactionActive());
+}
+
+void KFileItemListView::triggerIconSizeUpdate()
+{
+ if (!model()) {
+ return;
+ }
+ m_modelRolesUpdater->setPaused(true);
+ m_updateIconSizeTimer->start();
+
+ // The visible index range will be updated when m_updateIconSizeTimer expires.
+ // Stop m_updateVisibleIndexRangeTimer to prevent an expensive re-generation
+ // of all previews (note that the user might change the icon size again soon).
+ m_updateVisibleIndexRangeTimer->stop();
+}
+
+void KFileItemListView::updateIconSize()
+{
+ if (!m_modelRolesUpdater) {
+ return;
+ }
+
+ m_modelRolesUpdater->setIconSize(availableIconSize());
+
+ // Update the visible index range (which has most likely changed after the
+ // icon size change) before unpausing m_modelRolesUpdater.
+ const int index = firstVisibleIndex();
+ const int count = lastVisibleIndex() - index + 1;
+ m_modelRolesUpdater->setVisibleIndexRange(index, count);
+
+ m_modelRolesUpdater->setPaused(isTransactionActive());
+}
+
+void KFileItemListView::applyRolesToModel()
+{
+ if (!model()) {
+ return;
+ }
+
+ Q_ASSERT(qobject_cast<KFileItemModel*>(model()));
+ KFileItemModel* fileItemModel = static_cast<KFileItemModel*>(model());
+
+ // KFileItemModel does not distinct between "visible" and "invisible" roles.
+ // Add all roles that are mandatory for having a working KFileItemListView:
+ QSet<QByteArray> roles = visibleRoles().toSet();
+ roles.insert("iconPixmap");
+ roles.insert("iconName");
+ roles.insert("text");
+ roles.insert("isDir");
+ roles.insert("isLink");
+ if (supportsItemExpanding()) {
+ roles.insert("isExpanded");
+ roles.insert("isExpandable");
+ roles.insert("expandedParentsCount");
+ }
+
+ // Assure that the role that is used for sorting will be determined
+ roles.insert(fileItemModel->sortRole());
+
+ fileItemModel->setRoles(roles);
+ m_modelRolesUpdater->setRoles(roles);
+}
+
+QSize KFileItemListView::availableIconSize() const
+{
+ const KItemListStyleOption& option = styleOption();
+ const int iconSize = option.iconSize;
+ if (itemLayout() == IconsLayout) {
+ const int maxIconWidth = itemSize().width() - 2 * option.padding;
+ return QSize(maxIconWidth, iconSize);
+ }
+
+ return QSize(iconSize, iconSize);
+}
+
+#include "kfileitemlistview.moc"
diff --git a/dolphin/src/kitemviews/kfileitemlistview.h b/dolphin/src/kitemviews/kfileitemlistview.h
new file mode 100644
index 0000000..2fd21ed
--- /dev/null
+++ b/dolphin/src/kitemviews/kfileitemlistview.h
@@ -0,0 +1,131 @@
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef KFILEITEMLISTVIEW_H
+#define KFILEITEMLISTVIEW_H
+
+#include <libdolphin_export.h>
+
+#include <kitemviews/kstandarditemlistview.h>
+
+class KFileItemModelRolesUpdater;
+class QTimer;
+
+/**
+ * @brief View that allows to show the content of file-items.
+ *
+ * The corresponding model set by the controller must be an instance
+ * of KFileItemModel. Per default KFileItemListWidget is set as widget creator
+ * value and KItemListGroupHeader as group-header creator value. Use
+ * KItemListView::setWidgetCreator() and KItemListView::setGroupHeaderCreator()
+ * to apply customized generators.
+ */
+class LIBDOLPHINPRIVATE_EXPORT KFileItemListView : public KStandardItemListView
+{
+ Q_OBJECT
+
+public:
+ KFileItemListView(QGraphicsWidget* parent = 0);
+ virtual ~KFileItemListView();
+
+ void setPreviewsShown(bool show);
+ bool previewsShown() const;
+
+ /**
+ * If enabled a small preview gets upscaled to the icon size in case where
+ * the icon size is larger than the preview. Per default enlarging is
+ * enabled.
+ */
+ void setEnlargeSmallPreviews(bool enlarge);
+ bool enlargeSmallPreviews() const;
+
+ /**
+ * Sets the list of enabled thumbnail plugins that are used for previews.
+ * Per default all plugins enabled in the KConfigGroup "PreviewSettings"
+ * are used.
+ *
+ * For a list of available plugins, call KServiceTypeTrader::self()->query("ThumbCreator").
+ *
+ * @see enabledPlugins
+ */
+ void setEnabledPlugins(const QStringList& list);
+
+ /**
+ * Returns the list of enabled thumbnail plugins.
+ * @see setEnabledPlugins
+ */
+ QStringList enabledPlugins() const;
+
+ /** @reimp */
+ virtual QPixmap createDragPixmap(const KItemSet& indexes) const;
+
+protected:
+ virtual KItemListWidgetCreatorBase* defaultWidgetCreator() const;
+ virtual void initializeItemListWidget(KItemListWidget* item);
+ virtual void onPreviewsShownChanged(bool shown);
+ virtual void onItemLayoutChanged(ItemLayout current, ItemLayout previous);
+ virtual void onModelChanged(KItemModelBase* current, KItemModelBase* previous);
+ virtual void onScrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous);
+ virtual void onItemSizeChanged(const QSizeF& current, const QSizeF& previous);
+ virtual void onScrollOffsetChanged(qreal current, qreal previous);
+ virtual void onVisibleRolesChanged(const QList<QByteArray>& current, const QList<QByteArray>& previous);
+ virtual void onStyleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous);
+ virtual void onSupportsItemExpandingChanged(bool supportsExpanding);
+ virtual void onTransactionBegin();
+ virtual void onTransactionEnd();
+ virtual void resizeEvent(QGraphicsSceneResizeEvent* event);
+
+protected slots:
+ virtual void slotItemsRemoved(const KItemRangeList& itemRanges);
+ virtual void slotSortRoleChanged(const QByteArray& current, const QByteArray& previous);
+
+private slots:
+ void triggerVisibleIndexRangeUpdate();
+ void updateVisibleIndexRange();
+
+ void triggerIconSizeUpdate();
+ void updateIconSize();
+
+private:
+ /**
+ * Applies the roles defined by KItemListView::visibleRoles() to the
+ * KFileItemModel and KFileItemModelRolesUpdater. As the model does not
+ * distinct between visible and invisible roles also internal roles
+ * are applied that are mandatory for having a working KFileItemModel.
+ */
+ void applyRolesToModel();
+
+ /**
+ * @return Size that is available for the icons. The size might be larger than specified by
+ * KItemListStyleOption::iconSize: With the IconsLayout also the empty left area left
+ * and right of an icon will be included.
+ */
+ QSize availableIconSize() const;
+
+private:
+ KFileItemModelRolesUpdater* m_modelRolesUpdater;
+ QTimer* m_updateVisibleIndexRangeTimer;
+ QTimer* m_updateIconSizeTimer;
+
+ friend class KFileItemListViewTest; // For unit testing
+};
+
+#endif
+
+
diff --git a/dolphin/src/kitemviews/kfileitemlistwidget.cpp b/dolphin/src/kitemviews/kfileitemlistwidget.cpp
new file mode 100644
index 0000000..fe8c7e9
--- /dev/null
+++ b/dolphin/src/kitemviews/kfileitemlistwidget.cpp
@@ -0,0 +1,164 @@
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "kfileitemlistwidget.h"
+#include "kfileitemmodel.h"
+#include "kitemlistview.h"
+
+#include <kmimetype.h>
+#include <KDebug>
+#include <KGlobal>
+#include <KLocale>
+#include <KIO/MetaData>
+#include <QDateTime>
+
+KFileItemListWidgetInformant::KFileItemListWidgetInformant() :
+ KStandardItemListWidgetInformant()
+{
+}
+
+KFileItemListWidgetInformant::~KFileItemListWidgetInformant()
+{
+}
+
+QString KFileItemListWidgetInformant::itemText(int index, const KItemListView* view) const
+{
+ Q_ASSERT(qobject_cast<KFileItemModel*>(view->model()));
+ KFileItemModel* fileItemModel = static_cast<KFileItemModel*>(view->model());
+
+ const KFileItem item = fileItemModel->fileItem(index);
+ return item.text();
+}
+
+bool KFileItemListWidgetInformant::itemIsLink(int index, const KItemListView* view) const
+{
+ Q_ASSERT(qobject_cast<KFileItemModel*>(view->model()));
+ KFileItemModel* fileItemModel = static_cast<KFileItemModel*>(view->model());
+
+ const KFileItem item = fileItemModel->fileItem(index);
+ return item.isLink();
+}
+
+QString KFileItemListWidgetInformant::roleText(const QByteArray& role,
+ const QHash<QByteArray, QVariant>& values) const
+{
+ QString text;
+ const QVariant roleValue = values.value(role);
+
+ // Implementation note: In case if more roles require a custom handling
+ // use a hash + switch for a linear runtime.
+
+ if (role == "size") {
+ if (values.value("isDir").toBool()) {
+ // The item represents a directory. Show the number of sub directories
+ // instead of the file size of the directory.
+ if (!roleValue.isNull()) {
+ const int count = roleValue.toInt();
+ if (count < 0) {
+ text = i18nc("@item:intable", "Unknown");
+ } else {
+ text = i18ncp("@item:intable", "%1 item", "%1 items", count);
+ }
+ }
+ } else {
+ const KIO::filesize_t size = roleValue.value<KIO::filesize_t>();
+ text = KGlobal::locale()->formatByteSize(size);
+ }
+ } else if (role == "date") {
+ const QDateTime dateTime = roleValue.toDateTime();
+ text = KGlobal::locale()->formatDateTime(dateTime);
+ } else {
+ text = KStandardItemListWidgetInformant::roleText(role, values);
+ }
+
+ return text;
+}
+
+QFont KFileItemListWidgetInformant::customizedFontForLinks(const QFont& baseFont) const
+{
+ // The customized font should be italic if the file is a symbolic link.
+ QFont font(baseFont);
+ font.setItalic(true);
+ return font;
+}
+
+
+KFileItemListWidget::KFileItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent) :
+ KStandardItemListWidget(informant, parent)
+{
+}
+
+KFileItemListWidget::~KFileItemListWidget()
+{
+}
+
+KItemListWidgetInformant* KFileItemListWidget::createInformant()
+{
+ return new KFileItemListWidgetInformant();
+}
+
+bool KFileItemListWidget::isRoleRightAligned(const QByteArray& role) const
+{
+ return role == "size";
+}
+
+bool KFileItemListWidget::isHidden() const
+{
+ return data().value("text").toString().startsWith(QLatin1Char('.'));
+}
+
+QFont KFileItemListWidget::customizedFont(const QFont& baseFont) const
+{
+ // The customized font should be italic if the file is a symbolic link.
+ QFont font(baseFont);
+ font.setItalic(data().value("isLink").toBool());
+ return font;
+}
+
+int KFileItemListWidget::selectionLength(const QString& text) const
+{
+ // Select the text without MIME-type extension
+ int selectionLength = text.length();
+
+ // If item is a directory, use the whole text length for
+ // selection (ignore all points)
+ if(data().value("isDir").toBool()) {
+ return selectionLength;
+ }
+
+ const QString extension = KMimeType::extractKnownExtension(text);
+ if (extension.isEmpty()) {
+ // For an unknown extension just exclude the extension after
+ // the last point. This does not work for multiple extensions like
+ // *.tar.gz but usually this is anyhow a known extension.
+ selectionLength = text.lastIndexOf(QLatin1Char('.'));
+
+ // If no point could be found, use whole text length for selection.
+ if (selectionLength < 1) {
+ selectionLength = text.length();
+ }
+
+ } else {
+ selectionLength -= extension.length() + 1;
+ }
+
+ return selectionLength;
+}
+
+#include "kfileitemlistwidget.moc"
diff --git a/dolphin/src/kitemviews/kfileitemlistwidget.h b/dolphin/src/kitemviews/kfileitemlistwidget.h
new file mode 100644
index 0000000..8e8958b
--- /dev/null
+++ b/dolphin/src/kitemviews/kfileitemlistwidget.h
@@ -0,0 +1,63 @@
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef KFILEITEMLISTWIDGET_H
+#define KFILEITEMLISTWIDGET_H
+
+#include <libdolphin_export.h>
+
+#include <kitemviews/kstandarditemlistwidget.h>
+
+class LIBDOLPHINPRIVATE_EXPORT KFileItemListWidgetInformant : public KStandardItemListWidgetInformant
+{
+public:
+ KFileItemListWidgetInformant();
+ virtual ~KFileItemListWidgetInformant();
+
+protected:
+ virtual QString itemText(int index, const KItemListView* view) const;
+ virtual bool itemIsLink(int index, const KItemListView* view) const;
+ virtual QString roleText(const QByteArray& role, const QHash<QByteArray, QVariant>& values) const;
+ virtual QFont customizedFontForLinks(const QFont& baseFont) const;
+};
+
+class LIBDOLPHINPRIVATE_EXPORT KFileItemListWidget : public KStandardItemListWidget
+{
+ Q_OBJECT
+
+public:
+ KFileItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent);
+ virtual ~KFileItemListWidget();
+
+ static KItemListWidgetInformant* createInformant();
+
+protected:
+ virtual bool isRoleRightAligned(const QByteArray& role) const;
+ virtual bool isHidden() const;
+ virtual QFont customizedFont(const QFont& baseFont) const;
+
+ /**
+ * @return Selection length without MIME-type extension
+ */
+ virtual int selectionLength(const QString& text) const;
+};
+
+#endif
+
+
diff --git a/dolphin/src/kitemviews/kfileitemmodel.cpp b/dolphin/src/kitemviews/kfileitemmodel.cpp
new file mode 100644
index 0000000..72acf77
--- /dev/null
+++ b/dolphin/src/kitemviews/kfileitemmodel.cpp
@@ -0,0 +1,2248 @@
+/*****************************************************************************
+ * Copyright (C) 2011 by Peter Penz <[email protected]> *
+ * Copyright (C) 2013 by Frank Reininghaus <[email protected]> *
+ * Copyright (C) 2013 by Emmanuel Pescosta <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ *****************************************************************************/
+
+#include "kfileitemmodel.h"
+
+#include <KGlobalSettings>
+#include <KLocale>
+#include <KStringHandler>
+#include <KDebug>
+
+#include "private/kfileitemmodelsortalgorithm.h"
+#include "private/kfileitemmodeldirlister.h"
+
+#include <QApplication>
+#include <QMimeData>
+#include <QTimer>
+#include <QWidget>
+
+#include <algorithm>
+#include <vector>
+
+// #define KFILEITEMMODEL_DEBUG
+
+KFileItemModel::KFileItemModel(QObject* parent) :
+ KItemModelBase("text", parent),
+ m_dirLister(0),
+ m_naturalSorting(KGlobalSettings::naturalSorting()),
+ m_sortDirsFirst(true),
+ m_sortRole(NameRole),
+ m_sortingProgressPercent(-1),
+ m_roles(),
+ m_caseSensitivity(Qt::CaseInsensitive),
+ m_itemData(),
+ m_items(),
+ m_filter(),
+ m_filteredItems(),
+ m_requestRole(),
+ m_maximumUpdateIntervalTimer(0),
+ m_resortAllItemsTimer(0),
+ m_pendingItemsToInsert(),
+ m_groups(),
+ m_expandedDirs(),
+ m_urlsToExpand()
+{
+ m_dirLister = new KFileItemModelDirLister(this);
+ m_dirLister->setDelayedMimeTypes(true);
+
+ const QWidget* parentWidget = qobject_cast<QWidget*>(parent);
+ if (parentWidget) {
+ m_dirLister->setMainWindow(parentWidget->window());
+ }
+
+ connect(m_dirLister, SIGNAL(started(KUrl)), this, SIGNAL(directoryLoadingStarted()));
+ connect(m_dirLister, SIGNAL(canceled()), this, SLOT(slotCanceled()));
+ connect(m_dirLister, SIGNAL(completed(KUrl)), this, SLOT(slotCompleted()));
+ connect(m_dirLister, SIGNAL(itemsAdded(KUrl,KFileItemList)), this, SLOT(slotItemsAdded(KUrl,KFileItemList)));
+ connect(m_dirLister, SIGNAL(itemsDeleted(KFileItemList)), this, SLOT(slotItemsDeleted(KFileItemList)));
+ connect(m_dirLister, SIGNAL(refreshItems(QList<QPair<KFileItem,KFileItem> >)), this, SLOT(slotRefreshItems(QList<QPair<KFileItem,KFileItem> >)));
+ connect(m_dirLister, SIGNAL(clear()), this, SLOT(slotClear()));
+ connect(m_dirLister, SIGNAL(infoMessage(QString)), this, SIGNAL(infoMessage(QString)));
+ connect(m_dirLister, SIGNAL(errorMessage(QString)), this, SIGNAL(errorMessage(QString)));
+ connect(m_dirLister, SIGNAL(redirection(KUrl,KUrl)), this, SIGNAL(directoryRedirection(KUrl,KUrl)));
+ connect(m_dirLister, SIGNAL(urlIsFileError(KUrl)), this, SIGNAL(urlIsFileError(KUrl)));
+
+ // Apply default roles that should be determined
+ resetRoles();
+ m_requestRole[NameRole] = true;
+ m_requestRole[IsDirRole] = true;
+ m_requestRole[IsLinkRole] = true;
+ m_roles.insert("text");
+ m_roles.insert("isDir");
+ m_roles.insert("isLink");
+
+ // For slow KIO-slaves like used for searching it makes sense to show results periodically even
+ // before the completed() or canceled() signal has been emitted.
+ m_maximumUpdateIntervalTimer = new QTimer(this);
+ m_maximumUpdateIntervalTimer->setInterval(2000);
+ m_maximumUpdateIntervalTimer->setSingleShot(true);
+ connect(m_maximumUpdateIntervalTimer, SIGNAL(timeout()), this, SLOT(dispatchPendingItemsToInsert()));
+
+ // When changing the value of an item which represents the sort-role a resorting must be
+ // triggered. Especially in combination with KFileItemModelRolesUpdater this might be done
+ // for a lot of items within a quite small timeslot. To prevent expensive resortings the
+ // resorting is postponed until the timer has been exceeded.
+ m_resortAllItemsTimer = new QTimer(this);
+ m_resortAllItemsTimer->setInterval(500);
+ m_resortAllItemsTimer->setSingleShot(true);
+ connect(m_resortAllItemsTimer, SIGNAL(timeout()), this, SLOT(resortAllItems()));
+
+ connect(KGlobalSettings::self(), SIGNAL(naturalSortingChanged()), this, SLOT(slotNaturalSortingChanged()));
+}
+
+KFileItemModel::~KFileItemModel()
+{
+ qDeleteAll(m_itemData);
+ qDeleteAll(m_filteredItems.values());
+ qDeleteAll(m_pendingItemsToInsert);
+}
+
+void KFileItemModel::loadDirectory(const KUrl& url)
+{
+ m_dirLister->openUrl(url);
+}
+
+void KFileItemModel::refreshDirectory(const KUrl& url)
+{
+ // Refresh all expanded directories first (Bug 295300)
+ QHashIterator<KUrl, KUrl> expandedDirs(m_expandedDirs);
+ while (expandedDirs.hasNext()) {
+ expandedDirs.next();
+ m_dirLister->openUrl(expandedDirs.value(), KDirLister::Reload);
+ }
+
+ m_dirLister->openUrl(url, KDirLister::Reload);
+}
+
+KUrl KFileItemModel::directory() const
+{
+ return m_dirLister->url();
+}
+
+void KFileItemModel::cancelDirectoryLoading()
+{
+ m_dirLister->stop();
+}
+
+int KFileItemModel::count() const
+{
+ return m_itemData.count();
+}
+
+QHash<QByteArray, QVariant> KFileItemModel::data(int index) const
+{
+ if (index >= 0 && index < count()) {
+ ItemData* data = m_itemData.at(index);
+ if (data->values.isEmpty()) {
+ data->values = retrieveData(data->item, data->parent);
+ }
+
+ return data->values;
+ }
+ return QHash<QByteArray, QVariant>();
+}
+
+bool KFileItemModel::setData(int index, const QHash<QByteArray, QVariant>& values)
+{
+ if (index < 0 || index >= count()) {
+ return false;
+ }
+
+ QHash<QByteArray, QVariant> currentValues = data(index);
+
+ // Determine which roles have been changed
+ QSet<QByteArray> changedRoles;
+ QHashIterator<QByteArray, QVariant> it(values);
+ while (it.hasNext()) {
+ it.next();
+ const QByteArray role = sharedValue(it.key());
+ const QVariant value = it.value();
+
+ if (currentValues[role] != value) {
+ currentValues[role] = value;
+ changedRoles.insert(role);
+ }
+ }
+
+ if (changedRoles.isEmpty()) {
+ return false;
+ }
+
+ m_itemData[index]->values = currentValues;
+ if (changedRoles.contains("text")) {
+ KUrl url = m_itemData[index]->item.url();
+ url.setFileName(currentValues["text"].toString());
+ m_itemData[index]->item.setUrl(url);
+ }
+
+ emitItemsChangedAndTriggerResorting(KItemRangeList() << KItemRange(index, 1), changedRoles);
+
+ return true;
+}
+
+void KFileItemModel::setSortDirectoriesFirst(bool dirsFirst)
+{
+ if (dirsFirst != m_sortDirsFirst) {
+ m_sortDirsFirst = dirsFirst;
+ resortAllItems();
+ }
+}
+
+bool KFileItemModel::sortDirectoriesFirst() const
+{
+ return m_sortDirsFirst;
+}
+
+void KFileItemModel::setShowHiddenFiles(bool show)
+{
+ m_dirLister->setShowingDotFiles(show);
+ m_dirLister->emitChanges();
+ if (show) {
+ dispatchPendingItemsToInsert();
+ }
+}
+
+bool KFileItemModel::showHiddenFiles() const
+{
+ return m_dirLister->showingDotFiles();
+}
+
+void KFileItemModel::setShowDirectoriesOnly(bool enabled)
+{
+ m_dirLister->setDirOnlyMode(enabled);
+}
+
+bool KFileItemModel::showDirectoriesOnly() const
+{
+ return m_dirLister->dirOnlyMode();
+}
+
+QMimeData* KFileItemModel::createMimeData(const KItemSet& indexes) const
+{
+ QMimeData* data = new QMimeData();
+
+ // The following code has been taken from KDirModel::mimeData()
+ // (kdelibs/kio/kio/kdirmodel.cpp)
+ // Copyright (C) 2006 David Faure <[email protected]>
+ KUrl::List urls;
+ KUrl::List mostLocalUrls;
+ bool canUseMostLocalUrls = true;
+ const ItemData* lastAddedItem = 0;
+
+ foreach (int index, indexes) {
+ const ItemData* itemData = m_itemData.at(index);
+ const ItemData* parent = itemData->parent;
+
+ while (parent && parent != lastAddedItem) {
+ parent = parent->parent;
+ }
+
+ if (parent && parent == lastAddedItem) {
+ // A parent of 'itemData' has been added already.
+ continue;
+ }
+
+ lastAddedItem = itemData;
+ const KFileItem& item = itemData->item;
+ if (!item.isNull()) {
+ urls << item.targetUrl();
+
+ bool isLocal;
+ mostLocalUrls << item.mostLocalUrl(isLocal);
+ if (!isLocal) {
+ canUseMostLocalUrls = false;
+ }
+ }
+ }
+
+ const bool different = canUseMostLocalUrls && mostLocalUrls != urls;
+ if (different) {
+ urls.populateMimeData(mostLocalUrls, data);
+ } else {
+ urls.populateMimeData(data);
+ }
+
+ return data;
+}
+
+int KFileItemModel::indexForKeyboardSearch(const QString& text, int startFromIndex) const
+{
+ startFromIndex = qMax(0, startFromIndex);
+ for (int i = startFromIndex; i < count(); ++i) {
+ if (fileItem(i).text().startsWith(text, Qt::CaseInsensitive)) {
+ return i;
+ }
+ }
+ for (int i = 0; i < startFromIndex; ++i) {
+ if (fileItem(i).text().startsWith(text, Qt::CaseInsensitive)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+bool KFileItemModel::supportsDropping(int index) const
+{
+ const KFileItem item = fileItem(index);
+ return !item.isNull() && (item.isDir() || item.isDesktopFile());
+}
+
+QString KFileItemModel::roleDescription(const QByteArray& role) const
+{
+ static QHash<QByteArray, QString> description;
+ if (description.isEmpty()) {
+ int count = 0;
+ const RoleInfoMap* map = rolesInfoMap(count);
+ for (int i = 0; i < count; ++i) {
+ description.insert(map[i].role, i18nc(map[i].roleTranslationContext, map[i].roleTranslation));
+ }
+ }
+
+ return description.value(role);
+}
+
+QList<QPair<int, QVariant> > KFileItemModel::groups() const
+{
+ if (!m_itemData.isEmpty() && m_groups.isEmpty()) {
+#ifdef KFILEITEMMODEL_DEBUG
+ QElapsedTimer timer;
+ timer.start();
+#endif
+ switch (typeForRole(sortRole())) {
+ case NameRole: m_groups = nameRoleGroups(); break;
+ case SizeRole: m_groups = sizeRoleGroups(); break;
+ case DateRole: m_groups = dateRoleGroups(); break;
+ case PermissionsRole: m_groups = permissionRoleGroups(); break;
+ case RatingRole: m_groups = ratingRoleGroups(); break;
+ default: m_groups = genericStringRoleGroups(sortRole()); break;
+ }
+
+#ifdef KFILEITEMMODEL_DEBUG
+ kDebug() << "[TIME] Calculating groups for" << count() << "items:" << timer.elapsed();
+#endif
+ }
+
+ return m_groups;
+}
+
+KFileItem KFileItemModel::fileItem(int index) const
+{
+ if (index >= 0 && index < count()) {
+ return m_itemData.at(index)->item;
+ }
+
+ return KFileItem();
+}
+
+KFileItem KFileItemModel::fileItem(const KUrl& url) const
+{
+ const int indexForUrl = index(url);
+ if (indexForUrl >= 0) {
+ return m_itemData.at(indexForUrl)->item;
+ }
+ return KFileItem();
+}
+
+int KFileItemModel::index(const KFileItem& item) const
+{
+ return index(item.url());
+}
+
+int KFileItemModel::index(const KUrl& url) const
+{
+ KUrl urlToFind = url;
+ urlToFind.adjustPath(KUrl::RemoveTrailingSlash);
+
+ const int itemCount = m_itemData.count();
+ int itemsInHash = m_items.count();
+
+ int index = m_items.value(urlToFind, -1);
+ while (index < 0 && itemsInHash < itemCount) {
+ // Not all URLs are stored yet in m_items. We grow m_items until either
+ // urlToFind is found, or all URLs have been stored in m_items.
+ // Note that we do not add the URLs to m_items one by one, but in
+ // larger blocks. After each block, we check if urlToFind is in
+ // m_items. We could in principle compare urlToFind with each URL while
+ // we are going through m_itemData, but comparing two QUrls will,
+ // unlike calling qHash for the URLs, trigger a parsing of the URLs
+ // which costs both CPU cycles and memory.
+ const int blockSize = 1000;
+ const int currentBlockEnd = qMin(itemsInHash + blockSize, itemCount);
+ for (int i = itemsInHash; i < currentBlockEnd; ++i) {
+ const KUrl nextUrl = m_itemData.at(i)->item.url();
+ m_items.insert(nextUrl, i);
+ }
+
+ itemsInHash = currentBlockEnd;
+ index = m_items.value(urlToFind, -1);
+ }
+
+ if (index < 0) {
+ // The item could not be found, even though all items from m_itemData
+ // should be in m_items now. We print some diagnostic information which
+ // might help to find the cause of the problem, but only once. This
+ // prevents that obtaining and printing the debugging information
+ // wastes CPU cycles and floods the shell or .xsession-errors.
+ static bool printDebugInfo = true;
+
+ if (m_items.count() != m_itemData.count() && printDebugInfo) {
+ printDebugInfo = false;
+
+ kWarning() << "The model is in an inconsistent state.";
+ kWarning() << "m_items.count() ==" << m_items.count();
+ kWarning() << "m_itemData.count() ==" << m_itemData.count();
+
+ // Check if there are multiple items with the same URL.
+ QMultiHash<KUrl, int> indexesForUrl;
+ for (int i = 0; i < m_itemData.count(); ++i) {
+ indexesForUrl.insert(m_itemData.at(i)->item.url(), i);
+ }
+
+ foreach (const KUrl& url, indexesForUrl.uniqueKeys()) {
+ if (indexesForUrl.count(url) > 1) {
+ kWarning() << "Multiple items found with the URL" << url;
+ foreach (int index, indexesForUrl.values(url)) {
+ const ItemData* data = m_itemData.at(index);
+ kWarning() << "index" << index << ":" << data->item;
+ if (data->parent) {
+ kWarning() << "parent" << data->parent->item;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return index;
+}
+
+KFileItem KFileItemModel::rootItem() const
+{
+ return m_dirLister->rootItem();
+}
+
+void KFileItemModel::clear()
+{
+ slotClear();
+}
+
+void KFileItemModel::setRoles(const QSet<QByteArray>& roles)
+{
+ if (m_roles == roles) {
+ return;
+ }
+
+ const QSet<QByteArray> changedRoles = (roles - m_roles) + (m_roles - roles);
+ m_roles = roles;
+
+ if (count() > 0) {
+ const bool supportedExpanding = m_requestRole[ExpandedParentsCountRole];
+ const bool willSupportExpanding = roles.contains("expandedParentsCount");
+ if (supportedExpanding && !willSupportExpanding) {
+ // No expanding is supported anymore. Take care to delete all items that have an expansion level
+ // that is not 0 (and hence are part of an expanded item).
+ removeExpandedItems();
+ }
+ }
+
+ m_groups.clear();
+ resetRoles();
+
+ QSetIterator<QByteArray> it(roles);
+ while (it.hasNext()) {
+ const QByteArray& role = it.next();
+ m_requestRole[typeForRole(role)] = true;
+ }
+
+ if (count() > 0) {
+ // Update m_data with the changed requested roles
+ const int maxIndex = count() - 1;
+ for (int i = 0; i <= maxIndex; ++i) {
+ m_itemData[i]->values = retrieveData(m_itemData.at(i)->item, m_itemData.at(i)->parent);
+ }
+
+ emit itemsChanged(KItemRangeList() << KItemRange(0, count()), changedRoles);
+ }
+
+ // Clear the 'values' of all filtered items. They will be re-populated with the
+ // correct roles the next time 'values' will be accessed via data(int).
+ QHash<KFileItem, ItemData*>::iterator filteredIt = m_filteredItems.begin();
+ const QHash<KFileItem, ItemData*>::iterator filteredEnd = m_filteredItems.end();
+ while (filteredIt != filteredEnd) {
+ (*filteredIt)->values.clear();
+ ++filteredIt;
+ }
+}
+
+QSet<QByteArray> KFileItemModel::roles() const
+{
+ return m_roles;
+}
+
+bool KFileItemModel::setExpanded(int index, bool expanded)
+{
+ if (!isExpandable(index) || isExpanded(index) == expanded) {
+ return false;
+ }
+
+ QHash<QByteArray, QVariant> values;
+ values.insert(sharedValue("isExpanded"), expanded);
+ if (!setData(index, values)) {
+ return false;
+ }
+
+ const KFileItem item = m_itemData.at(index)->item;
+ const KUrl url = item.url();
+ const KUrl targetUrl = item.targetUrl();
+ if (expanded) {
+ m_expandedDirs.insert(targetUrl, url);
+ m_dirLister->openUrl(url, KDirLister::Keep);
+
+ const KUrl::List previouslyExpandedChildren = m_itemData.at(index)->values.value("previouslyExpandedChildren").value<KUrl::List>();
+ foreach (const KUrl& url, previouslyExpandedChildren) {
+ m_urlsToExpand.insert(url);
+ }
+ } else {
+ // Note that there might be (indirect) children of the folder which is to be collapsed in
+ // m_pendingItemsToInsert. To prevent that they will be inserted into the model later,
+ // possibly without a parent, which might result in a crash, we insert all pending items
+ // right now. All new items which would be without a parent will then be removed.
+ dispatchPendingItemsToInsert();
+
+ // Check if the index of the collapsed folder has changed. If that is the case, then items
+ // were inserted before the collapsed folder, and its index needs to be updated.
+ if (m_itemData.at(index)->item != item) {
+ index = this->index(item);
+ }
+
+ m_expandedDirs.remove(targetUrl);
+ m_dirLister->stop(url);
+
+ const int parentLevel = expandedParentsCount(index);
+ const int itemCount = m_itemData.count();
+ const int firstChildIndex = index + 1;
+
+ KUrl::List expandedChildren;
+
+ int childIndex = firstChildIndex;
+ while (childIndex < itemCount && expandedParentsCount(childIndex) > parentLevel) {
+ ItemData* itemData = m_itemData.at(childIndex);
+ if (itemData->values.value("isExpanded").toBool()) {
+ const KUrl targetUrl = itemData->item.targetUrl();
+ const KUrl url = itemData->item.url();
+ m_expandedDirs.remove(targetUrl);
+ m_dirLister->stop(url); // TODO: try to unit-test this, see https://bugs.kde.org/show_bug.cgi?id=332102#c11
+ expandedChildren.append(targetUrl);
+ }
+ ++childIndex;
+ }
+ const int childrenCount = childIndex - firstChildIndex;
+
+ removeFilteredChildren(KItemRangeList() << KItemRange(index, 1 + childrenCount));
+ removeItems(KItemRangeList() << KItemRange(firstChildIndex, childrenCount), DeleteItemData);
+
+ m_itemData.at(index)->values.insert("previouslyExpandedChildren", expandedChildren);
+ }
+
+ return true;
+}
+
+bool KFileItemModel::isExpanded(int index) const
+{
+ if (index >= 0 && index < count()) {
+ return m_itemData.at(index)->values.value("isExpanded").toBool();
+ }
+ return false;
+}
+
+bool KFileItemModel::isExpandable(int index) const
+{
+ if (index >= 0 && index < count()) {
+ // Call data (instead of accessing m_itemData directly)
+ // to ensure that the value is initialized.
+ return data(index).value("isExpandable").toBool();
+ }
+ return false;
+}
+
+int KFileItemModel::expandedParentsCount(int index) const
+{
+ if (index >= 0 && index < count()) {
+ return expandedParentsCount(m_itemData.at(index));
+ }
+ return 0;
+}
+
+QSet<KUrl> KFileItemModel::expandedDirectories() const
+{
+ return m_expandedDirs.values().toSet();
+}
+
+void KFileItemModel::restoreExpandedDirectories(const QSet<KUrl>& urls)
+{
+ m_urlsToExpand = urls;
+}
+
+void KFileItemModel::expandParentDirectories(const KUrl& url)
+{
+ const int pos = m_dirLister->url().path().length();
+
+ // Assure that each sub-path of the URL that should be
+ // expanded is added to m_urlsToExpand. KDirLister
+ // does not care whether the parent-URL has already been
+ // expanded.
+ KUrl urlToExpand = m_dirLister->url();
+ const QStringList subDirs = url.path().mid(pos).split(QDir::separator());
+ for (int i = 0; i < subDirs.count() - 1; ++i) {
+ urlToExpand.addPath(subDirs.at(i));
+ m_urlsToExpand.insert(urlToExpand);
+ }
+
+ // KDirLister::open() must called at least once to trigger an initial
+ // loading. The pending URLs that must be restored are handled
+ // in slotCompleted().
+ QSetIterator<KUrl> it2(m_urlsToExpand);
+ while (it2.hasNext()) {
+ const int idx = index(it2.next());
+ if (idx >= 0 && !isExpanded(idx)) {
+ setExpanded(idx, true);
+ break;
+ }
+ }
+}
+
+void KFileItemModel::setNameFilter(const QString& nameFilter)
+{
+ if (m_filter.pattern() != nameFilter) {
+ dispatchPendingItemsToInsert();
+ m_filter.setPattern(nameFilter);
+ applyFilters();
+ }
+}
+
+QString KFileItemModel::nameFilter() const
+{
+ return m_filter.pattern();
+}
+
+void KFileItemModel::setMimeTypeFilters(const QStringList& filters)
+{
+ if (m_filter.mimeTypes() != filters) {
+ dispatchPendingItemsToInsert();
+ m_filter.setMimeTypes(filters);
+ applyFilters();
+ }
+}
+
+QStringList KFileItemModel::mimeTypeFilters() const
+{
+ return m_filter.mimeTypes();
+}
+
+
+void KFileItemModel::applyFilters()
+{
+ // Check which shown items from m_itemData must get
+ // hidden and hence moved to m_filteredItems.
+ QVector<int> newFilteredIndexes;
+
+ const int itemCount = m_itemData.count();
+ for (int index = 0; index < itemCount; ++index) {
+ ItemData* itemData = m_itemData.at(index);
+
+ // Only filter non-expanded items as child items may never
+ // exist without a parent item
+ if (!itemData->values.value("isExpanded").toBool()) {
+ const KFileItem item = itemData->item;
+ if (!m_filter.matches(item)) {
+ newFilteredIndexes.append(index);
+ m_filteredItems.insert(item, itemData);
+ }
+ }
+ }
+
+ const KItemRangeList removedRanges = KItemRangeList::fromSortedContainer(newFilteredIndexes);
+ removeItems(removedRanges, KeepItemData);
+
+ // Check which hidden items from m_filteredItems should
+ // get visible again and hence removed from m_filteredItems.
+ QList<ItemData*> newVisibleItems;
+
+ QHash<KFileItem, ItemData*>::iterator it = m_filteredItems.begin();
+ while (it != m_filteredItems.end()) {
+ if (m_filter.matches(it.key())) {
+ newVisibleItems.append(it.value());
+ it = m_filteredItems.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ insertItems(newVisibleItems);
+}
+
+void KFileItemModel::removeFilteredChildren(const KItemRangeList& itemRanges)
+{
+ if (m_filteredItems.isEmpty() || !m_requestRole[ExpandedParentsCountRole]) {
+ // There are either no filtered items, or it is not possible to expand
+ // folders -> there cannot be any filtered children.
+ return;
+ }
+
+ QSet<ItemData*> parents;
+ foreach (const KItemRange& range, itemRanges) {
+ for (int index = range.index; index < range.index + range.count; ++index) {
+ parents.insert(m_itemData.at(index));
+ }
+ }
+
+ QHash<KFileItem, ItemData*>::iterator it = m_filteredItems.begin();
+ while (it != m_filteredItems.end()) {
+ if (parents.contains(it.value()->parent)) {
+ delete it.value();
+ it = m_filteredItems.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+QList<KFileItemModel::RoleInfo> KFileItemModel::rolesInformation()
+{
+ static QList<RoleInfo> rolesInfo;
+ if (rolesInfo.isEmpty()) {
+ int count = 0;
+ const RoleInfoMap* map = rolesInfoMap(count);
+ for (int i = 0; i < count; ++i) {
+ if (map[i].roleType != NoRole) {
+ RoleInfo info;
+ info.role = map[i].role;
+ info.translation = i18nc(map[i].roleTranslationContext, map[i].roleTranslation);
+ if (map[i].groupTranslation) {
+ info.group = i18nc(map[i].groupTranslationContext, map[i].groupTranslation);
+ } else {
+ // For top level roles, groupTranslation is 0. We must make sure that
+ // info.group is an empty string then because the code that generates
+ // menus tries to put the actions into sub menus otherwise.
+ info.group = QString();
+ }
+ info.requiresBaloo = map[i].requiresBaloo;
+ info.requiresIndexer = map[i].requiresIndexer;
+ rolesInfo.append(info);
+ }
+ }
+ }
+
+ return rolesInfo;
+}
+
+void KFileItemModel::onGroupedSortingChanged(bool current)
+{
+ Q_UNUSED(current);
+ m_groups.clear();
+}
+
+void KFileItemModel::onSortRoleChanged(const QByteArray& current, const QByteArray& previous)
+{
+ Q_UNUSED(previous);
+ m_sortRole = typeForRole(current);
+
+ if (!m_requestRole[m_sortRole]) {
+ QSet<QByteArray> newRoles = m_roles;
+ newRoles << current;
+ setRoles(newRoles);
+ }
+
+ resortAllItems();
+}
+
+void KFileItemModel::onSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous)
+{
+ Q_UNUSED(current);
+ Q_UNUSED(previous);
+ resortAllItems();
+}
+
+void KFileItemModel::resortAllItems()
+{
+ m_resortAllItemsTimer->stop();
+
+ const int itemCount = count();
+ if (itemCount <= 0) {
+ return;
+ }
+
+#ifdef KFILEITEMMODEL_DEBUG
+ QElapsedTimer timer;
+ timer.start();
+ kDebug() << "===========================================================";
+ kDebug() << "Resorting" << itemCount << "items";
+#endif
+
+ // Remember the order of the current URLs so
+ // that it can be determined which indexes have
+ // been moved because of the resorting.
+ QList<KUrl> oldUrls;
+ oldUrls.reserve(itemCount);
+ foreach (const ItemData* itemData, m_itemData) {
+ oldUrls.append(itemData->item.url());
+ }
+
+ m_items.clear();
+ m_items.reserve(itemCount);
+
+ // Resort the items
+ sort(m_itemData.begin(), m_itemData.end());
+ for (int i = 0; i < itemCount; ++i) {
+ m_items.insert(m_itemData.at(i)->item.url(), i);
+ }
+
+ // Determine the first index that has been moved.
+ int firstMovedIndex = 0;
+ while (firstMovedIndex < itemCount
+ && firstMovedIndex == m_items.value(oldUrls.at(firstMovedIndex))) {
+ ++firstMovedIndex;
+ }
+
+ const bool itemsHaveMoved = firstMovedIndex < itemCount;
+ if (itemsHaveMoved) {
+ m_groups.clear();
+
+ int lastMovedIndex = itemCount - 1;
+ while (lastMovedIndex > firstMovedIndex
+ && lastMovedIndex == m_items.value(oldUrls.at(lastMovedIndex))) {
+ --lastMovedIndex;
+ }
+
+ Q_ASSERT(firstMovedIndex <= lastMovedIndex);
+
+ // Create a list movedToIndexes, which has the property that
+ // movedToIndexes[i] is the new index of the item with the old index
+ // firstMovedIndex + i.
+ const int movedItemsCount = lastMovedIndex - firstMovedIndex + 1;
+ QList<int> movedToIndexes;
+ movedToIndexes.reserve(movedItemsCount);
+ for (int i = firstMovedIndex; i <= lastMovedIndex; ++i) {
+ const int newIndex = m_items.value(oldUrls.at(i));
+ movedToIndexes.append(newIndex);
+ }
+
+ emit itemsMoved(KItemRange(firstMovedIndex, movedItemsCount), movedToIndexes);
+ } else if (groupedSorting()) {
+ // The groups might have changed even if the order of the items has not.
+ const QList<QPair<int, QVariant> > oldGroups = m_groups;
+ m_groups.clear();
+ if (groups() != oldGroups) {
+ emit groupsChanged();
+ }
+ }
+
+#ifdef KFILEITEMMODEL_DEBUG
+ kDebug() << "[TIME] Resorting of" << itemCount << "items:" << timer.elapsed();
+#endif
+}
+
+void KFileItemModel::slotCompleted()
+{
+ dispatchPendingItemsToInsert();
+
+ if (!m_urlsToExpand.isEmpty()) {
+ // Try to find a URL that can be expanded.
+ // Note that the parent folder must be expanded before any of its subfolders become visible.
+ // Therefore, some URLs in m_restoredExpandedUrls might not be visible yet
+ // -> we expand the first visible URL we find in m_restoredExpandedUrls.
+ foreach (const KUrl& url, m_urlsToExpand) {
+ const int indexForUrl = index(url);
+ if (indexForUrl >= 0) {
+ m_urlsToExpand.remove(url);
+ if (setExpanded(indexForUrl, true)) {
+ // The dir lister has been triggered. This slot will be called
+ // again after the directory has been expanded.
+ return;
+ }
+ }
+ }
+
+ // None of the URLs in m_restoredExpandedUrls could be found in the model. This can happen
+ // if these URLs have been deleted in the meantime.
+ m_urlsToExpand.clear();
+ }
+
+ emit directoryLoadingCompleted();
+}
+
+void KFileItemModel::slotCanceled()
+{
+ m_maximumUpdateIntervalTimer->stop();
+ dispatchPendingItemsToInsert();
+
+ emit directoryLoadingCanceled();
+}
+
+void KFileItemModel::slotItemsAdded(const KUrl& directoryUrl, const KFileItemList& items)
+{
+ Q_ASSERT(!items.isEmpty());
+
+ KUrl parentUrl;
+ if (m_expandedDirs.contains(directoryUrl)) {
+ parentUrl = m_expandedDirs.value(directoryUrl);
+ } else {
+ parentUrl = directoryUrl;
+ parentUrl.adjustPath(KUrl::RemoveTrailingSlash);
+ }
+
+ if (m_requestRole[ExpandedParentsCountRole]) {
+ // If the expanding of items is enabled, the call
+ // dirLister->openUrl(url, KDirLister::Keep) in KFileItemModel::setExpanded()
+ // might result in emitting the same items twice due to the Keep-parameter.
+ // This case happens if an item gets expanded, collapsed and expanded again
+ // before the items could be loaded for the first expansion.
+ if (index(items.first().url()) >= 0) {
+ // The items are already part of the model.
+ return;
+ }
+
+ if (directoryUrl != directory()) {
+ // To be able to compare whether the new items may be inserted as children
+ // of a parent item the pending items must be added to the model first.
+ dispatchPendingItemsToInsert();
+ }
+
+ // KDirLister keeps the children of items that got expanded once even if
+ // they got collapsed again with KFileItemModel::setExpanded(false). So it must be
+ // checked whether the parent for new items is still expanded.
+ const int parentIndex = index(parentUrl);
+ if (parentIndex >= 0 && !m_itemData[parentIndex]->values.value("isExpanded").toBool()) {
+ // The parent is not expanded.
+ return;
+ }
+ }
+
+ QList<ItemData*> itemDataList = createItemDataList(parentUrl, items);
+
+ if (!m_filter.hasSetFilters()) {
+ m_pendingItemsToInsert.append(itemDataList);
+ } else {
+ // The name or type filter is active. Hide filtered items
+ // before inserting them into the model and remember
+ // the filtered items in m_filteredItems.
+ foreach (ItemData* itemData, itemDataList) {
+ if (m_filter.matches(itemData->item)) {
+ m_pendingItemsToInsert.append(itemData);
+ } else {
+ m_filteredItems.insert(itemData->item, itemData);
+ }
+ }
+ }
+
+ if (useMaximumUpdateInterval() && !m_maximumUpdateIntervalTimer->isActive()) {
+ // Assure that items get dispatched if no completed() or canceled() signal is
+ // emitted during the maximum update interval.
+ m_maximumUpdateIntervalTimer->start();
+ }
+}
+
+void KFileItemModel::slotItemsDeleted(const KFileItemList& items)
+{
+ dispatchPendingItemsToInsert();
+
+ QVector<int> indexesToRemove;
+ indexesToRemove.reserve(items.count());
+
+ foreach (const KFileItem& item, items) {
+ const int indexForItem = index(item);
+ if (indexForItem >= 0) {
+ indexesToRemove.append(indexForItem);
+ } else {
+ // Probably the item has been filtered.
+ QHash<KFileItem, ItemData*>::iterator it = m_filteredItems.find(item);
+ if (it != m_filteredItems.end()) {
+ delete it.value();
+ m_filteredItems.erase(it);
+ }
+ }
+ }
+
+ std::sort(indexesToRemove.begin(), indexesToRemove.end());
+
+ if (m_requestRole[ExpandedParentsCountRole] && !m_expandedDirs.isEmpty()) {
+ // Assure that removing a parent item also results in removing all children
+ QVector<int> indexesToRemoveWithChildren;
+ indexesToRemoveWithChildren.reserve(m_itemData.count());
+
+ const int itemCount = m_itemData.count();
+ foreach (int index, indexesToRemove) {
+ indexesToRemoveWithChildren.append(index);
+
+ const int parentLevel = expandedParentsCount(index);
+ int childIndex = index + 1;
+ while (childIndex < itemCount && expandedParentsCount(childIndex) > parentLevel) {
+ indexesToRemoveWithChildren.append(childIndex);
+ ++childIndex;
+ }
+ }
+
+ indexesToRemove = indexesToRemoveWithChildren;
+ }
+
+ const KItemRangeList itemRanges = KItemRangeList::fromSortedContainer(indexesToRemove);
+ removeFilteredChildren(itemRanges);
+ removeItems(itemRanges, DeleteItemData);
+}
+
+void KFileItemModel::slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >& items)
+{
+ Q_ASSERT(!items.isEmpty());
+#ifdef KFILEITEMMODEL_DEBUG
+ kDebug() << "Refreshing" << items.count() << "items";
+#endif
+
+ // Get the indexes of all items that have been refreshed
+ QList<int> indexes;
+ indexes.reserve(items.count());
+
+ QSet<QByteArray> changedRoles;
+
+ QListIterator<QPair<KFileItem, KFileItem> > it(items);
+ while (it.hasNext()) {
+ const QPair<KFileItem, KFileItem>& itemPair = it.next();
+ const KFileItem& oldItem = itemPair.first;
+ const KFileItem& newItem = itemPair.second;
+ const int indexForItem = index(oldItem);
+ if (indexForItem >= 0) {
+ m_itemData[indexForItem]->item = newItem;
+
+ // Keep old values as long as possible if they could not retrieved synchronously yet.
+ // The update of the values will be done asynchronously by KFileItemModelRolesUpdater.
+ QHashIterator<QByteArray, QVariant> it(retrieveData(newItem, m_itemData.at(indexForItem)->parent));
+ QHash<QByteArray, QVariant>& values = m_itemData[indexForItem]->values;
+ while (it.hasNext()) {
+ it.next();
+ const QByteArray& role = it.key();
+ if (values.value(role) != it.value()) {
+ values.insert(role, it.value());
+ changedRoles.insert(role);
+ }
+ }
+
+ m_items.remove(oldItem.url());
+ m_items.insert(newItem.url(), indexForItem);
+ indexes.append(indexForItem);
+ } else {
+ // Check if 'oldItem' is one of the filtered items.
+ QHash<KFileItem, ItemData*>::iterator it = m_filteredItems.find(oldItem);
+ if (it != m_filteredItems.end()) {
+ ItemData* itemData = it.value();
+ itemData->item = newItem;
+
+ // The data stored in 'values' might have changed. Therefore, we clear
+ // 'values' and re-populate it the next time it is requested via data(int).
+ itemData->values.clear();
+
+ m_filteredItems.erase(it);
+ m_filteredItems.insert(newItem, itemData);
+ }
+ }
+ }
+
+ // If the changed items have been created recently, they might not be in m_items yet.
+ // In that case, the list 'indexes' might be empty.
+ if (indexes.isEmpty()) {
+ return;
+ }
+
+ // Extract the item-ranges out of the changed indexes
+ qSort(indexes);
+ const KItemRangeList itemRangeList = KItemRangeList::fromSortedContainer(indexes);
+ emitItemsChangedAndTriggerResorting(itemRangeList, changedRoles);
+}
+
+void KFileItemModel::slotClear()
+{
+#ifdef KFILEITEMMODEL_DEBUG
+ kDebug() << "Clearing all items";
+#endif
+
+ qDeleteAll(m_filteredItems.values());
+ m_filteredItems.clear();
+ m_groups.clear();
+
+ m_maximumUpdateIntervalTimer->stop();
+ m_resortAllItemsTimer->stop();
+
+ qDeleteAll(m_pendingItemsToInsert);
+ m_pendingItemsToInsert.clear();
+
+ const int removedCount = m_itemData.count();
+ if (removedCount > 0) {
+ qDeleteAll(m_itemData);
+ m_itemData.clear();
+ m_items.clear();
+ emit itemsRemoved(KItemRangeList() << KItemRange(0, removedCount));
+ }
+
+ m_expandedDirs.clear();
+}
+
+void KFileItemModel::slotNaturalSortingChanged()
+{
+ m_naturalSorting = KGlobalSettings::naturalSorting();
+ resortAllItems();
+}
+
+void KFileItemModel::dispatchPendingItemsToInsert()
+{
+ if (!m_pendingItemsToInsert.isEmpty()) {
+ insertItems(m_pendingItemsToInsert);
+ m_pendingItemsToInsert.clear();
+ }
+}
+
+void KFileItemModel::insertItems(QList<ItemData*>& newItems)
+{
+ if (newItems.isEmpty()) {
+ return;
+ }
+
+#ifdef KFILEITEMMODEL_DEBUG
+ QElapsedTimer timer;
+ timer.start();
+ kDebug() << "===========================================================";
+ kDebug() << "Inserting" << newItems.count() << "items";
+#endif
+
+ m_groups.clear();
+ prepareItemsForSorting(newItems);
+
+ if (m_sortRole == NameRole && m_naturalSorting) {
+ // Natural sorting of items can be very slow. However, it becomes much
+ // faster if the input sequence is already mostly sorted. Therefore, we
+ // first sort 'newItems' according to the QStrings returned by
+ // KFileItem::text() using QString::operator<(), which is quite fast.
+ parallelMergeSort(newItems.begin(), newItems.end(), nameLessThan, QThread::idealThreadCount());
+ }
+
+ sort(newItems.begin(), newItems.end());
+
+#ifdef KFILEITEMMODEL_DEBUG
+ kDebug() << "[TIME] Sorting:" << timer.elapsed();
+#endif
+
+ KItemRangeList itemRanges;
+ const int existingItemCount = m_itemData.count();
+ const int newItemCount = newItems.count();
+ const int totalItemCount = existingItemCount + newItemCount;
+
+ if (existingItemCount == 0) {
+ // Optimization for the common special case that there are no
+ // items in the model yet. Happens, e.g., when entering a folder.
+ m_itemData = newItems;
+ itemRanges << KItemRange(0, newItemCount);
+ } else {
+ m_itemData.reserve(totalItemCount);
+ for (int i = existingItemCount; i < totalItemCount; ++i) {
+ m_itemData.append(0);
+ }
+
+ // We build the new list m_itemData in reverse order to minimize
+ // the number of moves and guarantee O(N) complexity.
+ int targetIndex = totalItemCount - 1;
+ int sourceIndexExistingItems = existingItemCount - 1;
+ int sourceIndexNewItems = newItemCount - 1;
+
+ int rangeCount = 0;
+
+ while (sourceIndexNewItems >= 0) {
+ ItemData* newItem = newItems.at(sourceIndexNewItems);
+ if (sourceIndexExistingItems >= 0 && lessThan(newItem, m_itemData.at(sourceIndexExistingItems))) {
+ // Move an existing item to its new position. If any new items
+ // are behind it, push the item range to itemRanges.
+ if (rangeCount > 0) {
+ itemRanges << KItemRange(sourceIndexExistingItems + 1, rangeCount);
+ rangeCount = 0;
+ }
+
+ m_itemData[targetIndex] = m_itemData.at(sourceIndexExistingItems);
+ --sourceIndexExistingItems;
+ } else {
+ // Insert a new item into the list.
+ ++rangeCount;
+ m_itemData[targetIndex] = newItem;
+ --sourceIndexNewItems;
+ }
+ --targetIndex;
+ }
+
+ // Push the final item range to itemRanges.
+ if (rangeCount > 0) {
+ itemRanges << KItemRange(sourceIndexExistingItems + 1, rangeCount);
+ }
+
+ // Note that itemRanges is still sorted in reverse order.
+ std::reverse(itemRanges.begin(), itemRanges.end());
+ }
+
+ // The indexes in m_items are not correct anymore. Therefore, we clear m_items.
+ // It will be re-populated with the updated indices if index(const KUrl&) is called.
+ m_items.clear();
+
+ emit itemsInserted(itemRanges);
+
+#ifdef KFILEITEMMODEL_DEBUG
+ kDebug() << "[TIME] Inserting of" << newItems.count() << "items:" << timer.elapsed();
+#endif
+}
+
+void KFileItemModel::removeItems(const KItemRangeList& itemRanges, RemoveItemsBehavior behavior)
+{
+ if (itemRanges.isEmpty()) {
+ return;
+ }
+
+ m_groups.clear();
+
+ // Step 1: Remove the items from m_itemData, and free the ItemData.
+ int removedItemsCount = 0;
+ foreach (const KItemRange& range, itemRanges) {
+ removedItemsCount += range.count;
+
+ for (int index = range.index; index < range.index + range.count; ++index) {
+ if (behavior == DeleteItemData) {
+ delete m_itemData.at(index);
+ }
+
+ m_itemData[index] = 0;
+ }
+ }
+
+ // Step 2: Remove the ItemData pointers from the list m_itemData.
+ int target = itemRanges.at(0).index;
+ int source = itemRanges.at(0).index + itemRanges.at(0).count;
+ int nextRange = 1;
+
+ const int oldItemDataCount = m_itemData.count();
+ while (source < oldItemDataCount) {
+ m_itemData[target] = m_itemData[source];
+ ++target;
+ ++source;
+
+ if (nextRange < itemRanges.count() && source == itemRanges.at(nextRange).index) {
+ // Skip the items in the next removed range.
+ source += itemRanges.at(nextRange).count;
+ ++nextRange;
+ }
+ }
+
+ m_itemData.erase(m_itemData.end() - removedItemsCount, m_itemData.end());
+
+ // The indexes in m_items are not correct anymore. Therefore, we clear m_items.
+ // It will be re-populated with the updated indices if index(const KUrl&) is called.
+ m_items.clear();
+
+ emit itemsRemoved(itemRanges);
+}
+
+QList<KFileItemModel::ItemData*> KFileItemModel::createItemDataList(const KUrl& parentUrl, const KFileItemList& items) const
+{
+ if (m_sortRole == TypeRole) {
+ // Try to resolve the MIME-types synchronously to prevent a reordering of
+ // the items when sorting by type (per default MIME-types are resolved
+ // asynchronously by KFileItemModelRolesUpdater).
+ determineMimeTypes(items, 200);
+ }
+
+ const int parentIndex = index(parentUrl);
+ ItemData* parentItem = parentIndex < 0 ? 0 : m_itemData.at(parentIndex);
+
+ QList<ItemData*> itemDataList;
+ itemDataList.reserve(items.count());
+
+ foreach (const KFileItem& item, items) {
+ ItemData* itemData = new ItemData();
+ itemData->item = item;
+ itemData->parent = parentItem;
+ itemDataList.append(itemData);
+ }
+
+ return itemDataList;
+}
+
+void KFileItemModel::prepareItemsForSorting(QList<ItemData*>& itemDataList)
+{
+ switch (m_sortRole) {
+ case PermissionsRole:
+ case OwnerRole:
+ case GroupRole:
+ case DestinationRole:
+ case PathRole:
+ // These roles can be determined with retrieveData, and they have to be stored
+ // in the QHash "values" for the sorting.
+ foreach (ItemData* itemData, itemDataList) {
+ if (itemData->values.isEmpty()) {
+ itemData->values = retrieveData(itemData->item, itemData->parent);
+ }
+ }
+ break;
+
+ case TypeRole:
+ // At least store the data including the file type for items with known MIME type.
+ foreach (ItemData* itemData, itemDataList) {
+ if (itemData->values.isEmpty()) {
+ const KFileItem item = itemData->item;
+ if (item.isDir() || item.isMimeTypeKnown()) {
+ itemData->values = retrieveData(itemData->item, itemData->parent);
+ }
+ }
+ }
+ break;
+
+ default:
+ // The other roles are either resolved by KFileItemModelRolesUpdater
+ // (this includes the SizeRole for directories), or they do not need
+ // to be stored in the QHash "values" for sorting because the data can
+ // be retrieved directly from the KFileItem (NameRole, SizeRole for files,
+ // DateRole).
+ break;
+ }
+}
+
+int KFileItemModel::expandedParentsCount(const ItemData* data)
+{
+ // The hash 'values' is only guaranteed to contain the key "expandedParentsCount"
+ // if the corresponding item is expanded, and it is not a top-level item.
+ const ItemData* parent = data->parent;
+ if (parent) {
+ if (parent->parent) {
+ Q_ASSERT(parent->values.contains("expandedParentsCount"));
+ return parent->values.value("expandedParentsCount").toInt() + 1;
+ } else {
+ return 1;
+ }
+ } else {
+ return 0;
+ }
+}
+
+void KFileItemModel::removeExpandedItems()
+{
+ QVector<int> indexesToRemove;
+
+ const int maxIndex = m_itemData.count() - 1;
+ for (int i = 0; i <= maxIndex; ++i) {
+ const ItemData* itemData = m_itemData.at(i);
+ if (itemData->parent) {
+ indexesToRemove.append(i);
+ }
+ }
+
+ removeItems(KItemRangeList::fromSortedContainer(indexesToRemove), DeleteItemData);
+ m_expandedDirs.clear();
+
+ // Also remove all filtered items which have a parent.
+ QHash<KFileItem, ItemData*>::iterator it = m_filteredItems.begin();
+ const QHash<KFileItem, ItemData*>::iterator end = m_filteredItems.end();
+
+ while (it != end) {
+ if (it.value()->parent) {
+ delete it.value();
+ it = m_filteredItems.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+void KFileItemModel::emitItemsChangedAndTriggerResorting(const KItemRangeList& itemRanges, const QSet<QByteArray>& changedRoles)
+{
+ emit itemsChanged(itemRanges, changedRoles);
+
+ // Trigger a resorting if necessary. Note that this can happen even if the sort
+ // role has not changed at all because the file name can be used as a fallback.
+ if (changedRoles.contains(sortRole()) || changedRoles.contains(roleForType(NameRole))) {
+ foreach (const KItemRange& range, itemRanges) {
+ bool needsResorting = false;
+
+ const int first = range.index;
+ const int last = range.index + range.count - 1;
+
+ // Resorting the model is necessary if
+ // (a) The first item in the range is "lessThan" its predecessor,
+ // (b) the successor of the last item is "lessThan" the last item, or
+ // (c) the internal order of the items in the range is incorrect.
+ if (first > 0
+ && lessThan(m_itemData.at(first), m_itemData.at(first - 1))) {
+ needsResorting = true;
+ } else if (last < count() - 1
+ && lessThan(m_itemData.at(last + 1), m_itemData.at(last))) {
+ needsResorting = true;
+ } else {
+ for (int index = first; index < last; ++index) {
+ if (lessThan(m_itemData.at(index + 1), m_itemData.at(index))) {
+ needsResorting = true;
+ break;
+ }
+ }
+ }
+
+ if (needsResorting) {
+ m_resortAllItemsTimer->start();
+ return;
+ }
+ }
+ }
+
+ if (groupedSorting() && changedRoles.contains(sortRole())) {
+ // The position is still correct, but the groups might have changed
+ // if the changed item is either the first or the last item in a
+ // group.
+ // In principle, we could try to find out if the item really is the
+ // first or last one in its group and then update the groups
+ // (possibly with a delayed timer to make sure that we don't
+ // re-calculate the groups very often if items are updated one by
+ // one), but starting m_resortAllItemsTimer is easier.
+ m_resortAllItemsTimer->start();
+ }
+}
+
+void KFileItemModel::resetRoles()
+{
+ for (int i = 0; i < RolesCount; ++i) {
+ m_requestRole[i] = false;
+ }
+}
+
+KFileItemModel::RoleType KFileItemModel::typeForRole(const QByteArray& role) const
+{
+ static QHash<QByteArray, RoleType> roles;
+ if (roles.isEmpty()) {
+ // Insert user visible roles that can be accessed with
+ // KFileItemModel::roleInformation()
+ int count = 0;
+ const RoleInfoMap* map = rolesInfoMap(count);
+ for (int i = 0; i < count; ++i) {
+ roles.insert(map[i].role, map[i].roleType);
+ }
+
+ // Insert internal roles (take care to synchronize the implementation
+ // with KFileItemModel::roleForType() in case if a change is done).
+ roles.insert("isDir", IsDirRole);
+ roles.insert("isLink", IsLinkRole);
+ roles.insert("isExpanded", IsExpandedRole);
+ roles.insert("isExpandable", IsExpandableRole);
+ roles.insert("expandedParentsCount", ExpandedParentsCountRole);
+
+ Q_ASSERT(roles.count() == RolesCount);
+ }
+
+ return roles.value(role, NoRole);
+}
+
+QByteArray KFileItemModel::roleForType(RoleType roleType) const
+{
+ static QHash<RoleType, QByteArray> roles;
+ if (roles.isEmpty()) {
+ // Insert user visible roles that can be accessed with
+ // KFileItemModel::roleInformation()
+ int count = 0;
+ const RoleInfoMap* map = rolesInfoMap(count);
+ for (int i = 0; i < count; ++i) {
+ roles.insert(map[i].roleType, map[i].role);
+ }
+
+ // Insert internal roles (take care to synchronize the implementation
+ // with KFileItemModel::typeForRole() in case if a change is done).
+ roles.insert(IsDirRole, "isDir");
+ roles.insert(IsLinkRole, "isLink");
+ roles.insert(IsExpandedRole, "isExpanded");
+ roles.insert(IsExpandableRole, "isExpandable");
+ roles.insert(ExpandedParentsCountRole, "expandedParentsCount");
+
+ Q_ASSERT(roles.count() == RolesCount);
+ };
+
+ return roles.value(roleType);
+}
+
+QHash<QByteArray, QVariant> KFileItemModel::retrieveData(const KFileItem& item, const ItemData* parent) const
+{
+ // It is important to insert only roles that are fast to retrieve. E.g.
+ // KFileItem::iconName() can be very expensive if the MIME-type is unknown
+ // and hence will be retrieved asynchronously by KFileItemModelRolesUpdater.
+ QHash<QByteArray, QVariant> data;
+ data.insert(sharedValue("url"), item.url());
+
+ const bool isDir = item.isDir();
+ if (m_requestRole[IsDirRole] && isDir) {
+ data.insert(sharedValue("isDir"), true);
+ }
+
+ if (m_requestRole[IsLinkRole] && item.isLink()) {
+ data.insert(sharedValue("isLink"), true);
+ }
+
+ if (m_requestRole[NameRole]) {
+ data.insert(sharedValue("text"), item.text());
+ }
+
+ if (m_requestRole[SizeRole] && !isDir) {
+ data.insert(sharedValue("size"), item.size());
+ }
+
+ if (m_requestRole[DateRole]) {
+ // Don't use KFileItem::timeString() as this is too expensive when
+ // having several thousands of items. Instead the formatting of the
+ // date-time will be done on-demand by the view when the date will be shown.
+ const KDateTime dateTime = item.time(KFileItem::ModificationTime);
+ data.insert(sharedValue("date"), dateTime.dateTime());
+ }
+
+ if (m_requestRole[PermissionsRole]) {
+ data.insert(sharedValue("permissions"), item.permissionsString());
+ }
+
+ if (m_requestRole[OwnerRole]) {
+ data.insert(sharedValue("owner"), item.user());
+ }
+
+ if (m_requestRole[GroupRole]) {
+ data.insert(sharedValue("group"), item.group());
+ }
+
+ if (m_requestRole[DestinationRole]) {
+ QString destination = item.linkDest();
+ if (destination.isEmpty()) {
+ destination = QLatin1String("-");
+ }
+ data.insert(sharedValue("destination"), destination);
+ }
+
+ if (m_requestRole[PathRole]) {
+ QString path;
+ if (item.url().protocol() == QLatin1String("trash")) {
+ path = item.entry().stringValue(KIO::UDSEntry::UDS_EXTRA);
+ } else {
+ // For performance reasons cache the home-path in a static QString
+ // (see QDir::homePath() for more details)
+ static QString homePath;
+ if (homePath.isEmpty()) {
+ homePath = QDir::homePath();
+ }
+
+ path = item.localPath();
+ if (path.startsWith(homePath)) {
+ path.replace(0, homePath.length(), QLatin1Char('~'));
+ }
+ }
+
+ const int index = path.lastIndexOf(item.text());
+ path = path.mid(0, index - 1);
+ data.insert(sharedValue("path"), path);
+ }
+
+ if (m_requestRole[IsExpandableRole] && isDir) {
+ data.insert(sharedValue("isExpandable"), true);
+ }
+
+ if (m_requestRole[ExpandedParentsCountRole]) {
+ if (parent) {
+ const int level = expandedParentsCount(parent) + 1;
+ data.insert(sharedValue("expandedParentsCount"), level);
+ }
+ }
+
+ if (item.isMimeTypeKnown()) {
+ data.insert(sharedValue("iconName"), item.iconName());
+
+ if (m_requestRole[TypeRole]) {
+ data.insert(sharedValue("type"), item.mimeComment());
+ }
+ } else if (m_requestRole[TypeRole] && isDir) {
+ static const QString folderMimeType = item.mimeComment();
+ data.insert(sharedValue("type"), folderMimeType);
+ }
+
+ return data;
+}
+
+bool KFileItemModel::lessThan(const ItemData* a, const ItemData* b) const
+{
+ int result = 0;
+
+ if (a->parent != b->parent) {
+ const int expansionLevelA = expandedParentsCount(a);
+ const int expansionLevelB = expandedParentsCount(b);
+
+ // If b has a higher expansion level than a, check if a is a parent
+ // of b, and make sure that both expansion levels are equal otherwise.
+ for (int i = expansionLevelB; i > expansionLevelA; --i) {
+ if (b->parent == a) {
+ return true;
+ }
+ b = b->parent;
+ }
+
+ // If a has a higher expansion level than a, check if b is a parent
+ // of a, and make sure that both expansion levels are equal otherwise.
+ for (int i = expansionLevelA; i > expansionLevelB; --i) {
+ if (a->parent == b) {
+ return false;
+ }
+ a = a->parent;
+ }
+
+ Q_ASSERT(expandedParentsCount(a) == expandedParentsCount(b));
+
+ // Compare the last parents of a and b which are different.
+ while (a->parent != b->parent) {
+ a = a->parent;
+ b = b->parent;
+ }
+ }
+
+ if (m_sortDirsFirst || m_sortRole == SizeRole) {
+ const bool isDirA = a->item.isDir();
+ const bool isDirB = b->item.isDir();
+ if (isDirA && !isDirB) {
+ return true;
+ } else if (!isDirA && isDirB) {
+ return false;
+ }
+ }
+
+ result = sortRoleCompare(a, b);
+
+ return (sortOrder() == Qt::AscendingOrder) ? result < 0 : result > 0;
+}
+
+/**
+ * Helper class for KFileItemModel::sort().
+ */
+class KFileItemModelLessThan
+{
+public:
+ KFileItemModelLessThan(const KFileItemModel* model) :
+ m_model(model)
+ {
+ }
+
+ bool operator()(const KFileItemModel::ItemData* a, const KFileItemModel::ItemData* b) const
+ {
+ return m_model->lessThan(a, b);
+ }
+
+private:
+ const KFileItemModel* m_model;
+};
+
+void KFileItemModel::sort(QList<KFileItemModel::ItemData*>::iterator begin,
+ QList<KFileItemModel::ItemData*>::iterator end) const
+{
+ KFileItemModelLessThan lessThan(this);
+
+ if (m_sortRole == NameRole) {
+ // Sorting by name can be expensive, in particular if natural sorting is
+ // enabled. Use all CPU cores to speed up the sorting process.
+ static const int numberOfThreads = QThread::idealThreadCount();
+ parallelMergeSort(begin, end, lessThan, numberOfThreads);
+ } else {
+ // Sorting by other roles is quite fast. Use only one thread to prevent
+ // problems caused by non-reentrant comparison functions, see
+ // https://bugs.kde.org/show_bug.cgi?id=312679
+ mergeSort(begin, end, lessThan);
+ }
+}
+
+int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b) const
+{
+ const KFileItem& itemA = a->item;
+ const KFileItem& itemB = b->item;
+
+ int result = 0;
+
+ switch (m_sortRole) {
+ case NameRole:
+ // The name role is handled as default fallback after the switch
+ break;
+
+ case SizeRole: {
+ if (itemA.isDir()) {
+ // See "if (m_sortFoldersFirst || m_sortRole == SizeRole)" in KFileItemModel::lessThan():
+ Q_ASSERT(itemB.isDir());
+
+ const QVariant valueA = a->values.value("size");
+ const QVariant valueB = b->values.value("size");
+ if (valueA.isNull() && valueB.isNull()) {
+ result = 0;
+ } else if (valueA.isNull()) {
+ result = -1;
+ } else if (valueB.isNull()) {
+ result = +1;
+ } else {
+ result = valueA.toInt() - valueB.toInt();
+ }
+ } else {
+ // See "if (m_sortFoldersFirst || m_sortRole == SizeRole)" in KFileItemModel::lessThan():
+ Q_ASSERT(!itemB.isDir());
+ const KIO::filesize_t sizeA = itemA.size();
+ const KIO::filesize_t sizeB = itemB.size();
+ if (sizeA > sizeB) {
+ result = +1;
+ } else if (sizeA < sizeB) {
+ result = -1;
+ } else {
+ result = 0;
+ }
+ }
+ break;
+ }
+
+ case DateRole: {
+ const KDateTime dateTimeA = itemA.time(KFileItem::ModificationTime);
+ const KDateTime dateTimeB = itemB.time(KFileItem::ModificationTime);
+ if (dateTimeA < dateTimeB) {
+ result = -1;
+ } else if (dateTimeA > dateTimeB) {
+ result = +1;
+ }
+ break;
+ }
+
+ case RatingRole: {
+ result = a->values.value("rating").toInt() - b->values.value("rating").toInt();
+ break;
+ }
+
+ case ImageSizeRole: {
+ // Alway use a natural comparing to interpret the numbers of a string like
+ // "1600 x 1200" for having a correct sorting.
+ result = KStringHandler::naturalCompare(a->values.value("imageSize").toString(),
+ b->values.value("imageSize").toString(),
+ Qt::CaseSensitive);
+ break;
+ }
+
+ default: {
+ const QByteArray role = roleForType(m_sortRole);
+ result = QString::compare(a->values.value(role).toString(),
+ b->values.value(role).toString());
+ break;
+ }
+
+ }
+
+ if (result != 0) {
+ // The current sort role was sufficient to define an order
+ return result;
+ }
+
+ // Fallback #1: Compare the text of the items
+ result = stringCompare(itemA.text(), itemB.text());
+ if (result != 0) {
+ return result;
+ }
+
+ // Fallback #2: KFileItem::text() may not be unique in case UDS_DISPLAY_NAME is used
+ result = stringCompare(itemA.name(m_caseSensitivity == Qt::CaseInsensitive),
+ itemB.name(m_caseSensitivity == Qt::CaseInsensitive));
+ if (result != 0) {
+ return result;
+ }
+
+ // Fallback #3: It must be assured that the sort order is always unique even if two values have been
+ // equal. In this case a comparison of the URL is done which is unique in all cases
+ // within KDirLister.
+ return QString::compare(itemA.url().url(), itemB.url().url(), Qt::CaseSensitive);
+}
+
+int KFileItemModel::stringCompare(const QString& a, const QString& b) const
+{
+ // Taken from KDirSortFilterProxyModel (kdelibs/kfile/kdirsortfilterproxymodel.*)
+ // Copyright (C) 2006 by Peter Penz <[email protected]>
+ // Copyright (C) 2006 by Dominic Battre <[email protected]>
+ // Copyright (C) 2006 by Martin Pool <[email protected]>
+
+ if (m_caseSensitivity == Qt::CaseInsensitive) {
+ const int result = m_naturalSorting ? KStringHandler::naturalCompare(a, b, Qt::CaseInsensitive)
+ : QString::compare(a, b, Qt::CaseInsensitive);
+ if (result != 0) {
+ // Only return the result, if the strings are not equal. If they are equal by a case insensitive
+ // comparison, still a deterministic sort order is required. A case sensitive
+ // comparison is done as fallback.
+ return result;
+ }
+ }
+
+ return m_naturalSorting ? KStringHandler::naturalCompare(a, b, Qt::CaseSensitive)
+ : QString::compare(a, b, Qt::CaseSensitive);
+}
+
+bool KFileItemModel::useMaximumUpdateInterval() const
+{
+ return !m_dirLister->url().isLocalFile();
+}
+
+static bool localeAwareLessThan(const QChar& c1, const QChar& c2)
+{
+ return QString::localeAwareCompare(c1, c2) < 0;
+}
+
+QList<QPair<int, QVariant> > KFileItemModel::nameRoleGroups() const
+{
+ Q_ASSERT(!m_itemData.isEmpty());
+
+ const int maxIndex = count() - 1;
+ QList<QPair<int, QVariant> > groups;
+
+ QString groupValue;
+ QChar firstChar;
+ for (int i = 0; i <= maxIndex; ++i) {
+ if (isChildItem(i)) {
+ continue;
+ }
+
+ const QString name = m_itemData.at(i)->item.text();
+
+ // Use the first character of the name as group indication
+ QChar newFirstChar = name.at(0).toUpper();
+ if (newFirstChar == QLatin1Char('~') && name.length() > 1) {
+ newFirstChar = name.at(1).toUpper();
+ }
+
+ if (firstChar != newFirstChar) {
+ QString newGroupValue;
+ if (newFirstChar.isLetter()) {
+ // Try to find a matching group in the range 'A' to 'Z'.
+ static std::vector<QChar> lettersAtoZ;
+ if (lettersAtoZ.empty()) {
+ for (char c = 'A'; c <= 'Z'; ++c) {
+ lettersAtoZ.push_back(QLatin1Char(c));
+ }
+ }
+
+ std::vector<QChar>::iterator it = std::lower_bound(lettersAtoZ.begin(), lettersAtoZ.end(), newFirstChar, localeAwareLessThan);
+ if (it != lettersAtoZ.end()) {
+ if (localeAwareLessThan(newFirstChar, *it) && it != lettersAtoZ.begin()) {
+ // newFirstChar belongs to the group preceding *it.
+ // Example: for an umlaut 'A' in the German locale, *it would be 'B' now.
+ --it;
+ }
+ newGroupValue = *it;
+ } else {
+ newGroupValue = newFirstChar;
+ }
+ } else if (newFirstChar >= QLatin1Char('0') && newFirstChar <= QLatin1Char('9')) {
+ // Apply group '0 - 9' for any name that starts with a digit
+ newGroupValue = i18nc("@title:group Groups that start with a digit", "0 - 9");
+ } else {
+ newGroupValue = i18nc("@title:group", "Others");
+ }
+
+ if (newGroupValue != groupValue) {
+ groupValue = newGroupValue;
+ groups.append(QPair<int, QVariant>(i, newGroupValue));
+ }
+
+ firstChar = newFirstChar;
+ }
+ }
+ return groups;
+}
+
+QList<QPair<int, QVariant> > KFileItemModel::sizeRoleGroups() const
+{
+ Q_ASSERT(!m_itemData.isEmpty());
+
+ const int maxIndex = count() - 1;
+ QList<QPair<int, QVariant> > groups;
+
+ QString groupValue;
+ for (int i = 0; i <= maxIndex; ++i) {
+ if (isChildItem(i)) {
+ continue;
+ }
+
+ const KFileItem& item = m_itemData.at(i)->item;
+ const KIO::filesize_t fileSize = !item.isNull() ? item.size() : ~0U;
+ QString newGroupValue;
+ if (!item.isNull() && item.isDir()) {
+ newGroupValue = i18nc("@title:group Size", "Folders");
+ } else if (fileSize < 5 * 1024 * 1024) {
+ newGroupValue = i18nc("@title:group Size", "Small");
+ } else if (fileSize < 10 * 1024 * 1024) {
+ newGroupValue = i18nc("@title:group Size", "Medium");
+ } else {
+ newGroupValue = i18nc("@title:group Size", "Big");
+ }
+
+ if (newGroupValue != groupValue) {
+ groupValue = newGroupValue;
+ groups.append(QPair<int, QVariant>(i, newGroupValue));
+ }
+ }
+
+ return groups;
+}
+
+QList<QPair<int, QVariant> > KFileItemModel::dateRoleGroups() const
+{
+ Q_ASSERT(!m_itemData.isEmpty());
+
+ const int maxIndex = count() - 1;
+ QList<QPair<int, QVariant> > groups;
+
+ const QDate currentDate = KDateTime::currentLocalDateTime().date();
+
+ QDate previousModifiedDate;
+ QString groupValue;
+ for (int i = 0; i <= maxIndex; ++i) {
+ if (isChildItem(i)) {
+ continue;
+ }
+
+ const KDateTime modifiedTime = m_itemData.at(i)->item.time(KFileItem::ModificationTime);
+ const QDate modifiedDate = modifiedTime.date();
+ if (modifiedDate == previousModifiedDate) {
+ // The current item is in the same group as the previous item
+ continue;
+ }
+ previousModifiedDate = modifiedDate;
+
+ const int daysDistance = modifiedDate.daysTo(currentDate);
+
+ QString newGroupValue;
+ if (currentDate.year() == modifiedDate.year() && currentDate.month() == modifiedDate.month()) {
+ switch (daysDistance / 7) {
+ case 0:
+ switch (daysDistance) {
+ case 0: newGroupValue = i18nc("@title:group Date", "Today"); break;
+ case 1: newGroupValue = i18nc("@title:group Date", "Yesterday"); break;
+ default: newGroupValue = modifiedTime.toString(i18nc("@title:group The week day name: %A", "%A"));
+ }
+ break;
+ case 1:
+ newGroupValue = i18nc("@title:group Date", "One Week Ago");
+ break;
+ case 2:
+ newGroupValue = i18nc("@title:group Date", "Two Weeks Ago");
+ break;
+ case 3:
+ newGroupValue = i18nc("@title:group Date", "Three Weeks Ago");
+ break;
+ case 4:
+ case 5:
+ newGroupValue = i18nc("@title:group Date", "Earlier this Month");
+ break;
+ default:
+ Q_ASSERT(false);
+ }
+ } else {
+ const QDate lastMonthDate = currentDate.addMonths(-1);
+ if (lastMonthDate.year() == modifiedDate.year() && lastMonthDate.month() == modifiedDate.month()) {
+ if (daysDistance == 1) {
+ newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Yesterday (%B, %Y)"));
+ } else if (daysDistance <= 7) {
+ newGroupValue = modifiedTime.toString(i18nc("@title:group The week day name: %A, %B is full month name in current locale, and %Y is full year number", "%A (%B, %Y)"));
+ } else if (daysDistance <= 7 * 2) {
+ newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "One Week Ago (%B, %Y)"));
+ } else if (daysDistance <= 7 * 3) {
+ newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Two Weeks Ago (%B, %Y)"));
+ } else if (daysDistance <= 7 * 4) {
+ newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Three Weeks Ago (%B, %Y)"));
+ } else {
+ newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Earlier on %B, %Y"));
+ }
+ } else {
+ newGroupValue = modifiedTime.toString(i18nc("@title:group The month and year: %B is full month name in current locale, and %Y is full year number", "%B, %Y"));
+ }
+ }
+
+ if (newGroupValue != groupValue) {
+ groupValue = newGroupValue;
+ groups.append(QPair<int, QVariant>(i, newGroupValue));
+ }
+ }
+
+ return groups;
+}
+
+QList<QPair<int, QVariant> > KFileItemModel::permissionRoleGroups() const
+{
+ Q_ASSERT(!m_itemData.isEmpty());
+
+ const int maxIndex = count() - 1;
+ QList<QPair<int, QVariant> > groups;
+
+ QString permissionsString;
+ QString groupValue;
+ for (int i = 0; i <= maxIndex; ++i) {
+ if (isChildItem(i)) {
+ continue;
+ }
+
+ const ItemData* itemData = m_itemData.at(i);
+ const QString newPermissionsString = itemData->values.value("permissions").toString();
+ if (newPermissionsString == permissionsString) {
+ continue;
+ }
+ permissionsString = newPermissionsString;
+
+ const QFileInfo info(itemData->item.url().pathOrUrl());
+
+ // Set user string
+ QString user;
+ if (info.permission(QFile::ReadUser)) {
+ user = i18nc("@item:intext Access permission, concatenated", "Read, ");
+ }
+ if (info.permission(QFile::WriteUser)) {
+ user += i18nc("@item:intext Access permission, concatenated", "Write, ");
+ }
+ if (info.permission(QFile::ExeUser)) {
+ user += i18nc("@item:intext Access permission, concatenated", "Execute, ");
+ }
+ user = user.isEmpty() ? i18nc("@item:intext Access permission, concatenated", "Forbidden") : user.mid(0, user.count() - 2);
+
+ // Set group string
+ QString group;
+ if (info.permission(QFile::ReadGroup)) {
+ group = i18nc("@item:intext Access permission, concatenated", "Read, ");
+ }
+ if (info.permission(QFile::WriteGroup)) {
+ group += i18nc("@item:intext Access permission, concatenated", "Write, ");
+ }
+ if (info.permission(QFile::ExeGroup)) {
+ group += i18nc("@item:intext Access permission, concatenated", "Execute, ");
+ }
+ group = group.isEmpty() ? i18nc("@item:intext Access permission, concatenated", "Forbidden") : group.mid(0, group.count() - 2);
+
+ // Set others string
+ QString others;
+ if (info.permission(QFile::ReadOther)) {
+ others = i18nc("@item:intext Access permission, concatenated", "Read, ");
+ }
+ if (info.permission(QFile::WriteOther)) {
+ others += i18nc("@item:intext Access permission, concatenated", "Write, ");
+ }
+ if (info.permission(QFile::ExeOther)) {
+ others += i18nc("@item:intext Access permission, concatenated", "Execute, ");
+ }
+ others = others.isEmpty() ? i18nc("@item:intext Access permission, concatenated", "Forbidden") : others.mid(0, others.count() - 2);
+
+ const QString newGroupValue = i18nc("@title:group Files and folders by permissions", "User: %1 | Group: %2 | Others: %3", user, group, others);
+ if (newGroupValue != groupValue) {
+ groupValue = newGroupValue;
+ groups.append(QPair<int, QVariant>(i, newGroupValue));
+ }
+ }
+
+ return groups;
+}
+
+QList<QPair<int, QVariant> > KFileItemModel::ratingRoleGroups() const
+{
+ Q_ASSERT(!m_itemData.isEmpty());
+
+ const int maxIndex = count() - 1;
+ QList<QPair<int, QVariant> > groups;
+
+ int groupValue = -1;
+ for (int i = 0; i <= maxIndex; ++i) {
+ if (isChildItem(i)) {
+ continue;
+ }
+ const int newGroupValue = m_itemData.at(i)->values.value("rating", 0).toInt();
+ if (newGroupValue != groupValue) {
+ groupValue = newGroupValue;
+ groups.append(QPair<int, QVariant>(i, newGroupValue));
+ }
+ }
+
+ return groups;
+}
+
+QList<QPair<int, QVariant> > KFileItemModel::genericStringRoleGroups(const QByteArray& role) const
+{
+ Q_ASSERT(!m_itemData.isEmpty());
+
+ const int maxIndex = count() - 1;
+ QList<QPair<int, QVariant> > groups;
+
+ bool isFirstGroupValue = true;
+ QString groupValue;
+ for (int i = 0; i <= maxIndex; ++i) {
+ if (isChildItem(i)) {
+ continue;
+ }
+ const QString newGroupValue = m_itemData.at(i)->values.value(role).toString();
+ if (newGroupValue != groupValue || isFirstGroupValue) {
+ groupValue = newGroupValue;
+ groups.append(QPair<int, QVariant>(i, newGroupValue));
+ isFirstGroupValue = false;
+ }
+ }
+
+ return groups;
+}
+
+void KFileItemModel::emitSortProgress(int resolvedCount)
+{
+ // Be tolerant against a resolvedCount with a wrong range.
+ // Although there should not be a case where KFileItemModelRolesUpdater
+ // (= caller) provides a wrong range, it is important to emit
+ // a useful progress information even if there is an unexpected
+ // implementation issue.
+
+ const int itemCount = count();
+ if (resolvedCount >= itemCount) {
+ m_sortingProgressPercent = -1;
+ if (m_resortAllItemsTimer->isActive()) {
+ m_resortAllItemsTimer->stop();
+ resortAllItems();
+ }
+
+ emit directorySortingProgress(100);
+ } else if (itemCount > 0) {
+ resolvedCount = qBound(0, resolvedCount, itemCount);
+
+ const int progress = resolvedCount * 100 / itemCount;
+ if (m_sortingProgressPercent != progress) {
+ m_sortingProgressPercent = progress;
+ emit directorySortingProgress(progress);
+ }
+ }
+}
+
+const KFileItemModel::RoleInfoMap* KFileItemModel::rolesInfoMap(int& count)
+{
+ static const RoleInfoMap rolesInfoMap[] = {
+ // | role | roleType | role translation | group translation | requires Baloo | requires indexer
+ { 0, NoRole, 0, 0, 0, 0, false, false },
+ { "text", NameRole, I18N_NOOP2_NOSTRIP("@label", "Name"), 0, 0, false, false },
+ { "size", SizeRole, I18N_NOOP2_NOSTRIP("@label", "Size"), 0, 0, false, false },
+ { "date", DateRole, I18N_NOOP2_NOSTRIP("@label", "Date"), 0, 0, false, false },
+ { "type", TypeRole, I18N_NOOP2_NOSTRIP("@label", "Type"), 0, 0, false, false },
+ { "rating", RatingRole, I18N_NOOP2_NOSTRIP("@label", "Rating"), 0, 0, true, false },
+ { "tags", TagsRole, I18N_NOOP2_NOSTRIP("@label", "Tags"), 0, 0, true, false },
+ { "comment", CommentRole, I18N_NOOP2_NOSTRIP("@label", "Comment"), 0, 0, true, false },
+ { "wordCount", WordCountRole, I18N_NOOP2_NOSTRIP("@label", "Word Count"), I18N_NOOP2_NOSTRIP("@label", "Document"), true, true },
+ { "lineCount", LineCountRole, I18N_NOOP2_NOSTRIP("@label", "Line Count"), I18N_NOOP2_NOSTRIP("@label", "Document"), true, true },
+ { "imageSize", ImageSizeRole, I18N_NOOP2_NOSTRIP("@label", "Image Size"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true },
+ { "orientation", OrientationRole, I18N_NOOP2_NOSTRIP("@label", "Orientation"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true },
+ { "artist", ArtistRole, I18N_NOOP2_NOSTRIP("@label", "Artist"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true },
+ { "album", AlbumRole, I18N_NOOP2_NOSTRIP("@label", "Album"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true },
+ { "duration", DurationRole, I18N_NOOP2_NOSTRIP("@label", "Duration"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true },
+ { "track", TrackRole, I18N_NOOP2_NOSTRIP("@label", "Track"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true },
+ { "path", PathRole, I18N_NOOP2_NOSTRIP("@label", "Path"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false },
+ { "destination", DestinationRole, I18N_NOOP2_NOSTRIP("@label", "Link Destination"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false },
+ { "copiedFrom", CopiedFromRole, I18N_NOOP2_NOSTRIP("@label", "Copied From"), I18N_NOOP2_NOSTRIP("@label", "Other"), true, false },
+ { "permissions", PermissionsRole, I18N_NOOP2_NOSTRIP("@label", "Permissions"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false },
+ { "owner", OwnerRole, I18N_NOOP2_NOSTRIP("@label", "Owner"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false },
+ { "group", GroupRole, I18N_NOOP2_NOSTRIP("@label", "User Group"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false },
+ };
+
+ count = sizeof(rolesInfoMap) / sizeof(RoleInfoMap);
+ return rolesInfoMap;
+}
+
+void KFileItemModel::determineMimeTypes(const KFileItemList& items, int timeout)
+{
+ QElapsedTimer timer;
+ timer.start();
+ foreach (const KFileItem& item, items) { // krazy:exclude=foreach
+ // Only determine mime types for files here. For directories,
+ // KFileItem::determineMimeType() reads the .directory file inside to
+ // load the icon, but this is not necessary at all if we just need the
+ // type. Some special code for setting the correct mime type for
+ // directories is in retrieveData().
+ if (!item.isDir()) {
+ item.determineMimeType();
+ }
+
+ if (timer.elapsed() > timeout) {
+ // Don't block the user interface, let the remaining items
+ // be resolved asynchronously.
+ return;
+ }
+ }
+}
+
+QByteArray KFileItemModel::sharedValue(const QByteArray& value)
+{
+ static QSet<QByteArray> pool;
+ const QSet<QByteArray>::const_iterator it = pool.constFind(value);
+
+ if (it != pool.constEnd()) {
+ return *it;
+ } else {
+ pool.insert(value);
+ return value;
+ }
+}
+
+bool KFileItemModel::isConsistent() const
+{
+ // m_items may contain less items than m_itemData because m_items
+ // is populated lazily, see KFileItemModel::index(const KUrl& url).
+ if (m_items.count() > m_itemData.count()) {
+ return false;
+ }
+
+ for (int i = 0; i < count(); ++i) {
+ // Check if m_items and m_itemData are consistent.
+ const KFileItem item = fileItem(i);
+ if (item.isNull()) {
+ qWarning() << "Item" << i << "is null";
+ return false;
+ }
+
+ const int itemIndex = index(item);
+ if (itemIndex != i) {
+ qWarning() << "Item" << i << "has a wrong index:" << itemIndex;
+ return false;
+ }
+
+ // Check if the items are sorted correctly.
+ if (i > 0 && !lessThan(m_itemData.at(i - 1), m_itemData.at(i))) {
+ qWarning() << "The order of items" << i - 1 << "and" << i << "is wrong:"
+ << fileItem(i - 1) << fileItem(i);
+ return false;
+ }
+
+ // Check if all parent-child relationships are consistent.
+ const ItemData* data = m_itemData.at(i);
+ const ItemData* parent = data->parent;
+ if (parent) {
+ if (expandedParentsCount(data) != expandedParentsCount(parent) + 1) {
+ qWarning() << "expandedParentsCount is inconsistent for parent" << parent->item << "and child" << data->item;
+ return false;
+ }
+
+ const int parentIndex = index(parent->item);
+ if (parentIndex >= i) {
+ qWarning() << "Index" << parentIndex << "of parent" << parent->item << "is not smaller than index" << i << "of child" << data->item;
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+#include "kfileitemmodel.moc"
diff --git a/dolphin/src/kitemviews/kfileitemmodel.h b/dolphin/src/kitemviews/kfileitemmodel.h
new file mode 100644
index 0000000..62a283d
--- /dev/null
+++ b/dolphin/src/kitemviews/kfileitemmodel.h
@@ -0,0 +1,523 @@
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef KFILEITEMMODEL_H
+#define KFILEITEMMODEL_H
+
+#include <libdolphin_export.h>
+#include <KFileItemList>
+#include <KUrl>
+#include <kitemviews/kitemmodelbase.h>
+#include <kitemviews/private/kfileitemmodelfilter.h>
+
+#include <QHash>
+#include <QSet>
+
+class KFileItemModelDirLister;
+class QTimer;
+
+/**
+ * @brief KItemModelBase implementation for KFileItems.
+ *
+ * Allows to load items of a directory. Sorting and grouping of
+ * items are supported. Roles that are not part of KFileItem can
+ * be added with KFileItemModel::setData().
+ *
+ * Recursive expansion of sub-directories is supported by
+ * KFileItemModel::setExpanded().
+ */
+class LIBDOLPHINPRIVATE_EXPORT KFileItemModel : public KItemModelBase
+{
+ Q_OBJECT
+
+public:
+ explicit KFileItemModel(QObject* parent = 0);
+ virtual ~KFileItemModel();
+
+ /**
+ * Loads the directory specified by \a url. The signals
+ * directoryLoadingStarted(), directoryLoadingProgress() and directoryLoadingCompleted()
+ * indicate the current state of the loading process. The items
+ * of the directory are added after the loading has been completed.
+ */
+ void loadDirectory(const KUrl& url);
+
+ /**
+ * Throws away all currently loaded items and refreshes the directory
+ * by reloading all items again.
+ */
+ void refreshDirectory(const KUrl& url);
+
+ /**
+ * @return Parent directory of the items that are shown. In case
+ * if a directory tree is shown, KFileItemModel::dir() returns
+ * the root-parent of all items.
+ * @see rootItem()
+ */
+ KUrl directory() const;
+
+ /**
+ * Cancels the loading of a directory which has been started by either
+ * loadDirectory() or refreshDirectory().
+ */
+ void cancelDirectoryLoading();
+
+ virtual int count() const;
+ virtual QHash<QByteArray, QVariant> data(int index) const;
+ virtual bool setData(int index, const QHash<QByteArray, QVariant>& values);
+
+ /**
+ * Sets a separate sorting with directories first (true) or a mixed
+ * sorting of files and directories (false).
+ */
+ void setSortDirectoriesFirst(bool dirsFirst);
+ bool sortDirectoriesFirst() const;
+
+ void setShowHiddenFiles(bool show);
+ bool showHiddenFiles() const;
+
+ /**
+ * If set to true, only directories are shown as items of the model. Files
+ * are ignored.
+ */
+ void setShowDirectoriesOnly(bool enabled);
+ bool showDirectoriesOnly() const;
+
+ /** @reimp */
+ virtual QMimeData* createMimeData(const KItemSet& indexes) const;
+
+ /** @reimp */
+ virtual int indexForKeyboardSearch(const QString& text, int startFromIndex = 0) const;
+
+ /** @reimp */
+ virtual bool supportsDropping(int index) const;
+
+ /** @reimp */
+ virtual QString roleDescription(const QByteArray& role) const;
+
+ /** @reimp */
+ virtual QList<QPair<int, QVariant> > groups() const;
+
+ /**
+ * @return The file-item for the index \a index. If the index is in a valid
+ * range it is assured that the file-item is not null. The runtime
+ * complexity of this call is O(1).
+ */
+ KFileItem fileItem(int index) const;
+
+ /**
+ * @return The file-item for the url \a url. If no file-item with the given
+ * URL is found KFileItem::isNull() will be true for the returned
+ * file-item. The runtime complexity of this call is O(1).
+ */
+ KFileItem fileItem(const KUrl& url) const;
+
+ /**
+ * @return The index for the file-item \a item. -1 is returned if no file-item
+ * is found or if the file-item is null. The amortized runtime
+ * complexity of this call is O(1).
+ */
+ int index(const KFileItem& item) const;
+
+ /**
+ * @return The index for the URL \a url. -1 is returned if no file-item
+ * is found. The amortized runtime complexity of this call is O(1).
+ */
+ int index(const KUrl& url) const;
+
+ /**
+ * @return Root item of all items representing the item
+ * for KFileItemModel::dir().
+ */
+ KFileItem rootItem() const;
+
+ /**
+ * Clears all items of the model.
+ */
+ void clear();
+
+ /**
+ * Sets the roles that should be shown for each item.
+ */
+ void setRoles(const QSet<QByteArray>& roles);
+ QSet<QByteArray> roles() const;
+
+ virtual bool setExpanded(int index, bool expanded);
+ virtual bool isExpanded(int index) const;
+ virtual bool isExpandable(int index) const;
+ virtual int expandedParentsCount(int index) const;
+
+ QSet<KUrl> expandedDirectories() const;
+
+ /**
+ * Marks the URLs in \a urls as sub-directories which were expanded previously.
+ * After calling loadDirectory() or refreshDirectory() the marked sub-directories
+ * will be expanded step-by-step.
+ */
+ void restoreExpandedDirectories(const QSet<KUrl>& urls);
+
+ /**
+ * Expands all parent-directories of the item \a url.
+ */
+ void expandParentDirectories(const KUrl& url);
+
+ void setNameFilter(const QString& nameFilter);
+ QString nameFilter() const;
+
+ void setMimeTypeFilters(const QStringList& filters);
+ QStringList mimeTypeFilters() const;
+
+ struct RoleInfo
+ { QByteArray role;
+ QString translation;
+ QString group;
+ bool requiresBaloo;
+ bool requiresIndexer;
+ };
+
+ /**
+ * @return Provides static information for all available roles that
+ * are supported by KFileItemModel. Some roles can only be
+ * determined if Baloo is enabled and/or the Baloo
+ * indexing is enabled.
+ */
+ static QList<RoleInfo> rolesInformation();
+
+signals:
+ /**
+ * Is emitted if the loading of a directory has been started. It is
+ * assured that a signal directoryLoadingCompleted() will be send after
+ * the loading has been finished. For tracking the loading progress
+ * the signal directoryLoadingProgress() gets emitted in between.
+ */
+ void directoryLoadingStarted();
+
+ /**
+ * Is emitted after the loading of a directory has been completed or new
+ * items have been inserted to an already loaded directory. Usually
+ * one or more itemsInserted() signals are emitted before loadingCompleted()
+ * (the only exception is loading an empty directory, where only a
+ * loadingCompleted() signal gets emitted).
+ */
+ void directoryLoadingCompleted();
+
+ /**
+ * Is emitted after the loading of a directory has been canceled.
+ */
+ void directoryLoadingCanceled();
+
+ /**
+ * Informs about the progress in percent when loading a directory. It is assured
+ * that the signal directoryLoadingStarted() has been emitted before.
+ */
+ void directoryLoadingProgress(int percent);
+
+ /**
+ * Is emitted if the sort-role gets resolved asynchronously and provides
+ * the progress-information of the sorting in percent. It is assured
+ * that the last sortProgress-signal contains 100 as value.
+ */
+ void directorySortingProgress(int percent);
+
+ /**
+ * Is emitted if an information message (e.g. "Connecting to host...")
+ * should be shown.
+ */
+ void infoMessage(const QString& message);
+
+ /**
+ * Is emitted if an error message (e.g. "Unknown location")
+ * should be shown.
+ */
+ void errorMessage(const QString& message);
+
+ /**
+ * Is emitted if a redirection from the current URL \a oldUrl
+ * to the new URL \a newUrl has been done.
+ */
+ void directoryRedirection(const KUrl& oldUrl, const KUrl& newUrl);
+
+ /**
+ * Is emitted when the URL passed by KFileItemModel::setUrl() represents a file.
+ * In this case no signal errorMessage() will be emitted.
+ */
+ void urlIsFileError(const KUrl& url);
+
+protected:
+ virtual void onGroupedSortingChanged(bool current);
+ virtual void onSortRoleChanged(const QByteArray& current, const QByteArray& previous);
+ virtual void onSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous);
+
+private slots:
+ /**
+ * Resorts all items dependent on the set sortRole(), sortOrder()
+ * and foldersFirst() settings.
+ */
+ void resortAllItems();
+
+ void slotCompleted();
+ void slotCanceled();
+ void slotItemsAdded(const KUrl& directoryUrl, const KFileItemList& items);
+ void slotItemsDeleted(const KFileItemList& items);
+ void slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >& items);
+ void slotClear();
+ void slotNaturalSortingChanged();
+
+ void dispatchPendingItemsToInsert();
+
+private:
+ enum RoleType {
+ // User visible roles:
+ NoRole, NameRole, SizeRole, DateRole, PermissionsRole, OwnerRole,
+ GroupRole, TypeRole, DestinationRole, PathRole,
+ // User visible roles available with Baloo:
+ CommentRole, TagsRole, RatingRole, ImageSizeRole, OrientationRole,
+ WordCountRole, LineCountRole, ArtistRole, AlbumRole, DurationRole, TrackRole,
+ CopiedFromRole,
+ // Non-visible roles:
+ IsDirRole, IsLinkRole, IsExpandedRole, IsExpandableRole, ExpandedParentsCountRole,
+ // Mandatory last entry:
+ RolesCount
+ };
+
+ struct ItemData
+ {
+ KFileItem item;
+ QHash<QByteArray, QVariant> values;
+ ItemData* parent;
+ };
+
+ enum RemoveItemsBehavior {
+ KeepItemData,
+ DeleteItemData
+ };
+
+ void insertItems(QList<ItemData*>& items);
+ void removeItems(const KItemRangeList& itemRanges, RemoveItemsBehavior behavior);
+
+ /**
+ * Helper method for insertItems() and removeItems(): Creates
+ * a list of ItemData elements based on the given items.
+ * Note that the ItemData instances are created dynamically and
+ * must be deleted by the caller.
+ */
+ QList<ItemData*> createItemDataList(const KUrl& parentUrl, const KFileItemList& items) const;
+
+ /**
+ * Prepares the items for sorting. Normally, the hash 'values' in ItemData is filled
+ * lazily to save time and memory, but for some sort roles, it is expected that the
+ * sort role data is stored in 'values'.
+ */
+ void prepareItemsForSorting(QList<ItemData*>& itemDataList);
+
+ static int expandedParentsCount(const ItemData* data);
+
+ void removeExpandedItems();
+
+ /**
+ * This function is called by setData() and slotRefreshItems(). It emits
+ * the itemsChanged() signal, checks if the sort order is still correct,
+ * and starts m_resortAllItemsTimer if that is not the case.
+ */
+ void emitItemsChangedAndTriggerResorting(const KItemRangeList& itemRanges, const QSet<QByteArray>& changedRoles);
+
+ /**
+ * Resets all values from m_requestRole to false.
+ */
+ void resetRoles();
+
+ /**
+ * @return Role-type for the given role.
+ * Runtime complexity is O(1).
+ */
+ RoleType typeForRole(const QByteArray& role) const;
+
+ /**
+ * @return Role-byte-array for the given role-type.
+ * Runtime complexity is O(1).
+ */
+ QByteArray roleForType(RoleType roleType) const;
+
+ QHash<QByteArray, QVariant> retrieveData(const KFileItem& item, const ItemData* parent) const;
+
+ /**
+ * @return True if \a a has a KFileItem whose text is 'less than' the one
+ * of \a b according to QString::operator<(const QString&).
+ */
+ static bool nameLessThan(const ItemData* a, const ItemData* b);
+
+ /**
+ * @return True if the item-data \a a should be ordered before the item-data
+ * \b. The item-data may have different parent-items.
+ */
+ bool lessThan(const ItemData* a, const ItemData* b) const;
+
+ /**
+ * Sorts the items between \a begin and \a end using the comparison
+ * function lessThan().
+ */
+ void sort(QList<ItemData*>::iterator begin, QList<ItemData*>::iterator end) const;
+
+ /**
+ * Helper method for lessThan() and expandedParentsCountCompare(): Compares
+ * the passed item-data using m_sortRole as criteria. Both items must
+ * have the same parent item, otherwise the comparison will be wrong.
+ */
+ int sortRoleCompare(const ItemData* a, const ItemData* b) const;
+
+ int stringCompare(const QString& a, const QString& b) const;
+
+ bool useMaximumUpdateInterval() const;
+
+ QList<QPair<int, QVariant> > nameRoleGroups() const;
+ QList<QPair<int, QVariant> > sizeRoleGroups() const;
+ QList<QPair<int, QVariant> > dateRoleGroups() const;
+ QList<QPair<int, QVariant> > permissionRoleGroups() const;
+ QList<QPair<int, QVariant> > ratingRoleGroups() const;
+ QList<QPair<int, QVariant> > genericStringRoleGroups(const QByteArray& typeForRole) const;
+
+ /**
+ * Helper method for all xxxRoleGroups() methods to check whether the
+ * item with the given index is a child-item. A child-item is defined
+ * as item having an expansion-level > 0. All xxxRoleGroups() methods
+ * should skip the grouping if the item is a child-item (although
+ * KItemListView would be capable to show sub-groups in groups this
+ * results in visual clutter for most usecases).
+ */
+ bool isChildItem(int index) const;
+
+ /**
+ * Is invoked by KFileItemModelRolesUpdater and results in emitting the
+ * sortProgress signal with a percent-value of the progress.
+ */
+ void emitSortProgress(int resolvedCount);
+
+ /**
+ * Applies the filters set through @ref setNameFilter and @ref setMimeTypeFilters.
+ */
+ void applyFilters();
+
+ /**
+ * Removes filtered items whose expanded parents have been deleted
+ * or collapsed via setExpanded(parentIndex, false).
+ */
+ void removeFilteredChildren(const KItemRangeList& parents);
+
+ /**
+ * Maps the QByteArray-roles to RoleTypes and provides translation- and
+ * group-contexts.
+ */
+ struct RoleInfoMap
+ {
+ const char* const role;
+ const RoleType roleType;
+ const char* const roleTranslationContext;
+ const char* const roleTranslation;
+ const char* const groupTranslationContext;
+ const char* const groupTranslation;
+ const bool requiresBaloo;
+ const bool requiresIndexer;
+ };
+
+ /**
+ * @return Map of user visible roles that are accessible by KFileItemModel::rolesInformation().
+ */
+ static const RoleInfoMap* rolesInfoMap(int& count);
+
+ /**
+ * Determines the MIME-types of all items that can be done within
+ * the given timeout.
+ */
+ static void determineMimeTypes(const KFileItemList& items, int timeout);
+
+ /**
+ * @return Returns a copy of \a value that is implicitly shared
+ * with other users to save memory.
+ */
+ static QByteArray sharedValue(const QByteArray& value);
+
+ /**
+ * Checks if the model's internal data structures are consistent.
+ */
+ bool isConsistent() const;
+
+private:
+ KFileItemModelDirLister* m_dirLister;
+
+ bool m_naturalSorting;
+ bool m_sortDirsFirst;
+
+ RoleType m_sortRole;
+ int m_sortingProgressPercent; // Value of directorySortingProgress() signal
+ QSet<QByteArray> m_roles;
+ Qt::CaseSensitivity m_caseSensitivity;
+
+ QList<ItemData*> m_itemData;
+
+ // m_items is a cache for the method index(const KUrl&). If it contains N
+ // entries, it is guaranteed that these correspond to the first N items in
+ // the model, i.e., that (for every i between 0 and N - 1)
+ // m_items.value(fileItem(i).url()) == i
+ mutable QHash<KUrl, int> m_items;
+
+ KFileItemModelFilter m_filter;
+ QHash<KFileItem, ItemData*> m_filteredItems; // Items that got hidden by KFileItemModel::setNameFilter()
+
+ bool m_requestRole[RolesCount];
+
+ QTimer* m_maximumUpdateIntervalTimer;
+ QTimer* m_resortAllItemsTimer;
+ QList<ItemData*> m_pendingItemsToInsert;
+
+ // Cache for KFileItemModel::groups()
+ mutable QList<QPair<int, QVariant> > m_groups;
+
+ // Stores the URLs (key: target url, value: url) of the expanded directories.
+ QHash<KUrl, KUrl> m_expandedDirs;
+
+ // URLs that must be expanded. The expanding is initially triggered in setExpanded()
+ // and done step after step in slotCompleted().
+ QSet<KUrl> m_urlsToExpand;
+
+ friend class KFileItemModelLessThan; // Accesses lessThan() method
+ friend class KFileItemModelRolesUpdater; // Accesses emitSortProgress() method
+ friend class KFileItemModelTest; // For unit testing
+ friend class KFileItemModelBenchmark; // For unit testing
+ friend class KFileItemListViewTest; // For unit testing
+ friend class DolphinPart; // Accesses m_dirLister
+};
+
+inline bool KFileItemModel::nameLessThan(const ItemData* a, const ItemData* b)
+{
+ return a->item.text() < b->item.text();
+}
+
+
+inline bool KFileItemModel::isChildItem(int index) const
+{
+ if (m_itemData.at(index)->parent) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+#endif
+
+
diff --git a/dolphin/src/kitemviews/kfileitemmodelrolesupdater.cpp b/dolphin/src/kitemviews/kfileitemmodelrolesupdater.cpp
new file mode 100644
index 0000000..0865d40
--- /dev/null
+++ b/dolphin/src/kitemviews/kfileitemmodelrolesupdater.cpp
@@ -0,0 +1,1171 @@
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "kfileitemmodelrolesupdater.h"
+
+#include "kfileitemmodel.h"
+
+#include <KConfig>
+#include <KConfigGroup>
+#include <KDebug>
+#include <KFileItem>
+#include <KGlobal>
+#include <KIO/JobUiDelegate>
+#include <KIO/PreviewJob>
+
+#include "private/kpixmapmodifier.h"
+#include "private/kdirectorycontentscounter.h"
+
+#include <QApplication>
+#include <QPainter>
+#include <QPixmap>
+#include <QElapsedTimer>
+#include <QTimer>
+
+#include <algorithm>
+
+#ifdef HAVE_BALOO
+ #include "private/kbaloorolesprovider.h"
+ #include <baloo/file.h>
+ #include <baloo/filefetchjob.h>
+ #include <baloo/filemonitor.h>
+#endif
+
+// #define KFILEITEMMODELROLESUPDATER_DEBUG
+
+namespace {
+ // Maximum time in ms that the KFileItemModelRolesUpdater
+ // may perform a blocking operation
+ const int MaxBlockTimeout = 200;
+
+ // If the number of items is smaller than ResolveAllItemsLimit,
+ // the roles of all items will be resolved.
+ const int ResolveAllItemsLimit = 500;
+
+ // Not only the visible area, but up to ReadAheadPages before and after
+ // this area will be resolved.
+ const int ReadAheadPages = 5;
+}
+
+KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel* model, QObject* parent) :
+ QObject(parent),
+ m_state(Idle),
+ m_previewChangedDuringPausing(false),
+ m_iconSizeChangedDuringPausing(false),
+ m_rolesChangedDuringPausing(false),
+ m_previewShown(false),
+ m_enlargeSmallPreviews(true),
+ m_clearPreviews(false),
+ m_finishedItems(),
+ m_model(model),
+ m_iconSize(),
+ m_firstVisibleIndex(0),
+ m_lastVisibleIndex(-1),
+ m_maximumVisibleItems(50),
+ m_roles(),
+ m_resolvableRoles(),
+ m_enabledPlugins(),
+ m_pendingSortRoleItems(),
+ m_pendingIndexes(),
+ m_pendingPreviewItems(),
+ m_previewJob(),
+ m_recentlyChangedItemsTimer(0),
+ m_recentlyChangedItems(),
+ m_changedItems(),
+ m_directoryContentsCounter(0)
+ #ifdef HAVE_BALOO
+ , m_balooFileMonitor(0)
+ #endif
+{
+ Q_ASSERT(model);
+
+ const KConfigGroup globalConfig(KGlobal::config(), "PreviewSettings");
+ m_enabledPlugins = globalConfig.readEntry("Plugins", QStringList()
+ << "directorythumbnail"
+ << "imagethumbnail"
+ << "jpegthumbnail");
+
+ connect(m_model, SIGNAL(itemsInserted(KItemRangeList)),
+ this, SLOT(slotItemsInserted(KItemRangeList)));
+ connect(m_model, SIGNAL(itemsRemoved(KItemRangeList)),
+ this, SLOT(slotItemsRemoved(KItemRangeList)));
+ connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
+ this, SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
+ connect(m_model, SIGNAL(itemsMoved(KItemRange,QList<int>)),
+ this, SLOT(slotItemsMoved(KItemRange,QList<int>)));
+ connect(m_model, SIGNAL(sortRoleChanged(QByteArray,QByteArray)),
+ this, SLOT(slotSortRoleChanged(QByteArray,QByteArray)));
+
+ // Use a timer to prevent that each call of slotItemsChanged() results in a synchronous
+ // resolving of the roles. Postpone the resolving until no update has been done for 1 second.
+ m_recentlyChangedItemsTimer = new QTimer(this);
+ m_recentlyChangedItemsTimer->setInterval(1000);
+ m_recentlyChangedItemsTimer->setSingleShot(true);
+ connect(m_recentlyChangedItemsTimer, SIGNAL(timeout()), this, SLOT(resolveRecentlyChangedItems()));
+
+ m_resolvableRoles.insert("size");
+ m_resolvableRoles.insert("type");
+ m_resolvableRoles.insert("isExpandable");
+#ifdef HAVE_BALOO
+ m_resolvableRoles += KBalooRolesProvider::instance().roles();
+#endif
+
+ m_directoryContentsCounter = new KDirectoryContentsCounter(m_model, this);
+ connect(m_directoryContentsCounter, SIGNAL(result(QString,int)),
+ this, SLOT(slotDirectoryContentsCountReceived(QString,int)));
+}
+
+KFileItemModelRolesUpdater::~KFileItemModelRolesUpdater()
+{
+ killPreviewJob();
+}
+
+void KFileItemModelRolesUpdater::setIconSize(const QSize& size)
+{
+ if (size != m_iconSize) {
+ m_iconSize = size;
+ if (m_state == Paused) {
+ m_iconSizeChangedDuringPausing = true;
+ } else if (m_previewShown) {
+ // An icon size change requires the regenerating of
+ // all previews
+ m_finishedItems.clear();
+ startUpdating();
+ }
+ }
+}
+
+QSize KFileItemModelRolesUpdater::iconSize() const
+{
+ return m_iconSize;
+}
+
+void KFileItemModelRolesUpdater::setVisibleIndexRange(int index, int count)
+{
+ if (index < 0) {
+ index = 0;
+ }
+ if (count < 0) {
+ count = 0;
+ }
+
+ if (index == m_firstVisibleIndex && count == m_lastVisibleIndex - m_firstVisibleIndex + 1) {
+ // The range has not been changed
+ return;
+ }
+
+ m_firstVisibleIndex = index;
+ m_lastVisibleIndex = qMin(index + count - 1, m_model->count() - 1);
+
+ startUpdating();
+}
+
+void KFileItemModelRolesUpdater::setMaximumVisibleItems(int count)
+{
+ m_maximumVisibleItems = count;
+}
+
+void KFileItemModelRolesUpdater::setPreviewsShown(bool show)
+{
+ if (show == m_previewShown) {
+ return;
+ }
+
+ m_previewShown = show;
+ if (!show) {
+ m_clearPreviews = true;
+ }
+
+ updateAllPreviews();
+}
+
+bool KFileItemModelRolesUpdater::previewsShown() const
+{
+ return m_previewShown;
+}
+
+void KFileItemModelRolesUpdater::setEnlargeSmallPreviews(bool enlarge)
+{
+ if (enlarge != m_enlargeSmallPreviews) {
+ m_enlargeSmallPreviews = enlarge;
+ if (m_previewShown) {
+ updateAllPreviews();
+ }
+ }
+}
+
+bool KFileItemModelRolesUpdater::enlargeSmallPreviews() const
+{
+ return m_enlargeSmallPreviews;
+}
+
+void KFileItemModelRolesUpdater::setEnabledPlugins(const QStringList& list)
+{
+ if (m_enabledPlugins != list) {
+ m_enabledPlugins = list;
+ if (m_previewShown) {
+ updateAllPreviews();
+ }
+ }
+}
+
+void KFileItemModelRolesUpdater::setPaused(bool paused)
+{
+ if (paused == (m_state == Paused)) {
+ return;
+ }
+
+ if (paused) {
+ m_state = Paused;
+ killPreviewJob();
+ } else {
+ const bool updatePreviews = (m_iconSizeChangedDuringPausing && m_previewShown) ||
+ m_previewChangedDuringPausing;
+ const bool resolveAll = updatePreviews || m_rolesChangedDuringPausing;
+ if (resolveAll) {
+ m_finishedItems.clear();
+ }
+
+ m_iconSizeChangedDuringPausing = false;
+ m_previewChangedDuringPausing = false;
+ m_rolesChangedDuringPausing = false;
+
+ if (!m_pendingSortRoleItems.isEmpty()) {
+ m_state = ResolvingSortRole;
+ resolveNextSortRole();
+ } else {
+ m_state = Idle;
+ }
+
+ startUpdating();
+ }
+}
+
+void KFileItemModelRolesUpdater::setRoles(const QSet<QByteArray>& roles)
+{
+ if (m_roles != roles) {
+ m_roles = roles;
+
+#ifdef HAVE_BALOO
+ // Check whether there is at least one role that must be resolved
+ // with the help of Baloo. If this is the case, a (quite expensive)
+ // resolving will be done in KFileItemModelRolesUpdater::rolesData() and
+ // the role gets watched for changes.
+ const KBalooRolesProvider& rolesProvider = KBalooRolesProvider::instance();
+ bool hasBalooRole = false;
+ QSetIterator<QByteArray> it(roles);
+ while (it.hasNext()) {
+ const QByteArray& role = it.next();
+ if (rolesProvider.roles().contains(role)) {
+ hasBalooRole = true;
+ break;
+ }
+ }
+
+ if (hasBalooRole && !m_balooFileMonitor) {
+ m_balooFileMonitor = new Baloo::FileMonitor(this);
+ connect(m_balooFileMonitor, SIGNAL(fileMetaDataChanged(QString)),
+ this, SLOT(applyChangedBalooRoles(QString)));
+ } else if (!hasBalooRole && m_balooFileMonitor) {
+ delete m_balooFileMonitor;
+ m_balooFileMonitor = 0;
+ }
+#endif
+
+ if (m_state == Paused) {
+ m_rolesChangedDuringPausing = true;
+ } else {
+ startUpdating();
+ }
+ }
+}
+
+QSet<QByteArray> KFileItemModelRolesUpdater::roles() const
+{
+ return m_roles;
+}
+
+bool KFileItemModelRolesUpdater::isPaused() const
+{
+ return m_state == Paused;
+}
+
+QStringList KFileItemModelRolesUpdater::enabledPlugins() const
+{
+ return m_enabledPlugins;
+}
+
+void KFileItemModelRolesUpdater::slotItemsInserted(const KItemRangeList& itemRanges)
+{
+ QElapsedTimer timer;
+ timer.start();
+
+ // Determine the sort role synchronously for as many items as possible.
+ if (m_resolvableRoles.contains(m_model->sortRole())) {
+ int insertedCount = 0;
+ foreach (const KItemRange& range, itemRanges) {
+ const int lastIndex = insertedCount + range.index + range.count - 1;
+ for (int i = insertedCount + range.index; i <= lastIndex; ++i) {
+ if (timer.elapsed() < MaxBlockTimeout) {
+ applySortRole(i);
+ } else {
+ m_pendingSortRoleItems.insert(m_model->fileItem(i));
+ }
+ }
+ insertedCount += range.count;
+ }
+
+ applySortProgressToModel();
+
+ // If there are still items whose sort role is unknown, check if the
+ // asynchronous determination of the sort role is already in progress,
+ // and start it if that is not the case.
+ if (!m_pendingSortRoleItems.isEmpty() && m_state != ResolvingSortRole) {
+ killPreviewJob();
+ m_state = ResolvingSortRole;
+ resolveNextSortRole();
+ }
+ }
+
+ startUpdating();
+}
+
+void KFileItemModelRolesUpdater::slotItemsRemoved(const KItemRangeList& itemRanges)
+{
+ Q_UNUSED(itemRanges);
+
+ const bool allItemsRemoved = (m_model->count() == 0);
+
+#ifdef HAVE_BALOO
+ if (m_balooFileMonitor) {
+ // Don't let the FileWatcher watch for removed items
+ if (allItemsRemoved) {
+ m_balooFileMonitor->clear();
+ } else {
+ QStringList newFileList;
+ foreach (const QString& itemUrl, m_balooFileMonitor->files()) {
+ if (m_model->index(itemUrl) >= 0) {
+ newFileList.append(itemUrl);
+ }
+ }
+ m_balooFileMonitor->setFiles(newFileList);
+ }
+ }
+#endif
+
+ if (allItemsRemoved) {
+ m_state = Idle;
+
+ m_finishedItems.clear();
+ m_pendingSortRoleItems.clear();
+ m_pendingIndexes.clear();
+ m_pendingPreviewItems.clear();
+ m_recentlyChangedItems.clear();
+ m_recentlyChangedItemsTimer->stop();
+ m_changedItems.clear();
+
+ killPreviewJob();
+ } else {
+ // Only remove the items from m_finishedItems. They will be removed
+ // from the other sets later on.
+ QSet<KFileItem>::iterator it = m_finishedItems.begin();
+ while (it != m_finishedItems.end()) {
+ if (m_model->index(*it) < 0) {
+ it = m_finishedItems.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ // The visible items might have changed.
+ startUpdating();
+ }
+}
+
+void KFileItemModelRolesUpdater::slotItemsMoved(const KItemRange& itemRange, QList<int> movedToIndexes)
+{
+ Q_UNUSED(itemRange);
+ Q_UNUSED(movedToIndexes);
+
+ // The visible items might have changed.
+ startUpdating();
+}
+
+void KFileItemModelRolesUpdater::slotItemsChanged(const KItemRangeList& itemRanges,
+ const QSet<QByteArray>& roles)
+{
+ Q_UNUSED(roles);
+
+ // Find out if slotItemsChanged() has been done recently. If that is the
+ // case, resolving the roles is postponed until a timer has exceeded
+ // to prevent expensive repeated updates if files are updated frequently.
+ const bool itemsChangedRecently = m_recentlyChangedItemsTimer->isActive();
+
+ QSet<KFileItem>& targetSet = itemsChangedRecently ? m_recentlyChangedItems : m_changedItems;
+
+ foreach (const KItemRange& itemRange, itemRanges) {
+ int index = itemRange.index;
+ for (int count = itemRange.count; count > 0; --count) {
+ const KFileItem item = m_model->fileItem(index);
+ targetSet.insert(item);
+ ++index;
+ }
+ }
+
+ m_recentlyChangedItemsTimer->start();
+
+ if (!itemsChangedRecently) {
+ updateChangedItems();
+ }
+}
+
+void KFileItemModelRolesUpdater::slotSortRoleChanged(const QByteArray& current,
+ const QByteArray& previous)
+{
+ Q_UNUSED(current);
+ Q_UNUSED(previous);
+
+ if (m_resolvableRoles.contains(current)) {
+ m_pendingSortRoleItems.clear();
+ m_finishedItems.clear();
+
+ const int count = m_model->count();
+ QElapsedTimer timer;
+ timer.start();
+
+ // Determine the sort role synchronously for as many items as possible.
+ for (int index = 0; index < count; ++index) {
+ if (timer.elapsed() < MaxBlockTimeout) {
+ applySortRole(index);
+ } else {
+ m_pendingSortRoleItems.insert(m_model->fileItem(index));
+ }
+ }
+
+ applySortProgressToModel();
+
+ if (!m_pendingSortRoleItems.isEmpty()) {
+ // Trigger the asynchronous determination of the sort role.
+ killPreviewJob();
+ m_state = ResolvingSortRole;
+ resolveNextSortRole();
+ }
+ } else {
+ m_state = Idle;
+ m_pendingSortRoleItems.clear();
+ applySortProgressToModel();
+ }
+}
+
+void KFileItemModelRolesUpdater::slotGotPreview(const KFileItem& item, const QPixmap& pixmap)
+{
+ if (m_state != PreviewJobRunning) {
+ return;
+ }
+
+ m_changedItems.remove(item);
+
+ const int index = m_model->index(item);
+ if (index < 0) {
+ return;
+ }
+
+ QPixmap scaledPixmap = pixmap;
+
+ const QString mimeType = item.mimetype();
+ const int slashIndex = mimeType.indexOf(QLatin1Char('/'));
+ const QString mimeTypeGroup = mimeType.left(slashIndex);
+ if (mimeTypeGroup == QLatin1String("image")) {
+ if (m_enlargeSmallPreviews) {
+ KPixmapModifier::applyFrame(scaledPixmap, m_iconSize);
+ } else {
+ // Assure that small previews don't get enlarged. Instead they
+ // should be shown centered within the frame.
+ const QSize contentSize = KPixmapModifier::sizeInsideFrame(m_iconSize);
+ const bool enlargingRequired = scaledPixmap.width() < contentSize.width() &&
+ scaledPixmap.height() < contentSize.height();
+ if (enlargingRequired) {
+ QSize frameSize = scaledPixmap.size();
+ frameSize.scale(m_iconSize, Qt::KeepAspectRatio);
+
+ QPixmap largeFrame(frameSize);
+ largeFrame.fill(Qt::transparent);
+
+ KPixmapModifier::applyFrame(largeFrame, frameSize);
+
+ QPainter painter(&largeFrame);
+ painter.drawPixmap((largeFrame.width() - scaledPixmap.width()) / 2,
+ (largeFrame.height() - scaledPixmap.height()) / 2,
+ scaledPixmap);
+ scaledPixmap = largeFrame;
+ } else {
+ // The image must be shrinked as it is too large to fit into
+ // the available icon size
+ KPixmapModifier::applyFrame(scaledPixmap, m_iconSize);
+ }
+ }
+ } else {
+ KPixmapModifier::scale(scaledPixmap, m_iconSize);
+ }
+
+ QHash<QByteArray, QVariant> data = rolesData(item);
+
+ const QStringList overlays = data["iconOverlays"].toStringList();
+ // Strangely KFileItem::overlays() returns empty string-values, so
+ // we need to check first whether an overlay must be drawn at all.
+ // It is more efficient to do it here, as KIconLoader::drawOverlays()
+ // assumes that an overlay will be drawn and has some additional
+ // setup time.
+ foreach (const QString& overlay, overlays) {
+ if (!overlay.isEmpty()) {
+ // There is at least one overlay, draw all overlays above m_pixmap
+ // and cancel the check
+ KIconLoader::global()->drawOverlays(overlays, scaledPixmap, KIconLoader::Desktop);
+ break;
+ }
+ }
+
+ data.insert("iconPixmap", scaledPixmap);
+
+ disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
+ this, SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
+ m_model->setData(index, data);
+ connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
+ this, SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
+
+ m_finishedItems.insert(item);
+}
+
+void KFileItemModelRolesUpdater::slotPreviewFailed(const KFileItem& item)
+{
+ if (m_state != PreviewJobRunning) {
+ return;
+ }
+
+ m_changedItems.remove(item);
+
+ const int index = m_model->index(item);
+ if (index >= 0) {
+ QHash<QByteArray, QVariant> data;
+ data.insert("iconPixmap", QPixmap());
+
+ disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
+ this, SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
+ m_model->setData(index, data);
+ connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
+ this, SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
+
+ applyResolvedRoles(index, ResolveAll);
+ m_finishedItems.insert(item);
+ }
+}
+
+void KFileItemModelRolesUpdater::slotPreviewJobFinished()
+{
+ m_previewJob = 0;
+
+ if (m_state != PreviewJobRunning) {
+ return;
+ }
+
+ m_state = Idle;
+
+ if (!m_pendingPreviewItems.isEmpty()) {
+ startPreviewJob();
+ } else {
+ if (!m_changedItems.isEmpty()) {
+ updateChangedItems();
+ }
+ }
+}
+
+void KFileItemModelRolesUpdater::resolveNextSortRole()
+{
+ if (m_state != ResolvingSortRole) {
+ return;
+ }
+
+ QSet<KFileItem>::iterator it = m_pendingSortRoleItems.begin();
+ while (it != m_pendingSortRoleItems.end()) {
+ const KFileItem item = *it;
+ const int index = m_model->index(item);
+
+ // Continue if the sort role has already been determined for the
+ // item, and the item has not been changed recently.
+ if (!m_changedItems.contains(item) && m_model->data(index).contains(m_model->sortRole())) {
+ it = m_pendingSortRoleItems.erase(it);
+ continue;
+ }
+
+ applySortRole(index);
+ m_pendingSortRoleItems.erase(it);
+ break;
+ }
+
+ if (!m_pendingSortRoleItems.isEmpty()) {
+ applySortProgressToModel();
+ QTimer::singleShot(0, this, SLOT(resolveNextSortRole()));
+ } else {
+ m_state = Idle;
+
+ // Prevent that we try to update the items twice.
+ disconnect(m_model, SIGNAL(itemsMoved(KItemRange,QList<int>)),
+ this, SLOT(slotItemsMoved(KItemRange,QList<int>)));
+ applySortProgressToModel();
+ connect(m_model, SIGNAL(itemsMoved(KItemRange,QList<int>)),
+ this, SLOT(slotItemsMoved(KItemRange,QList<int>)));
+ startUpdating();
+ }
+}
+
+void KFileItemModelRolesUpdater::resolveNextPendingRoles()
+{
+ if (m_state != ResolvingAllRoles) {
+ return;
+ }
+
+ while (!m_pendingIndexes.isEmpty()) {
+ const int index = m_pendingIndexes.takeFirst();
+ const KFileItem item = m_model->fileItem(index);
+
+ if (m_finishedItems.contains(item)) {
+ continue;
+ }
+
+ applyResolvedRoles(index, ResolveAll);
+ m_finishedItems.insert(item);
+ m_changedItems.remove(item);
+ break;
+ }
+
+ if (!m_pendingIndexes.isEmpty()) {
+ QTimer::singleShot(0, this, SLOT(resolveNextPendingRoles()));
+ } else {
+ m_state = Idle;
+
+ if (m_clearPreviews) {
+ // Only go through the list if there are items which might still have previews.
+ if (m_finishedItems.count() != m_model->count()) {
+ QHash<QByteArray, QVariant> data;
+ data.insert("iconPixmap", QPixmap());
+
+ disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
+ this, SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
+ for (int index = 0; index <= m_model->count(); ++index) {
+ if (m_model->data(index).contains("iconPixmap")) {
+ m_model->setData(index, data);
+ }
+ }
+ connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
+ this, SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
+
+ }
+ m_clearPreviews = false;
+ }
+
+ if (!m_changedItems.isEmpty()) {
+ updateChangedItems();
+ }
+ }
+}
+
+void KFileItemModelRolesUpdater::resolveRecentlyChangedItems()
+{
+ m_changedItems += m_recentlyChangedItems;
+ m_recentlyChangedItems.clear();
+ updateChangedItems();
+}
+
+void KFileItemModelRolesUpdater::applyChangedBalooRoles(const QString& itemUrl)
+{
+#ifdef HAVE_BALOO
+ const KFileItem item = m_model->fileItem(itemUrl);
+
+ if (item.isNull()) {
+ // itemUrl is not in the model anymore, probably because
+ // the corresponding file has been deleted in the meantime.
+ return;
+ }
+
+ Baloo::FileFetchJob* job = new Baloo::FileFetchJob(item.localPath());
+ connect(job, SIGNAL(finished(KJob*)), this, SLOT(applyChangedBalooRolesJobFinished(KJob*)));
+ job->setProperty("item", QVariant::fromValue(item));
+ job->start();
+#else
+#ifndef Q_CC_MSVC
+ Q_UNUSED(itemUrl);
+#endif
+#endif
+}
+
+void KFileItemModelRolesUpdater::applyChangedBalooRolesJobFinished(KJob* kjob)
+{
+#ifdef HAVE_BALOO
+ const KFileItem item = kjob->property("item").value<KFileItem>();
+
+ const KBalooRolesProvider& rolesProvider = KBalooRolesProvider::instance();
+ QHash<QByteArray, QVariant> data;
+
+ foreach (const QByteArray& role, rolesProvider.roles()) {
+ // Overwrite all the role values with an empty QVariant, because the roles
+ // provider doesn't overwrite it when the property value list is empty.
+ // See bug 322348
+ data.insert(role, QVariant());
+ }
+
+ Baloo::FileFetchJob* job = static_cast<Baloo::FileFetchJob*>(kjob);
+ QHashIterator<QByteArray, QVariant> it(rolesProvider.roleValues(job->file(), m_roles));
+ while (it.hasNext()) {
+ it.next();
+ data.insert(it.key(), it.value());
+ }
+
+ disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
+ this, SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
+ const int index = m_model->index(item);
+ m_model->setData(index, data);
+ connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
+ this, SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
+#endif
+}
+
+void KFileItemModelRolesUpdater::slotDirectoryContentsCountReceived(const QString& path, int count)
+{
+ const bool getSizeRole = m_roles.contains("size");
+ const bool getIsExpandableRole = m_roles.contains("isExpandable");
+
+ if (getSizeRole || getIsExpandableRole) {
+ const int index = m_model->index(KUrl(path));
+ if (index >= 0) {
+ QHash<QByteArray, QVariant> data;
+
+ if (getSizeRole) {
+ data.insert("size", count);
+ }
+ if (getIsExpandableRole) {
+ data.insert("isExpandable", count > 0);
+ }
+
+ disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
+ this, SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
+ m_model->setData(index, data);
+ connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
+ this, SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
+ }
+ }
+}
+
+void KFileItemModelRolesUpdater::startUpdating()
+{
+ if (m_state == Paused) {
+ return;
+ }
+
+ if (m_finishedItems.count() == m_model->count()) {
+ // All roles have been resolved already.
+ m_state = Idle;
+ return;
+ }
+
+ // Terminate all updates that are currently active.
+ killPreviewJob();
+ m_pendingIndexes.clear();
+
+ QElapsedTimer timer;
+ timer.start();
+
+ // Determine the icons for the visible items synchronously.
+ updateVisibleIcons();
+
+ // A detailed update of the items in and near the visible area
+ // only makes sense if sorting is finished.
+ if (m_state == ResolvingSortRole) {
+ return;
+ }
+
+ // Start the preview job or the asynchronous resolving of all roles.
+ QList<int> indexes = indexesToResolve();
+
+ if (m_previewShown) {
+ m_pendingPreviewItems.clear();
+ m_pendingPreviewItems.reserve(indexes.count());
+
+ foreach (int index, indexes) {
+ const KFileItem item = m_model->fileItem(index);
+ if (!m_finishedItems.contains(item)) {
+ m_pendingPreviewItems.append(item);
+ }
+ }
+
+ startPreviewJob();
+ } else {
+ m_pendingIndexes = indexes;
+ // Trigger the asynchronous resolving of all roles.
+ m_state = ResolvingAllRoles;
+ QTimer::singleShot(0, this, SLOT(resolveNextPendingRoles()));
+ }
+}
+
+void KFileItemModelRolesUpdater::updateVisibleIcons()
+{
+ int lastVisibleIndex = m_lastVisibleIndex;
+ if (lastVisibleIndex <= 0) {
+ // Guess a reasonable value for the last visible index if the view
+ // has not told us about the real value yet.
+ lastVisibleIndex = qMin(m_firstVisibleIndex + m_maximumVisibleItems, m_model->count() - 1);
+ if (lastVisibleIndex <= 0) {
+ lastVisibleIndex = qMin(200, m_model->count() - 1);
+ }
+ }
+
+ QElapsedTimer timer;
+ timer.start();
+
+ // Try to determine the final icons for all visible items.
+ int index;
+ for (index = m_firstVisibleIndex; index <= lastVisibleIndex && timer.elapsed() < MaxBlockTimeout; ++index) {
+ applyResolvedRoles(index, ResolveFast);
+ }
+
+ // KFileItemListView::initializeItemListWidget(KItemListWidget*) will load
+ // preliminary icons (i.e., without mime type determination) for the
+ // remaining items.
+}
+
+void KFileItemModelRolesUpdater::startPreviewJob()
+{
+ m_state = PreviewJobRunning;
+
+ if (m_pendingPreviewItems.isEmpty()) {
+ QTimer::singleShot(0, this, SLOT(slotPreviewJobFinished()));
+ return;
+ }
+
+ // PreviewJob internally caches items always with the size of
+ // 128 x 128 pixels or 256 x 256 pixels. A (slow) downscaling is done
+ // by PreviewJob if a smaller size is requested. For images KFileItemModelRolesUpdater must
+ // do a downscaling anyhow because of the frame, so in this case only the provided
+ // cache sizes are requested.
+ const QSize cacheSize = (m_iconSize.width() > 128) || (m_iconSize.height() > 128)
+ ? QSize(256, 256) : QSize(128, 128);
+
+ // KIO::filePreview() will request the MIME-type of all passed items, which (in the
+ // worst case) might block the application for several seconds. To prevent such
+ // a blocking, we only pass items with known mime type to the preview job.
+ const int count = m_pendingPreviewItems.count();
+ KFileItemList itemSubSet;
+ itemSubSet.reserve(count);
+
+ if (m_pendingPreviewItems.first().isMimeTypeKnown()) {
+ // Some mime types are known already, probably because they were
+ // determined when loading the icons for the visible items. Start
+ // a preview job for all items at the beginning of the list which
+ // have a known mime type.
+ do {
+ itemSubSet.append(m_pendingPreviewItems.takeFirst());
+ } while (!m_pendingPreviewItems.isEmpty() && m_pendingPreviewItems.first().isMimeTypeKnown());
+ } else {
+ // Determine mime types for MaxBlockTimeout ms, and start a preview
+ // job for the corresponding items.
+ QElapsedTimer timer;
+ timer.start();
+
+ do {
+ const KFileItem item = m_pendingPreviewItems.takeFirst();
+ item.determineMimeType();
+ itemSubSet.append(item);
+ } while (!m_pendingPreviewItems.isEmpty() && timer.elapsed() < MaxBlockTimeout);
+ }
+
+ KIO::PreviewJob* job = new KIO::PreviewJob(itemSubSet, cacheSize, &m_enabledPlugins);
+
+ job->setIgnoreMaximumSize(itemSubSet.first().isLocalFile());
+ if (job->ui()) {
+ job->ui()->setWindow(qApp->activeWindow());
+ }
+
+ connect(job, SIGNAL(gotPreview(KFileItem,QPixmap)),
+ this, SLOT(slotGotPreview(KFileItem,QPixmap)));
+ connect(job, SIGNAL(failed(KFileItem)),
+ this, SLOT(slotPreviewFailed(KFileItem)));
+ connect(job, SIGNAL(finished(KJob*)),
+ this, SLOT(slotPreviewJobFinished()));
+
+ m_previewJob = job;
+}
+
+void KFileItemModelRolesUpdater::updateChangedItems()
+{
+ if (m_state == Paused) {
+ return;
+ }
+
+ if (m_changedItems.isEmpty()) {
+ return;
+ }
+
+ m_finishedItems -= m_changedItems;
+
+ if (m_resolvableRoles.contains(m_model->sortRole())) {
+ m_pendingSortRoleItems += m_changedItems;
+
+ if (m_state != ResolvingSortRole) {
+ // Stop the preview job if necessary, and trigger the
+ // asynchronous determination of the sort role.
+ killPreviewJob();
+ m_state = ResolvingSortRole;
+ QTimer::singleShot(0, this, SLOT(resolveNextSortRole()));
+ }
+
+ return;
+ }
+
+ QList<int> visibleChangedIndexes;
+ QList<int> invisibleChangedIndexes;
+
+ foreach (const KFileItem& item, m_changedItems) {
+ const int index = m_model->index(item);
+
+ if (index < 0) {
+ m_changedItems.remove(item);
+ continue;
+ }
+
+ if (index >= m_firstVisibleIndex && index <= m_lastVisibleIndex) {
+ visibleChangedIndexes.append(index);
+ } else {
+ invisibleChangedIndexes.append(index);
+ }
+ }
+
+ std::sort(visibleChangedIndexes.begin(), visibleChangedIndexes.end());
+
+ if (m_previewShown) {
+ foreach (int index, visibleChangedIndexes) {
+ m_pendingPreviewItems.append(m_model->fileItem(index));
+ }
+
+ foreach (int index, invisibleChangedIndexes) {
+ m_pendingPreviewItems.append(m_model->fileItem(index));
+ }
+
+ if (!m_previewJob) {
+ startPreviewJob();
+ }
+ } else {
+ const bool resolvingInProgress = !m_pendingIndexes.isEmpty();
+ m_pendingIndexes = visibleChangedIndexes + m_pendingIndexes + invisibleChangedIndexes;
+ if (!resolvingInProgress) {
+ // Trigger the asynchronous resolving of the changed roles.
+ m_state = ResolvingAllRoles;
+ QTimer::singleShot(0, this, SLOT(resolveNextPendingRoles()));
+ }
+ }
+}
+
+void KFileItemModelRolesUpdater::applySortRole(int index)
+{
+ QHash<QByteArray, QVariant> data;
+ const KFileItem item = m_model->fileItem(index);
+
+ if (m_model->sortRole() == "type") {
+ if (!item.isMimeTypeKnown()) {
+ item.determineMimeType();
+ }
+
+ data.insert("type", item.mimeComment());
+ } else if (m_model->sortRole() == "size" && item.isLocalFile() && item.isDir()) {
+ const QString path = item.localPath();
+ data.insert("size", m_directoryContentsCounter->countDirectoryContentsSynchronously(path));
+ } else {
+ // Probably the sort role is a baloo role - just determine all roles.
+ data = rolesData(item);
+ }
+
+ disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
+ this, SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
+ m_model->setData(index, data);
+ connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
+ this, SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
+}
+
+void KFileItemModelRolesUpdater::applySortProgressToModel()
+{
+ // Inform the model about the progress of the resolved items,
+ // so that it can give an indication when the sorting has been finished.
+ const int resolvedCount = m_model->count() - m_pendingSortRoleItems.count();
+ m_model->emitSortProgress(resolvedCount);
+}
+
+bool KFileItemModelRolesUpdater::applyResolvedRoles(int index, ResolveHint hint)
+{
+ const KFileItem item = m_model->fileItem(index);
+ const bool resolveAll = (hint == ResolveAll);
+
+ bool iconChanged = false;
+ if (!item.isMimeTypeKnown() || !item.isFinalIconKnown()) {
+ item.determineMimeType();
+ iconChanged = true;
+ } else if (!m_model->data(index).contains("iconName")) {
+ iconChanged = true;
+ }
+
+ if (iconChanged || resolveAll || m_clearPreviews) {
+ if (index < 0) {
+ return false;
+ }
+
+ QHash<QByteArray, QVariant> data;
+ if (resolveAll) {
+ data = rolesData(item);
+ }
+
+ data.insert("iconName", item.iconName());
+
+ if (m_clearPreviews) {
+ data.insert("iconPixmap", QPixmap());
+ }
+
+ disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
+ this, SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
+ m_model->setData(index, data);
+ connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
+ this, SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
+ return true;
+ }
+
+ return false;
+}
+
+QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileItem& item)
+{
+ QHash<QByteArray, QVariant> data;
+
+ const bool getSizeRole = m_roles.contains("size");
+ const bool getIsExpandableRole = m_roles.contains("isExpandable");
+
+ if ((getSizeRole || getIsExpandableRole) && item.isDir()) {
+ if (item.isLocalFile()) {
+ // Tell m_directoryContentsCounter that we want to count the items
+ // inside the directory. The result will be received in slotDirectoryContentsCountReceived.
+ const QString path = item.localPath();
+ m_directoryContentsCounter->addDirectory(path);
+ } else if (getSizeRole) {
+ data.insert("size", -1); // -1 indicates an unknown number of items
+ }
+ }
+
+ if (m_roles.contains("type")) {
+ data.insert("type", item.mimeComment());
+ }
+
+ data.insert("iconOverlays", item.overlays());
+
+#ifdef HAVE_BALOO
+ if (m_balooFileMonitor) {
+ m_balooFileMonitor->addFile(item.localPath());
+ applyChangedBalooRoles(item.localPath());
+ }
+#endif
+ return data;
+}
+
+void KFileItemModelRolesUpdater::updateAllPreviews()
+{
+ if (m_state == Paused) {
+ m_previewChangedDuringPausing = true;
+ } else {
+ m_finishedItems.clear();
+ startUpdating();
+ }
+}
+
+void KFileItemModelRolesUpd