1 #include "mainwindow.h"
9 #include "ui_mainwindow.h"
13 string pts_to_string(int64_t pts)
15 int64_t t = lrint((pts / double(TIMEBASE)) * 1e3); // In milliseconds.
25 snprintf(buf, sizeof(buf), "%d:%02d:%02d.%03d", hour, min, sec, ms);
29 string duration_to_string(int64_t pts_diff)
31 int64_t t = lrint((pts_diff / double(TIMEBASE)) * 1e3); // In milliseconds.
39 snprintf(buf, sizeof(buf), "%d:%02d.%03d", min, sec, ms);
43 int ClipList::rowCount(const QModelIndex &parent) const {
44 if (parent.isValid()) return 0;
48 int PlayList::rowCount(const QModelIndex &parent) const {
49 if (parent.isValid()) return 0;
53 int ClipList::columnCount(const QModelIndex &parent) const {
54 if (parent.isValid()) return 0;
55 return int(Column::NUM_COLUMNS);
58 int PlayList::columnCount(const QModelIndex &parent) const {
59 if (parent.isValid()) return 0;
60 return int(Column::NUM_COLUMNS);
63 QVariant ClipList::data(const QModelIndex &parent, int role) const {
64 if (!parent.isValid())
66 const int row = parent.row(), column = parent.column();
67 if (size_t(row) >= clips.size())
70 if (role == Qt::TextAlignmentRole) {
71 switch (Column(column)) {
74 case Column::DURATION:
75 return Qt::AlignRight + Qt::AlignVCenter;
77 return Qt::AlignLeft + Qt::AlignVCenter;
81 if (role != Qt::DisplayRole && role != Qt::EditRole)
84 switch (Column(column)) {
86 return QString::fromStdString(pts_to_string(clips[row].pts_in));
88 if (clips[row].pts_out >= 0) {
89 return QString::fromStdString(pts_to_string(clips[row].pts_out));
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));
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]);
111 QVariant PlayList::data(const QModelIndex &parent, int role) const {
112 if (!parent.isValid())
114 const int row = parent.row(), column = parent.column();
115 if (size_t(row) >= clips.size())
118 if (role == Qt::TextAlignmentRole) {
119 switch (Column(column)) {
120 case Column::PLAYING:
121 return Qt::AlignCenter;
124 case Column::DURATION:
125 case Column::FADE_TIME:
126 return Qt::AlignRight + Qt::AlignVCenter;
128 return Qt::AlignCenter;
130 return Qt::AlignLeft + Qt::AlignVCenter;
133 if (role == Qt::BackgroundRole) {
134 if (Column(column) == Column::PLAYING) {
135 if (row == currently_playing_index) {
136 // This only really works well for the first column, for whatever odd Qt reason.
137 QLinearGradient grad(QPointF(0, 0), QPointF(1, 0));
138 grad.setCoordinateMode(grad.QGradient::ObjectBoundingMode);
139 grad.setColorAt(0.0f, QColor::fromRgbF(0.0f, 0.0f, 1.0f, 0.2f));
140 grad.setColorAt(play_progress, QColor::fromRgbF(0.0f, 0.0f, 1.0f, 0.2f));
141 if (play_progress + 0.01f <= 1.0f) {
142 grad.setColorAt(play_progress + 0.01f, QColor::fromRgbF(0.0f, 0.0f, 1.0f, 0.0f));
153 if (role != Qt::DisplayRole && role != Qt::EditRole)
156 switch (Column(column)) {
157 case Column::PLAYING:
158 return (row == currently_playing_index) ? "→" : "";
160 return QString::fromStdString(pts_to_string(clips[row].pts_in));
162 if (clips[row].pts_out >= 0) {
163 return QString::fromStdString(pts_to_string(clips[row].pts_out));
167 case Column::DURATION:
168 if (clips[row].pts_out >= 0) {
169 return QString::fromStdString(duration_to_string(clips[row].pts_out - clips[row].pts_in));
174 return qlonglong(clips[row].stream_idx + 1);
175 case Column::DESCRIPTION:
176 return QString::fromStdString(clips[row].descriptions[clips[row].stream_idx]);
177 case Column::FADE_TIME: {
179 ss.imbue(locale("C"));
181 ss << fixed << clips[row].fade_time_seconds;
182 return QString::fromStdString(ss.str());
189 QVariant ClipList::headerData(int section, Qt::Orientation orientation, int role) const {
190 if (role != Qt::DisplayRole)
192 if (orientation != Qt::Horizontal)
195 switch (Column(section)) {
200 case Column::DURATION:
202 case Column::CAMERA_1:
204 case Column::CAMERA_2:
206 case Column::CAMERA_3:
208 case Column::CAMERA_4:
215 QVariant PlayList::headerData(int section, Qt::Orientation orientation, int role) const {
216 if (role != Qt::DisplayRole)
218 if (orientation != Qt::Horizontal)
221 switch (Column(section)) {
222 case Column::PLAYING:
228 case Column::DURATION:
232 case Column::DESCRIPTION:
233 return "Description";
234 case Column::FADE_TIME:
241 Qt::ItemFlags ClipList::flags(const QModelIndex &index) const
243 if (!index.isValid())
244 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
245 const int row = index.row(), column = index.column();
246 if (size_t(row) >= clips.size())
247 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
249 switch (Column(column)) {
250 case Column::CAMERA_1:
251 case Column::CAMERA_2:
252 case Column::CAMERA_3:
253 case Column::CAMERA_4:
254 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
256 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
260 Qt::ItemFlags PlayList::flags(const QModelIndex &index) const
262 if (!index.isValid())
263 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
264 const int row = index.row(), column = index.column();
265 if (size_t(row) >= clips.size())
266 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
268 switch (Column(column)) {
269 case Column::DESCRIPTION:
271 case Column::FADE_TIME:
272 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
273 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
275 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
279 bool ClipList::setData(const QModelIndex &index, const QVariant &value, int role)
281 if (!index.isValid() || role != Qt::EditRole) {
285 const int row = index.row(), column = index.column();
286 if (size_t(row) >= clips.size())
289 switch (Column(column)) {
290 case Column::CAMERA_1:
291 case Column::CAMERA_2:
292 case Column::CAMERA_3:
293 case Column::CAMERA_4: {
294 unsigned stream_idx = column - int(Column::CAMERA_1);
295 clips[row].descriptions[stream_idx] = value.toString().toStdString();
296 emit_data_changed(row);
304 bool PlayList::setData(const QModelIndex &index, const QVariant &value, int role)
306 if (!index.isValid() || role != Qt::EditRole) {
310 const int row = index.row(), column = index.column();
311 if (size_t(row) >= clips.size())
314 switch (Column(column)) {
315 case Column::DESCRIPTION:
316 clips[row].descriptions[clips[row].stream_idx] = value.toString().toStdString();
317 emit_data_changed(row);
319 case Column::CAMERA: {
321 int camera_idx = value.toInt(&ok);
322 if (!ok || camera_idx < 1 || camera_idx > NUM_CAMERAS) {
325 clips[row].stream_idx = camera_idx - 1;
326 emit_data_changed(row);
329 case Column::FADE_TIME: {
331 double val = value.toDouble(&ok);
332 if (!ok || !(val >= 0.0)) {
335 clips[row].fade_time_seconds = val;
336 emit_data_changed(row);
344 void ClipList::add_clip(const Clip &clip)
346 beginInsertRows(QModelIndex(), clips.size(), clips.size());
347 clips.push_back(clip);
349 emit any_content_changed();
352 void PlayList::add_clip(const Clip &clip)
354 beginInsertRows(QModelIndex(), clips.size(), clips.size());
355 clips.push_back(clip);
357 emit any_content_changed();
360 void PlayList::duplicate_clips(size_t first, size_t last)
362 beginInsertRows(QModelIndex(), first, last);
363 clips.insert(clips.begin() + first, clips.begin() + first, clips.begin() + last + 1);
365 emit any_content_changed();
368 void PlayList::erase_clips(size_t first, size_t last)
370 beginRemoveRows(QModelIndex(), first, last);
371 clips.erase(clips.begin() + first, clips.begin() + last + 1);
373 emit any_content_changed();
376 void PlayList::move_clips(size_t first, size_t last, int delta)
379 beginMoveRows(QModelIndex(), first, last, QModelIndex(), first - 1);
380 rotate(clips.begin() + first - 1, clips.begin() + first, clips.begin() + last + 1);
382 beginMoveRows(QModelIndex(), first, last, QModelIndex(), first + (last-first+1) + 1);
383 first = clips.size() - first - 1;
384 last = clips.size() - last - 1;
385 rotate(clips.rbegin() + last - 1, clips.rbegin() + last, clips.rbegin() + first + 1);
388 emit any_content_changed();
391 void ClipList::emit_data_changed(size_t row)
393 emit dataChanged(index(row, 0), index(row, int(Column::NUM_COLUMNS)));
394 emit any_content_changed();
397 void PlayList::emit_data_changed(size_t row)
399 emit dataChanged(index(row, 0), index(row, int(Column::NUM_COLUMNS)));
400 emit any_content_changed();
403 void PlayList::set_currently_playing(int index, double progress)
405 int old_index = currently_playing_index;
406 int column = int(Column::PLAYING);
407 if (index != old_index) {
408 currently_playing_index = index;
409 play_progress = progress;
410 if (old_index != -1) {
411 emit dataChanged(this->index(old_index, column), this->index(old_index, column));
414 emit dataChanged(this->index(index, column), this->index(index, column));
416 } else if (index != -1 && fabs(progress - play_progress) > 1e-3) {
417 play_progress = progress;
418 emit dataChanged(this->index(index, column), this->index(index, column));
424 Clip deserialize_clip(const ClipProto &clip_proto)
427 clip.pts_in = clip_proto.pts_in();
428 clip.pts_out = clip_proto.pts_out();
429 for (int camera_idx = 0; camera_idx < min(clip_proto.description_size(), NUM_CAMERAS); ++camera_idx) {
430 clip.descriptions[camera_idx] = clip_proto.description(camera_idx);
432 clip.stream_idx = clip_proto.stream_idx();
433 clip.fade_time_seconds = clip_proto.fade_time_seconds();
437 void serialize_clip(const Clip &clip, ClipProto *clip_proto)
439 clip_proto->set_pts_in(clip.pts_in);
440 clip_proto->set_pts_out(clip.pts_out);
441 for (int camera_idx = 0; camera_idx < NUM_CAMERAS; ++camera_idx) {
442 *clip_proto->add_description() = clip.descriptions[camera_idx];
444 clip_proto->set_stream_idx(clip.stream_idx);
445 clip_proto->set_fade_time_seconds(clip.fade_time_seconds);
450 ClipList::ClipList(const ClipListProto &serialized)
452 for (const ClipProto &clip_proto : serialized.clip()) {
453 clips.push_back(deserialize_clip(clip_proto));
457 ClipListProto ClipList::serialize() const
460 for (const Clip &clip : clips) {
461 serialize_clip(clip, ret.add_clip());
466 PlayList::PlayList(const ClipListProto &serialized)
468 for (const ClipProto &clip_proto : serialized.clip()) {
469 clips.push_back(deserialize_clip(clip_proto));
473 ClipListProto PlayList::serialize() const
476 for (const Clip &clip : clips) {
477 serialize_clip(clip, ret.add_clip());