OSDN Git Service

It's 2011 now.
[qt-creator-jp/qt-creator-jp.git] / src / shared / qrceditor / resourceview.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights.  These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "resourceview.h"
35
36 #include "undocommands_p.h"
37
38 #include <QtCore/QDebug>
39
40 #include <QtGui/QAction>
41 #include <QtGui/QApplication>
42 #include <QtGui/QFileDialog>
43 #include <QtGui/QHeaderView>
44 #include <QtGui/QInputDialog>
45 #include <QtGui/QMenu>
46 #include <QtGui/QMouseEvent>
47 #include <QtGui/QUndoStack>
48
49 namespace SharedTools {
50
51 /*!
52     \class FileEntryBackup
53
54     Backups a file node.
55 */
56 class FileEntryBackup : public EntryBackup
57 {
58 private:
59     int m_fileIndex;
60     QString m_alias;
61
62 public:
63     FileEntryBackup(ResourceModel &model, int prefixIndex, int fileIndex,
64             const QString &fileName, const QString &alias)
65             : EntryBackup(model, prefixIndex, fileName), m_fileIndex(fileIndex),
66             m_alias(alias) { }
67     void restore() const;
68 };
69
70 void FileEntryBackup::restore() const
71 {
72     m_model->insertFile(m_prefixIndex, m_fileIndex, m_name, m_alias);
73 }
74
75 /*!
76     \class PrefixEntryBackup
77
78     Backups a prefix node including children.
79 */
80 class PrefixEntryBackup : public EntryBackup
81 {
82 private:
83     QString m_language;
84     QList<FileEntryBackup> m_files;
85
86 public:
87     PrefixEntryBackup(ResourceModel &model, int prefixIndex, const QString &prefix,
88             const QString &language, const QList<FileEntryBackup> &files)
89             : EntryBackup(model, prefixIndex, prefix), m_language(language), m_files(files) { }
90     void restore() const;
91 };
92
93 void PrefixEntryBackup::restore() const
94 {
95     m_model->insertPrefix(m_prefixIndex, m_name, m_language);
96     foreach (const FileEntryBackup &entry, m_files) {
97         entry.restore();
98     }
99 }
100
101 namespace Internal {
102
103 class RelativeResourceModel : public ResourceModel
104 {
105 public:
106     RelativeResourceModel(const ResourceFile &resource_file, QObject *parent = 0);
107
108     QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const
109     {
110         if (!index.isValid())
111             return QVariant();
112 /*
113         void const * const internalPointer = index.internalPointer();
114
115         if ((role == Qt::DisplayRole) && (internalPointer != NULL))
116             return ResourceModel::data(index, Qt::ToolTipRole);
117 */
118         return ResourceModel::data(index, role);
119     }
120
121     void setResourceDragEnabled(bool e) { m_resourceDragEnabled = e; }
122     bool resourceDragEnabled() const { return m_resourceDragEnabled; }
123
124     virtual Qt::ItemFlags flags(const QModelIndex &index) const;
125
126     EntryBackup * removeEntry(const QModelIndex &index);
127
128 private:
129     bool m_resourceDragEnabled;
130 };
131
132 RelativeResourceModel::RelativeResourceModel(const ResourceFile &resource_file, QObject *parent)  :
133     ResourceModel(resource_file, parent),
134     m_resourceDragEnabled(false)
135 {
136 }
137
138 Qt::ItemFlags RelativeResourceModel::flags(const QModelIndex &index) const
139 {
140     Qt::ItemFlags rc = ResourceModel::flags(index);
141     if ((rc & Qt::ItemIsEnabled) && m_resourceDragEnabled)
142         rc |= Qt::ItemIsDragEnabled;
143     return rc;
144 }
145
146 EntryBackup * RelativeResourceModel::removeEntry(const QModelIndex &index)
147 {
148     const QModelIndex prefixIndex = this->prefixIndex(index);
149     const bool isPrefixNode = (prefixIndex == index);
150
151     // Create backup, remove, return backup
152     if (isPrefixNode) {
153         QString dummy;
154         QString prefixBackup;
155         getItem(index, prefixBackup, dummy);
156         const QString languageBackup = lang(index);
157         const int childCount = rowCount(index);
158         QList<FileEntryBackup> filesBackup;
159         for (int i = 0; i < childCount; i++) {
160             const QModelIndex childIndex = this->index(i, 0, index);
161             const QString fileNameBackup = file(childIndex);
162             const QString aliasBackup = alias(childIndex);
163             FileEntryBackup entry(*this, index.row(), i, fileNameBackup, aliasBackup);
164             filesBackup << entry;
165         }
166         deleteItem(index);
167         return new PrefixEntryBackup(*this, index.row(), prefixBackup, languageBackup, filesBackup);
168     } else {
169         const QString fileNameBackup = file(index);
170         const QString aliasBackup = alias(index);
171         deleteItem(index);
172         return new FileEntryBackup(*this, prefixIndex.row(), index.row(), fileNameBackup, aliasBackup);
173     }
174 }
175
176 } // namespace Internal
177
178 ResourceView::ResourceView(QUndoStack *history, QWidget *parent) :
179     QTreeView(parent),
180     m_qrcModel(new Internal::RelativeResourceModel(m_qrcFile, this)),
181     m_addFile(0),
182     m_editAlias(0),
183     m_removeItem(0),
184     m_addPrefix(0),
185     m_editPrefix(0),
186     m_editLang(0),
187     m_viewMenu(0),
188     m_defaultAddFile(false),
189     m_history(history),
190     m_mergeId(-1)
191 {
192     advanceMergeId();
193     setModel(m_qrcModel);
194
195     header()->hide();
196
197     connect(m_qrcModel, SIGNAL(dirtyChanged(bool)),
198         this, SIGNAL(dirtyChanged(bool)));
199
200     setupMenu();
201
202     setDefaultAddFileEnabled(true);
203     enableContextMenu(true);
204 }
205
206 ResourceView::~ResourceView()
207 {
208 }
209
210 bool ResourceView::isDirty() const
211 {
212     return m_qrcModel->dirty();
213 }
214
215 void ResourceView::setDirty(bool dirty)
216 {
217     m_qrcModel->setDirty(dirty);
218 }
219
220 void ResourceView::setDefaultAddFileEnabled(bool enable)
221 {
222     m_defaultAddFile = enable;
223 }
224
225 bool ResourceView::defaultAddFileEnabled() const
226 {
227     return m_defaultAddFile;
228 }
229
230 void ResourceView::findSamePlacePostDeletionModelIndex(int &row, QModelIndex &parent) const
231 {
232     // Concept:
233     // - Make selection stay on same Y level
234     // - Enable user to hit delete several times in row
235     const bool hasLowerBrother = m_qrcModel->hasIndex(row + 1,
236             0, parent);
237     if (hasLowerBrother) {
238         // First or mid child -> lower brother
239         //  o
240         //  +--o
241         //  +-[o]  <-- deleted
242         //  +--o   <-- chosen
243         //  o
244         // --> return unmodified
245     } else {
246         if (parent == QModelIndex()) {
247             // Last prefix node
248             if (row == 0) {
249                 // Last and only prefix node
250                 // [o]  <-- deleted
251                 //  +--o
252                 //  +--o
253                 row = -1;
254                 parent = QModelIndex();
255             } else {
256                 const QModelIndex upperBrother = m_qrcModel->index(row - 1,
257                         0, parent);
258                 if (m_qrcModel->hasChildren(upperBrother)) {
259                     //  o
260                     //  +--o  <-- selected
261                     // [o]    <-- deleted
262                     row = m_qrcModel->rowCount(upperBrother) - 1;
263                     parent = upperBrother;
264                 } else {
265                     //  o
266                     //  o  <-- selected
267                     // [o] <-- deleted
268                     row--;
269                 }
270             }
271         } else {
272             // Last file node
273             const bool hasPrefixBelow = m_qrcModel->hasIndex(parent.row() + 1,
274                     parent.column(), QModelIndex());
275             if (hasPrefixBelow) {
276                 // Last child or parent with lower brother -> lower brother of parent
277                 //  o
278                 //  +--o
279                 //  +-[o]  <-- deleted
280                 //  o      <-- chosen
281                 row = parent.row() + 1;
282                 parent = QModelIndex();
283             } else {
284                 const bool onlyChild = row == 0;
285                 if (onlyChild) {
286                     // Last and only child of last parent -> parent
287                     //  o      <-- chosen
288                     //  +-[o]  <-- deleted
289                     row = parent.row();
290                     parent = m_qrcModel->parent(parent);
291                 } else {
292                     // Last child of last parent -> upper brother
293                     //  o
294                     //  +--o   <-- chosen
295                     //  +-[o]  <-- deleted
296                     row--;
297                 }
298             }
299         }
300     }
301 }
302
303 EntryBackup * ResourceView::removeEntry(const QModelIndex &index)
304 {
305     Q_ASSERT(m_qrcModel);
306     return m_qrcModel->removeEntry(index);
307 }
308
309 void ResourceView::addFiles(int prefixIndex, const QStringList &fileNames, int cursorFile,
310         int &firstFile, int &lastFile)
311 {
312     Q_ASSERT(m_qrcModel);
313     m_qrcModel->addFiles(prefixIndex, fileNames, cursorFile, firstFile, lastFile);
314
315     // Expand prefix node
316     const QModelIndex prefixModelIndex = m_qrcModel->index(prefixIndex, 0, QModelIndex());
317     if (prefixModelIndex.isValid()) {
318         this->setExpanded(prefixModelIndex, true);
319     }
320 }
321
322 void ResourceView::removeFiles(int prefixIndex, int firstFileIndex, int lastFileIndex)
323 {
324     Q_ASSERT(prefixIndex >= 0 && prefixIndex < m_qrcModel->rowCount(QModelIndex()));
325     const QModelIndex prefixModelIndex = m_qrcModel->index(prefixIndex, 0, QModelIndex());
326     Q_ASSERT(prefixModelIndex != QModelIndex());
327     Q_ASSERT(firstFileIndex >= 0 && firstFileIndex < m_qrcModel->rowCount(prefixModelIndex));
328     Q_ASSERT(lastFileIndex >= 0 && lastFileIndex < m_qrcModel->rowCount(prefixModelIndex));
329
330     for (int i = lastFileIndex; i >= firstFileIndex; i--) {
331         const QModelIndex index = m_qrcModel->index(i, 0, prefixModelIndex);
332         delete removeEntry(index);
333     }
334 }
335
336 void ResourceView::enableContextMenu(bool enable)
337 {
338     if (enable) {
339         connect(this, SIGNAL(clicked(const QModelIndex &)),
340             this, SLOT(popupMenu(const QModelIndex &)));
341     } else {
342         disconnect(this, SIGNAL(clicked(const QModelIndex &)),
343             this, SLOT(popupMenu(const QModelIndex &)));
344     }
345 }
346
347 void ResourceView::setupMenu()
348 {
349     m_viewMenu = new QMenu(this);
350 /*
351     m_addFile = m_viewMenu->addAction(tr("Add Files..."), this, SIGNAL(addFiles()));
352     m_editAlias = m_viewMenu->addAction(tr("Change Alias..."), this, SLOT(onEditAlias()));
353     m_addPrefix = m_viewMenu->addAction(tr("Add Prefix..."), this, SLOT(addPrefix()));
354     m_editPrefix = m_viewMenu->addAction(tr("Change Prefix..."), this, SLOT(onEditPrefix()));
355     m_editLang = m_viewMenu->addAction(tr("Change Language..."), this, SLOT(onEditLang()));
356     m_viewMenu->addSeparator();
357     m_removeItem = m_viewMenu->addAction(tr("Remove Item"), this, SLOT(removeItem()));
358 */
359     m_addFile = m_viewMenu->addAction(tr("Add Files..."), this, SLOT(onAddFiles()));
360     m_editAlias = m_viewMenu->addAction(tr("Change Alias..."), this, SLOT(onEditAlias()));
361     m_addPrefix = m_viewMenu->addAction(tr("Add Prefix..."), this, SIGNAL(addPrefixTriggered()));
362     m_editPrefix = m_viewMenu->addAction(tr("Change Prefix..."), this, SLOT(onEditPrefix()));
363     m_editLang = m_viewMenu->addAction(tr("Change Language..."), this, SLOT(onEditLang()));
364     m_viewMenu->addSeparator();
365     m_removeItem = m_viewMenu->addAction(tr("Remove Item"), this, SIGNAL(removeItem()));
366 }
367
368 void ResourceView::mouseReleaseEvent(QMouseEvent *e)
369 {
370     m_releasePos = e->globalPos();
371     if (e->button() != Qt::RightButton)
372         m_releasePos = QPoint();
373
374     QTreeView::mouseReleaseEvent(e);
375 }
376
377 void ResourceView::keyPressEvent(QKeyEvent *e)
378 {
379     if (e->key() == Qt::Key_Delete)
380         removeItem();
381     else
382         QTreeView::keyPressEvent(e);
383 }
384
385 void ResourceView::popupMenu(const QModelIndex &index)
386 {
387     if (!m_releasePos.isNull()) {
388         m_addFile->setEnabled(index.isValid());
389         m_editPrefix->setEnabled(index.isValid());
390         m_editLang->setEnabled(index.isValid());
391         m_removeItem->setEnabled(index.isValid());
392
393         m_viewMenu->popup(m_releasePos);
394     }
395 }
396
397 QModelIndex ResourceView::addPrefix()
398 {
399     const QModelIndex idx = m_qrcModel->addNewPrefix();
400     selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
401     return idx;
402 }
403
404 QStringList ResourceView::fileNamesToAdd()
405 {
406     return QFileDialog::getOpenFileNames(this, tr("Open File"),
407             m_qrcModel->absolutePath(QString()),
408             tr("All files (*)"));
409 }
410
411 void ResourceView::onAddFiles()
412 {
413     emit addFilesTriggered(currentPrefix());
414 }
415
416 void ResourceView::addFiles(QStringList fileList, const QModelIndex &index)
417 {
418     if (fileList.isEmpty())
419         return;
420     QModelIndex idx = index;
421     if (!m_qrcModel->hasChildren(QModelIndex())) {
422         idx = addPrefix();
423         expand(idx);
424     }
425
426     idx = m_qrcModel->addFiles(idx, fileList);
427
428     if (idx.isValid()) {
429         const QModelIndex preindex = m_qrcModel->prefixIndex(index);
430         setExpanded(preindex, true);
431         selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
432         QString prefix, file;
433         m_qrcModel->getItem(preindex, prefix, file);
434 // XXX        emit filesAdded(prefix, fileList);
435     }
436 }
437
438 void ResourceView::addFile(const QString &prefix, const QString &file)
439 {
440     const QModelIndex preindex = m_qrcModel->getIndex(prefix, QString());
441     addFiles(QStringList(file), preindex);
442 }
443
444 /*
445 void ResourceView::removeItem()
446 {
447     const QModelIndex index = currentIndex();
448     m_qrcModel->deleteItem(index);
449 }
450
451 void ResourceView::removeFile(const QString &prefix, const QString &file)
452 {
453     const QModelIndex index = m_qrcModel->getIndex(prefix, file);
454     if (index.isValid())
455         m_qrcModel->deleteItem(index);
456 }
457 */
458 void ResourceView::onEditPrefix()
459 {
460     QModelIndex index = currentIndex();
461     changePrefix(index);
462 }
463
464 void ResourceView::onEditLang()
465 {
466     const QModelIndex index = currentIndex();
467     changeLang(index);
468 }
469
470 void ResourceView::onEditAlias()
471 {
472     const QModelIndex index = currentIndex();
473     changeAlias(index);
474 }
475
476 bool ResourceView::load(const QString &fileName)
477 {
478     const QFileInfo fi(fileName);
479     m_qrcModel->setFileName(fi.absoluteFilePath());
480
481     if (!fi.exists())
482         return false;
483
484     return m_qrcModel->reload();
485 }
486
487 bool ResourceView::save()
488 {
489     return m_qrcModel->save();
490 }
491
492 void ResourceView::changePrefix(const QModelIndex &index)
493 {
494     bool ok = false;
495     const QModelIndex preindex = m_qrcModel->prefixIndex(index);
496
497     QString prefixBefore;
498     QString dummy;
499     m_qrcModel->getItem(preindex, prefixBefore, dummy);
500
501     QString const prefixAfter = QInputDialog::getText(this, tr("Change Prefix"), tr("Input prefix:"),
502         QLineEdit::Normal, prefixBefore, &ok);
503
504     if (ok)
505         addUndoCommand(preindex, PrefixProperty, prefixBefore, prefixAfter);
506 }
507
508 void ResourceView::changeLang(const QModelIndex &index)
509 {
510     bool ok = false;
511     const QModelIndex preindex = m_qrcModel->prefixIndex(index);
512
513     QString const langBefore = m_qrcModel->lang(preindex);
514     QString const langAfter = QInputDialog::getText(this, tr("Change Language"), tr("Language:"),
515         QLineEdit::Normal, langBefore, &ok);
516
517     if (ok) {
518         addUndoCommand(preindex, LanguageProperty, langBefore, langAfter);
519     }
520 }
521
522 void ResourceView::changeAlias(const QModelIndex &index)
523 {
524     if (!index.parent().isValid())
525         return;
526
527     bool ok = false;
528
529     QString const aliasBefore = m_qrcModel->alias(index);
530     QString const aliasAfter = QInputDialog::getText(this, tr("Change File Alias"), tr("Alias:"),
531         QLineEdit::Normal, aliasBefore, &ok);
532
533     if (ok)
534         addUndoCommand(index, AliasProperty, aliasBefore, aliasAfter);
535 }
536
537 QString ResourceView::currentAlias() const
538 {
539     const QModelIndex current = currentIndex();
540     if (!current.isValid())
541         return QString();
542     return m_qrcModel->alias(current);
543 }
544
545 QString ResourceView::currentPrefix() const
546 {
547     const QModelIndex current = currentIndex();
548     if (!current.isValid())
549         return QString();
550     const QModelIndex preindex = m_qrcModel->prefixIndex(current);
551     QString prefix, file;
552     m_qrcModel->getItem(preindex, prefix, file);
553     return prefix;
554 }
555
556 QString ResourceView::currentLanguage() const
557 {
558     const QModelIndex current = currentIndex();
559     if (!current.isValid())
560         return QString();
561     const QModelIndex preindex = m_qrcModel->prefixIndex(current);
562     return m_qrcModel->lang(preindex);
563 }
564
565 QString ResourceView::getCurrentValue(NodeProperty property) const
566 {
567     switch (property) {
568     case AliasProperty: return currentAlias();
569     case PrefixProperty: return currentPrefix();
570     case LanguageProperty: return currentLanguage();
571     default: Q_ASSERT(false); return QString(); // Kill warning
572     }
573 }
574
575 void ResourceView::changeValue(const QModelIndex &nodeIndex, NodeProperty property,
576         const QString &value)
577 {
578     switch (property) {
579     case AliasProperty: m_qrcModel->changeAlias(nodeIndex, value); return;
580     case PrefixProperty: m_qrcModel->changePrefix(nodeIndex, value); return;
581     case LanguageProperty: m_qrcModel->changeLang(nodeIndex, value); return;
582     default: Q_ASSERT(false);
583     }
584 }
585
586 void ResourceView::advanceMergeId()
587 {
588     m_mergeId++;
589     if (m_mergeId < 0)
590         m_mergeId = 0;
591 }
592
593 void ResourceView::addUndoCommand(const QModelIndex &nodeIndex, NodeProperty property,
594         const QString &before, const QString &after)
595 {
596     QUndoCommand * const command = new ModifyPropertyCommand(this, nodeIndex, property,
597             m_mergeId, before, after);
598     m_history->push(command);
599 }
600
601 void ResourceView::setCurrentAlias(const QString &before, const QString &after)
602 {
603     const QModelIndex current = currentIndex();
604     if (!current.isValid())
605         return;
606
607     addUndoCommand(current, AliasProperty, before, after);
608 }
609
610 void ResourceView::setCurrentPrefix(const QString &before, const QString &after)
611 {
612     const QModelIndex current = currentIndex();
613     if (!current.isValid())
614         return;
615     const QModelIndex preindex = m_qrcModel->prefixIndex(current);
616
617     addUndoCommand(preindex, PrefixProperty, before, after);
618 }
619
620 void ResourceView::setCurrentLanguage(const QString &before, const QString &after)
621 {
622     const QModelIndex current = currentIndex();
623     if (!current.isValid())
624         return;
625     const QModelIndex preindex = m_qrcModel->prefixIndex(current);
626
627     addUndoCommand(preindex, LanguageProperty, before, after);
628 }
629
630 bool ResourceView::isPrefix(const QModelIndex &index) const
631 {
632     if (!index.isValid())
633         return false;
634     const QModelIndex preindex = m_qrcModel->prefixIndex(index);
635     if (preindex == index)
636         return true;
637     return false;
638 }
639
640 QString ResourceView::fileName() const
641 {
642     return m_qrcModel->fileName();
643 }
644
645 void ResourceView::setFileName(const QString &fileName)
646 {
647     m_qrcModel->setFileName(fileName);
648 }
649
650 void ResourceView::setResourceDragEnabled(bool e)
651 {
652     setDragEnabled(e);
653     m_qrcModel->setResourceDragEnabled(e);
654 }
655
656 bool ResourceView::resourceDragEnabled() const
657 {
658     return m_qrcModel->resourceDragEnabled();
659 }
660
661 } // namespace SharedTools