WSL/SLF GitLab Repository

IniFolderView.cc 10.9 KB
Newer Older
Mathias Bavay's avatar
Mathias Bavay committed
1
//SPDX-License-Identifier: GPL-3.0-or-later
2
3
4
5
6
/*****************************************************************************/
/*  Copyright 2019 WSL Institute for Snow and Avalanche Research  SLF-DAVOS  */
/*****************************************************************************/
/* This file is part of INIshell.
   INIshell is free software: you can redistribute it and/or modify
7
   it under the terms of the GNU General Public License as published by
8
9
10
11
12
13
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   INIshell 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
14
   GNU General Public License for more details.
15

16
17
   You should have received a copy of the GNU General Public License
   along with INIshell.  If not, see <http://www.gnu.org/licenses/>.
18
19
*/

20
21
22
23
24
#include <src/gui/IniFolderView.h>
#include <src/main/common.h>
#include <src/main/constants.h>
#include <src/main/inishell.h>
#include <src/main/settings.h>
25

26
#include <QCursor>
27
#include <QDesktopServices>
28
29
30
#include <QDir>
#include <QFile>
#include <QFileInfo>
31
#include <QMessageBox>
32
#include <QHBoxLayout>
33
#include <QHeaderView>
34
35
36
37
38
39
40
#include <QToolButton>
#include <QVBoxLayout>

#ifdef DEBUG
	#include <iostream>
#endif

41
42
43
44
45
46
47
48
/**
 * @class IniFolderView
 * @brief Constructor for the folder navigation panel.
 * @details This constructor initializes a file system model and connects a tree view with
 * it, which the user can navigate via separate buttons.
 * @param[in] parent This panel's parent window.
 */
