3 #include "mainwindow.h"
5 #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
50 int PlayList::rowCount(const QModelIndex &parent) const
57 int ClipList::columnCount(const QModelIndex &parent) const
61 return int(Column::NUM_COLUMNS);
64 int PlayList::columnCount(const QModelIndex &parent) const
68 return int(Column::NUM_COLUMNS);
71 QVariant ClipList::data(const QModelIndex &parent, int role) const
73 if (!parent.isValid())
75 const int row = parent.row(), column = parent.column();
76 if (size_t(row) >= clips.size())
79 if (role == Qt::TextAlignmentRole) {
80 switch (Column(column)) {
83 case Column::DURATION:
84 return Qt::AlignRight + Qt::AlignVCenter;
86 return Qt::AlignLeft + Qt::AlignVCenter;
90 if (role != Qt::DisplayRole && role != Qt::EditRole)
93 switch (Column(column)) {
95 return QString::fromStdString(pts_to_string(clips[row].pts_in));
97 if (clips[row].pts_out >= 0) {
98 return QString::fromStdString(pts_to_string(clips[row].pts_out));
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));
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]);
120 QVariant PlayList::data(const QModelIndex &parent, int role) const
122 if (!parent.isValid())
124 const int row = parent.row(), column = parent.column();
125 if (size_t(row) >= clips.size())
128 if (role == Qt::TextAlignmentRole) {
129 switch (Column(column)) {
130 case Column::PLAYING:
131 return Qt::AlignCenter;
134 case Column::DURATION:
135 case Column::FADE_TIME:
136 return Qt::AlignRight + Qt::AlignVCenter;
138 return Qt::AlignCenter;
140 return Qt::AlignLeft + Qt::AlignVCenter;
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));
163 if (role != Qt::DisplayRole && role != Qt::EditRole)
166 switch (Column(column)) {
167 case Column::PLAYING:
168 return (row == currently_playing_index) ? "→" : "";
170 return QString::fromStdString(pts_to_string(clips[row].pts_in));
172 if (clips[row].pts_out >= 0) {
173 return QString::fromStdString(pts_to_string(clips[row].pts_out));
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));
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: {
189 ss.imbue(locale("C"));
191 ss << fixed << clips[row].fade_time_seconds;
192 return QString::fromStdString(ss.str());
199 QVariant ClipList::headerData(int section, Qt::Orientation orientation, int role) const
201 if (role != Qt::DisplayRole)
203 if (orientation != Qt::Horizontal)
206 switch (Column(section)) {
211 case Column::DURATION:
213 case Column::CAMERA_1:
215 case Column::CAMERA_2:
217 case Column::CAMERA_3:
219 case Column::CAMERA_4:
226 QVariant PlayList::headerData(int section, Qt::Orientation orientation, int role) const
228 if (role != Qt::DisplayRole)
230 if (orientation != Qt::Horizontal)
233 switch (Column(section)) {
234 case Column::PLAYING:
240 case Column::DURATION:
244 case Column::DESCRIPTION:
245 return "Description";
246 case Column::FADE_TIME:
253 Qt::ItemFlags ClipList::flags(const QModelIndex &index) const
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;
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;
268 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
272 Qt::ItemFlags PlayList::flags(const QModelIndex &index) const
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;
280 switch (Column(column)) {
281 case Column::DESCRIPTION:
283 case Column::FADE_TIME:
284 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
285 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
287 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
291 bool ClipList::setData(const QModelIndex &index, const QVariant &value, int role)
293 if (!index.isValid() || role != Qt::EditRole) {
297 const int row = index.row(), column = index.column();
298 if (size_t(row) >= clips.size())
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);
316 bool PlayList::setData(const QModelIndex &index, const QVariant &value, int role)
318 if (!index.isValid() || role != Qt::EditRole) {
322 const int row = index.row(), column = index.column();
323 if (size_t(row) >= clips.size())
326 switch (Column(column)) {
327 case Column::DESCRIPTION:
328 clips[row].descriptions[clips[row].stream_idx] = value.toString().toStdString();
329 emit_data_changed(row);
331 case Column::CAMERA: {
333 int camera_idx = value.toInt(&ok);
334 if (!ok || camera_idx < 1 || camera_idx > NUM_CAMERAS) {
337 clips[row].stream_idx = camera_idx - 1;
338 emit_data_changed(row);
341 case Column::FADE_TIME: {
343 double val = value.toDouble(&ok);
344 if (!ok || !(val >= 0.0)) {
347 clips[row].fade_time_seconds = val;
348 emit_data_changed(row);
356 void ClipList::add_clip(const Clip &clip)
358 beginInsertRows(QModelIndex(), clips.size(), clips.size());
359 clips.push_back(clip);
361 emit any_content_changed();
364 void PlayList::add_clip(const Clip &clip)
366 beginInsertRows(QModelIndex(), clips.size(), clips.size());
367 clips.push_back(clip);
369 emit any_content_changed();
372 void PlayList::duplicate_clips(size_t first, size_t last)
374 beginInsertRows(QModelIndex(), first, last);
375 clips.insert(clips.begin() + first, clips.begin() + first, clips.begin() + last + 1);
377 emit any_content_changed();
380 void PlayList::erase_clips(size_t first, size_t last)
382 beginRemoveRows(QModelIndex(), first, last);
383 clips.erase(clips.begin() + first, clips.begin() + last + 1);
385 emit any_content_changed();
388 void PlayList::move_clips(size_t first, size_t last, int delta)
391 beginMoveRows(QModelIndex(), first, last, QModelIndex(), first - 1);
392 rotate(clips.begin() + first - 1, clips.begin() + first, clips.begin() + last + 1);
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);
400 emit any_content_changed();
403 void ClipList::emit_data_changed(size_t row)
405 emit dataChanged(index(row, 0), index(row, int(Column::NUM_COLUMNS)));
406 emit any_content_changed();
409 void PlayList::emit_data_changed(size_t row)
411 emit dataChanged(index(row, 0), index(row, int(Column::NUM_COLUMNS)));
412 emit any_content_changed();
415 void PlayList::set_currently_playing(int index, double progress)
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));
426 emit dataChanged(this->index(index, column), this->index(index, column));
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));
436 Clip deserialize_clip(const ClipProto &clip_proto)
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);
444 clip.stream_idx = clip_proto.stream_idx();
445 clip.fade_time_seconds = clip_proto.fade_time_seconds();
449 void serialize_clip(const Clip &clip, ClipProto *clip_proto)
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];
456 clip_proto->set_stream_idx(clip.stream_idx);
457 clip_proto->set_fade_time_seconds(clip.fade_time_seconds);
462 ClipList::ClipList(const ClipListProto &serialized)
464 for (const ClipProto &clip_proto : serialized.clip()) {
465 clips.push_back(deserialize_clip(clip_proto));
469 ClipListProto ClipList::serialize() const
472 for (const Clip &clip : clips) {
473 serialize_clip(clip, ret.add_clip());
478 PlayList::PlayList(const ClipListProto &serialized)
480 for (const ClipProto &clip_proto : serialized.clip()) {
481 clips.push_back(deserialize_clip(clip_proto));
485 ClipListProto PlayList::serialize() const
488 for (const Clip &clip : clips) {
489 serialize_clip(clip, ret.add_clip());