2
* Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
4
* This library is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU Lesser General Public
6
* License as published by the Free Software Foundation; either
7
* version 2.1 of the License, or (at your option) any later version.
9
* This library is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
* Lesser General Public License for more details.
14
* You should have received a copy of the GNU Lesser General Public
15
* License along with this library; if not, write to the Free Software
16
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22
#include "pathedit_p.h"
24
#include <QStringListModel>
25
#include <QStringBuilder>
32
void PathEditJob::runJob() {
34
GFileEnumerator* enu = g_file_enumerate_children(dirName,
35
// G_FILE_ATTRIBUTE_STANDARD_NAME","
36
G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME","
37
G_FILE_ATTRIBUTE_STANDARD_TYPE,
38
G_FILE_QUERY_INFO_NONE, cancellable,
41
while(!g_cancellable_is_cancelled(cancellable)) {
42
GFileInfo* inf = g_file_enumerator_next_file(enu, cancellable, &err);
44
GFileType type = g_file_info_get_file_type(inf);
45
if(type == G_FILE_TYPE_DIRECTORY) {
46
const char* name = g_file_info_get_display_name(inf);
47
// FIXME: encoding conversion here?
48
subDirs.append(QString::fromUtf8(name));
61
g_file_enumerator_close(enu, cancellable, NULL);
64
// finished! let's update the UI in the main thread
69
PathEdit::PathEdit(QWidget* parent):
72
model_(new QStringListModel()),
73
completer_(new QCompleter()) {
74
setCompleter(completer_);
75
completer_->setModel(model_);
76
connect(this, &PathEdit::textChanged, this, &PathEdit::onTextChanged);
79
PathEdit::~PathEdit() {
84
g_cancellable_cancel(cancellable_);
85
g_object_unref(cancellable_);
89
void PathEdit::focusInEvent(QFocusEvent* e) {
90
QLineEdit::focusInEvent(e);
91
// build the completion list only when we have the keyboard focus
92
reloadCompleter(true);
95
void PathEdit::focusOutEvent(QFocusEvent* e) {
96
QLineEdit::focusOutEvent(e);
97
// free the completion list since we don't need it anymore
101
void PathEdit::onTextChanged(const QString& text) {
102
int pos = text.lastIndexOf('/');
107
QString newPrefix = text.left(pos);
108
if(currentPrefix_ != newPrefix) {
109
currentPrefix_ = newPrefix;
110
// only build the completion list if we have the keyboard focus
111
// if we don't have the focus now, then we'll rebuild the completion list
112
// when focusInEvent happens. this avoid unnecessary dir loading.
114
reloadCompleter(false);
119
void PathEdit::reloadCompleter(bool triggeredByFocusInEvent) {
120
// parent dir has been changed, reload dir list
121
// if(currentPrefix_[0] == "~") { // special case for home dir
122
// cancel running dir-listing jobs, if there's any
124
g_cancellable_cancel(cancellable_);
125
g_object_unref(cancellable_);
128
// create a new job to do dir listing
129
PathEditJob* job = new PathEditJob();
131
job->triggeredByFocusInEvent = triggeredByFocusInEvent;
132
// need to use fm_file_new_for_commandline_arg() rather than g_file_new_for_commandline_arg().
133
// otherwise, our own vfs, such as menu://, won't be loaded.
134
job->dirName = fm_file_new_for_commandline_arg(currentPrefix_.toLocal8Bit().constData());
135
// qDebug("load: %s", g_file_get_uri(data->dirName));
136
cancellable_ = g_cancellable_new();
137
job->cancellable = (GCancellable*)g_object_ref(cancellable_);
139
// launch a new worker thread to handle the job
140
QThread* thread = new QThread();
141
job->moveToThread(thread);
142
connect(thread, &QThread::started, job, &PathEditJob::runJob);
143
connect(thread, &QThread::finished, thread, &QObject::deleteLater);
144
connect(thread, &QThread::finished, job, &QObject::deleteLater);
145
connect(job, &PathEditJob::finished, this, &PathEdit::onJobFinished);
146
thread->start(QThread::LowPriority);
149
void PathEdit::freeCompleter() {
151
g_cancellable_cancel(cancellable_);
152
g_object_unref(cancellable_);
155
model_->setStringList(QStringList());
158
// This slot is called from main thread so it's safe to access the GUI
159
void PathEdit::onJobFinished() {
160
PathEditJob* data = static_cast<PathEditJob*>(sender());
161
if(!g_cancellable_is_cancelled(data->cancellable)) {
162
// update the completer only if the job is not cancelled
163
QStringList::iterator it;
164
for(it = data->subDirs.begin(); it != data->subDirs.end(); ++it) {
165
// qDebug("%s", it->toUtf8().constData());
166
*it = (currentPrefix_ % *it);
168
model_->setStringList(data->subDirs);
169
// trigger completion manually
170
if(hasFocus() && !data->triggeredByFocusInEvent)
171
completer_->complete();
174
model_->setStringList(QStringList());
176
g_object_unref(cancellable_);