IniFolderView::IniFolderView(QWidget *parent) : QWidget(parent)
49
{
50
	static const QStringList filters = {"*.ini"};
51

52
	/* info label and file system model */
53
	path_label_ = new QLabel;
54
	path_label_->setStyleSheet("QLabel {background-color: " + colors::getQColor("app_bg").name() + "}");
55
56
	filesystem_model_ = new QFileSystemModel;
	filesystem_model_->setNameFilters(filters);
57
	//the model's root path is the computer's root path; only the tree root is changed later
58
	filesystem_model_->setRootPath("");
Mathias Bavay's avatar
Mathias Bavay committed
59
	filesystem_model_->setNameFilterDisables(false); //hide unfit items instead of disabling them
60

61
	path_label_->setText(".");
62
	path_label_->setProperty("path", path_label_->text());
Mathias Bavay's avatar
Mathias Bavay committed
63
64
	path_label_->setWordWrap(true);
	path_label_->setAlignment(Qt::AlignCenter);
65

66
	/* file model tree view */
67
	filesystem_tree_ = new QTreeView;
68
	connect(filesystem_tree_, &QTreeView::doubleClicked, this, &IniFolderView::onFilesysDoubleclicked);
69
	filesystem_tree_->setContextMenuPolicy(Qt::CustomContextMenu);
70
71
	connect(filesystem_tree_, &QTreeView::customContextMenuRequested, this,
	    &IniFolderView::onContextMenuRequest);
72

73
	filesystem_tree_->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
Mathias Bavay's avatar
Mathias Bavay committed
74
75
	filesystem_tree_->setUniformRowHeights(true);
	filesystem_tree_->setWordWrap(true);
76
77
	//expand columns instead of abbreviations for the files:
	filesystem_tree_->header()->setSectionResizeMode(QHeaderView::Stretch);
Mathias Bavay's avatar
Mathias Bavay committed
78
	filesystem_tree_->resizeColumnToContents( 0 );
79
	filesystem_tree_->setIndentation(Cst::treeview_indentation);
80
81
	filesystem_tree_->setHeaderHidden(true);
	filesystem_tree_->setModel(filesystem_model_);
82
	filesystem_tree_->setEnabled(false); //will get enabled if an XML is loaded
83
84
	for (int ii = 1; ii < filesystem_model_->columnCount(); ++ii)
		filesystem_tree_->hideColumn(ii); //show only name column
85
	filesystem_tree_->setToolTip(tr("INI files on your computer.\nDouble-click to open, right-click for more options."));
86
	//set the directory the user has visited last time:
87
	const QString last_path( getSetting("auto::history::ini_folder", "path") );
88
	if (!last_path.isEmpty()) {
Michael Reisecker's avatar
Michael Reisecker committed
89
		setTreeIndex(filesystem_model_->index(last_path), true);
90
91
92
93
94
95
	} else { //by default, show the whole system tree and select the current directory:
		setTreeIndex(filesystem_model_->index(QDir::currentPath()));
		filesystem_tree_->scrollTo(filesystem_model_->index(QDir::currentPath()));
		filesystem_tree_->expand(filesystem_model_->index(QDir::currentPath()));
		filesystem_tree_->setCurrentIndex(filesystem_model_->index(QDir::currentPath()));
	}
Michael Reisecker's avatar
Michael Reisecker committed
96

97
	/* folder navigation toolbar */
98
	auto *button_back = new QToolButton;
Michael Reisecker's avatar
Michael Reisecker committed
99
	button_back->setIconSize(QSize(Cst::width_button_std, Cst::height_button_std));
100
	button_back->setAutoRaise(true);
Mathias Bavay's avatar
Mathias Bavay committed
101
	button_back->setIcon(getIcon("go-previous"));
102
	button_back->setToolTip(tr("Back"));
103
	connect(button_back, &QToolButton::clicked, this, &IniFolderView::onBackClicked);
104
	auto *button_up = new QToolButton;
105
	button_up->setIconSize(button_back->iconSize());
106
	button_up->setAutoRaise(true);
Mathias Bavay's avatar
Mathias Bavay committed
107
	button_up->setIcon(getIcon("go-up"));
108
	button_up->setToolTip(tr("Parent folder"));
109
	connect(button_up, &QToolButton::clicked, this, &IniFolderView::onUpClicked);
110
	auto *button_home = new QToolButton;
111
	button_home->setIconSize(button_back->iconSize());
112
	button_home->setAutoRaise(true);
Mathias Bavay's avatar
Mathias Bavay committed
113
	button_home->setIcon(getIcon("user-home"));
114
	button_home->setToolTip(tr("Home directory"));
115
	connect(button_home, &QToolButton::clicked, this, &IniFolderView::onHomeClicked);
116
117
118
	auto *button_working = new QToolButton;
	button_working->setIconSize(button_back->iconSize());
	button_working->setAutoRaise(true);
Mathias Bavay's avatar
Mathias Bavay committed
119
	button_working->setIcon(getIcon("folder-open"));
120
	button_working->setToolTip(tr("Working directory"));
121
	connect(button_working, &QToolButton::clicked, this, &IniFolderView::onWorkingClicked);
122
	//note: the connection to the file system is live, so we don't need a 'Refresh' button
123

124
125
	createContextMenu();

126
	/* layout of the navigation buttons */
127
128
129
130
131
	auto *toolbar_layout = new QHBoxLayout;
	toolbar_layout->setSpacing(0);
	toolbar_layout->addWidget(button_back);
	toolbar_layout->addWidget(button_up);
	toolbar_layout->addWidget(button_home);
132
	toolbar_layout->addWidget(button_working);
133
134
	toolbar_layout->addSpacerItem(new QSpacerItem(-1, -1, QSizePolicy::Expanding, QSizePolicy::Minimum));

135
	/* main layout */
136
137
138
139
140
141
142
143
	auto *main_layout = new QVBoxLayout;
	main_layout->addLayout(toolbar_layout);
	main_layout->addWidget(filesystem_tree_);
	main_layout->addWidget(path_label_);

	this->setLayout(main_layout);
}

144
145
146
147
148
/**
 * @brief Public access to enable/disable to folder tree view.
 * @param[in] enabled True to enable, false to disable.
 */
void IniFolderView::setEnabled(const bool &enabled)
149
150
151
152
{
	filesystem_tree_->setEnabled(enabled);
}

153
154
155
156
157
158
159
160
161
/**
 * @brief Set the root path of the tree view on user interaction.
 * @details This function basically hides the tree and switches to a clear list view of the
 * desired folder. This way, users can navigate to their main INI folder and have a cleaner
 * look of it until they navigate back out.
 * @param[in] index File model / tree view index.
 * @param[in] no_stack Don't add to ad-hoc history.
 */
void IniFolderView::setTreeIndex(const QModelIndex &index, const bool &no_stack)
162
{
163
	if (!no_stack) //current path for the back button
164
		prev_index_stack_.push(filesystem_tree_->rootIndex());
165
	filesystem_tree_->setRootIndex(index); //new path
166
	path_label_->setText(filesystem_model_->filePath(index));
167
168
	//remember the path for when the panel gets enabled and an info text disappears:
	path_label_->setProperty("path", path_label_->text());
169
170
}

171
172
173
174
/**
 * @brief Create the IniFolderView's context menu.
 */
