1#include "JobContainer.h"
2#include <JenkinsViewInfo.h>
3#include <JobFetcher.h>
4#include <JobButton.h>
5#include <ClickableFrame.h>
6#include <JenkinsJobPanel.h>
7#include <JobDetailsFetcher.h>
8
9#include <QVBoxLayout>
10#include <QScrollArea>
11#include <QTreeWidget>
12#include <QLabel>
13#include <QListWidget>
14
15namespace Jenkins
16{
17
18JobContainer::JobContainer(const IFetcher::Config &config, const JenkinsViewInfo &viewInfo, QWidget *parent)
19 : QFrame(parent)
20 , mConfig(config)
21 , mView(viewInfo)
22 , mJobFetcher(new JobFetcher(config, viewInfo.url, this))
23 , mJobListLayout(new QVBoxLayout())
24 , mJobPanel(new JenkinsJobPanel(config))
25{
26 mJobListLayout->setContentsMargins(QMargins());
27 mJobListLayout->setSpacing(0);
28
29 mMainLayout = new QHBoxLayout(this);
30 mMainLayout->setContentsMargins(10, 10, 10, 10);
31 mMainLayout->setSpacing(10);
32 mMainLayout->addLayout(mJobListLayout);
33 mMainLayout->addWidget(mJobPanel);
34 mMainLayout->setStretch(0, 30);
35 mMainLayout->setStretch(1, 70);
36
37 connect(mJobFetcher, &JobFetcher::signalJobsReceived, this, &JobContainer::addJobs);
38 mJobFetcher->triggerFetch();
39
40 connect(mJobPanel, &JenkinsJobPanel::gotoBranch, this, &JobContainer::gotoBranch);
41 connect(mJobPanel, &JenkinsJobPanel::gotoPullRequest, this, &JobContainer::gotoPullRequest);
42}
43
44void JobContainer::reload()
45{
46 mJobFetcher->triggerFetch();
47}
48
49void JobContainer::addJobs(const QMultiMap<QString, JenkinsJobInfo> &jobs)
50{
51 QVector<JenkinsViewInfo> views;
52
53 const auto keys = jobs.uniqueKeys();
54 const auto totalKeys = keys.count();
55 const auto splitView = totalKeys <= 2;
56
57 if (!splitView && !mJobsTree)
58 {
59 mJobsTree = new QTreeWidget();
60 mJobListLayout->addWidget(mJobsTree);
61 mJobListLayout->addStretch();
62 connect(mJobsTree, &QTreeWidget::itemClicked, this, &JobContainer::showJobInfo);
63 }
64
65 for (const auto &key : keys)
66 {
67 QTreeWidgetItem *item = nullptr;
68 QListWidget *listWidget = nullptr;
69
70 if (splitView)
71 {
72 if (mListsMap.contains(key))
73 listWidget = mListsMap.value(key);
74 else
75 {
76 listWidget = new QListWidget();
77 mListsMap.insert(key, listWidget);
78
79 connect(listWidget, &QListWidget::itemClicked, this, [this](QListWidgetItem *item) {
80 const auto job = qvariant_cast<JenkinsJobInfo>(item->data(Qt::UserRole));
81 requestUpdatedJobInfo(job);
82 });
83
84 createHeader(key, listWidget);
85 mJobListLayout->addWidget(listWidget);
86 }
87 }
88 else
89 {
90 item = new QTreeWidgetItem({ key });
91 mJobsTree->addTopLevelItem(item);
92 }
93
94 auto values = jobs.values(key);
95 std::sort(values.begin(), values.end());
96
97 for (const auto &job : qAsConst(values))
98 {
99 if (mJobsList.contains(job))
100 continue;
101
102 mJobsList.append(job);
103
104 if (job.builds.isEmpty() && job.color.isEmpty())
105 {
106 JenkinsViewInfo view;
107 view.name = job.name;
108 view.url = job.url;
109
110 views.append(std::move(view));
111 }
112 else
113 {
114 QVariant v;
115 v.setValue(job);
116
117 if (splitView)
118 {
119 auto jobItem = new QListWidgetItem(getIconForJob(job), job.name, listWidget);
120
121 QVariant v;
122 v.setValue(job);
123 jobItem->setData(Qt::UserRole, std::move(v));
124 }
125 else
126 {
127 QTreeWidgetItem *jobItem = nullptr;
128
129 if (item)
130 jobItem = new QTreeWidgetItem(item, { job.name });
131 else
132 jobItem = new QTreeWidgetItem(mJobsTree, { job.name });
133
134 jobItem->setData(0, Qt::UserRole, std::move(v));
135 jobItem->setIcon(0, getIconForJob(job));
136 }
137 }
138 }
139
140 if (item)
141 item->setExpanded(true);
142 }
143
144 if (!views.isEmpty())
145 emit signalJobAreViews(views);
146}
147
148void JobContainer::requestUpdatedJobInfo(const JenkinsJobInfo &jobInfo)
149{
150 const auto jobRequest = new JobDetailsFetcher(mConfig, jobInfo);
151 connect(jobRequest, &JobDetailsFetcher::signalJobDetailsRecieved, this,
152 [this, jobInfo](const JenkinsJobInfo &newInfo) { onJobInfoReceived(jobInfo, newInfo); });
153 connect(jobRequest, &JobDetailsFetcher::signalJobDetailsRecieved, jobRequest, &JobDetailsFetcher::deleteLater);
154
155 jobRequest->triggerFetch();
156}
157
158void JobContainer::onJobInfoReceived(JenkinsJobInfo oldInfo, const JenkinsJobInfo &newInfo)
159{
160 oldInfo.builds = newInfo.builds;
161 oldInfo.configFields = newInfo.configFields;
162 oldInfo.healthStatus = newInfo.healthStatus;
163
164 mJobPanel->loadJobInfo(oldInfo);
165}
166
167void JobContainer::showJobInfo(QTreeWidgetItem *item, int column)
168{
169 const auto job = qvariant_cast<JenkinsJobInfo>(item->data(column, Qt::UserRole));
170 requestUpdatedJobInfo(job);
171}
172
173QIcon JobContainer::getIconForJob(JenkinsJobInfo job) const
174{
175 job.color.remove("_anime");
176
177 if (job.color.contains("blue"))
178 job.color = "green";
179 else if (job.color.contains("disabled") || job.color.contains("grey") || job.color.contains("notbuilt"))
180 job.color = "grey";
181 else if (job.color.contains("aborted"))
182 job.color = "dark_grey";
183
184 return QIcon(QString(":/icons/%1").arg(job.color)).pixmap(15, 15);
185}
186
187void JobContainer::createHeader(const QString &name, QListWidget *listWidget)
188{
189 const auto headerFrame = new ClickableFrame();
190 headerFrame->setObjectName("sectionFrame");
191
192 const auto headerLayout = new QHBoxLayout(headerFrame);
193 headerLayout->setContentsMargins(20, 9, 10, 9);
194 headerLayout->setSpacing(10);
195 headerLayout->setAlignment(Qt::AlignTop);
196
197 headerLayout->addWidget(new QLabel(name));
198 headerLayout->addStretch();
199
200 const auto headerArrow = new QLabel();
201 headerArrow->setPixmap(QIcon(":/icons/arrow_down").pixmap(QSize(15, 15)));
202 headerLayout->addWidget(headerArrow);
203
204 connect(headerFrame, &ClickableFrame::clicked, this,
205 [this, listWidget, headerArrow]() { onHeaderClicked(listWidget, headerArrow); });
206
207 mJobListLayout->addWidget(headerFrame);
208}
209
210void JobContainer::onHeaderClicked(QListWidget *listWidget, QLabel *arrowIcon)
211{
212 const auto isVisible = listWidget->isVisible();
213 const auto icon = QIcon(isVisible ? QString(":/icons/arrow_up") : QString(":/icons/arrow_down"));
214 arrowIcon->setPixmap(icon.pixmap(QSize(15, 15)));
215 listWidget->setVisible(!isVisible);
216}
217
218}
219