~lubuntu-dev/lxde/libfm-qt-debian-git

« back to all changes in this revision

Viewing changes to src/pathedit.cpp

  • Committer: Alf Gaida
  • Date: 2015-12-17 15:45:00 UTC
  • Revision ID: git-v1:99d4cf5e0b3761023e2285ffb96a79d050f0bdf4
Tags: upstream/0.10.0+20151214
Adding upstream version 0.10.0+20151214.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
 
3
 *
 
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.
 
8
 *
 
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.
 
13
 *
 
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
 
17
 *
 
18
 */
 
19
 
 
20
 
 
21
#include "pathedit.h"
 
22
#include "pathedit_p.h"
 
23
#include <QCompleter>
 
24
#include <QStringListModel>
 
25
#include <QStringBuilder>
 
26
#include <QThread>
 
27
#include <QDebug>
 
28
#include <libfm/fm.h>
 
29
 
 
30
namespace Fm {
 
31
 
 
32
void PathEditJob::runJob() {
 
33
  GError *err = NULL;
 
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,
 
39
                                                   &err);
 
40
  if(enu) {
 
41
    while(!g_cancellable_is_cancelled(cancellable)) {
 
42
      GFileInfo* inf = g_file_enumerator_next_file(enu, cancellable, &err);
 
43
      if(inf) {
 
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));
 
49
        }
 
50
        g_object_unref(inf);
 
51
      }
 
52
      else {
 
53
        if(err) {
 
54
          g_error_free(err);
 
55
          err = NULL;
 
56
        }
 
57
        else /* EOF */
 
58
          break;
 
59
      }
 
60
    }
 
61
    g_file_enumerator_close(enu, cancellable, NULL);
 
62
    g_object_unref(enu);
 
63
  }
 
64
  // finished! let's update the UI in the main thread
 
65
  Q_EMIT finished();
 
66
}
 
67
 
 
68
 
 
69
PathEdit::PathEdit(QWidget* parent):
 
70
  QLineEdit(parent),
 
71
  cancellable_(NULL),
 
72
  model_(new QStringListModel()),
 
73
  completer_(new QCompleter()) {
 
74
  setCompleter(completer_);
 
75
  completer_->setModel(model_);
 
76
  connect(this, &PathEdit::textChanged, this, &PathEdit::onTextChanged);
 
77
}
 
78
 
 
79
PathEdit::~PathEdit() {
 
80
  delete completer_;
 
81
  if(model_)
 
82
    delete model_;
 
83
  if(cancellable_) {
 
84
    g_cancellable_cancel(cancellable_);
 
85
    g_object_unref(cancellable_);
 
86
  }
 
87
}
 
88
 
 
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);
 
93
}
 
94
 
 
95
void PathEdit::focusOutEvent(QFocusEvent* e) {
 
96
  QLineEdit::focusOutEvent(e);
 
97
  // free the completion list since we don't need it anymore
 
98
  freeCompleter();
 
99
}
 
100
 
 
101
void PathEdit::onTextChanged(const QString& text) {
 
102
  int pos = text.lastIndexOf('/');
 
103
  if(pos >= 0)
 
104
    ++pos;
 
105
  else
 
106
    pos = text.length();
 
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.
 
113
    if(hasFocus())
 
114
      reloadCompleter(false);
 
115
  }
 
116
}
 
117
 
 
118
 
 
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
 
123
  if(cancellable_) {
 
124
    g_cancellable_cancel(cancellable_);
 
125
    g_object_unref(cancellable_);
 
126
  }
 
127
 
 
128
  // create a new job to do dir listing
 
129
  PathEditJob* job = new PathEditJob();
 
130
  job->edit = this;
 
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_);
 
138
 
 
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);
 
147
}
 
148
 
 
149
void PathEdit::freeCompleter() {
 
150
  if(cancellable_) {
 
151
    g_cancellable_cancel(cancellable_);
 
152
    g_object_unref(cancellable_);
 
153
    cancellable_ = NULL;
 
154
  }
 
155
  model_->setStringList(QStringList());
 
156
}
 
157
 
 
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);
 
167
    }
 
168
    model_->setStringList(data->subDirs);
 
169
    // trigger completion manually
 
170
    if(hasFocus() && !data->triggeredByFocusInEvent)
 
171
      completer_->complete();
 
172
  }
 
173
  else
 
174
    model_->setStringList(QStringList());
 
175
  if(cancellable_) {
 
176
    g_object_unref(cancellable_);
 
177
    cancellable_ = NULL;
 
178
  }
 
179
}
 
180
 
 
181
} // namespace Fm