OSDN Git Service

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