OSDN Git Service

Ver0.21
[gefu/Gefu.git] / foldermodel.cpp
1 #include "global.h"\r
2 #include "preferences.h"\r
3 #include "thumbnailworker.h"\r
4 #include "win32.h"\r
5 #include "foldermodel.h"\r
6 \r
7 #include <QDateTime>\r
8 #include <QDebug>\r
9 #include <QApplication>\r
10 #include <QSettings>\r
11 #include <QPalette>\r
12 #include <QThread>\r
13 \r
14 FolderModel* FolderModel::m_activeModel = NULL;\r
15 \r
16 ///////////////////////////////////////////////////////////////////////////////\r
17 /// \brief FolderModel::FolderModel\r
18 /// \param parent   親オブジェクト\r
19 ///\r
20 /// コンストラクタ\r
21 ///\r
22 FolderModel::FolderModel(QObject *parent) :\r
23     QAbstractTableModel(parent),\r
24     m_dir(),\r
25     m_fileInfoList(),\r
26     m_checkStates(),\r
27     m_IconProvider(),\r
28     m_fsWatcher(this),\r
29     m_history(),\r
30     m_historyPos(-1),\r
31     m_pixmapCache(),\r
32     m_pixmapCacheMutex()\r
33 {\r
34     connect(&m_fsWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(fsWatcher_directoryChanged(QString)));\r
35 }\r
36 \r
37 ///////////////////////////////////////////////////////////////////////////////\r
38 /// \brief FolderModel::clearPixmapCache\r
39 ///\r
40 /// サムネイルキャッシュをクリアします。\r
41 ///\r
42 void FolderModel::clearPixmapCache()\r
43 {\r
44     beginResetModel();\r
45     m_pixmapCacheMutex.lock();\r
46     m_pixmapCache.clear();\r
47     m_pixmapCacheMutex.unlock();\r
48     endResetModel();\r
49 }\r
50 \r
51 ///////////////////////////////////////////////////////////////////////////////\r
52 /// \brief FolderModel::fileIcon\r
53 /// \param index    アイテムのインデックス\r
54 /// \return アイコンを返します。\r
55 ///\r
56 QIcon FolderModel::fileIcon(const QModelIndex &index) const\r
57 {\r
58     if (fileName(index) == "..") {\r
59         return QIcon("://images/Up.png");\r
60     }\r
61     return m_IconProvider.icon(fileInfo(index));\r
62 }\r
63 \r
64 ///////////////////////////////////////////////////////////////////////////////\r
65 /// \brief FolderModel::isMarked\r
66 /// \param index    アイテムのインデックス\r
67 /// \return マークされていればtrueを返します。\r
68 ///\r
69 bool FolderModel::isMarked(const QModelIndex &index) const\r
70 {\r
71     CheckContainer::const_iterator it = m_checkStates.find(fileName(index));\r
72     if (it != m_checkStates.end()) {\r
73         return *it == Qt::Checked;\r
74     }\r
75     return false;\r
76 }\r
77 \r
78 ///////////////////////////////////////////////////////////////////////////////\r
79 /// \brief FolderModel::markedItems\r
80 /// \return マークされているアイテムのリストを返します。\r
81 ///\r
82 QFileInfoList FolderModel::markedItems() const\r
83 {\r
84     QFileInfoList list;\r
85     for (int n = 0; n < rowCount(); ++n) {\r
86         QModelIndex index = this->index(n, 0);\r
87         if (isMarked(index)) {\r
88             list << fileInfo(index);\r
89         }\r
90     }\r
91 \r
92     return list;\r
93 }\r
94 \r
95 ///////////////////////////////////////////////////////////////////////////////\r
96 /// \brief FolderModel::mkdir\r
97 /// \param name 作成するフォルダ名\r
98 /// \return 作成したフォルダのインデックス\r
99 ///\r
100 /// フォルダを作成します。\r
101 ///\r
102 QModelIndex FolderModel::mkdir(const QString &name)\r
103 {\r
104     qDebug() << "FolderModel::mkdir()" << name;\r
105 \r
106     if (!m_dir.mkdir(name)) {\r
107         return QModelIndex();\r
108     }\r
109 \r
110     refresh();\r
111 \r
112     for (int n = 0; n < rowCount(); ++n) {\r
113         if (m_fileInfoList[n].fileName() == name) {\r
114             return index(n, 0);\r
115         }\r
116     }\r
117 \r
118     return QModelIndex();\r
119 }\r
120 \r
121 ///////////////////////////////////////////////////////////////////////////////\r
122 /// \brief FolderModel::pixmap\r
123 /// \param index    アイテムのインデックス\r
124 /// \param size     要求サイズ\r
125 /// \return 画像またはアイコンを返します。\r
126 ///\r
127 QPixmap FolderModel::pixmap(const QModelIndex &index, const QSize &size) const\r
128 {\r
129     QPixmap pixmap;\r
130     const_cast<FolderModel*>(this)->m_pixmapCacheMutex.lock();\r
131     if (m_pixmapCache.find(filePath(index)) != m_pixmapCache.end()) {\r
132         pixmap = m_pixmapCache[filePath(index)];\r
133     }\r
134     const_cast<FolderModel*>(this)->m_pixmapCacheMutex.unlock();\r
135 \r
136     if (!pixmap.isNull()) {\r
137         return pixmap;\r
138     }\r
139 \r
140     // 別スレッドでサムネイルを生成する\r
141     QThread *thread = new QThread();\r
142     ThumbnailWorker *worker = new ThumbnailWorker();\r
143     worker->setPath(filePath(index));\r
144     worker->setSize(size);\r
145     worker->moveToThread(thread);\r
146     connect(worker, SIGNAL(resultReady(QString,QPixmap)), this, SLOT(thumbnail_Ready(QString,QPixmap)));\r
147     connect(worker, SIGNAL(finished()), thread, SLOT(quit()));\r
148     connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));\r
149     connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));\r
150     connect(thread, SIGNAL(started()), worker, SLOT(doWork()));\r
151     thread->start();\r
152 \r
153     // とりあえずアイコンを返す。\r
154     return fileIcon(index).pixmap(32, 32);\r
155 }\r
156 \r
157 ///////////////////////////////////////////////////////////////////////////////\r
158 /// \brief FolderModel::search\r
159 /// \param value    検索する文字列\r
160 /// \param start    開始位置\r
161 /// \param step     ステップ数\r
162 /// \return 見つかったアイテムのインデックスを返します。\r
163 ///\r
164 QModelIndex FolderModel::search(const QString &value, const QModelIndex &start, int step)\r
165 {\r
166     qDebug() << "FolderModel::search()" << value << start << step;\r
167 \r
168     const QString searchText = value.toLower();\r
169     for (int n = start.row() + step; 0 <= n && n < rowCount(); n += step) {\r
170         if (m_fileInfoList[n].fileName().toLower().indexOf(searchText) != -1) {\r
171             return index(n, 0);\r
172         }\r
173     }\r
174 \r
175     return QModelIndex();\r
176 }\r
177 \r
178 ///////////////////////////////////////////////////////////////////////////////\r
179 /// \brief FolderModel::setHistoryAt\r
180 /// \param path 設定するパス\r
181 ///\r
182 /// 指定したパスに履歴を移動します。\r
183 ///\r
184 void FolderModel::setHistoryAt(const QString &path)\r
185 {\r
186     qDebug() << "FolderModel::setHistoryAt()" << path;\r
187 \r
188     for (int n = 0; n < m_history.size(); ++n) {\r
189         if (m_history[n] == path) {\r
190             m_historyPos = n;\r
191             setRootPath(path, false);\r
192             return;\r
193         }\r
194     }\r
195 }\r
196 \r
197 ///////////////////////////////////////////////////////////////////////////////\r
198 /// \brief FolderModel::setRootPath\r
199 /// \param path         設定するパス\r
200 /// \param addHistory   履歴に追加する場合はtrue\r
201 ///\r
202 /// ルートパスを設定します。\r
203 ///\r
204 void FolderModel::setRootPath(const QString &path, bool addHistory)\r
205 {\r
206     qDebug() << "FolderModel::setRootPath()" << path;\r
207 \r
208     beginResetModel();\r
209     setError();\r
210 \r
211     try {\r
212         if (!QFileInfo::exists(path)) {\r
213             throw tr("フォルダが存在しないか、利用できません:%1").arg(path);\r
214         }\r
215 \r
216         QDir dir(path);\r
217         dir.setFilter(m_dir.filter());\r
218         dir.setNameFilters(m_dir.nameFilters());\r
219         dir.setSorting(m_dir.sorting());\r
220 \r
221         QFileInfoList list = dir.entryInfoList();\r
222         if (list.isEmpty()) {\r
223             throw tr("ファイルリストを取得できません:%1").arg(path);\r
224         }\r
225 \r
226         m_fileInfoList.clear();\r
227         if (m_dir.absolutePath() != dir.absolutePath()) {\r
228             m_fsWatcher.removePath(m_dir.absolutePath());\r
229             m_fsWatcher.addPath(dir.absolutePath());\r
230             m_checkStates.clear();\r
231             m_dir.setPath(dir.absolutePath());\r
232             if (addHistory) {\r
233                 m_history.resize(m_historyPos + 1);\r
234                 m_history << m_dir.absolutePath();\r
235                 m_historyPos = m_history.size() - 1;\r
236             }\r
237             m_pixmapCacheMutex.lock();\r
238             m_pixmapCache.clear();\r
239             m_pixmapCacheMutex.unlock();\r
240         }\r
241 \r
242         foreach (const QFileInfo &fi, list) {\r
243             if (fi.fileName() == "..") {\r
244                 if (!m_dir.isRoot()) {\r
245                     m_fileInfoList.push_front(fi);\r
246                 }\r
247                 continue;\r
248             }\r
249             else if (!(filter() & QDir::System) &&\r
250                      Win32::hasSystemAttribute(fi.absoluteFilePath()))\r
251             {\r
252                 continue;\r
253             }\r
254 \r
255             m_fileInfoList << fi;\r
256             if (m_checkStates.find(fi.fileName()) == m_checkStates.end()) {\r
257                 m_checkStates[fi.fileName()] = Qt::Unchecked;\r
258             }\r
259         }\r
260     }\r
261     catch (const QString &s) {\r
262         setError(s);\r
263     }\r
264 \r
265     endResetModel();\r
266 }\r
267 \r
268 ///////////////////////////////////////////////////////////////////////////////\r
269 /// \brief FolderModel::touch\r
270 /// \param name 作成するファイル名\r
271 /// \return 作成したファイルのインデックス\r
272 ///\r
273 /// 空のファイルを作成します。\r
274 ///\r
275 QModelIndex FolderModel::touch(const QString &name)\r
276 {\r
277     QFile file(m_dir.absoluteFilePath(name));\r
278     if (!file.open(QIODevice::WriteOnly)) {\r
279         return QModelIndex();\r
280     }\r
281     file.close();\r
282 \r
283     refresh();\r
284 \r
285     for (int n = 0; n < rowCount(); ++n) {\r
286         if (m_fileInfoList[n].fileName() == name) {\r
287             return index(n, 0);\r
288         }\r
289     }\r
290 \r
291     return QModelIndex();\r
292 }\r
293 \r
294 ///////////////////////////////////////////////////////////////////////////////\r
295 /// \brief FolderModel::activeModel\r
296 /// \return アクティブなモデルを返します。\r
297 ///\r
298 FolderModel *FolderModel::activeModel()\r
299 {\r
300     return m_activeModel;\r
301 }\r
302 \r
303 ///////////////////////////////////////////////////////////////////////////////\r
304 /// \brief FolderModel::onCdHome\r
305 ///\r
306 /// ホームフォルダに移動します。\r
307 ///\r
308 void FolderModel::onCdHome()\r
309 {\r
310     setRootPath(QDir::homePath());\r
311 }\r
312 \r
313 ///////////////////////////////////////////////////////////////////////////////\r
314 /// \brief FolderModel::onCdRoot\r
315 ///\r
316 /// ルートフォルダに移動します。\r
317 ///\r
318 void FolderModel::onCdRoot()\r
319 {\r
320     setRootPath(QDir::rootPath());\r
321 }\r
322 \r
323 ///////////////////////////////////////////////////////////////////////////////\r
324 /// \brief FolderModel::onCdUp\r
325 ///\r
326 /// 親フォルダに移動します。\r
327 ///\r
328 void FolderModel::onCdUp()\r
329 {\r
330     if (!m_dir.isRoot()) {\r
331         QDir dir(m_dir);\r
332         dir.cdUp();\r
333         setRootPath(dir.absolutePath());\r
334     }\r
335 }\r
336 \r
337 ///////////////////////////////////////////////////////////////////////////////\r
338 /// \brief FolderModel::onHistoryBack\r
339 ///\r
340 /// 履歴を戻ります。\r
341 ///\r
342 void FolderModel::onHistoryBack()\r
343 {\r
344     if (m_historyPos > 0) {\r
345         setRootPath(m_history[--m_historyPos], false);\r
346     }\r
347 }\r
348 \r
349 ///////////////////////////////////////////////////////////////////////////////\r
350 /// \brief FolderModel::onHistoryForward\r
351 ///\r
352 /// 履歴を進みます。\r
353 ///\r
354 void FolderModel::onHistoryForward()\r
355 {\r
356     if (m_historyPos < m_history.size() - 1) {\r
357         setRootPath(m_history[++m_historyPos], false);\r
358     }\r
359 }\r
360 \r
361 ///////////////////////////////////////////////////////////////////////////////\r
362 /// \brief FolderModel::onMarkAll\r
363 ///\r
364 /// 全てのマークをONにします。\r
365 ///\r
366 void FolderModel::onMarkAll()\r
367 {\r
368     CheckContainer::iterator it;\r
369     for (it = m_checkStates.begin(); it != m_checkStates.end(); ++it) {\r
370         it.value() = Qt::Checked;\r
371     }\r
372     emit dataChanged(index(0, 0), index(rowCount(), columnCount()));\r
373 }\r
374 \r
375 ///////////////////////////////////////////////////////////////////////////////\r
376 /// \brief FolderModel::onMarkAllFiles\r
377 ///\r
378 /// 全てのフォルダをマークOFF、ファイルをマークONにします。\r
379 ///\r
380 void FolderModel::onMarkAllFiles()\r
381 {\r
382     foreach (const QFileInfo &fi, m_fileInfoList) {\r
383         if (fi.fileName() == "..") {\r
384             continue;\r
385         }\r
386         if (fi.isDir()) {\r
387             m_checkStates[fi.fileName()] = Qt::Unchecked;\r
388         }\r
389         else {\r
390             m_checkStates[fi.fileName()] = Qt::Checked;\r
391         }\r
392     }\r
393     emit dataChanged(index(0, 0), index(rowCount(), columnCount()));\r
394 }\r
395 \r
396 ///////////////////////////////////////////////////////////////////////////////\r
397 /// \brief FolderModel::onMarkAllOff\r
398 ///\r
399 /// 全てのマークをOFFにします。\r
400 ///\r
401 void FolderModel::onMarkAllOff()\r
402 {\r
403     CheckContainer::iterator it;\r
404     for (it = m_checkStates.begin(); it != m_checkStates.end(); ++it) {\r
405         it.value() = Qt::Unchecked;\r
406     }\r
407     emit dataChanged(index(0, 0), index(rowCount(), columnCount()));\r
408 }\r
409 \r
410 ///////////////////////////////////////////////////////////////////////////////\r
411 /// \brief FolderModel::onMarkInvert\r
412 ///\r
413 /// 全てのマークを反転します。\r
414 ///\r
415 void FolderModel::onMarkInvert()\r
416 {\r
417     CheckContainer::iterator it;\r
418     for (it = m_checkStates.begin(); it != m_checkStates.end(); ++it) {\r
419         it.value() = (it.value() == Qt::Checked) ? Qt::Unchecked : Qt::Checked;\r
420     }\r
421     emit dataChanged(index(0, 0), index(rowCount(), columnCount()));\r
422 }\r
423 \r
424 ///////////////////////////////////////////////////////////////////////////////\r
425 /// \brief FolderModel::thumbnail_Ready\r
426 /// \param path     ファイルパス\r
427 /// \param pixmap   サムネイル画像\r
428 ///\r
429 /// サムネイルの生成終了時の処理を行います。\r
430 ///\r
431 void FolderModel::thumbnail_Ready(const QString &path, const QPixmap &pixmap)\r
432 {\r
433     QFileInfo fi(path);\r
434     if (fi.absolutePath() == m_dir.absolutePath()) {\r
435         m_pixmapCacheMutex.lock();\r
436         m_pixmapCache[path] = pixmap;\r
437         m_pixmapCacheMutex.unlock();\r
438 \r
439         QModelIndex index = search(fi.fileName());\r
440         if (index.isValid()) {\r
441             emit dataChanged(index, index);\r
442         }\r
443     }\r
444 }\r
445 \r
446 int FolderModel::rowCount(const QModelIndex &parent) const\r
447 {\r
448     Q_UNUSED(parent);\r
449     return m_fileInfoList.size();\r
450 }\r
451 \r
452 int FolderModel::columnCount(const QModelIndex &parent) const\r
453 {\r
454     Q_UNUSED(parent);\r
455     return ColumnCount;\r
456 }\r
457 \r
458 QVariant FolderModel::data(const QModelIndex &index, int role) const\r
459 {\r
460     if (!index.isValid()) {\r
461         return QVariant();\r
462     }\r
463 \r
464     Preferences prefs(const_cast<FolderModel*>(this));\r
465 \r
466     switch (role) {\r
467     case Qt::DisplayRole:\r
468         switch (index.column()) {\r
469         case Name:\r
470             if (isDir(index) || isDotFile(index)) {\r
471                 return fileName(index);\r
472             }\r
473             return fileInfo(index).completeBaseName();\r
474 \r
475         case Extension:\r
476             if (isDir(index) || isDotFile(index)) {\r
477                 return QVariant();\r
478             }\r
479             return fileInfo(index).suffix();\r
480 \r
481         case Size: // サイズ\r
482             if (isDir(index)) {\r
483                 return QString("<DIR>");\r
484             }\r
485             return FileSizeToString(fileInfo(index).size());\r
486 \r
487         case LastModified: // 更新日時\r
488             return fileInfo(index).lastModified().toString("yy/MM/dd hh:mm");\r
489         }\r
490         break;\r
491 \r
492     case Qt::DecorationRole:\r
493         if (index.column() == Name) {\r
494             return fileIcon(index);\r
495         }\r
496         break;\r
497 \r
498     case Qt::FontRole:\r
499         return prefs.getFolderViewFont();\r
500 \r
501     case Qt::TextAlignmentRole:\r
502         switch (index.column()) {\r
503         case Size:\r
504         case LastModified:\r
505             return Qt::AlignRight + Qt::AlignVCenter;\r
506         }\r
507         return Qt::AlignLeft + Qt::AlignVCenter;\r
508 \r
509     case Qt::BackgroundRole:\r
510         if (isMarked(index)) {\r
511             return QBrush(prefs.folderViewMarkedBgColor(isActive()));\r
512         }\r
513         return QBrush(prefs.folderViewBgColor(isActive()));\r
514 \r
515     case Qt::ForegroundRole:\r
516         if (isMarked(index)) {\r
517             return QBrush(prefs.folderViewMarkedFgColor(isActive()));\r
518         }\r
519         if (fileName(index) != ".." && Win32::hasSystemAttribute(filePath(index))) {\r
520             return QBrush(prefs.folderViewSystemColor(isActive()));\r
521         }\r
522         if (fileName(index) != ".." && fileInfo(index).isHidden()) {\r
523             return QBrush(prefs.folderViewHiddenColor(isActive()));\r
524         }\r
525         if (fileName(index) != ".." && !fileInfo(index).isWritable()) {\r
526             return QBrush(prefs.folderViewReadOnlyColor(isActive()));\r
527         }\r
528         return QBrush(prefs.folderViewFgColor(isActive()));\r
529 \r
530     case Qt::CheckStateRole:\r
531         if (index.column() == Name && fileName(index) != "..") {\r
532             return isMarked(index) ? Qt::Checked : Qt::Unchecked;\r
533         }\r
534         break;\r
535     }\r
536 \r
537     return QVariant();\r
538 }\r
539 \r
540 QVariant FolderModel::headerData(int section, Qt::Orientation orientation, int role) const\r
541 {\r
542     if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {\r
543         switch (section) {\r
544         case Name:          return tr("名前");\r
545         case Extension:     return tr("拡張子");\r
546         case Size:          return tr("サイズ");\r
547         case LastModified:  return tr("更新日時");\r
548         }\r
549     }\r
550     return QVariant();\r
551 }\r
552 \r
553 Qt::ItemFlags FolderModel::flags(const QModelIndex &index) const\r
554 {\r
555     Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;\r
556 \r
557     if (!index.isValid()) {\r
558         return flags | Qt::ItemIsDropEnabled;\r
559     }\r
560 \r
561     if (m_fileInfoList[index.row()].fileName() != "..") {\r
562         if (index.column() == Name) {\r
563             flags |= Qt::ItemIsUserCheckable;\r
564         }\r
565         flags |= Qt::ItemIsDropEnabled;\r
566     }\r
567     return flags;\r
568 }\r
569 \r
570 bool FolderModel::setData(const QModelIndex &index, const QVariant &value, int role)\r
571 {\r
572     qDebug() << "FolderModel::setData()" << index;\r
573 \r
574     if (!index.isValid()) {\r
575         return false;\r
576     }\r
577 \r
578     switch (role) {\r
579     case Qt::CheckStateRole:\r
580         if (index.column() == Name && fileName(index) != "..") {\r
581             m_checkStates[fileName(index)] = value.toInt();\r
582             emit dataChanged(this->index(index.row(), 0),\r
583                              this->index(index.row(), ColumnCount - 1));\r
584             return true;\r
585         }\r
586         break;\r
587     }\r
588 \r
589     return false;\r
590 }\r
591 \r
592 Qt::DropActions FolderModel::supportedDropActions() const\r
593 {\r
594     qDebug() << "FolderModel::supportedDropActions()";\r
595 \r
596     return Qt::CopyAction;\r
597 }\r
598 \r
599 QStringList FolderModel::mimeTypes() const\r
600 {\r
601     qDebug() << "FolderModel::mimeTypes()";\r
602 \r
603     QStringList types;\r
604 \r
605     types << "text/uri-list";\r
606 \r
607     return types;\r
608 }\r