1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "saveitemsdialog.h"
27 
28 #include <coreplugin/diffservice.h>
29 #include <coreplugin/fileiconprovider.h>
30 #include <coreplugin/idocument.h>
31 
32 #include <utils/fileutils.h>
33 #include <utils/hostosinfo.h>
34 
35 #include <extensionsystem/pluginmanager.h>
36 
37 #include <QDir>
38 #include <QFileInfo>
39 #include <QPushButton>
40 #include <QDebug>
41 
42 Q_DECLARE_METATYPE(Core::IDocument*)
43 
44 using namespace Core;
45 using namespace Core::Internal;
46 
SaveItemsDialog(QWidget * parent,QList<IDocument * > items)47 SaveItemsDialog::SaveItemsDialog(QWidget *parent,
48                                  QList<IDocument *> items)
49     : QDialog(parent)
50 {
51     m_ui.setupUi(this);
52 
53     // QDialogButtonBox's behavior for "destructive" is wrong, the "do not save" should be left-aligned
54     const QDialogButtonBox::ButtonRole discardButtonRole = Utils::HostOsInfo::isMacHost()
55             ? QDialogButtonBox::ResetRole : QDialogButtonBox::DestructiveRole;
56 
57     if (DiffService::instance()) {
58         m_diffButton = m_ui.buttonBox->addButton(tr("&Diff"), discardButtonRole);
59         connect(m_diffButton, &QAbstractButton::clicked, this, &SaveItemsDialog::collectFilesToDiff);
60     }
61 
62     QPushButton *discardButton = m_ui.buttonBox->addButton(tr("Do &Not Save"), discardButtonRole);
63     m_ui.buttonBox->button(QDialogButtonBox::Save)->setDefault(true);
64     m_ui.treeWidget->setFocus();
65 
66     m_ui.saveBeforeBuildCheckBox->setVisible(false);
67 
68     foreach (IDocument *document, items) {
69         QString visibleName;
70         QString directory;
71         Utils::FilePath filePath = document->filePath();
72         if (filePath.isEmpty()) {
73             visibleName = document->fallbackSaveAsFileName();
74         } else {
75             directory = filePath.absolutePath().toUserOutput();
76             visibleName = filePath.fileName();
77         }
78         QTreeWidgetItem *item = new QTreeWidgetItem(m_ui.treeWidget, QStringList()
79                                                     << visibleName << QDir::toNativeSeparators(directory));
80         if (!filePath.isEmpty())
81             item->setIcon(0, FileIconProvider::icon(filePath));
82         item->setData(0, Qt::UserRole, QVariant::fromValue(document));
83     }
84 
85     m_ui.treeWidget->resizeColumnToContents(0);
86     m_ui.treeWidget->selectAll();
87     if (Utils::HostOsInfo::isMacHost())
88         m_ui.treeWidget->setAlternatingRowColors(true);
89     adjustButtonWidths();
90     updateButtons();
91 
92     connect(m_ui.buttonBox->button(QDialogButtonBox::Save), &QAbstractButton::clicked,
93             this, &SaveItemsDialog::collectItemsToSave);
94     connect(discardButton, &QAbstractButton::clicked, this, &SaveItemsDialog::discardAll);
95     connect(m_ui.treeWidget, &QTreeWidget::itemSelectionChanged,
96             this, &SaveItemsDialog::updateButtons);
97 }
98 
setMessage(const QString & msg)99 void SaveItemsDialog::setMessage(const QString &msg)
100 {
101     m_ui.msgLabel->setText(msg);
102 }
103 
updateButtons()104 void SaveItemsDialog::updateButtons()
105 {
106     int count = m_ui.treeWidget->selectedItems().count();
107     QPushButton *saveButton = m_ui.buttonBox->button(QDialogButtonBox::Save);
108     bool buttonsEnabled = true;
109     QString saveText = tr("&Save");
110     QString diffText = tr("&Diff && Cancel");
111     if (count == m_ui.treeWidget->topLevelItemCount()) {
112         saveText = tr("&Save All");
113         diffText = tr("&Diff All && Cancel");
114     } else if (count == 0) {
115         buttonsEnabled = false;
116     } else {
117         saveText = tr("&Save Selected");
118         diffText = tr("&Diff Selected && Cancel");
119     }
120     saveButton->setEnabled(buttonsEnabled);
121     saveButton->setText(saveText);
122     if (m_diffButton) {
123         m_diffButton->setEnabled(buttonsEnabled);
124         m_diffButton->setText(diffText);
125     }
126 }
127 
adjustButtonWidths()128 void SaveItemsDialog::adjustButtonWidths()
129 {
130     // give save button a size that all texts fit in, so it doesn't get resized
131     // Mac: make cancel + save button same size (work around dialog button box issue)
132     QStringList possibleTexts;
133     possibleTexts << tr("Save") << tr("Save All");
134     if (m_ui.treeWidget->topLevelItemCount() > 1)
135         possibleTexts << tr("Save Selected");
136     int maxTextWidth = 0;
137     QPushButton *saveButton = m_ui.buttonBox->button(QDialogButtonBox::Save);
138     foreach (const QString &text, possibleTexts) {
139         saveButton->setText(text);
140         int hint = saveButton->sizeHint().width();
141         if (hint > maxTextWidth)
142             maxTextWidth = hint;
143     }
144     if (Utils::HostOsInfo::isMacHost()) {
145         QPushButton *cancelButton = m_ui.buttonBox->button(QDialogButtonBox::Cancel);
146         int cancelButtonWidth = cancelButton->sizeHint().width();
147         if (cancelButtonWidth > maxTextWidth)
148             maxTextWidth = cancelButtonWidth;
149         cancelButton->setMinimumWidth(maxTextWidth);
150     }
151     saveButton->setMinimumWidth(maxTextWidth);
152 }
153 
collectItemsToSave()154 void SaveItemsDialog::collectItemsToSave()
155 {
156     m_itemsToSave.clear();
157     foreach (QTreeWidgetItem *item, m_ui.treeWidget->selectedItems()) {
158         m_itemsToSave.append(item->data(0, Qt::UserRole).value<IDocument*>());
159     }
160     accept();
161 }
162 
collectFilesToDiff()163 void SaveItemsDialog::collectFilesToDiff()
164 {
165     m_filesToDiff.clear();
166     foreach (QTreeWidgetItem *item, m_ui.treeWidget->selectedItems()) {
167         if (auto doc = item->data(0, Qt::UserRole).value<IDocument*>())
168             m_filesToDiff.append(doc->filePath().toString());
169     }
170     reject();
171 }
172 
discardAll()173 void SaveItemsDialog::discardAll()
174 {
175     m_ui.treeWidget->clearSelection();
176     collectItemsToSave();
177 }
178 
itemsToSave() const179 QList<IDocument*> SaveItemsDialog::itemsToSave() const
180 {
181     return m_itemsToSave;
182 }
183 
filesToDiff() const184 QStringList SaveItemsDialog::filesToDiff() const
185 {
186     return m_filesToDiff;
187 }
188 
setAlwaysSaveMessage(const QString & msg)189 void SaveItemsDialog::setAlwaysSaveMessage(const QString &msg)
190 {
191     m_ui.saveBeforeBuildCheckBox->setText(msg);
192     m_ui.saveBeforeBuildCheckBox->setVisible(true);
193 }
194 
alwaysSaveChecked()195 bool SaveItemsDialog::alwaysSaveChecked()
196 {
197     return m_ui.saveBeforeBuildCheckBox->isChecked();
198 }
199