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