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