OSDN Git Service

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