]> git.sesse.net Git - nageru/blob - clip_list.cpp
Don't set QGradient stops after 1.0.
[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                                 if (play_progress + 0.01f <= 1.0f) {
141                                         grad.setColorAt(play_progress + 0.01f, QColor::fromRgbF(0.0f, 0.0f, 1.0f, 0.0f));
142                                 }
143                                 return QBrush(grad);
144                         } else {
145                                 return QVariant();
146                         }
147                 } else {
148                         return QVariant();
149                 }
150         }
151
152         if (role != Qt::DisplayRole && role != Qt::EditRole)
153                 return QVariant();
154
155         switch (Column(column)) {
156         case Column::PLAYING:
157                 return (row == currently_playing_index) ? "→" : "";
158         case Column::IN:
159                 return QString::fromStdString(pts_to_string(clips[row].pts_in));
160         case Column::OUT:
161                 if (clips[row].pts_out >= 0) {
162                         return QString::fromStdString(pts_to_string(clips[row].pts_out));
163                 } else {
164                         return QVariant();
165                 }
166         case Column::DURATION:
167                 if (clips[row].pts_out >= 0) {
168                         return QString::fromStdString(duration_to_string(clips[row].pts_out - clips[row].pts_in));
169                 } else {
170                         return QVariant();
171                 }
172         case Column::CAMERA:
173                 return qlonglong(clips[row].stream_idx + 1);
174         case Column::DESCRIPTION:
175                 return QString::fromStdString(clips[row].descriptions[clips[row].stream_idx]);
176         default:
177                 return "";
178         }
179 }
180
181 QVariant ClipList::headerData(int section, Qt::Orientation orientation, int role) const {
182         if (role != Qt::DisplayRole)
183                 return QVariant();
184         if (orientation != Qt::Horizontal)
185                 return QVariant();
186
187         switch (Column(section)) {
188         case Column::IN:
189                 return "In";
190         case Column::OUT:
191                 return "Out";
192         case Column::DURATION:
193                 return "Duration";
194         case Column::CAMERA_1:
195                 return "Camera 1";
196         case Column::CAMERA_2:
197                 return "Camera 2";
198         case Column::CAMERA_3:
199                 return "Camera 3";
200         case Column::CAMERA_4:
201                 return "Camera 4";
202         default:
203                 return "";
204         }
205 }
206
207 QVariant PlayList::headerData(int section, Qt::Orientation orientation, int role) const {
208         if (role != Qt::DisplayRole)
209                 return QVariant();
210         if (orientation != Qt::Horizontal)
211                 return QVariant();
212
213         switch (Column(section)) {
214         case Column::PLAYING:
215                 return "";
216         case Column::IN:
217                 return "In";
218         case Column::OUT:
219                 return "Out";
220         case Column::DURATION:
221                 return "Duration";
222         case Column::CAMERA:
223                 return "Camera";
224         case Column::DESCRIPTION:
225                 return "Description";
226         default:
227                 return "";
228         }
229 }
230
231 Qt::ItemFlags ClipList::flags(const QModelIndex &index) const
232 {
233         if (!index.isValid())
234                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
235         const int row = index.row(), column = index.column();
236         if (size_t(row) >= clips.size())
237                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
238
239         switch (Column(column)) {
240         case Column::CAMERA_1:
241         case Column::CAMERA_2:
242         case Column::CAMERA_3:
243         case Column::CAMERA_4:
244                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
245         default:
246                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
247         }
248 }
249
250 Qt::ItemFlags PlayList::flags(const QModelIndex &index) const
251 {
252         if (!index.isValid())
253                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
254         const int row = index.row(), column = index.column();
255         if (size_t(row) >= clips.size())
256                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
257
258         switch (Column(column)) {
259         case Column::DESCRIPTION:
260         case Column::CAMERA:
261                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
262                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
263         default:
264                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
265         }
266 }
267
268 bool ClipList::setData(const QModelIndex &index, const QVariant &value, int role)
269 {
270         if (!index.isValid() || role != Qt::EditRole) {
271                 return false;
272         }
273
274         const int row = index.row(), column = index.column();
275         if (size_t(row) >= clips.size())
276                 return false;
277
278         switch (Column(column)) {
279         case Column::CAMERA_1:
280         case Column::CAMERA_2:
281         case Column::CAMERA_3:
282         case Column::CAMERA_4: {
283                 unsigned stream_idx = column - int(Column::CAMERA_1);
284                 clips[row].descriptions[stream_idx] = value.toString().toStdString();
285                 emit_data_changed(row);
286                 return true;
287         }
288         default:
289                 return false;
290         }
291 }
292
293 bool PlayList::setData(const QModelIndex &index, const QVariant &value, int role)
294 {
295         if (!index.isValid() || role != Qt::EditRole) {
296                 return false;
297         }
298
299         const int row = index.row(), column = index.column();
300         if (size_t(row) >= clips.size())
301                 return false;
302
303         switch (Column(column)) {
304         case Column::DESCRIPTION:
305                 clips[row].descriptions[clips[row].stream_idx] = value.toString().toStdString();
306                 emit_data_changed(row);
307                 return true;
308         case Column::CAMERA: {
309                 bool ok;
310                 int camera_idx = value.toInt(&ok);
311                 if (!ok || camera_idx < 1 || camera_idx > NUM_CAMERAS) {
312                         return false;
313                 }
314                 clips[row].stream_idx = camera_idx - 1;
315                 emit_data_changed(row);
316                 return true;
317         }
318         default:
319                 return false;
320         }
321 }
322
323 void ClipList::add_clip(const Clip &clip)
324 {
325         beginInsertRows(QModelIndex(), clips.size(), clips.size());
326         clips.push_back(clip);
327         endInsertRows();
328 }
329
330 void PlayList::add_clip(const Clip &clip)
331 {
332         beginInsertRows(QModelIndex(), clips.size(), clips.size());
333         clips.push_back(clip);
334         endInsertRows();
335 }
336
337 void PlayList::duplicate_clips(size_t first, size_t last)
338 {
339         beginInsertRows(QModelIndex(), first, last);
340         clips.insert(clips.begin() + first, clips.begin() + first, clips.begin() + last + 1);
341         endInsertRows();
342 }
343
344 void PlayList::erase_clips(size_t first, size_t last)
345 {
346         beginRemoveRows(QModelIndex(), first, last);
347         clips.erase(clips.begin() + first, clips.begin() + last + 1);
348         endRemoveRows();
349 }
350
351 void PlayList::move_clips(size_t first, size_t last, int delta)
352 {
353         if (delta == -1) {
354                 beginMoveRows(QModelIndex(), first, last, QModelIndex(), first - 1);
355                 rotate(clips.begin() + first - 1, clips.begin() + first, clips.begin() + last + 1);
356         } else {
357                 beginMoveRows(QModelIndex(), first, last, QModelIndex(), first + (last-first+1) + 1);
358                 first = clips.size() - first - 1;
359                 last = clips.size() - last - 1;
360                 rotate(clips.rbegin() + last - 1, clips.rbegin() + last, clips.rbegin() + first + 1);
361         }
362         endMoveRows();
363 }
364
365 void ClipList::emit_data_changed(size_t row)
366 {
367         emit dataChanged(index(row, 0), index(row, int(Column::NUM_COLUMNS)));
368 }
369
370 void PlayList::emit_data_changed(size_t row)
371 {
372         emit dataChanged(index(row, 0), index(row, int(Column::NUM_COLUMNS)));
373 }
374
375 void PlayList::set_currently_playing(int index, double progress)
376 {
377         int old_index = currently_playing_index;
378         int column = int(Column::PLAYING);
379         if (index != old_index) {
380                 currently_playing_index = index;
381                 play_progress = progress;
382                 if (old_index != -1) {
383                         emit dataChanged(this->index(old_index, column), this->index(old_index, column));
384                 }
385                 if (index != -1) {
386                         emit dataChanged(this->index(index, column), this->index(index, column));
387                 }
388         } else if (index != -1 && fabs(progress - play_progress) > 1e-3) {
389                 play_progress = progress;
390                 emit dataChanged(this->index(index, column), this->index(index, column));
391         }
392 }