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 "dependenciespanel.h"
27 #include "project.h"
28 #include "session.h"
29 
30 #include <coreplugin/fileiconprovider.h>
31 #include <coreplugin/icore.h>
32 
33 #include <utils/detailswidget.h>
34 #include <utils/algorithm.h>
35 
36 #include <QDebug>
37 #include <QSize>
38 #include <QCoreApplication>
39 
40 #include <QCheckBox>
41 #include <QGridLayout>
42 #include <QTreeView>
43 #include <QSpacerItem>
44 #include <QMessageBox>
45 
46 namespace ProjectExplorer {
47 namespace Internal {
48 
DependenciesModel(Project * project,QObject * parent)49 DependenciesModel::DependenciesModel(Project *project, QObject *parent)
50     : QAbstractListModel(parent)
51     , m_project(project)
52 {
53     resetModel();
54 
55     SessionManager *sessionManager = SessionManager::instance();
56     connect(sessionManager, &SessionManager::projectRemoved,
57             this, &DependenciesModel::resetModel);
58     connect(sessionManager, &SessionManager::projectAdded,
59             this, &DependenciesModel::resetModel);
60     connect(sessionManager, &SessionManager::sessionLoaded,
61             this, &DependenciesModel::resetModel);
62 }
63 
resetModel()64 void DependenciesModel::resetModel()
65 {
66     beginResetModel();
67     m_projects = SessionManager::projects();
68     m_projects.removeAll(m_project);
69     Utils::sort(m_projects, [](Project *a, Project *b) {
70         return a->displayName() < b->displayName();
71     });
72     endResetModel();
73 }
74 
rowCount(const QModelIndex & index) const75 int DependenciesModel::rowCount(const QModelIndex &index) const
76 {
77     return index.isValid() ? 0 : m_projects.isEmpty() ? 1 : m_projects.size();
78 }
79 
columnCount(const QModelIndex & index) const80 int DependenciesModel::columnCount(const QModelIndex &index) const
81 {
82     return index.isValid() ? 0 : 1;
83 }
84 
data(const QModelIndex & index,int role) const85 QVariant DependenciesModel::data(const QModelIndex &index, int role) const
86 {
87     if (m_projects.isEmpty())
88         return role == Qt::DisplayRole
89             ? tr("<No other projects in this session>")
90             : QVariant();
91 
92     const Project *p = m_projects.at(index.row());
93 
94     switch (role) {
95     case Qt::DisplayRole:
96         return p->displayName();
97     case Qt::ToolTipRole:
98         return p->projectFilePath().toUserOutput();
99     case Qt::CheckStateRole:
100         return SessionManager::hasDependency(m_project, p) ? Qt::Checked : Qt::Unchecked;
101     case Qt::DecorationRole:
102         return Core::FileIconProvider::icon(p->projectFilePath());
103     default:
104         return QVariant();
105     }
106 }
107 
setData(const QModelIndex & index,const QVariant & value,int role)108 bool DependenciesModel::setData(const QModelIndex &index, const QVariant &value, int role)
109 {
110     if (role == Qt::CheckStateRole) {
111         Project *p = m_projects.at(index.row());
112         const auto c = static_cast<Qt::CheckState>(value.toInt());
113 
114         if (c == Qt::Checked) {
115             if (SessionManager::addDependency(m_project, p)) {
116                 emit dataChanged(index, index);
117                 return true;
118             } else {
119                 QMessageBox::warning(Core::ICore::dialogParent(), QCoreApplication::translate("DependenciesModel", "Unable to Add Dependency"),
120                                      QCoreApplication::translate("DependenciesModel", "This would create a circular dependency."));
121             }
122         } else if (c == Qt::Unchecked) {
123             if (SessionManager::hasDependency(m_project, p)) {
124                 SessionManager::removeDependency(m_project, p);
125                 emit dataChanged(index, index);
126                 return true;
127             }
128         }
129     }
130     return false;
131 }
132 
flags(const QModelIndex & index) const133 Qt::ItemFlags DependenciesModel::flags(const QModelIndex &index) const
134 {
135     if (m_projects.isEmpty())
136         return Qt::NoItemFlags;
137 
138     Qt::ItemFlags rc = QAbstractListModel::flags(index);
139     if (index.column() == 0)
140         rc |= Qt::ItemIsUserCheckable | Qt::ItemIsEditable;
141     return rc;
142 }
143 
144 //
145 // DependenciesView
146 //
DependenciesView(QWidget * parent)147 DependenciesView::DependenciesView(QWidget *parent)
148     : QTreeView(parent)
149 {
150     m_sizeHint = QSize(250, 250);
151     setUniformRowHeights(true);
152     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding);
153     setRootIsDecorated(false);
154 }
155 
sizeHint() const156 QSize DependenciesView::sizeHint() const
157 {
158     return m_sizeHint;
159 }
160 
setModel(QAbstractItemModel * newModel)161 void DependenciesView::setModel(QAbstractItemModel *newModel)
162 {
163     if (QAbstractItemModel *oldModel = model()) {
164         disconnect(oldModel, &QAbstractItemModel::rowsInserted,
165                    this, &DependenciesView::updateSizeHint);
166         disconnect(oldModel, &QAbstractItemModel::rowsRemoved,
167                    this, &DependenciesView::updateSizeHint);
168         disconnect(oldModel, &QAbstractItemModel::modelReset,
169                    this, &DependenciesView::updateSizeHint);
170         disconnect(oldModel, &QAbstractItemModel::layoutChanged,
171                    this, &DependenciesView::updateSizeHint);
172     }
173 
174     QTreeView::setModel(newModel);
175 
176     if (newModel) {
177         connect(newModel, &QAbstractItemModel::rowsInserted,
178                 this, &DependenciesView::updateSizeHint);
179         connect(newModel, &QAbstractItemModel::rowsRemoved,
180                 this, &DependenciesView::updateSizeHint);
181         connect(newModel, &QAbstractItemModel::modelReset,
182                 this, &DependenciesView::updateSizeHint);
183         connect(newModel, &QAbstractItemModel::layoutChanged,
184                 this, &DependenciesView::updateSizeHint);
185     }
186     updateSizeHint();
187 }
188 
updateSizeHint()189 void DependenciesView::updateSizeHint()
190 {
191     if (!model()) {
192         m_sizeHint = QSize(250, 250);
193         return;
194     }
195 
196     int heightOffset = size().height() - viewport()->height();
197 
198     int heightPerRow = sizeHintForRow(0);
199     if (heightPerRow == -1)
200         heightPerRow = 30;
201     int rows = qMin(qMax(model()->rowCount(), 2), 10);
202     int height = rows * heightPerRow + heightOffset;
203     if (m_sizeHint.height() != height) {
204         m_sizeHint.setHeight(height);
205         updateGeometry();
206     }
207 }
208 
209 //
210 // DependenciesWidget
211 //
212 
DependenciesWidget(Project * project,QWidget * parent)213 DependenciesWidget::DependenciesWidget(Project *project, QWidget *parent) : QWidget(parent),
214     m_project(project),
215     m_model(new DependenciesModel(project, this))
216 {
217     auto vbox = new QVBoxLayout(this);
218     vbox->setContentsMargins(0, 0, 0, 0);
219     m_detailsContainer = new Utils::DetailsWidget(this);
220     m_detailsContainer->setState(Utils::DetailsWidget::NoSummary);
221     vbox->addWidget(m_detailsContainer);
222 
223     auto detailsWidget = new QWidget(m_detailsContainer);
224     m_detailsContainer->setWidget(detailsWidget);
225     auto layout = new QGridLayout(detailsWidget);
226     layout->setContentsMargins(0, -1, 0, -1);
227     auto treeView = new DependenciesView(this);
228     treeView->setModel(m_model);
229     treeView->setHeaderHidden(true);
230     layout->addWidget(treeView, 0 ,0);
231     layout->addItem(new QSpacerItem(0, 0 , QSizePolicy::Expanding, QSizePolicy::Fixed), 0, 1);
232 
233     m_cascadeSetActiveCheckBox = new QCheckBox;
234     m_cascadeSetActiveCheckBox->setText(tr("Synchronize configuration"));
235     m_cascadeSetActiveCheckBox->setToolTip(tr("Synchronize active kit, build, and deploy configuration between projects."));
236     m_cascadeSetActiveCheckBox->setChecked(SessionManager::isProjectConfigurationCascading());
237     connect(m_cascadeSetActiveCheckBox, &QCheckBox::toggled,
238             SessionManager::instance(), &SessionManager::setProjectConfigurationCascading);
239     layout->addWidget(m_cascadeSetActiveCheckBox, 1, 0, 2, 1);
240 }
241 
242 } // namespace Internal
243 } // namespace ProjectExplorer
244