Changeset View
Changeset View
Standalone View
Standalone View
kcm/output_model.cpp
Show First 20 Lines • Show All 59 Lines • ▼ Show 20 Line(s) | 39 | { | |||
---|---|---|---|---|---|
60 | case ScaleRole: | 60 | case ScaleRole: | ||
61 | return output->scale(); | 61 | return output->scale(); | ||
62 | case ResolutionIndexRole: | 62 | case ResolutionIndexRole: | ||
63 | return resolutionIndex(output); | 63 | return resolutionIndex(output); | ||
64 | case ResolutionsRole: | 64 | case ResolutionsRole: | ||
65 | return resolutionsStrings(output); | 65 | return resolutionsStrings(output); | ||
66 | case RefreshRateIndexRole: | 66 | case RefreshRateIndexRole: | ||
67 | return refreshRateIndex(output); | 67 | return refreshRateIndex(output); | ||
68 | case ReplicationSourceModelRole: | ||||
69 | return replicationSourceModel(output); | ||||
70 | case ReplicationSourceIndexRole: | ||||
71 | return replicationSourceIndex(index.row(), output->replicationSource()); | ||||
72 | case ReplicasModelRole: | ||||
73 | return replicasModel(output); | ||||
68 | case RefreshRatesRole: | 74 | case RefreshRatesRole: | ||
69 | QVariantList ret; | 75 | QVariantList ret; | ||
70 | for (const auto rate : refreshRates(output)) { | 76 | for (const auto rate : refreshRates(output)) { | ||
71 | ret << i18n("%1 Hz", int(rate + 0.5)); | 77 | ret << i18n("%1 Hz", int(rate + 0.5)); | ||
72 | } | 78 | } | ||
73 | return ret; | 79 | return ret; | ||
74 | } | 80 | } | ||
75 | return QVariant(); | 81 | return QVariant(); | ||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Line(s) | 136 | case RefreshRateIndexRole: | |||
133 | } | 139 | } | ||
134 | break; | 140 | break; | ||
135 | case RotationRole: | 141 | case RotationRole: | ||
136 | if (value.canConvert<KScreen::Output::Rotation>()) { | 142 | if (value.canConvert<KScreen::Output::Rotation>()) { | ||
137 | return setRotation(index.row(), | 143 | return setRotation(index.row(), | ||
138 | value.value<KScreen::Output::Rotation>()); | 144 | value.value<KScreen::Output::Rotation>()); | ||
139 | } | 145 | } | ||
140 | break; | 146 | break; | ||
147 | case ReplicationSourceIndexRole: | ||||
148 | if (value.canConvert<int>()) { | ||||
149 | return setReplicationSourceIndex(index.row(), value.toInt() - 1); | ||||
150 | } | ||||
151 | break; | ||||
141 | case ScaleRole: | 152 | case ScaleRole: | ||
142 | bool ok; | 153 | bool ok; | ||
143 | const qreal scale = value.toReal(&ok); | 154 | const qreal scale = value.toReal(&ok); | ||
144 | if (ok && !qFuzzyCompare(output.ptr->scale(), scale)) { | 155 | if (ok && !qFuzzyCompare(output.ptr->scale(), scale)) { | ||
145 | output.ptr->setScale(scale); | 156 | output.ptr->setScale(scale); | ||
146 | Q_EMIT sizeChanged(); | 157 | Q_EMIT sizeChanged(); | ||
147 | Q_EMIT dataChanged(index, index, {role, SizeRole}); | 158 | Q_EMIT dataChanged(index, index, {role, SizeRole}); | ||
148 | return true; | 159 | return true; | ||
Show All 11 Lines | 166 | QHash<int, QByteArray> OutputModel::roleNames() const { | |||
160 | roles[PositionRole] = "position"; | 171 | roles[PositionRole] = "position"; | ||
161 | roles[NormalizedPositionRole] = "normalizedPosition"; | 172 | roles[NormalizedPositionRole] = "normalizedPosition"; | ||
162 | roles[RotationRole] = "rotation"; | 173 | roles[RotationRole] = "rotation"; | ||
163 | roles[ScaleRole] = "scale"; | 174 | roles[ScaleRole] = "scale"; | ||
164 | roles[ResolutionIndexRole] = "resolutionIndex"; | 175 | roles[ResolutionIndexRole] = "resolutionIndex"; | ||
165 | roles[ResolutionsRole] = "resolutions"; | 176 | roles[ResolutionsRole] = "resolutions"; | ||
166 | roles[RefreshRateIndexRole] = "refreshRateIndex"; | 177 | roles[RefreshRateIndexRole] = "refreshRateIndex"; | ||
167 | roles[RefreshRatesRole] = "refreshRates"; | 178 | roles[RefreshRatesRole] = "refreshRates"; | ||
179 | roles[ReplicationSourceModelRole] = "replicationSourceModel"; | ||||
180 | roles[ReplicationSourceIndexRole] = "replicationSourceIndex"; | ||||
181 | roles[ReplicasModelRole] = "replicasModel"; | ||||
168 | return roles; | 182 | return roles; | ||
169 | } | 183 | } | ||
170 | 184 | | |||
171 | void OutputModel::add(const KScreen::OutputPtr &output) | 185 | void OutputModel::add(const KScreen::OutputPtr &output) | ||
172 | { | 186 | { | ||
173 | const int insertPos = m_outputs.count(); | 187 | const int insertPos = m_outputs.count(); | ||
174 | Q_EMIT beginInsertRows(QModelIndex(), insertPos, insertPos); | 188 | Q_EMIT beginInsertRows(QModelIndex(), insertPos, insertPos); | ||
175 | 189 | | |||
Show All 18 Lines | |||||
194 | } | 208 | } | ||
195 | m_outputs.insert(i, Output(output, pos)); | 209 | m_outputs.insert(i, Output(output, pos)); | ||
196 | 210 | | |||
197 | connect(output.data(), &KScreen::Output::isPrimaryChanged, | 211 | connect(output.data(), &KScreen::Output::isPrimaryChanged, | ||
198 | this, [this, output](){ | 212 | this, [this, output](){ | ||
199 | roleChanged(output->id(), {PrimaryRole}); | 213 | roleChanged(output->id(), {PrimaryRole}); | ||
200 | }); | 214 | }); | ||
201 | Q_EMIT endInsertRows(); | 215 | Q_EMIT endInsertRows(); | ||
216 | | ||||
217 | // Update replications. | ||||
218 | for (int j = 0; j < m_outputs.size(); j++) { | ||||
219 | if (i == j) { | ||||
220 | continue; | ||||
221 | } | ||||
222 | QModelIndex index = createIndex(j, 0); | ||||
223 | // Calling this directly ignores possible optimization when the | ||||
224 | // refresh rate hasn't changed in fact. But that's ok. | ||||
225 | Q_EMIT dataChanged(index, index, {ReplicationSourceModelRole, | ||||
226 | ReplicationSourceIndexRole}); | ||||
227 | } | ||||
202 | } | 228 | } | ||
203 | 229 | | |||
204 | void OutputModel::remove(int outputId) | 230 | void OutputModel::remove(int outputId) | ||
205 | { | 231 | { | ||
206 | auto it = std::find_if(m_outputs.begin(), m_outputs.end(), | 232 | auto it = std::find_if(m_outputs.begin(), m_outputs.end(), | ||
207 | [outputId](const Output &output) { | 233 | [outputId](const Output &output) { | ||
208 | return output.ptr->id() == outputId; | 234 | return output.ptr->id() == outputId; | ||
209 | }); | 235 | }); | ||
▲ Show 20 Lines • Show All 205 Lines • ▼ Show 20 Line(s) | 438 | if (std::find_if(hits.begin(), hits.end(), | |||
415 | }) != hits.end()) { | 441 | }) != hits.end()) { | ||
416 | continue; | 442 | continue; | ||
417 | } | 443 | } | ||
418 | hits << rate; | 444 | hits << rate; | ||
419 | } | 445 | } | ||
420 | return hits; | 446 | return hits; | ||
421 | } | 447 | } | ||
422 | 448 | | |||
449 | QStringList OutputModel::replicationSourceModel(const KScreen::OutputPtr &output) const | ||||
450 | { | ||||
451 | QStringList ret = { i18n("None") }; | ||||
452 | | ||||
453 | for (const auto &out : m_outputs) { | ||||
454 | if (out.ptr->id() != output->id()) { | ||||
455 | if (out.ptr->replicationSource() == output->id()) { | ||||
456 | // 'output' is already source for replication, can't be replica itself | ||||
457 | return { i18n("Replicated by other output") }; | ||||
458 | } | ||||
459 | if (out.ptr->replicationSource()) { | ||||
460 | // This 'out' is a replica. Can't be a replication source. | ||||
461 | continue; | ||||
462 | } | ||||
463 | ret.append(out.ptr->name()); | ||||
464 | } | ||||
465 | } | ||||
466 | return ret; | ||||
467 | } | ||||
468 | | ||||
469 | bool OutputModel::setReplicationSourceIndex(int outputIndex, int sourceIndex) | ||||
470 | { | ||||
471 | if (outputIndex <= sourceIndex) { | ||||
472 | sourceIndex++; | ||||
473 | } | ||||
474 | if (sourceIndex >= m_outputs.count()) { | ||||
475 | return false; | ||||
476 | } | ||||
477 | | ||||
478 | Output &output = m_outputs[outputIndex]; | ||||
479 | const int oldSourceId = output.ptr->replicationSource(); | ||||
480 | | ||||
481 | if (sourceIndex < 0) { | ||||
482 | if (oldSourceId == 0) { | ||||
483 | // no change | ||||
484 | return false; | ||||
485 | } | ||||
486 | output.ptr->setReplicationSource(0); | ||||
487 | | ||||
488 | if (output.replicaReset.isNull()) { | ||||
489 | // KCM was closed in between. | ||||
490 | for (const Output &out : m_outputs) { | ||||
491 | if (out.ptr->id() == output.ptr->id()) { | ||||
492 | continue; | ||||
493 | } | ||||
494 | if (out.ptr->geometry().right() > output.ptr->pos().x()) { | ||||
495 | output.ptr->setPos(out.ptr->geometry().topRight()); | ||||
496 | } | ||||
497 | } | ||||
498 | } else { | ||||
499 | output.ptr->setPos(output.ptr->pos() - output.replicaReset); | ||||
500 | } | ||||
501 | } else { | ||||
502 | const int sourceId = m_outputs[sourceIndex].ptr->id(); | ||||
503 | if (oldSourceId == sourceId) { | ||||
504 | // no change | ||||
505 | return false; | ||||
506 | } | ||||
507 | output.ptr->setReplicationSource(sourceId); | ||||
508 | output.replicaReset = m_outputs[sourceIndex].ptr->pos() - output.ptr->pos(); | ||||
509 | output.ptr->setPos(m_outputs[sourceIndex].ptr->pos()); | ||||
510 | } | ||||
511 | | ||||
512 | reposition(); | ||||
513 | | ||||
514 | QModelIndex index = createIndex(outputIndex, 0); | ||||
515 | Q_EMIT dataChanged(index, index, {ReplicationSourceIndexRole}); | ||||
516 | | ||||
517 | if (oldSourceId != 0) { | ||||
518 | auto it = std::find_if(m_outputs.begin(), m_outputs.end(), | ||||
519 | [oldSourceId](const Output &out) { | ||||
520 | return out.ptr->id() == oldSourceId; | ||||
521 | }); | ||||
522 | if (it != m_outputs.end()) { | ||||
523 | QModelIndex index = createIndex(it - m_outputs.begin(), 0); | ||||
524 | Q_EMIT dataChanged(index, index, {ReplicationSourceModelRole, ReplicasModelRole}); | ||||
525 | } | ||||
526 | } | ||||
527 | if (sourceIndex >= 0) { | ||||
528 | QModelIndex index = createIndex(sourceIndex, 0); | ||||
529 | Q_EMIT dataChanged(index, index, {ReplicationSourceModelRole, ReplicasModelRole}); | ||||
530 | } | ||||
531 | return true; | ||||
532 | } | ||||
533 | | ||||
534 | int OutputModel::replicationSourceIndex(int outputIndex, int sourceId) const | ||||
535 | { | ||||
536 | for (int i = 0; i < m_outputs.size(); i++) { | ||||
537 | const Output &output = m_outputs[i]; | ||||
538 | if (output.ptr->id() == sourceId) { | ||||
539 | return i + (outputIndex > i ? 1 : 0); | ||||
540 | } | ||||
541 | } | ||||
542 | return 0; | ||||
543 | } | ||||
544 | | ||||
545 | QVariantList OutputModel::replicasModel(const KScreen::OutputPtr &output) const | ||||
546 | { | ||||
547 | QVariantList ret; | ||||
548 | for (int i = 0; i < m_outputs.size(); i++) { | ||||
549 | const Output &out = m_outputs[i]; | ||||
550 | if (out.ptr->id() != output->id()) { | ||||
551 | if (out.ptr->replicationSource() == output->id()) { | ||||
552 | ret << i; | ||||
553 | } | ||||
554 | } | ||||
555 | } | ||||
556 | return ret; | ||||
557 | } | ||||
558 | | ||||
423 | void OutputModel::roleChanged(int outputId, OutputRoles role) | 559 | void OutputModel::roleChanged(int outputId, OutputRoles role) | ||
424 | { | 560 | { | ||
425 | for (int i = 0; i < m_outputs.size(); i++) { | 561 | for (int i = 0; i < m_outputs.size(); i++) { | ||
426 | Output &output = m_outputs[i]; | 562 | Output &output = m_outputs[i]; | ||
427 | if (output.ptr->id() == outputId) { | 563 | if (output.ptr->id() == outputId) { | ||
428 | QModelIndex index = createIndex(i, 0); | 564 | QModelIndex index = createIndex(i, 0); | ||
429 | Q_EMIT dataChanged(index, index, {role}); | 565 | Q_EMIT dataChanged(index, index, {role}); | ||
430 | return; | 566 | return; | ||
431 | } | 567 | } | ||
432 | } | 568 | } | ||
433 | } | 569 | } | ||
434 | 570 | | |||
435 | bool OutputModel::positionable(const Output &output) const | 571 | bool OutputModel::positionable(const Output &output) const | ||
436 | { | 572 | { | ||
437 | return output.ptr->isEnabled(); | 573 | return output.ptr->isPositionable(); | ||
438 | } | 574 | } | ||
439 | 575 | | |||
440 | void OutputModel::reposition() | 576 | void OutputModel::reposition() | ||
441 | { | 577 | { | ||
442 | int x = 0; | 578 | int x = 0; | ||
443 | int y = 0; | 579 | int y = 0; | ||
444 | 580 | | |||
445 | // Find first valid output. | 581 | // Find first valid output. | ||
▲ Show 20 Lines • Show All 104 Lines • ▼ Show 20 Line(s) | 685 | if (i != j) { | |||
550 | beginMoveRows(QModelIndex(), j, j, QModelIndex(), i); | 686 | beginMoveRows(QModelIndex(), j, j, QModelIndex(), i); | ||
551 | m_outputs.remove(j); | 687 | m_outputs.remove(j); | ||
552 | m_outputs.insert(i, order[i]); | 688 | m_outputs.insert(i, order[i]); | ||
553 | endMoveRows(); | 689 | endMoveRows(); | ||
554 | } | 690 | } | ||
555 | break; | 691 | break; | ||
556 | } | 692 | } | ||
557 | } | 693 | } | ||
694 | | ||||
695 | // TODO: Could this be optimized by only outputs updating where replica indices changed? | ||||
696 | for (int i = 0; i < m_outputs.size(); i++) { | ||||
697 | QModelIndex index = createIndex(i, 0); | ||||
698 | Q_EMIT dataChanged(index, index, { ReplicasModelRole }); | ||||
699 | } | ||||
558 | } | 700 | } | ||
559 | 701 | | |||
560 | bool OutputModel::normalizePositions() | 702 | bool OutputModel::normalizePositions() | ||
561 | { | 703 | { | ||
562 | bool changed = false; | 704 | bool changed = false; | ||
563 | for (int i = 0; i < m_outputs.size(); i++) { | 705 | for (int i = 0; i < m_outputs.size(); i++) { | ||
564 | auto &output = m_outputs[i]; | 706 | auto &output = m_outputs[i]; | ||
565 | if (output.pos == output.ptr->pos()) { | 707 | if (output.pos == output.ptr->pos()) { | ||
▲ Show 20 Lines • Show All 162 Lines • Show Last 20 Lines |