]> git.sesse.net Git - nageru/blob - clip_list.cpp
Assorted clang-format fixes (not complete).
[nageru] / clip_list.cpp
1 #include "clip_list.h"
2
3 #include "mainwindow.h"
4 #include "timebase.h"
5 #include "ui_mainwindow.h"
6
7 #include <math.h>
8 #include <string>
9 #include <vector>
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 {
45         if (parent.isValid())
46                 return 0;
47         return clips.size();
48 }
49
50 int PlayList::rowCount(const QModelIndex &parent) const
51 {
52         if (parent.isValid())
53                 return 0;
54         return clips.size();
55 }
56
57 int ClipList::columnCount(const QModelIndex &parent) const
58 {
59         if (parent.isValid())
60                 return 0;
61         return int(Column::NUM_COLUMNS);
62 }
63
64 int PlayList::columnCount(const QModelIndex &parent) const
65 {
66         if (parent.isValid())
67                 return 0;
68         return int(Column::NUM_COLUMNS);
69 }
70
71 QVariant ClipList::data(const QModelIndex &parent, int role) const
72 {
73         if (!parent.isValid())
74                 return QVariant();
75         const int row = parent.row(), column = parent.column();
76         if (size_t(row) >= clips.size())
77                 return QVariant();
78
79         if (role == Qt::TextAlignmentRole) {
80                 switch (Column(column)) {
81                 case Column::IN:
82                 case Column::OUT:
83                 case Column::DURATION:
84                         return Qt::AlignRight + Qt::AlignVCenter;
85                 default:
86                         return Qt::AlignLeft + Qt::AlignVCenter;
87                 }
88         }
89
90         if (role != Qt::DisplayRole && role != Qt::EditRole)
91                 return QVariant();
92
93         switch (Column(column)) {
94         case Column::IN:
95                 return QString::fromStdString(pts_to_string(clips[row].pts_in));
96         case Column::OUT:
97                 if (clips[row].pts_out >= 0) {
98                         return QString::fromStdString(pts_to_string(clips[row].pts_out));
99                 } else {
100                         return QVariant();
101                 }
102         case Column::DURATION:
103                 if (clips[row].pts_out >= 0) {
104                         return QString::fromStdString(duration_to_string(clips[row].pts_out - clips[row].pts_in));
105                 } else {
106                         return QVariant();
107                 }
108         case Column::CAMERA_1:
109         case Column::CAMERA_2:
110         case Column::CAMERA_3:
111         case Column::CAMERA_4: {
112                 unsigned stream_idx = column - int(Column::CAMERA_1);
113                 return QString::fromStdString(clips[row].descriptions[stream_idx]);
114         }
115         default:
116                 return "";
117         }
118 }
119
120 QVariant PlayList::data(const QModelIndex &parent, int role) const
121 {
122         if (!parent.isValid())
123                 return QVariant();
124         const int row = parent.row(), column = parent.column();
125         if (size_t(row) >= clips.size())
126                 return QVariant();
127
128         if (role == Qt::TextAlignmentRole) {
129                 switch (Column(column)) {
130                 case Column::PLAYING:
131                         return Qt::AlignCenter;
132                 case Column::IN:
133                 case Column::OUT:
134                 case Column::DURATION:
135                 case Column::FADE_TIME:
136                         return Qt::AlignRight + Qt::AlignVCenter;
137                 case Column::CAMERA:
138                         return Qt::AlignCenter;
139                 default:
140                         return Qt::AlignLeft + Qt::AlignVCenter;
141                 }
142         }
143         if (role == Qt::BackgroundRole) {
144                 if (Column(column) == Column::PLAYING) {
145                         if (row == currently_playing_index) {
146                                 // This only really works well for the first column, for whatever odd Qt reason.
147                                 QLinearGradient grad(QPointF(0, 0), QPointF(1, 0));
148                                 grad.setCoordinateMode(grad.QGradient::ObjectBoundingMode);
149                                 grad.setColorAt(0.0f, QColor::fromRgbF(0.0f, 0.0f, 1.0f, 0.2f));
150                                 grad.setColorAt(play_progress, QColor::fromRgbF(0.0f, 0.0f, 1.0f, 0.2f));
151                                 if (play_progress + 0.01f <= 1.0f) {
152                                         grad.setColorAt(play_progress + 0.01f, QColor::fromRgbF(0.0f, 0.0f, 1.0f, 0.0f));
153                                 }
154                                 return QBrush(grad);
155                         } else {
156                                 return QVariant();
157                         }
158                 } else {
159                         return QVariant();
160                 }
161         }
162
163         if (role != Qt::DisplayRole && role != Qt::EditRole)
164                 return QVariant();
165
166         switch (Column(column)) {
167         case Column::PLAYING:
168                 return (row == currently_playing_index) ? "→" : "";
169         case Column::IN:
170                 return QString::fromStdString(pts_to_string(clips[row].pts_in));
171         case Column::OUT:
172                 if (clips[row].pts_out >= 0) {
173                         return QString::fromStdString(pts_to_string(clips[row].pts_out));
174                 } else {
175                         return QVariant();
176                 }
177         case Column::DURATION:
178                 if (clips[row].pts_out >= 0) {
179                         return QString::fromStdString(duration_to_string(clips[row].pts_out - clips[row].pts_in));
180                 } else {
181                         return QVariant();
182                 }
183         case Column::CAMERA:
184                 return qlonglong(clips[row].stream_idx + 1);
185         case Column::DESCRIPTION:
186                 return QString::fromStdString(clips[row].descriptions[clips[row].stream_idx]);
187         case Column::FADE_TIME: {
188                 stringstream ss;
189                 ss.imbue(locale("C"));
190                 ss.precision(3);
191                 ss << fixed << clips[row].fade_time_seconds;
192                 return QString::fromStdString(ss.str());
193         }
194         default:
195                 return "";
196         }
197 }
198
199 QVariant ClipList::headerData(int section, Qt::Orientation orientation, int role) const
200 {
201         if (role != Qt::DisplayRole)
202                 return QVariant();
203         if (orientation != Qt::Horizontal)
204                 return QVariant();
205
206         switch (Column(section)) {
207         case Column::IN:
208                 return "In";
209         case Column::OUT:
210                 return "Out";
211         case Column::DURATION:
212                 return "Duration";
213         case Column::CAMERA_1:
214                 return "Camera 1";
215         case Column::CAMERA_2:
216                 return "Camera 2";
217         case Column::CAMERA_3:
218                 return "Camera 3";
219         case Column::CAMERA_4:
220                 return "Camera 4";
221         default:
222                 return "";
223         }
224 }
225
226 QVariant PlayList::headerData(int section, Qt::Orientation orientation, int role) const
227 {
228         if (role != Qt::DisplayRole)
229                 return QVariant();
230         if (orientation != Qt::Horizontal)
231                 return QVariant();
232
233         switch (Column(section)) {
234         case Column::PLAYING:
235                 return "";
236         case Column::IN:
237                 return "In";
238         case Column::OUT:
239                 return "Out";
240         case Column::DURATION:
241                 return "Duration";
242         case Column::CAMERA:
243                 return "Camera";
244         case Column::DESCRIPTION:
245                 return "Description";
246         case Column::FADE_TIME:
247                 return "Fade time";
248         default:
249                 return "";
250         }
251 }
252
253 Qt::ItemFlags ClipList::flags(const QModelIndex &index) const
254 {
255         if (!index.isValid())
256                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
257         const int row = index.row(), column = index.column();
258         if (size_t(row) >= clips.size())
259                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
260
261         switch (Column(column)) {
262         case Column::CAMERA_1:
263         case Column::CAMERA_2:
264         case Column::CAMERA_3:
265         case Column::CAMERA_4:
266                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
267         default:
268                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
269         }
270 }
271
272 Qt::ItemFlags PlayList::flags(const QModelIndex &index) const
273 {
274         if (!index.isValid())
275                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
276         const int row = index.row(), column = index.column();
277         if (size_t(row) >= clips.size())
278                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
279
280         switch (Column(column)) {
281         case Column::DESCRIPTION:
282         case Column::CAMERA:
283         case Column::FADE_TIME:
284                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
285                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
286         default:
287                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
288         }
289 }
290
291 bool ClipList::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::CAMERA_1:
303         case Column::CAMERA_2:
304         case Column::CAMERA_3:
305         case Column::CAMERA_4: {
306                 unsigned stream_idx = column - int(Column::CAMERA_1);
307                 clips[row].descriptions[stream_idx] = value.toString().toStdString();
308                 emit_data_changed(row);
309                 return true;
310         }
311         default:
312                 return false;
313         }
314 }
315
316 bool PlayList::setData(const QModelIndex &index, const QVariant &value, int role)
317 {
318         if (!index.isValid() || role != Qt::EditRole) {
319                 return false;
320         }
321
322         const int row = index.row(), column = index.column();
323         if (size_t(row) >= clips.size())
324                 return false;
325
326         switch (Column(column)) {
327         case Column::DESCRIPTION:
328                 clips[row].descriptions[clips[row].stream_idx] = value.toString().toStdString();
329                 emit_data_changed(row);
330                 return true;
331         case Column::CAMERA: {
332                 bool ok;
333                 int camera_idx = value.toInt(&ok);
334                 if (!ok || camera_idx < 1 || camera_idx > NUM_CAMERAS) {
335                         return false;
336                 }
337                 clips[row].stream_idx = camera_idx - 1;
338                 emit_data_changed(row);
339                 return true;
340         }
341         case Column::FADE_TIME: {
342                 bool ok;
343                 double val = value.toDouble(&ok);
344                 if (!ok || !(val >= 0.0)) {
345                         return false;
346                 }
347                 clips[row].fade_time_seconds = val;
348                 emit_data_changed(row);
349                 return true;
350         }
351         default:
352                 return false;
353         }
354 }
355
356 void ClipList::add_clip(const Clip &clip)
357 {
358         beginInsertRows(QModelIndex(), clips.size(), clips.size());
359         clips.push_back(clip);
360         endInsertRows();
361         emit any_content_changed();
362 }
363
364 void PlayList::add_clip(const Clip &clip)
365 {
366         beginInsertRows(QModelIndex(), clips.size(), clips.size());
367         clips.push_back(clip);
368         endInsertRows();
369         emit any_content_changed();
370 }
371
372 void PlayList::duplicate_clips(size_t first, size_t last)
373 {
374         beginInsertRows(QModelIndex(), first, last);
375         clips.insert(clips.begin() + first, clips.begin() + first, clips.begin() + last + 1);
376         endInsertRows();
377         emit any_content_changed();
378 }
379
380 void PlayList::erase_clips(size_t first, size_t last)
381 {
382         beginRemoveRows(QModelIndex(), first, last);
383         clips.erase(clips.begin() + first, clips.begin() + last + 1);
384         endRemoveRows();
385         emit any_content_changed();
386 }
387
388 void PlayList::move_clips(size_t first, size_t last, int delta)
389 {
390         if (delta == -1) {
391                 beginMoveRows(QModelIndex(), first, last, QModelIndex(), first - 1);
392                 rotate(clips.begin() + first - 1, clips.begin() + first, clips.begin() + last + 1);
393         } else {
394                 beginMoveRows(QModelIndex(), first, last, QModelIndex(), first + (last - first + 1) + 1);
395                 first = clips.size() - first - 1;
396                 last = clips.size() - last - 1;
397                 rotate(clips.rbegin() + last - 1, clips.rbegin() + last, clips.rbegin() + first + 1);
398         }
399         endMoveRows();
400         emit any_content_changed();
401 }
402
403 void ClipList::emit_data_changed(size_t row)
404 {
405         emit dataChanged(index(row, 0), index(row, int(Column::NUM_COLUMNS)));
406         emit any_content_changed();
407 }
408
409 void PlayList::emit_data_changed(size_t row)
410 {
411         emit dataChanged(index(row, 0), index(row, int(Column::NUM_COLUMNS)));
412         emit any_content_changed();
413 }
414
415 void PlayList::set_currently_playing(int index, double progress)
416 {
417         int old_index = currently_playing_index;
418         int column = int(Column::PLAYING);
419         if (index != old_index) {
420                 currently_playing_index = index;
421                 play_progress = progress;
422                 if (old_index != -1) {
423                         emit dataChanged(this->index(old_index, column), this->index(old_index, column));
424                 }
425                 if (index != -1) {
426                         emit dataChanged(this->index(index, column), this->index(index, column));
427                 }
428         } else if (index != -1 && fabs(progress - play_progress) > 1e-3) {
429                 play_progress = progress;
430                 emit dataChanged(this->index(index, column), this->index(index, column));
431         }
432 }
433
434 namespace {
435
436 Clip deserialize_clip(const ClipProto &clip_proto)
437 {
438         Clip clip;
439         clip.pts_in = clip_proto.pts_in();
440         clip.pts_out = clip_proto.pts_out();
441         for (int camera_idx = 0; camera_idx < min(clip_proto.description_size(), NUM_CAMERAS); ++camera_idx) {
442                 clip.descriptions[camera_idx] = clip_proto.description(camera_idx);
443         }
444         clip.stream_idx = clip_proto.stream_idx();
445         clip.fade_time_seconds = clip_proto.fade_time_seconds();
446         return clip;
447 }
448
449 void serialize_clip(const Clip &clip, ClipProto *clip_proto)
450 {
451         clip_proto->set_pts_in(clip.pts_in);
452         clip_proto->set_pts_out(clip.pts_out);
453         for (int camera_idx = 0; camera_idx < NUM_CAMERAS; ++camera_idx) {
454                 *clip_proto->add_description() = clip.descriptions[camera_idx];
455         }
456         clip_proto->set_stream_idx(clip.stream_idx);
457         clip_proto->set_fade_time_seconds(clip.fade_time_seconds);
458 }
459
460 }  // namespace
461
462 ClipList::ClipList(const ClipListProto &serialized)
463 {
464         for (const ClipProto &clip_proto : serialized.clip()) {
465                 clips.push_back(deserialize_clip(clip_proto));
466         }
467 }
468
469 ClipListProto ClipList::serialize() const
470 {
471         ClipListProto ret;
472         for (const Clip &clip : clips) {
473                 serialize_clip(clip, ret.add_clip());
474         }
475         return ret;
476 }
477
478 PlayList::PlayList(const ClipListProto &serialized)
479 {
480         for (const ClipProto &clip_proto : serialized.clip()) {
481                 clips.push_back(deserialize_clip(clip_proto));
482         }
483 }
484
485 ClipListProto PlayList::serialize() const
486 {
487         ClipListProto ret;
488         for (const Clip &clip : clips) {
489                 serialize_clip(clip, ret.add_clip());
490         }
491         return ret;
492 }