OSDN Git Service

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