1 /***************************************************************************
2 rkfilebrowser - description
3 -------------------
4 begin : Thu Apr 26 2007
5 copyright : (C) 2007-2018 by Thomas Friedrichsmeier
6 email : thomas.friedrichsmeier@kdemail.net
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include "rkfilebrowser.h"
19
20 #include <kdiroperator.h>
21 #include <kurlcombobox.h>
22 #include <kurlcompletion.h>
23 #include <kcompletionbox.h>
24 #include <ktoolbar.h>
25 #include <kactioncollection.h>
26 #include <kconfiggroup.h>
27 #include <KSharedConfig>
28 #include <kfileitemactions.h>
29 #include <kfileitemlistproperties.h>
30 #include <KLocalizedString>
31 #include <kio/copyjob.h>
32
33 #include <qdir.h>
34 #include <qlayout.h>
35 #include <QEvent>
36 #include <QVBoxLayout>
37 #include <QScrollBar>
38 #include <QMenu>
39 #include <QInputDialog>
40
41 #include "rkworkplace.h"
42 #include "../rkglobals.h"
43 #include "../rbackend/rkrinterface.h"
44 #include "../rkward.h"
45 #include "../misc/rkdummypart.h"
46
47 #include "../debug.h"
48
49 // static
50 RKFileBrowser *RKFileBrowser::main_browser = 0;
51
RKFileBrowser(QWidget * parent,bool tool_window,const char * name)52 RKFileBrowser::RKFileBrowser (QWidget *parent, bool tool_window, const char *name) : RKMDIWindow (parent, FileBrowserWindow, tool_window, name) {
53 RK_TRACE (APP);
54
55 real_widget = 0;
56
57 QVBoxLayout *layout = new QVBoxLayout (this);
58 layout->setContentsMargins (0, 0, 0, 0);
59 layout_widget = new QWidget (this);
60 layout->addWidget (layout_widget);
61 layout_widget->setFocusPolicy (Qt::StrongFocus);
62
63 RKDummyPart *part = new RKDummyPart (this, layout_widget);
64 setPart (part);
65 initializeActivationSignals ();
66 }
67
~RKFileBrowser()68 RKFileBrowser::~RKFileBrowser () {
69 RK_TRACE (APP);
70
71 hide ();
72 }
73
showEvent(QShowEvent * e)74 void RKFileBrowser::showEvent (QShowEvent *e) {
75 RK_TRACE (APP);
76
77 if (!real_widget) {
78 RK_DEBUG (APP, DL_INFO, "creating file browser");
79
80 real_widget = new RKFileBrowserWidget (layout_widget);
81 QVBoxLayout *l = new QVBoxLayout (layout_widget);
82 l->setContentsMargins (0, 0, 0, 0);
83 l->addWidget (real_widget);
84 setFocusProxy (real_widget);
85 }
86
87 RKMDIWindow::showEvent (e);
88 real_widget->syncToWD();
89 }
90
91 /////////////////// RKFileBrowserWidget ////////////////////
92
RKFileBrowserWidget(QWidget * parent)93 RKFileBrowserWidget::RKFileBrowserWidget (QWidget *parent) : QWidget (parent) {
94 RK_TRACE (APP);
95
96 QVBoxLayout *layout = new QVBoxLayout (this);
97
98 KToolBar *toolbar = new KToolBar (this);
99 toolbar->setIconSize (QSize (16, 16));
100 toolbar->setToolButtonStyle (Qt::ToolButtonIconOnly);
101 layout->addWidget (toolbar);
102
103 urlbox = new KUrlComboBox (KUrlComboBox::Directories, true, this);
104 KUrlCompletion* cmpl = new KUrlCompletion (KUrlCompletion::DirCompletion);
105 urlbox->setCompletionObject (cmpl);
106 urlbox->setAutoDeleteCompletionObject (true);
107 urlbox->setSizePolicy (QSizePolicy (QSizePolicy::Expanding, QSizePolicy::Fixed));
108 urlbox->completionBox (true)->installEventFilter (this);
109 setFocusProxy (urlbox);
110 layout->addWidget (urlbox);
111
112 dir = new KDirOperator (QUrl (), this);
113 dir->setPreviewWidget (0);
114 KConfigGroup config = KSharedConfig::openConfig ()->group ("file browser window");
115 dir->readConfig (config);
116 dir->setView (KFile::Tree);
117 connect (RKWardMainWindow::getMain (), &RKWardMainWindow::aboutToQuitRKWard, this, &RKFileBrowserWidget::saveConfig);
118 layout->addWidget (dir);
119
120 toolbar->addAction (dir->actionCollection ()->action ("up"));
121 toolbar->addAction (dir->actionCollection ()->action ("back"));
122 toolbar->addAction (dir->actionCollection ()->action ("forward"));
123 toolbar->addAction (dir->actionCollection ()->action ("home"));
124 QAction* action = new QAction (QIcon::fromTheme ("folder-sync"), i18n ("Working directory"), this);
125 action->setToolTip (action->text ());
126 connect(action, &QAction::triggered, this, [=] () { follow_working_directory = true; syncToWD(); });
127 toolbar->addAction (action);
128 toolbar->addSeparator ();
129 toolbar->addAction (dir->actionCollection ()->action ("short view"));
130 toolbar->addAction (dir->actionCollection ()->action ("tree view"));
131 toolbar->addAction (dir->actionCollection ()->action ("detailed view"));
132 // toolbar->addAction (dir->actionCollection ()->action ("detailed tree view")); // should we have this as well? Trying to avoid crowding in the toolbar
133
134 fi_actions = new KFileItemActions (this);
135 rename_action = new QAction (i18n ("Rename"), this); // Oh my, why isn't there a standard action for this?
136 rename_action->setIcon (QIcon::fromTheme (QStringLiteral("edit-rename")));
137 connect (rename_action, &QAction::triggered, this, &RKFileBrowserWidget::rename);
138 connect (dir, &KDirOperator::contextMenuAboutToShow, this, &RKFileBrowserWidget::contextMenuHook);
139
140 connect (dir, &KDirOperator::urlEntered, this, &RKFileBrowserWidget::urlChangedInView);
141 connect (urlbox, static_cast<void (KUrlComboBox::*)(const QString&)>(&KUrlComboBox::returnPressed), this, &RKFileBrowserWidget::stringChangedInCombo);
142 connect (urlbox, &KUrlComboBox::urlActivated, this, &RKFileBrowserWidget::urlChangedInCombo);
143
144 connect (dir, &KDirOperator::fileSelected, this, &RKFileBrowserWidget::fileActivated);
145 connect (RKGlobals::rInterface (), &RInterface::backendWorkdirChanged, this, &RKFileBrowserWidget::syncToWD);
146
147 setURL (QUrl::fromLocalFile (QDir::currentPath ()));
148 }
149
~RKFileBrowserWidget()150 RKFileBrowserWidget::~RKFileBrowserWidget () {
151 RK_TRACE (APP);
152 }
153
syncToWD()154 void RKFileBrowserWidget::syncToWD () {
155 RK_TRACE (APP);
156
157 if (!follow_working_directory) return;
158 if (isVisible()) setURL (QUrl::fromLocalFile (QDir::currentPath ()));
159 }
160
rename()161 void RKFileBrowserWidget::rename () {
162 RK_TRACE (APP);
163
164 QString name = QInputDialog::getText (this, i18n ("Rename..."), i18n ("New name for '%1':", context_menu_url.fileName ()), QLineEdit::Normal, context_menu_url.fileName ());
165 if (name.isEmpty ()) return;
166
167 QUrl dest_url = context_menu_url;
168 dest_url.setPath (context_menu_url.adjusted (QUrl::RemoveFilename).path () + '/' + name);
169 KIO::moveAs (context_menu_url, dest_url);
170 }
171
contextMenuHook(const KFileItem & item,QMenu * menu)172 void RKFileBrowserWidget::contextMenuHook(const KFileItem& item, QMenu* menu) {
173 RK_TRACE (APP);
174
175 QList<KFileItem> dummy;
176 dummy.append (item);
177 fi_actions->setItemListProperties (KFileItemListProperties (dummy));
178 context_menu_url = item.url ();
179
180 // some versions of KDE appear to re-use the actions, others don't, and yet other are just plain broken (see this thread: https://mail.kde.org/pipermail/rkward-devel/2011-March/002770.html)
181 // Therefore, we remove all actions, explicitly, each time the menu is shown, then add them again.
182 QList<QAction*> menu_actions = menu->actions ();
183 QAction *first_sep = 0;
184 foreach (QAction* act, menu_actions) {
185 if (added_service_actions.contains (act)) menu->removeAction (act);
186 if (!first_sep && act->isSeparator ()) first_sep = act;
187 }
188 added_service_actions.clear ();
189 menu_actions = menu->actions ();
190
191 menu->insertAction (first_sep, rename_action);
192 fi_actions->addOpenWithActionsTo (menu, QString ());
193 fi_actions->addServiceActionsTo (menu);
194
195 QList<QAction*> menu_actions_after = menu->actions ();
196 foreach (QAction* act, menu_actions_after) if (!menu_actions.contains (act)) added_service_actions.append (act);
197 }
198
199 // does not work in d-tor. Apparently it's too late, then
saveConfig()200 void RKFileBrowserWidget::saveConfig () {
201 RK_TRACE (APP);
202
203 KConfigGroup config = KSharedConfig::openConfig ()->group ("file browser window");
204 dir->writeConfig (config);
205 }
206
setURL(const QUrl & url)207 void RKFileBrowserWidget::setURL (const QUrl &url) {
208 RK_TRACE (APP);
209
210 urlbox->setUrl (url);
211 dir->setUrl (url, true);
212 follow_working_directory = (url.path() == QDir::currentPath ());
213 }
214
urlChangedInView(const QUrl & url)215 void RKFileBrowserWidget::urlChangedInView (const QUrl &url) {
216 RK_TRACE (APP);
217
218 urlbox->setUrl (url);
219 follow_working_directory = (url.path() == QDir::currentPath ());
220 }
221
stringChangedInCombo(const QString & url)222 void RKFileBrowserWidget::stringChangedInCombo (const QString &url) {
223 RK_TRACE (APP);
224
225 dir->setUrl (QUrl::fromUserInput (url, QDir::currentPath (), QUrl::AssumeLocalFile), true);
226 }
227
urlChangedInCombo(const QUrl & url)228 void RKFileBrowserWidget::urlChangedInCombo (const QUrl &url) {
229 RK_TRACE (APP);
230
231 dir->setUrl (url, true);
232 }
233
eventFilter(QObject * o,QEvent * e)234 bool RKFileBrowserWidget::eventFilter (QObject* o, QEvent* e) {
235 // don't trace here
236
237 if (o == urlbox->completionBox () && e->type () == QEvent::Resize) {
238 RK_TRACE (APP);
239 // this hack (originally from a KDE 3 version of kate allows the completion popup to span beyond the border of the filebrowser widget itself
240
241 KCompletionBox* box = urlbox->completionBox ();
242 RK_ASSERT (box);
243
244 int add = box->verticalScrollBar ()->isVisible () ? box->verticalScrollBar ()->width () : 0;
245 box->setMinimumWidth (qMin (topLevelWidget ()->width (), box->sizeHintForColumn (0) + add));
246
247 return false;
248 }
249
250 return (QWidget::eventFilter (o, e));
251 }
252
fileActivated(const KFileItem & item)253 void RKFileBrowserWidget::fileActivated (const KFileItem& item) {
254 RK_TRACE (APP);
255
256 RKWorkplace::mainWorkplace ()->openAnyUrl (item.url ());
257 }
258
259