MuseScore/mscore/scoreaccessibility.cpp
2019-04-17 22:17:47 +08:00

252 lines
9.3 KiB
C++

#include <QMainWindow>
#include <QWidget>
#include "scoreaccessibility.h"
#include "musescore.h"
#include "libmscore/segment.h"
#include "libmscore/timesig.h"
#include "libmscore/score.h"
#include "libmscore/measure.h"
#include "libmscore/spanner.h"
#include "libmscore/sig.h"
#include "inspector/inspector.h"
#include "selectionwindow.h"
#include "playpanel.h"
#include "synthcontrol.h"
#include "mixer.h"
#include "drumroll.h"
#include "pianoroll.h"
namespace Ms{
//---------------------------------------------------------
// AccessibleScoreView
//---------------------------------------------------------
AccessibleScoreView::AccessibleScoreView(ScoreView* scView)
: QAccessibleWidget(scView)
{
s = scView;
}
int AccessibleScoreView::childCount() const
{
return 0;
}
QAccessibleInterface* AccessibleScoreView::child(int /*index*/) const
{
return 0;
}
QAccessibleInterface* AccessibleScoreView::parent() const
{
return QAccessibleWidget::parent();
}
QRect AccessibleScoreView::rect() const
{
return s->rect();
}
QAccessible::Role AccessibleScoreView::role() const
{
return QAccessible::NoRole;
}
QString AccessibleScoreView::text(QAccessible::Text t) const
{
switch (t) {
case QAccessible::Name:
//TODO return tr("Score %1").arg(s->score()->name());
return "Score ???";
case QAccessible::Value:
return s->score()->accessibleInfo();
default:
return QString();
}
}
QWindow* AccessibleScoreView::window() const {
return qApp->focusWindow();
}
QAccessibleInterface* AccessibleScoreView::ScoreViewFactory(const QString &classname, QObject *object)
{
QAccessibleInterface *iface = 0;
if (classname == QLatin1String("Ms::ScoreView") && object && object->isWidgetType()){
// qDebug("Creating interface for ScoreView object");
iface = static_cast<QAccessibleInterface*>(new AccessibleScoreView(static_cast<ScoreView*>(object)));
}
return iface;
}
ScoreAccessibility* ScoreAccessibility::inst = 0;
ScoreAccessibility::ScoreAccessibility(QMainWindow* mainWindow) : QObject(mainWindow)
{
this->mainWindow = mainWindow;
statusBarLabel = new QLabel(mainWindow->statusBar());
mainWindow->statusBar()->addWidget(statusBarLabel);
}
void ScoreAccessibility::createInstance(QMainWindow* mainWindow)
{
if (!inst) {
inst = new ScoreAccessibility(mainWindow);
}
}
ScoreAccessibility::~ScoreAccessibility()
{
}
void ScoreAccessibility::clearAccessibilityInfo()
{
statusBarLabel->setText("");
MuseScoreView* view = static_cast<MuseScore*>(mainWindow)->currentScoreView();
if (view)
view->score()->setAccessibleInfo(tr("No selection"));
}
void ScoreAccessibility::currentInfoChanged()
{
clearAccessibilityInfo();
ScoreView* scoreView = static_cast<MuseScore*>(mainWindow)->currentScoreView();
Score* score = scoreView->score();
if (score->selection().isSingle()) {
Element* e = score->selection().element();
if (!e) {
return;
}
Element* el = e->isSpannerSegment() ? static_cast<SpannerSegment*>(e)->spanner() : e;
QString barsAndBeats = "";
if (el->isSpanner()){
Spanner* s = static_cast<Spanner*>(el);
std::pair<int, float> bar_beat = barbeat(s->startSegment());
barsAndBeats += tr("Start Measure: %1; Start Beat: %2").arg(QString::number(bar_beat.first)).arg(QString::number(bar_beat.second));
Segment* seg = s->endSegment();
if(!seg)
seg = score->lastSegment()->prev1MM(SegmentType::ChordRest);
if (seg->tick() != score->lastSegment()->prev1MM(SegmentType::ChordRest)->tick() &&
s->type() != ElementType::SLUR &&
s->type() != ElementType::TIE )
seg = seg->prev1MM(SegmentType::ChordRest);
bar_beat = barbeat(seg);
barsAndBeats += "; " + tr("End Measure: %1; End Beat: %2").arg(QString::number(bar_beat.first)).arg(QString::number(bar_beat.second));
}
else {
std::pair<int, float>bar_beat = barbeat(el);
if (bar_beat.first) {
barsAndBeats += " " + tr("Measure: %1").arg(QString::number(bar_beat.first));
if (bar_beat.second)
barsAndBeats += "; " + tr("Beat: %1").arg(QString::number(bar_beat.second));
}
}
QString rez = e->accessibleInfo();
if (!barsAndBeats.isEmpty())
rez += "; " + barsAndBeats;
QString staff = "";
if (e->staffIdx() + 1) {
staff = tr("Staff %1").arg(QString::number(e->staffIdx() + 1));
rez = QString("%1; %2").arg(rez).arg(staff);
}
statusBarLabel->setText(rez);
QString screenReaderRez = QString("%1%2 %3 %4").arg(e->screenReaderInfo()).arg(barsAndBeats).arg(staff).arg(e->accessibleExtraInfo());
score->setAccessibleInfo(screenReaderRez);
}
else if (score->selection().isRange()) {
QString barsAndBeats = "";
std::pair<int, float> bar_beat;
bar_beat = barbeat(score->selection().startSegment());
barsAndBeats += " " + tr("Start Measure: %1; Start Beat: %2").arg(QString::number(bar_beat.first)).arg(QString::number(bar_beat.second));
Segment* endSegment = score->selection().endSegment();
if (!endSegment)
endSegment = score->lastSegment();
else
endSegment = endSegment->prev1MM();
bar_beat = barbeat(endSegment);
barsAndBeats += " " + tr("End Measure: %1; End Beat: %2").arg(QString::number(bar_beat.first)).arg(QString::number(bar_beat.second));
statusBarLabel->setText(tr("Range Selection") + barsAndBeats);
score->setAccessibleInfo(tr("Range Selection") + barsAndBeats);
}
else if (score->selection().isList()) {
statusBarLabel->setText(tr("List Selection"));
score->setAccessibleInfo(tr("List Selection"));
}
}
ScoreAccessibility* ScoreAccessibility::instance()
{
return inst;
}
void ScoreAccessibility::updateAccessibilityInfo()
{
ScoreView* w = static_cast<MuseScore*>(mainWindow)->currentScoreView();
if (!w) return;
currentInfoChanged();
//getInspector->isAncestorOf is used so that inspector and search dialog don't loose focus
//when this method is called
//TODO: create a class to manage focus and replace this massive if
if ( (qApp->focusWidget() != w) &&
!mscore->inspector()->isAncestorOf(qApp->focusWidget()) &&
!(mscore->searchDialog() && mscore->searchDialog()->isAncestorOf(qApp->focusWidget())) &&
!(mscore->getSelectionWindow() && mscore->getSelectionWindow()->isAncestorOf(qApp->focusWidget())) &&
!(mscore->getPlayPanel() && mscore->getPlayPanel()->isAncestorOf(qApp->focusWidget())) &&
!(mscore->getSynthControl() && mscore->getSynthControl()->isAncestorOf(qApp->focusWidget())) &&
!(mscore->getMixer() && mscore->getMixer()->isAncestorOf(qApp->focusWidget())) &&
!(mscore->searchDialog() && mscore->searchDialog()->isAncestorOf(qApp->focusWidget())) &&
!(mscore->getDrumrollEditor() && mscore->getDrumrollEditor()->isAncestorOf(qApp->focusWidget())) &&
!(mscore->getPianorollEditor() && mscore->getPianorollEditor()->isAncestorOf(qApp->focusWidget()))) {
mscore->activateWindow();
w->setFocus();
}
QObject* obj = static_cast<QObject*>(w);
QAccessibleValueChangeEvent ev(obj, w->score()->accessibleInfo());
QAccessible::updateAccessibility(&ev);
}
std::pair<int, float> ScoreAccessibility::barbeat(Element *e)
{
if (!e) {
return std::pair<int, float>(0, 0.0F);
}
int bar = 0;
int beat = 0;
int ticks = 0;
TimeSigMap* tsm = e->score()->sigmap();
Element* p = e;
int ticksB = ticks_beat(tsm->timesig(0).timesig().denominator());
while(p && p->type() != ElementType::SEGMENT && p->type() != ElementType::MEASURE)
p = p->parent();
if (!p) {
return std::pair<int, float>(0, 0.0F);
}
else if (p->type() == ElementType::SEGMENT) {
Segment* seg = static_cast<Segment*>(p);
tsm->tickValues(seg->tick().ticks(), &bar, &beat, &ticks);
ticksB = ticks_beat(tsm->timesig(seg->tick().ticks()).timesig().denominator());
}
else if (p->type() == ElementType::MEASURE) {
Measure* m = static_cast<Measure*>(p);
bar = m->no();
beat = -1;
ticks = 0;
}
return pair<int,float>(bar + 1, beat + 1 + ticks / static_cast<float>(ticksB));
}
}