]> git.sesse.net Git - nageru/blob - clip_list.cpp
Mark play progress with background color in the play list.
[nageru] / clip_list.cpp
1 #include "mainwindow.h"
2
3 #include <math.h>
4 #include <string>
5 #include <vector>
6
7 #include "clip_list.h"
8 #include "timebase.h"
9 #include "ui_mainwindow.h"
10
11 using namespace std;
12
13 string pts_to_string(int64_t pts)
14 {
15         int64_t t = lrint((pts / double(TIMEBASE)) * 1e3);  // In milliseconds.
16         int ms = t % 1000;
17         t /= 1000;
18         int sec = t % 60;
19         t /= 60;
20         int min = t % 60;
21         t /= 60;
22         int hour = t;
23
24         char buf[256];
25         snprintf(buf, sizeof(buf), "%d:%02d:%02d.%03d", hour, min, sec, ms);
26         return buf;
27 }
28
29 string duration_to_string(int64_t pts_diff)
30 {
31         int64_t t = lrint((pts_diff / double(TIMEBASE)) * 1e3);  // In milliseconds.
32         int ms = t % 1000;
33         t /= 1000;
34         int sec = t % 60;
35         t /= 60;
36         int min = t;
37
38         char buf[256];
39         snprintf(buf, sizeof(buf), "%d:%02d.%03d", min, sec, ms);
40         return buf;
41 }
42
43 int ClipList::rowCount(const QModelIndex &parent) const {
44         if (parent.isValid()) return 0;
45         return clips.size();
46 }
47
48 int PlayList::rowCount(const QModelIndex &parent) const {
49         if (parent.isValid()) return 0;
50         return clips.size();
51 }
52
53 int ClipList::columnCount(const QModelIndex &parent) const {
54         if (parent.isValid()) return 0;
55         return int(Column::NUM_COLUMNS);
56 }
57
58 int PlayList::columnCount(const QModelIndex &parent) const {
59         if (parent.isValid()) return 0;
60         return int(Column::NUM_COLUMNS);
61 }
62
63 QVariant ClipList::data(const QModelIndex &parent, int role) const {
64         if (!parent.isValid())
65                 return QVariant();
66         const int row = parent.row(), column = parent.column();
67         if (size_t(row) >= clips.size())
68                 return QVariant();
69
70         if (role == Qt::TextAlignmentRole) {
71                 switch (Column(column)) {
72                 case Column::IN:
73                 case Column::OUT:
74                 case Column::DURATION:
75                         return Qt::AlignRight + Qt::AlignVCenter;
76                 default:
77                         return Qt::AlignLeft + Qt::AlignVCenter;
78                 }
79         }
80
81         if (role != Qt::DisplayRole && role != Qt::EditRole)
82                 return QVariant();
83
84         switch (Column(column)) {
85         case Column::IN:
86                 return QString::fromStdString(pts_to_string(clips[row].pts_in));
87         case Column::OUT:
88                 if (clips[row].pts_out >= 0) {
89                         return QString::fromStdString(pts_to_string(clips[row].pts_out));
90                 } else {
91                         return QVariant();
92                 }
93         case Column::DURATION:
94                 if (clips[row].pts_out >= 0) {
95                         return QString::fromStdString(duration_to_string(clips[row].pts_out - clips[row].pts_in));
96                 } else {
97                         return QVariant();
98                 }
99         case Column::CAMERA_1:
100         case Column::CAMERA_2:
101         case Column::CAMERA_3:
102         case Column::CAMERA_4: {
103                 unsigned stream_idx = column - int(Column::CAMERA_1);
104                 return QString::fromStdString(clips[row].descriptions[stream_idx]);
105         }
106         default:
107                 return "";
108         }
109 }
110
111 QVariant PlayList::data(const QModelIndex &parent, int role) const {
112         if (!parent.isValid())
113                 return QVariant();
114         const int row = parent.row(), column = parent.column();
115         if (size_t(row) >= clips.size())
116                 return QVariant();
117
118         if (role == Qt::TextAlignmentRole) {
119                 switch (Column(column)) {
120                 case Column::PLAYING:
121                         return Qt::AlignCenter;
122                 case Column::IN:
123                 case Column::OUT:
124                 case Column::DURATION:
125                         return Qt::AlignRight + Qt::AlignVCenter;
126                 case Column::CAMERA:
127                         return Qt::AlignCenter;
128                 default:
129                         return Qt::AlignLeft + Qt::AlignVCenter;
130                 }
131         }
132         if (role == Qt::BackgroundRole) {
133                 if (Column(column) == Column::PLAYING) {
134                         if (row == currently_playing_index) {
135                                 // This only really works well for the first column, for whatever odd Qt reason.
136                                 QLinearGradient grad(QPointF(0, 0), QPointF(1, 0));
137                                 grad.setCoordinateMode(grad.QGradient::ObjectBoundingMode);
138                                 grad.setColorAt(0.0f, QColor::fromRgbF(0.0f, 0.0f, 1.0f, 0.2f));
139                                 grad.setColorAt(play_progress, QColor::fromRgbF(0.0f, 0.0f, 1.0f, 0.2f));
140                                 grad.setColorAt(play_progress + 0.01f, QColor::fromRgbF(0.0f, 0.0f, 1.0f, 0.0f));
141                                 return QBrush(grad);
142                         } else {
143                                 return QVariant();
144                         }
145                 } else {
146                         return QVariant();
147                 }
148         }
149
150         if (role != Qt::DisplayRole && role != Qt::EditRole)
151                 return QVariant();
152
153         switch (Column(column)) {
154         case Column::PLAYING:
155                 return (row == currently_playing_index) ? "→" : "";
156         case Column::IN:
157                 return QString::fromStdString(pts_to_string(clips[row].pts_in));
158         case Column::OUT:
159                 if (clips[row].pts_out >= 0) {
160                         return QString::fromStdString(pts_to_string(clips[row].pts_out));
161                 } else {
162                         return QVariant();
163                 }
164         case Column::DURATION:
165                 if (clips[row].pts_out >= 0) {
166                         return QString::fromStdString(duration_to_string(clips[row].pts_out - clips[row].pts_in));
167                 } else {
168                         return QVariant();
169                 }
170         case Column::CAMERA:
171                 return qlonglong(clips[row].stream_idx + 1);
172         case Column::DESCRIPTION:
173                 return QString::fromStdString(clips[row].descriptions[clips[row].stream_idx]);
174         default:
175                 return "";
176         }
177 }
178
179 QVariant ClipList::headerData(int section, Qt::Orientation orientation, int role) const {
180         if (role != Qt::DisplayRole)
181                 return QVariant();
182         if (orientation != Qt::Horizontal)
183                 return QVariant();
184
185         switch (Column(section)) {
186         case Column::IN:
187                 return "In";
188         case Column::OUT:
189                 return "Out";
190         case Column::DURATION:
191                 return "Duration";
192         case Column::CAMERA_1:
193                 return "Camera 1";
194         case Column::CAMERA_2:
195                 return "Camera 2";
196         case Column::CAMERA_3:
197                 return "Camera 3";
198         case Column::CAMERA_4:
199                 return "Camera 4";
200         default:
201                 return "";
202         }
203 }
204
205 QVariant PlayList::headerData(int section, Qt::Orientation orientation, int role) const {
206         if (role != Qt::DisplayRole)
207                 return QVariant();
208         if (orientation != Qt::Horizontal)
209                 return QVariant();
210
211         switch (Column(section)) {
212         case Column::PLAYING:
213                 return "";
214         case Column::IN:
215                 return "In";
216         case Column::OUT:
217                 return "Out";
218         case Column::DURATION:
219                 return "Duration";
220         case Column::CAMERA:
221                 return "Camera";
222         case Column::DESCRIPTION:
223                 return "Description";
224         default:
225                 return "";
226         }
227 }
228
229 Qt::ItemFlags ClipList::flags(const QModelIndex &index) const
230 {
231         if (!index.isValid())
232                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
233         const int row = index.row(), column = index.column();
234         if (size_t(row) >= clips.size())
235                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
236
237         switch (Column(column)) {
238         case Column::CAMERA_1:
239         case Column::CAMERA_2:
240         case Column::CAMERA_3:
241         case Column::CAMERA_4:
242                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
243         default:
244                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
245         }
246 }
247
248 Qt::ItemFlags PlayList::flags(const QModelIndex &index) const
249 {
250         if (!index.isValid())
251                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
252         const int row = index.row(), column = index.column();
253         if (size_t(row) >= clips.size())
254                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
255
256         switch (Column(column)) {
257         case Column::DESCRIPTION:
258         case Column::CAMERA:
259                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
260                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
261         default:
262                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
263         }
264 }
265
266 bool ClipList::setData(const QModelIndex &index, const QVariant &value, int role)
267 {
268         if (!index.isValid() || role != Qt::EditRole) {
269                 return false;
270         }
271
272         const int row = index.row(), column = index.column();
273         if (size_t(row) >= clips.size())
274                 return false;
275
276         switch (Column(column)) {
277         case Column::CAMERA_1:
278         case Column::CAMERA_2:
279         case Column::CAMERA_3:
280         case Column::CAMERA_4: {
281                 unsigned stream_idx = column - int(Column::CAMERA_1);
282                 clips[row].descriptions[stream_idx] = value.toString().toStdString();
283                 emit_data_changed(row);
284                 return true;
285         }
286         default:
287                 return false;
288         }
289 }
290
291 bool PlayList::setData(const QModelIndex &index, const QVariant &value, int role)
292 {
293         if (!index.isValid() || role != Qt::EditRole) {
294                 return false;
295         }
296
297         const int row = index.row(), column = index.column();
298         if (size_t(row) >= clips.size())
299                 return false;
300
301         switch (Column(column)) {
302         case Column::DESCRIPTION:
303                 clips[row].descriptions[clips[row].stream_idx] = value.toString().toStdString();
304                 emit_data_changed(row);
305                 return true;
306         case Column::CAMERA: {
307                 bool ok;
308                 int camera_idx = value.toInt(&ok);
309                 if (!ok || camera_idx < 1 || camera_idx > NUM_CAMERAS) {
310                         return false;
311                 }
312                 clips[row].stream_idx = camera_idx - 1;
313                 emit_data_changed(row);
314                 return true;
315         }
316         default:
317                 return false;
318         }
319 }
320
321 void ClipList::add_clip(const Clip &clip)
322 {
323         beginInsertRows(QModelIndex(), clips.size(), clips.size());
324         clips.push_back(clip);
325         endInsertRows();
326 }
327
328 void PlayList::add_clip(const Clip &clip)
329 {
330         beginInsertRows(QModelIndex(), clips.size(), clips.size());
331         clips.push_back(clip);
332         endInsertRows();
333 }
334
335 void PlayList::duplicate_clips(size_t first, size_t last)
336 {
337         beginInsertRows(QModelIndex(), first, last);
338         clips.insert(clips.begin() + first, clips.begin() + first, clips.begin() + last + 1);
339         endInsertRows();
340 }
341
342 void PlayList::erase_clips(size_t first, size_t last)
343 {
344         beginRemoveRows(QModelIndex(), first, last);
345         clips.erase(clips.begin() + first, clips.begin() + last + 1);
346         endRemoveRows();
347 }
348
349 void PlayList::move_clips(size_t first, size_t last, int delta)
350 {
351         if (delta == -1) {
352                 beginMoveRows(QModelIndex(), first, last, QModelIndex(), first - 1);
353                 rotate(clips.begin() + first - 1, clips.begin() + first, clips.begin() + last + 1);
354         } else {
355                 beginMoveRows(QModelIndex(), first, last, QModelIndex(), first + (last-first+1) + 1);
356                 first = clips.size() - first - 1;
357                 last = clips.size() - last - 1;
358                 rotate(clips.rbegin() + last - 1, clips.rbegin() + last, clips.rbegin() + first + 1);
359         }
360         endMoveRows();
361 }
362
363 void ClipList::emit_data_changed(size_t row)
364 {
365         emit dataChanged(index(row, 0), index(row, int(Column::NUM_COLUMNS)));
366 }
367
368 void PlayList::emit_data_changed(size_t row)
369 {
370         emit dataChanged(index(row, 0), index(row, int(Column::NUM_COLUMNS)));
371 }
372
373 void PlayList::set_currently_playing(int index, double progress)
374 {
375         int old_index = currently_playing_index;
376         int column = int(Column::PLAYING);
377         if (index != old_index) {
378                 currently_playing_index = index;
379                 play_progress = progress;
380                 if (old_index != -1) {
381                         emit dataChanged(this->index(old_index, column), this->index(old_index, column));
382                 }
383                 if (index != -1) {
384                         emit dataChanged(this->index(index, column), this->index(index, column));
385                 }
386         } else if (index != -1 && fabs(progress - play_progress) > 1e-3) {
387                 play_progress = progress;
388                 emit dataChanged(this->index(index, column), this->index(index, column));
389         }
390 }