void IniFolderView::createContextMenu()
175
{
176
	ini_folder_context_menu_.addAction(tr("Open in editor"));
177
	//"append" would be more logical (like for XMLs), but then we have to handle keeping the
178
	//original file path in the INIParser etc. pp. while this way it's a free feature:
179
	ini_folder_context_menu_.addAction(tr("Load on top of current INI values"));
180
181
	ini_folder_context_menu_.addSeparator();
	ini_folder_context_menu_.addAction(tr("Duplicate"));
182
183
	ini_folder_context_menu_.addAction(tr("Open parent folder"));
	ini_folder_context_menu_.addAction(tr("Delete"));
184
185
}

186
187
188
189
190
191
192
193
/**
 * @brief Helper function to remember the last INI path the user has selected.
 */
void IniFolderView::updateLastPath()
{
	setSetting("auto::history::ini_folder", "path",
	    filesystem_model_->filePath(filesystem_tree_->currentIndex()));
}
194
195
196
197
198
199
/**
 * @brief Event listener for double-clicks in the file system.
 * @details This function switches folders and opens files, depending on what is double-clicked.
 * @param[in] index Clicked file model / tree view index.
 */
void IniFolderView::onFilesysDoubleclicked(const QModelIndex &index)
200
{
201
202
	auto finfo = QFileInfo(filesystem_model_->filePath(index));
	if (finfo.isDir()) {
203
		setTreeIndex(index);
204
		updateLastPath();
205
	} else {
206
		getMainWindow()->openIni(finfo.filePath());
207
208
209
	}
}

210
211
212
213
/**
 * @brief Event listener for when the context menu is requested.
 */
void IniFolderView::onContextMenuRequest(QPoint /*coords*/)
214
215
{
	QAction *selected = ini_folder_context_menu_.exec(QCursor::pos());
216
	if (selected) { //something was clicked
217
218
219
		auto finfo = QFileInfo(filesystem_model_->filePath(filesystem_tree_->currentIndex()));
		if (!finfo.exists())
			return;
220
221
		//:Translation hint: This is a check to match a context menu click
		if (selected->text().startsWith(tr("Open in editor"))) {
222
			QDesktopServices::openUrl(QUrl(finfo.filePath()));
223
224
		//:Translation hint: This is a check to match a context menu click
		} else if (selected->text().startsWith(tr("Load on top"))) {
225
			getMainWindow()->openIni(finfo.filePath(), false, false);
226
227
228
229
230
231
232
		} else if (selected->text() == tr("Duplicate")) {
			QString new_name( finfo.filePath() );
			const QString ext( QFileInfo( new_name ).completeSuffix() );
			new_name.chop(ext.length() + 1);
			//:Translation hint: noun
			new_name = new_name + tr("(copy).") + ext;
			QFile::copy(finfo.filePath(), new_name);
233
234
235
		} else if (selected->text() == tr("Delete")) {
			QMessageBox msgWarnClear(this);
			msgWarnClear.setWindowTitle("Warning ~ " + QCoreApplication::applicationName());
236
			msgWarnClear.setText(tr("<b>Delete file \"%1\"?</b>").arg(finfo.fileName()));
237
238
239
240
241
242
243
244
245
			msgWarnClear.setIcon(QMessageBox::Warning);
			msgWarnClear.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
			msgWarnClear.setDefaultButton(QMessageBox::Cancel);
			int clicked = msgWarnClear.exec();
			if (clicked == QMessageBox::Cancel)
				return;
			QFile::remove(finfo.filePath());
		} else if (selected->text() == tr("Open parent folder")) {
			QDesktopServices::openUrl(QUrl(finfo.absolutePath()));
246
247
248
249
		}
	}
}

250
251
252
253
/**
 * @brief Navigate one level up in the folder hirarchy.
 */
void IniFolderView::onUpClicked()
254
255
{
	setTreeIndex(filesystem_tree_->rootIndex().parent());
256
	updateLastPath();
257
258
}

259
/**
260
 * @brief Navigate to the user's home folder.
261
262
 */
void IniFolderView::onHomeClicked()
263
264
{
	setTreeIndex(filesystem_model_->index(QDir::homePath()));
265
	updateLastPath();
266
267
}

268
269
270
271
272
/**
 * @brief Navigate back one step in the folder hierarchy.
 * @details Note that this only navigates back to folders that have been double-clicked.
 */
void IniFolderView::onBackClicked()
273
274
275
{
	if (!prev_index_stack_.isEmpty())
		setTreeIndex(prev_index_stack_.pop(), true);
276
	updateLastPath();
277
278
}

279
/**
280
 * @brief Navigate to the current working directory.
281
 */
282
283
void IniFolderView::onWorkingClicked()
{
284
	setTreeIndex(filesystem_model_->index(QDir::currentPath()));
285
	updateLastPath();
286
}