OSDN Git Service

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