diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 18:37:05 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 18:37:05 +0000 |
commit | 145364a8af6a1fec06556221e66d4b724a62fc9a (patch) | |
tree | 53bd71a544008c518034f208d64c932dc2883f50 /src/gui/editors/guitar | |
download | rosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.tar.gz rosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.zip |
Added old abandoned KDE3 version of the RoseGarden MIDI tool
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/rosegarden@1097595 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/gui/editors/guitar')
-rw-r--r-- | src/gui/editors/guitar/Chord.cpp | 113 | ||||
-rw-r--r-- | src/gui/editors/guitar/Chord.h | 106 | ||||
-rw-r--r-- | src/gui/editors/guitar/ChordMap.cpp | 223 | ||||
-rw-r--r-- | src/gui/editors/guitar/ChordMap.h | 87 | ||||
-rw-r--r-- | src/gui/editors/guitar/ChordXmlHandler.cpp | 154 | ||||
-rw-r--r-- | src/gui/editors/guitar/ChordXmlHandler.h | 78 | ||||
-rw-r--r-- | src/gui/editors/guitar/Fingering.cpp | 152 | ||||
-rw-r--r-- | src/gui/editors/guitar/Fingering.h | 95 | ||||
-rw-r--r-- | src/gui/editors/guitar/FingeringBox.cpp | 293 | ||||
-rw-r--r-- | src/gui/editors/guitar/FingeringBox.h | 106 | ||||
-rw-r--r-- | src/gui/editors/guitar/FingeringListBoxItem.cpp | 36 | ||||
-rw-r--r-- | src/gui/editors/guitar/FingeringListBoxItem.h | 46 | ||||
-rw-r--r-- | src/gui/editors/guitar/GuitarChordEditorDialog.cpp | 109 | ||||
-rw-r--r-- | src/gui/editors/guitar/GuitarChordEditorDialog.h | 67 | ||||
-rw-r--r-- | src/gui/editors/guitar/GuitarChordSelectorDialog.cpp | 475 | ||||
-rw-r--r-- | src/gui/editors/guitar/GuitarChordSelectorDialog.h | 120 | ||||
-rw-r--r-- | src/gui/editors/guitar/NoteSymbols.cpp | 486 | ||||
-rw-r--r-- | src/gui/editors/guitar/NoteSymbols.h | 192 |
18 files changed, 2938 insertions, 0 deletions
diff --git a/src/gui/editors/guitar/Chord.cpp b/src/gui/editors/guitar/Chord.cpp new file mode 100644 index 0000000..23efe7d --- /dev/null +++ b/src/gui/editors/guitar/Chord.cpp @@ -0,0 +1,113 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <[email protected]>, + Chris Cannam <[email protected]>, + Richard Bown <[email protected]> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "Chord.h" +#include "base/Event.h" + +#include <qstring.h> + +namespace Rosegarden +{ + +namespace Guitar +{ +const std::string Chord::EventType = "guitarchord"; +const short Chord::EventSubOrdering = -60; +const PropertyName Chord::RootPropertyName = "root"; +const PropertyName Chord::ExtPropertyName = "ext"; +const PropertyName Chord::FingeringPropertyName = "fingering"; + + +Chord::Chord() + : m_isUserChord(false) +{ +} + +Chord::Chord(const QString& root, const QString& ext) + : m_root(root), + m_ext(ext), + m_isUserChord(false) +{ + if (m_ext.isEmpty()) + m_ext = QString::null; +} + +Chord::Chord(const Event& e) + : m_isUserChord(false) +{ + std::string f; + bool ok; + + ok = e.get<String>(RootPropertyName, f); + if (ok) + m_root = f; + + ok = e.get<String>(ExtPropertyName, f); + if (ok) { + if (f.length() == 0) + m_ext = QString::null; + else + m_ext = f; + } + + ok = e.get<String>(FingeringPropertyName, f); + if (ok) { + QString qf(f); + QString errString; + + Fingering fingering = Fingering::parseFingering(qf, errString); + setFingering(fingering); + } +} + +Event* Chord::getAsEvent(timeT absoluteTime) const +{ + Event *e = new Event(EventType, absoluteTime, 0, EventSubOrdering); + e->set<String>(RootPropertyName, m_root); + e->set<String>(ExtPropertyName, m_ext); + e->set<String>(FingeringPropertyName, getFingering().toString()); + return e; +} + +const QRegExp Chord::ALT_BASS_REGEXP("/[A-G]"); + +bool operator<(const Chord& a, const Chord& b) +{ + if (a.m_root != b.m_root) { + return a.m_root < b.m_root; + } else if (a.m_ext != b.m_ext) { + if (a.m_ext.isEmpty()) // chords with no ext need to be stored first + return true; + if (b.m_ext.isEmpty()) + return false; + return a.m_ext < b.m_ext; + } else { + return a.m_fingering < b.m_fingering; + } + +} + +} + +} diff --git a/src/gui/editors/guitar/Chord.h b/src/gui/editors/guitar/Chord.h new file mode 100644 index 0000000..9e84cc3 --- /dev/null +++ b/src/gui/editors/guitar/Chord.h @@ -0,0 +1,106 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <[email protected]>, + Chris Cannam <[email protected]>, + Richard Bown <[email protected]> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _RG_CHORD_H_ +#define _RG_CHORD_H_ + +#include "Fingering.h" +#include "base/Event.h" +#include "misc/Debug.h" + +#include <vector> +#include <qstring.h> +#include <qregexp.h> + +namespace Rosegarden +{ + +class Event; + +namespace Guitar +{ + +class Chord +{ + friend bool operator<(const Chord&, const Chord&); + +public: + static const std::string EventType; + static const short EventSubOrdering; + static const PropertyName RootPropertyName; + static const PropertyName ExtPropertyName; + static const PropertyName FingeringPropertyName; + + Chord(); + Chord(const QString& root, const QString& ext = QString::null); + Chord(const Event&); + + Event* getAsEvent(timeT absoluteTime) const; + + bool isEmpty() const { return m_root.isEmpty(); } + bool operator!() const { return !m_root; } + + bool isUserChord() const { return m_isUserChord; } + void setUserChord(bool c) { m_isUserChord = c; } + + QString getRoot() const { return m_root; } + void setRoot(QString r) { m_root = r; } + + QString getExt() const { return m_ext; } + void setExt(QString r) { m_ext = r.isEmpty() ? QString::null : r; } + + bool hasAltBass() const { return m_ext.contains(ALT_BASS_REGEXP); } + + Fingering getFingering() const { return m_fingering; } + void setFingering(Fingering f) { m_fingering = f; } + + struct ChordCmp + { + bool operator()(const Chord &e1, const Chord &e2) const { + return e1 < e2; + } + bool operator()(const Chord *e1, const Chord *e2) const { + return *e1 < *e2; + } + }; + +protected: + + static const QRegExp ALT_BASS_REGEXP; + + QString m_root; + QString m_ext; + + Fingering m_fingering; + + bool m_isUserChord; +}; + +bool operator<(const Chord&, const Chord&); + +} + +} + +#endif /*_RG_CHORD2_H_*/ diff --git a/src/gui/editors/guitar/ChordMap.cpp b/src/gui/editors/guitar/ChordMap.cpp new file mode 100644 index 0000000..06662d9 --- /dev/null +++ b/src/gui/editors/guitar/ChordMap.cpp @@ -0,0 +1,223 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <[email protected]>, + Chris Cannam <[email protected]>, + Richard Bown <[email protected]> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "misc/Debug.h" +#include "ChordMap.h" + +#include <qfile.h> +#include <qtextstream.h> + +namespace Rosegarden +{ + +namespace Guitar +{ + +ChordMap::ChordMap() + : m_needSave(false) +{ +} + +void ChordMap::insert(const Chord& c) +{ + m_map.insert(c); + m_needSave = true; +} + + +ChordMap::chordarray +ChordMap::getChords(const QString& root, const QString& ext) const +{ + chordarray res; + + Chord tmp(root, ext); + NOTATION_DEBUG << "ChordMap::getChords : chord = " << tmp << " - ext is empty : " << ext.isEmpty() << endl; + + for (chordset::const_iterator i = m_map.lower_bound(tmp); i != m_map.end(); ++i) { + NOTATION_DEBUG << "ChordMap::getChords : checking chord " << *i << endl; + + if (i->getRoot() != root) + break; + + if (/* ext.isNull() || */ i->getExt() == ext) { + NOTATION_DEBUG << "ChordMap::getChords : adding chord " << *i << endl; + res.push_back(*i); + } else { + break; + } + } + + return res; +} + +QStringList +ChordMap::getRootList() const +{ + static QStringList rootNotes; + + if (rootNotes.count() == 0) { + rootNotes = QStringList::split(QString(","), "A,A#/Bb,B,C,C#/Db,D,D#/Eb,E,F,F#/Gb,G,G#/Ab"); + } + + // extract roots from map itself - not a very good idea + // +// QString currentRoot; +// +// for(chordset::const_iterator i = m_map.begin(); i != m_map.end(); ++i) { +// const Chord& chord = *i; +// if (chord.getRoot() != currentRoot) { +// rootNotes.push_back(chord.getRoot()); +// currentRoot = chord.getRoot(); +// } +// } + + return rootNotes; +} + +QStringList +ChordMap::getExtList(const QString& root) const +{ + QStringList extList; + QString currentExt = "ZZ"; + + Chord tmp(root); + + for(chordset::const_iterator i = m_map.lower_bound(tmp); i != m_map.end(); ++i) { + const Chord& chord = *i; +// NOTATION_DEBUG << "ChordMap::getExtList : chord = " << chord << endl; + + if (chord.getRoot() != root) + break; + + if (chord.getExt() != currentExt) { +// NOTATION_DEBUG << "ChordMap::getExtList : adding ext " << chord.getExt() << " for root " << root << endl; + extList.push_back(chord.getExt()); + currentExt = chord.getExt(); + } + } + + return extList; +} + +void +ChordMap::substitute(const Chord& oldChord, const Chord& newChord) +{ + remove(oldChord); + insert(newChord); +} + +void +ChordMap::remove(const Chord& c) +{ + m_map.erase(c); + m_needSave = true; +} + +bool ChordMap::saveDocument(const QString& filename, bool userChordsOnly, QString& errMsg) +{ + QFile file(filename); + file.open(IO_WriteOnly); + + QTextStream outStream(&file); + + outStream.setEncoding(QTextStream::UnicodeUTF8); + + outStream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + << "<!DOCTYPE rosegarden-chord-data>\n" + << "<rosegarden-chord-data version=\"" << VERSION + << "\" format-version-major=\"" << FILE_FORMAT_VERSION_MAJOR + << "\" format-version-minor=\"" << FILE_FORMAT_VERSION_MINOR + << "\" format-version-point=\"" << FILE_FORMAT_VERSION_POINT + << "\">\n"; + + outStream << "<chords>\n"; + + QString currentExt, currentRoot; + + for(iterator i = begin(); i != end(); ++i) { + const Chord& chord = *i; + + if (userChordsOnly && !chord.isUserChord()) + continue; // skip non-user chords + + if (chord.getRoot() != currentRoot) { + + currentRoot = chord.getRoot(); + + // close current chordset (if there was one) + if (i != begin()) + outStream << "\n</chordset>\n"; + + // open new chordset + outStream << "<chordset root=\"" << chord.getRoot() << "\">\n"; + currentExt = "NEWEXT"; // to make sure we open a new chord right after that + } + + if (chord.getExt() != currentExt) { + + currentExt = chord.getExt(); + + // close current chord (if there was one) + if (i != begin()) + outStream << "</chord>\n"; + + // open new chord + outStream << "<chord"; + if (!chord.getExt().isEmpty()) + outStream << " ext=\"" << chord.getExt() << "\""; + if (chord.isUserChord()) + outStream << " user=\"true\""; + + outStream << ">\n"; + } + + outStream << "<fingering>" << chord.getFingering().toString() << "</fingering>\n"; + } + + if (!m_map.empty()) + outStream << "</chord>\n"; // close last written chord + + outStream << "</chords>\n"; + outStream << "</rosegarden-chord-data>\n"; + + return outStream.device()->status() == IO_Ok; +} + +int ChordMap::FILE_FORMAT_VERSION_MAJOR = 1; +int ChordMap::FILE_FORMAT_VERSION_MINOR = 0; +int ChordMap::FILE_FORMAT_VERSION_POINT = 0; + + +void +ChordMap::debugDump() const +{ + for(chordset::const_iterator i = m_map.begin(); i != m_map.end(); ++i) { + Chord chord = *i; + NOTATION_DEBUG << "ChordMap::debugDump " << chord << endl; + } +} + +} + +} diff --git a/src/gui/editors/guitar/ChordMap.h b/src/gui/editors/guitar/ChordMap.h new file mode 100644 index 0000000..5b7488d --- /dev/null +++ b/src/gui/editors/guitar/ChordMap.h @@ -0,0 +1,87 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <[email protected]>, + Chris Cannam <[email protected]>, + Richard Bown <[email protected]> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _RG_CHORDMAP_H_ +#define _RG_CHORDMAP_H_ + +#include "Chord.h" + +#include <qstringlist.h> +#include <set> + +namespace Rosegarden +{ + +namespace Guitar +{ + +class ChordMap +{ + typedef std::set<Chord, Chord::ChordCmp> chordset; + +public: + typedef std::vector<Chord> chordarray; + + typedef chordset::iterator iterator; + typedef chordset::const_iterator const_iterator; + + static int FILE_FORMAT_VERSION_MAJOR; + static int FILE_FORMAT_VERSION_MINOR; + static int FILE_FORMAT_VERSION_POINT; + + ChordMap(); + + void insert(const Chord&); + void substitute(const Chord& oldChord, const Chord& newChord); + void remove(const Chord&); + + chordarray getChords(const QString& root, const QString& ext) const; + + QStringList getRootList() const; + QStringList getExtList(const QString& root) const; + + void debugDump() const; + + bool needSave() const { return m_needSave; } + void clearNeedSave() { m_needSave = false; } + + bool saveDocument(const QString& filename, bool userChordsOnly, QString& errMsg); + + iterator begin() { return m_map.begin(); } + iterator end() { return m_map.end(); } + const_iterator begin() const { return m_map.begin(); } + const_iterator end() const { return m_map.end(); } + +protected: + + chordset m_map; + + bool m_needSave; +}; + +} + +} + +#endif /*_RG_CHORDMAP2_H_*/ diff --git a/src/gui/editors/guitar/ChordXmlHandler.cpp b/src/gui/editors/guitar/ChordXmlHandler.cpp new file mode 100644 index 0000000..701c43c --- /dev/null +++ b/src/gui/editors/guitar/ChordXmlHandler.cpp @@ -0,0 +1,154 @@ +// -*- c-basic-offset: 4 -*- + +/* + Rosegarden + A sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <[email protected]>, + Chris Cannam <[email protected]>, + Richard Bown <[email protected]> + + The moral right of the authors to claim authorship of this work + has been asserted. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "ChordXmlHandler.h" +#include "misc/Debug.h" + +namespace Rosegarden +{ + +ChordXmlHandler::ChordXmlHandler(Guitar::ChordMap& map) + : ProgressReporter(0), + m_chordMap(map) +{ +} + +ChordXmlHandler::~ChordXmlHandler() +{ +} + +bool ChordXmlHandler::startDocument() +{ + // nothing to do ? + return true; +} + +bool ChordXmlHandler::startElement(const QString& namespaceURI, + const QString& localName, + const QString& qName, + const QXmlAttributes& atts) +{ + QString lcName = qName.lower(); + + if (lcName == "chordset") { + // start new chord set + m_currentRoot = atts.value("root").stripWhiteSpace(); + + } else if (lcName == "chord") { + + m_currentChord = Guitar::Chord(m_currentRoot); + + if (atts.index("ext") >= 0) + m_currentChord.setExt(atts.value("ext").stripWhiteSpace()); + + if (atts.index("user") >= 0) { + QString userVal = atts.value("user").stripWhiteSpace().lower(); + bool res = (userVal == "yes" || userVal == "1" || userVal == "true"); + m_currentChord.setUserChord(res); + } else { + m_currentChord.setUserChord(false); + } + + } else if (lcName == "fingering") { + m_inFingering = true; + } + + return true; +} + +bool ChordXmlHandler::endElement(const QString& namespaceURI, + const QString& localName, + const QString& qName) +{ + QString lcName = qName.lower(); + + if (lcName == "fingering") { + + m_inFingering = false; + m_chordMap.insert(m_currentChord); + NOTATION_DEBUG << "ChordXmlHandler::endElement (fingering) : adding chord " << m_currentChord << endl; + + } else if (lcName == "chord") { + + // adding is done after each parsing of fingering + // +// m_chordMap.insert(m_currentChord); + + } + + return true; +} + +bool ChordXmlHandler::characters(const QString& ch) +{ + QString ch2 = ch.simplifyWhiteSpace(); + + if (!ch2.isEmpty() && m_inFingering) { + if (!parseFingering(ch2)) + return false; + } + + return true; +} + +bool ChordXmlHandler::endDocument() +{ + return true; +} + +bool ChordXmlHandler::parseFingering(const QString& ch) { + + QString errString; + + Guitar::Fingering fingering = Guitar::Fingering::parseFingering(ch, errString); + + if (m_errorString.isEmpty()) { + NOTATION_DEBUG << "ChordXmlHandler::parseFingering : fingering " << ch << endl; + m_currentChord.setFingering(fingering); + return true; + } else { + m_errorString = errString; + return false; + } +} + +bool +ChordXmlHandler::error(const QXmlParseException& exception) +{ + m_errorString = QString("%1 at line %2, column %3") + .arg(exception.message()) + .arg(exception.lineNumber()) + .arg(exception.columnNumber()); + return QXmlDefaultHandler::error( exception ); +} + +bool +ChordXmlHandler::fatalError(const QXmlParseException& exception) +{ + m_errorString = QString("%1 at line %2, column %3") + .arg(exception.message()) + .arg(exception.lineNumber()) + .arg(exception.columnNumber()); + return QXmlDefaultHandler::fatalError( exception ); +} + + +} diff --git a/src/gui/editors/guitar/ChordXmlHandler.h b/src/gui/editors/guitar/ChordXmlHandler.h new file mode 100644 index 0000000..ca25168 --- /dev/null +++ b/src/gui/editors/guitar/ChordXmlHandler.h @@ -0,0 +1,78 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <[email protected]>, + Chris Cannam <[email protected]>, + Richard Bown <[email protected]> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + + +#ifndef _RG_CHORDXMLHANDLER_H_ +#define _RG_CHORDXMLHANDLER_H_ + +#include "gui/general/ProgressReporter.h" +#include "Chord.h" +#include "ChordMap.h" + +#include <qxml.h> + + +namespace Rosegarden +{ + +class ChordXmlHandler : public ProgressReporter, public QXmlDefaultHandler +{ +public: + ChordXmlHandler(Guitar::ChordMap&); + virtual ~ChordXmlHandler(); + + /// overloaded handler functions + virtual bool startDocument(); + virtual bool startElement(const QString& namespaceURI, + const QString& localName, + const QString& qName, + const QXmlAttributes& atts); + + virtual bool endElement(const QString& namespaceURI, + const QString& localName, + const QString& qName); + + virtual bool characters(const QString& ch); + + virtual bool endDocument (); + + /// Return the error string set during the parsing (if any) + QString errorString() { return m_errorString; } + bool error(const QXmlParseException& exception); + bool fatalError(const QXmlParseException& exception); + +protected: + + bool parseFingering(const QString& ch); + + Guitar::Chord m_currentChord; + QString m_currentRoot; + QString m_errorString; + bool m_inFingering; + Guitar::ChordMap& m_chordMap; +}; + +} + +#endif /*_RG_CHORDXMLHANDLER_H_*/ diff --git a/src/gui/editors/guitar/Fingering.cpp b/src/gui/editors/guitar/Fingering.cpp new file mode 100644 index 0000000..dd1edbd --- /dev/null +++ b/src/gui/editors/guitar/Fingering.cpp @@ -0,0 +1,152 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <[email protected]>, + Chris Cannam <[email protected]>, + Richard Bown <[email protected]> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "Fingering.h" + +#include "misc/Debug.h" + +#include <qstringlist.h> +#include <sstream> +#include <algorithm> +#include <klocale.h> + +namespace Rosegarden +{ + +namespace Guitar +{ + +Fingering::Fingering(unsigned int nbStrings) : + m_strings(nbStrings, MUTED) +{ +} + +Fingering::Fingering(QString s) +{ + QString errString; + Fingering t = parseFingering(s, errString); + m_strings = t.m_strings; +} + +unsigned int +Fingering::getStartFret() const +{ + int min = 999, max = 0; + for(std::vector<int>::const_iterator i = m_strings.begin(); i != m_strings.end(); ++i) { + if (*i < min && *i > 0) + min = *i; + if (*i > max) + max = *i; + } + + if (max < 4) + min = 1; + + return min == 999 ? 1 : min; +} + +bool +Fingering::hasBarre() const +{ + int lastStringStatus = m_strings[getNbStrings() - 1]; + + return ((m_strings[0] > OPEN && m_strings[0] == lastStringStatus) || + (m_strings[1] > OPEN && m_strings[1] == lastStringStatus) || + (m_strings[2] > OPEN && m_strings[2] == lastStringStatus)); +} + +Fingering::Barre +Fingering::getBarre() const +{ + int lastStringStatus = m_strings[getNbStrings() - 1]; + + Barre res; + + res.fret = lastStringStatus; + + for(unsigned int i = 0; i < 3; ++i) { + if (m_strings[i] > OPEN && m_strings[i] == lastStringStatus) + res.start = i; + break; + } + + res.end = 5; + + return res; +} + +Fingering +Fingering::parseFingering(const QString& ch, QString& errorString) +{ + QStringList tokens = QStringList::split(' ', ch); + + unsigned int idx = 0; + Fingering fingering; + + for(QStringList::iterator i = tokens.begin(); i != tokens.end() && idx < fingering.getNbStrings(); ++i, ++idx) { + QString t = *i; + bool b = false; + unsigned int fn = t.toUInt(&b); + if (b) { +// NOTATION_DEBUG << "Fingering::parseFingering : '" << t << "' = " << fn << endl; + fingering[idx] = fn; + } else if (t.lower() == "x") { +// NOTATION_DEBUG << "Fingering::parseFingering : '" << t << "' = MUTED\n"; + fingering[idx] = MUTED; + } else { + errorString = i18n("couldn't parse fingering '%1' in '%2'").arg(t).arg(ch); + } + } + + return fingering; +} + + +std::string Fingering::toString() const +{ + std::stringstream s; + + for(std::vector<int>::const_iterator i = m_strings.begin(); i != m_strings.end(); ++i) { + if (*i >= 0) + s << *i << ' '; + else + s << "x "; + } + + return s.str(); +} + +bool operator<(const Fingering& a, const Fingering& b) +{ + for(unsigned int i = 0; i < Fingering::DEFAULT_NB_STRINGS; ++i) { + if (a.getStringStatus(i) != b.getStringStatus(i)) { + return a.getStringStatus(i) < b.getStringStatus(i); + } + } + return false; +} + +} + +} diff --git a/src/gui/editors/guitar/Fingering.h b/src/gui/editors/guitar/Fingering.h new file mode 100644 index 0000000..41d9799 --- /dev/null +++ b/src/gui/editors/guitar/Fingering.h @@ -0,0 +1,95 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <[email protected]>, + Chris Cannam <[email protected]>, + Richard Bown <[email protected]> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _RG_FINGERING_H_ +#define _RG_FINGERING_H_ + +#include <vector> +#include <qstring.h> +#include "base/Event.h" + +namespace Rosegarden +{ + +namespace Guitar +{ + +class Fingering +{ +public: + friend bool operator<(const Fingering&, const Fingering&); + + typedef std::vector<int>::iterator iterator; + typedef std::vector<int>::const_iterator const_iterator; + + struct Barre { + unsigned int fret; + unsigned int start; + unsigned int end; + }; + + static const unsigned int DEFAULT_NB_STRINGS = 6; + + Fingering(unsigned int nbStrings = DEFAULT_NB_STRINGS); + Fingering(QString); + + enum { MUTED = -1, OPEN = 0 }; + + /** + * returns the fret number on which the string is pressed, or one of MUTED and OPEN + * + */ + int getStringStatus(int stringNb) const { return m_strings[stringNb]; } + void setStringStatus(int stringNb, int status) { m_strings[stringNb] = status; } + unsigned int getStartFret() const; + unsigned int getNbStrings() const { return m_strings.size(); } + + bool hasBarre() const; + Barre getBarre() const; + + int operator[](int i) const { return m_strings[i]; } + int& operator[](int i) { return m_strings[i]; } + + bool operator==(const Fingering& o) const { return m_strings == o.m_strings; } + + iterator begin() { return m_strings.begin(); } + iterator end() { return m_strings.end(); } + const_iterator begin() const { return m_strings.begin(); } + const_iterator end() const { return m_strings.end(); } + + static Fingering parseFingering(const QString&, QString& errorString); + std::string toString() const; + +protected: + + std::vector<int> m_strings; +}; + +bool operator<(const Fingering&, const Fingering&); + +} + +} + +#endif /*_RG_FINGERING2_H_*/ diff --git a/src/gui/editors/guitar/FingeringBox.cpp b/src/gui/editors/guitar/FingeringBox.cpp new file mode 100644 index 0000000..885ba83 --- /dev/null +++ b/src/gui/editors/guitar/FingeringBox.cpp @@ -0,0 +1,293 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <[email protected]>, + Chris Cannam <[email protected]>, + Richard Bown <[email protected]> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + + +#include "FingeringBox.h" +#include "Fingering.h" + +#include "misc/Debug.h" + +namespace Rosegarden +{ + +FingeringBox::FingeringBox(unsigned int nbFrets, unsigned int nbStrings, bool editable, QWidget *parent, const char* name) + : QFrame(parent, name), + m_nbFretsDisplayed(nbFrets), + m_startFret(1), + m_nbStrings(nbStrings), + m_transientFretNb(0), + m_transientStringNb(0), + m_editable(editable), + m_noteSymbols(m_nbStrings, m_nbFretsDisplayed) +{ + init(); +} + +FingeringBox::FingeringBox(bool editable, QWidget *parent, const char* name) + : QFrame(parent, name), + m_nbFretsDisplayed(DEFAULT_NB_DISPLAYED_FRETS), + m_startFret(1), + m_nbStrings(Guitar::Fingering::DEFAULT_NB_STRINGS), + m_editable(editable), + m_noteSymbols(m_nbStrings, m_nbFretsDisplayed) +{ + init(); +} + +void +FingeringBox::init() +{ + setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + setFixedSize(IMG_WIDTH, IMG_HEIGHT); + setBackgroundMode(PaletteBase); + if (m_editable) + setMouseTracking(true); + +} + +void +FingeringBox::drawContents(QPainter* p) +{ +// NOTATION_DEBUG << "FingeringBox::drawContents()" << endl; + + // For all strings on guitar + // check state of string + // If pressed display note + // Else display muted or open symbol + // For all bars + // display bar + // Horizontal separator line + + // draw guitar chord fingering + // + m_noteSymbols.drawFretNumber(p, m_startFret); + m_noteSymbols.drawFrets(p); + m_noteSymbols.drawStrings(p); + + unsigned int stringNb = 0; + + // draw notes + // + for (Guitar::Fingering::const_iterator pos = m_fingering.begin(); + pos != m_fingering.end(); + ++pos, ++stringNb) { + + switch (*pos) { + case Guitar::Fingering::OPEN: +// NOTATION_DEBUG << "Fingering::drawContents - drawing Open symbol on string " << stringNb << endl; + m_noteSymbols.drawOpenSymbol(p, stringNb); + break; + + case Guitar::Fingering::MUTED: +// NOTATION_DEBUG << "Fingering::drawContents - drawing Mute symbol on string" << stringNb << endl; + m_noteSymbols.drawMuteSymbol(p, stringNb); + break; + + default: +// NOTATION_DEBUG << "Fingering::drawContents - drawing note symbol at " << *pos << " on string " << stringNb << endl; + m_noteSymbols.drawNoteSymbol(p, stringNb, *pos - (m_startFret - 1), false); + break; + } + } + + // TODO: detect barres and draw them in a special way ? + + // draw transient note (visual feedback for mouse move) + // + if (hasMouse() && + m_transientFretNb > 0 && m_transientFretNb <= m_nbFretsDisplayed && + m_transientStringNb >= 0 && m_transientStringNb <= m_nbStrings) { + m_noteSymbols.drawNoteSymbol(p, m_transientStringNb, m_transientFretNb - (m_startFret - 1), true); + } + + // DEBUG +// p->save(); +// p->setPen(Qt::red); +// unsigned int topBorderY = m_noteSymbols.getTopBorder(maximumHeight()); +// p->drawLine(0, topBorderY, 20, topBorderY); +// p->drawRect(m_r1); +// p->setPen(Qt::blue); +// p->drawRect(m_r2); +// p->restore(); +} + +void +FingeringBox::setFingering(const Guitar::Fingering& f) { + m_fingering = f; + m_startFret = m_fingering.getStartFret(); + update(); +} + +unsigned int +FingeringBox::getStringNumber(const QPoint& pos) +{ + PositionPair result = m_noteSymbols.getStringNumber(maximumHeight(), + pos.x(), + m_nbStrings); + unsigned int stringNum = -1; + + if(result.first){ + stringNum = result.second; +// RG_DEBUG << "FingeringBox::getStringNumber : res = " << stringNum << endl; + } + + return stringNum; +} + +unsigned int +FingeringBox::getFretNumber(const QPoint& pos) +{ + unsigned int fretNum = 0; + + if(true || pos.y() > m_noteSymbols.getTopBorder(maximumHeight())) { + // If fret position is below the top line of the guitar chord image. + PositionPair result = m_noteSymbols.getFretNumber(maximumWidth(), + pos.y(), + m_nbFretsDisplayed); + + if(result.first) { + fretNum = result.second + (m_startFret - 1); +// RG_DEBUG << "FingeringBox::getFretNumber : res = " << fretNum << " startFret = " << m_startFret << endl; + } else { +// RG_DEBUG << "FingeringBox::getFretNumber : no res\n"; + } + } + + return fretNum; +} + +void +FingeringBox::mousePressEvent(QMouseEvent *event) +{ + if (!m_editable) + return; + + if((event->button() == LeftButton) && m_editable) { + + // Find string position + m_press_string_num = getStringNumber(event->pos()); + + // Find fret position + m_press_fret_num = getFretNumber(event->pos()); + } +} + +void +FingeringBox::mouseReleaseEvent(QMouseEvent *event) +{ + if(!m_editable) + return ; + + unsigned int release_string_num = getStringNumber(event->pos()); + unsigned int release_fret_num = getFretNumber(event->pos()); + + processMouseRelease(release_string_num, release_fret_num); +} + +void +FingeringBox::processMouseRelease(unsigned int release_string_num, + unsigned int release_fret_num) +{ + if(m_press_fret_num == release_fret_num) { + // If press string & fret pos == release string & fret position, display chord + if(m_press_string_num == release_string_num) { + + if(m_press_fret_num < (m_startFret + m_nbFretsDisplayed)) { + + unsigned int aVal = m_press_fret_num; + + if(m_press_fret_num == 0) { + + int stringStatus = m_fingering.getStringStatus(m_press_string_num); + + if (stringStatus == Guitar::Fingering::OPEN) + aVal = Guitar::Fingering::MUTED; + else if (stringStatus > Guitar::Fingering::OPEN) + aVal = Guitar::Fingering::OPEN; + + } + + m_fingering.setStringStatus(m_press_string_num, aVal); + + update(); + } + } + // else if press fret pos == release fret pos & press string pos != release string pos, display bar + else { + if(((m_press_string_num > 0)&&(release_string_num > 0)) && + (( m_press_string_num <= m_nbStrings)&& + (release_string_num <= m_nbStrings)) && + (( m_press_fret_num <(m_startFret + m_nbFretsDisplayed)) && + (release_fret_num <(m_startFret + m_nbFretsDisplayed)))) { + + // TODO deal with barre later on + + } + } + } +} + + +void +FingeringBox::mouseMoveEvent( QMouseEvent *event ) +{ + if (!m_editable) + return; + + unsigned int transientStringNb = getStringNumber(event->pos()); + unsigned int transientFretNb = getFretNumber(event->pos()); + + if (transientStringNb != m_transientStringNb || + transientFretNb != m_transientFretNb) { + + QRect r1 = m_noteSymbols.getTransientNoteSymbolRect(size(), + m_transientStringNb, + m_transientFretNb - (m_startFret - 1)); + m_transientStringNb = transientStringNb; + m_transientFretNb = transientFretNb; + QRect r2 = m_noteSymbols.getTransientNoteSymbolRect(size(), + m_transientStringNb, + m_transientFretNb - (m_startFret - 1)); + + m_r1 = r1; + m_r2 = r2; + +// RG_DEBUG << "Fingering::updateTransientPos r1 = " << r1 << " - r2 = " << r2 << endl; + +// QRect updateRect = r1 | r2; +// update(updateRect); + + update(); + + } + +} + +void +FingeringBox::leaveEvent(QEvent*) +{ + update(); +} + +} diff --git a/src/gui/editors/guitar/FingeringBox.h b/src/gui/editors/guitar/FingeringBox.h new file mode 100644 index 0000000..b54c0a8 --- /dev/null +++ b/src/gui/editors/guitar/FingeringBox.h @@ -0,0 +1,106 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <[email protected]>, + Chris Cannam <[email protected]>, + Richard Bown <[email protected]> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + + + +#ifndef _RG_FINGERINGBOX_H_ +#define _RG_FINGERINGBOX_H_ + +#include <qframe.h> + +#include "NoteSymbols.h" +#include "Fingering.h" + +namespace Rosegarden +{ + +class Fingering; + +class FingeringBox : public QFrame +{ + static const unsigned int IMG_WIDTH = 200; + static const unsigned int IMG_HEIGHT = 200; + +public: + FingeringBox(unsigned int nbFrets, unsigned int nbStrings, bool editable, QWidget *parent, const char* name = 0); + FingeringBox(bool editable, QWidget *parent, const char* name = 0); + + void setStartFret(unsigned int f) { m_startFret = f; update(); } + unsigned int getStartFret() const { return m_startFret; } + + void setFingering(const Guitar::Fingering&); + const Guitar::Fingering& getFingering() { return m_fingering; } + + const Guitar::NoteSymbols& getNoteSymbols() const { return m_noteSymbols; } + + static const unsigned int DEFAULT_NB_DISPLAYED_FRETS = 4; + +protected: + void init(); + + virtual void drawContents(QPainter*); + + virtual void mousePressEvent(QMouseEvent*); + virtual void mouseReleaseEvent(QMouseEvent*); + virtual void mouseMoveEvent(QMouseEvent*); + virtual void leaveEvent(QEvent*); + + void processMouseRelease( unsigned int release_string_num, unsigned int release_fret_num); + + typedef std::pair<bool, unsigned int> PositionPair; + + unsigned int getStringNumber(const QPoint&); + + unsigned int getFretNumber(const QPoint&); + + //! Maximum number of frets displayed by FingeringBox + unsigned int m_nbFretsDisplayed; + + unsigned int m_startFret; + + unsigned int m_nbStrings; + + unsigned int m_transientFretNb; + unsigned int m_transientStringNb; + + //! Present mode + bool m_editable; + + //! Handle to the present fingering + Guitar::Fingering m_fingering; + + //! String number where a mouse press event was located + unsigned int m_press_string_num; + + //! Fret number where a mouse press event was located + unsigned int m_press_fret_num; + + Guitar::NoteSymbols m_noteSymbols; + + QRect m_r1, m_r2; +}; + +} + +#endif /*_RG_FINGERINGBOX2_H_*/ diff --git a/src/gui/editors/guitar/FingeringListBoxItem.cpp b/src/gui/editors/guitar/FingeringListBoxItem.cpp new file mode 100644 index 0000000..31b92e9 --- /dev/null +++ b/src/gui/editors/guitar/FingeringListBoxItem.cpp @@ -0,0 +1,36 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <[email protected]>, + Chris Cannam <[email protected]>, + Richard Bown <[email protected]> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + + +#include "FingeringListBoxItem.h" + +namespace Rosegarden { + +FingeringListBoxItem::FingeringListBoxItem(const Guitar::Chord& chord, QListBox* parent, QPixmap pixmap, QString fingeringString) + : QListBoxPixmap(parent, pixmap, fingeringString), + m_chord(chord) +{ +} + +} diff --git a/src/gui/editors/guitar/FingeringListBoxItem.h b/src/gui/editors/guitar/FingeringListBoxItem.h new file mode 100644 index 0000000..b7625e2 --- /dev/null +++ b/src/gui/editors/guitar/FingeringListBoxItem.h @@ -0,0 +1,46 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <[email protected]>, + Chris Cannam <[email protected]>, + Richard Bown <[email protected]> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + + +#ifndef _RG_FINGERINGLISTBOXITEM_H_ +#define _RG_FINGERINGLISTBOXITEM_H_ + +#include <qlistbox.h> +#include "Chord.h" + +namespace Rosegarden { + +class FingeringListBoxItem : public QListBoxPixmap +{ +public: + FingeringListBoxItem(const Guitar::Chord& chord, QListBox* parent, QPixmap pixmap, QString fingeringString); + + const Guitar::Chord& getChord() { return m_chord; } +protected: + Guitar::Chord m_chord; +}; + +} + +#endif /*_RG_FINGERINGLISTBOXITEM_H_*/ diff --git a/src/gui/editors/guitar/GuitarChordEditorDialog.cpp b/src/gui/editors/guitar/GuitarChordEditorDialog.cpp new file mode 100644 index 0000000..60da8b6 --- /dev/null +++ b/src/gui/editors/guitar/GuitarChordEditorDialog.cpp @@ -0,0 +1,109 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <[email protected]>, + Chris Cannam <[email protected]>, + Richard Bown <[email protected]> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "GuitarChordEditorDialog.h" +#include "FingeringBox.h" +#include "Chord.h" +#include "ChordMap.h" + +#include <klineedit.h> +#include <qcombobox.h> +#include <qspinbox.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kstddirs.h> +#include <qlayout.h> +#include <qlabel.h> + +namespace Rosegarden +{ + +GuitarChordEditorDialog::GuitarChordEditorDialog(Guitar::Chord& chord, const Guitar::ChordMap& chordMap, QWidget *parent) + : KDialogBase(parent, "GuitarChordEditor", true, i18n("Guitar Chord Editor"), Ok|Cancel), + m_chord(chord), + m_chordMap(chordMap) +{ + QWidget *page = new QWidget(this); + setMainWidget(page); + QGridLayout *topLayout = new QGridLayout(page, 7, 2, spacingHint()); + + topLayout->addWidget(new QLabel(i18n("Start fret"), page), 0, 1); + m_startFret = new QSpinBox(1, 24, 1, page); + topLayout->addWidget(m_startFret, 1, 1); + + connect(m_startFret, SIGNAL(valueChanged(int)), + this, SLOT(slotStartFretChanged(int))); + + topLayout->addWidget(new QLabel(i18n("Root"), page), 2, 1); + m_rootNotesList = new QComboBox(page); + topLayout->addWidget(m_rootNotesList, 3, 1); + + topLayout->addWidget(new QLabel(i18n("Extension"), page), 4, 1); + m_ext = new QComboBox(true, page); + topLayout->addWidget(m_ext, 5, 1); + + topLayout->addItem(new QSpacerItem(1, 1), 6, 1); + + m_fingeringBox = new FingeringBox(true, page); + m_fingeringBox->setFingering(m_chord.getFingering()); + topLayout->addMultiCellWidget(m_fingeringBox, 0, 7, 0, 0); + + NOTATION_DEBUG << "GuitarChordEditorDialog : chord = " << m_chord << endl; + + + QStringList rootList = m_chordMap.getRootList(); + if (rootList.count() > 0) { + m_rootNotesList->insertStringList(rootList); + m_rootNotesList->setCurrentItem(rootList.findIndex(m_chord.getRoot())); + } + + QStringList extList = m_chordMap.getExtList(m_chord.getRoot()); + if (extList.count() > 0) { + m_ext->insertStringList(extList); + m_ext->setCurrentItem(extList.findIndex(m_chord.getExt())); + } + +} + +void +GuitarChordEditorDialog::slotStartFretChanged(int startFret) +{ + m_fingeringBox->setStartFret(startFret); +} + +void +GuitarChordEditorDialog::slotOk() +{ + m_chord.setFingering(m_fingeringBox->getFingering()); + m_chord.setExt(m_ext->currentText()); + m_chord.setRoot(m_rootNotesList->currentText()); + m_chord.setUserChord(true); + KDialogBase::slotOk(); +} + + +} + +#include "GuitarChordEditorDialog.moc" + diff --git a/src/gui/editors/guitar/GuitarChordEditorDialog.h b/src/gui/editors/guitar/GuitarChordEditorDialog.h new file mode 100644 index 0000000..fc01605 --- /dev/null +++ b/src/gui/editors/guitar/GuitarChordEditorDialog.h @@ -0,0 +1,67 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <[email protected]>, + Chris Cannam <[email protected]>, + Richard Bown <[email protected]> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _RG_GUITARCHORDEDITOR2_H_ +#define _RG_GUITARCHORDEDITOR2_H_ + +#include <kdialogbase.h> + +#include "Chord.h" +#include "ChordMap.h" + +class QComboBox; +class QSpinBox; + +namespace Rosegarden +{ + +class FingeringBox; + + +class GuitarChordEditorDialog : public KDialogBase +{ + Q_OBJECT + +public: + GuitarChordEditorDialog(Guitar::Chord&, const Guitar::ChordMap& chordMap, QWidget *parent=0); + +protected slots: + void slotStartFretChanged(int); + virtual void slotOk(); + +protected: + + void populateExtensions(const QStringList&); + + FingeringBox* m_fingeringBox; + QComboBox* m_rootNotesList; + QSpinBox* m_startFret; + QComboBox* m_ext; + Guitar::Chord& m_chord; + const Guitar::ChordMap& m_chordMap; +}; + +} + +#endif /*_RG_GUITARCHORDEDITOR2_H_*/ diff --git a/src/gui/editors/guitar/GuitarChordSelectorDialog.cpp b/src/gui/editors/guitar/GuitarChordSelectorDialog.cpp new file mode 100644 index 0000000..bd62c1f --- /dev/null +++ b/src/gui/editors/guitar/GuitarChordSelectorDialog.cpp @@ -0,0 +1,475 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <[email protected]>, + Chris Cannam <[email protected]>, + Richard Bown <[email protected]> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "GuitarChordSelectorDialog.h" +#include "GuitarChordEditorDialog.h" +#include "ChordXmlHandler.h" +#include "FingeringBox.h" +#include "FingeringListBoxItem.h" + +#include "misc/Debug.h" +#include <qlistbox.h> +#include <qlayout.h> +#include <qcombobox.h> +#include <qpushbutton.h> +#include <qlabel.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kstddirs.h> + +namespace Rosegarden +{ + +GuitarChordSelectorDialog::GuitarChordSelectorDialog(QWidget *parent) + : KDialogBase(parent, "GuitarChordSelector", true, i18n("Guitar Chord Selector"), Ok|Cancel) +{ + QWidget *page = new QWidget(this); + setMainWidget(page); + QGridLayout *topLayout = new QGridLayout(page, 3, 4, spacingHint()); + + topLayout->addWidget(new QLabel(i18n("Root"), page), 0, 0); + m_rootNotesList = new QListBox(page); + topLayout->addWidget(m_rootNotesList, 1, 0); + + topLayout->addWidget(new QLabel(i18n("Extension"), page), 0, 1); + m_chordExtList = new QListBox(page); + topLayout->addWidget(m_chordExtList, 1, 1); + + m_newFingeringButton = new QPushButton(i18n("New"), page); + m_deleteFingeringButton = new QPushButton(i18n("Delete"), page); + m_editFingeringButton = new QPushButton(i18n("Edit"), page); + + m_chordComplexityCombo = new QComboBox(page); + m_chordComplexityCombo->insertItem(i18n("beginner")); + m_chordComplexityCombo->insertItem(i18n("common")); + m_chordComplexityCombo->insertItem(i18n("all")); + + connect(m_chordComplexityCombo, SIGNAL(activated(int)), + this, SLOT(slotComplexityChanged(int))); + + QVBoxLayout* vboxLayout = new QVBoxLayout(page, 5); + topLayout->addMultiCellLayout(vboxLayout, 1, 3, 2, 2); + vboxLayout->addWidget(m_chordComplexityCombo); + vboxLayout->addStretch(10); + vboxLayout->addWidget(m_newFingeringButton); + vboxLayout->addWidget(m_deleteFingeringButton); + vboxLayout->addWidget(m_editFingeringButton); + + connect(m_newFingeringButton, SIGNAL(clicked()), + this, SLOT(slotNewFingering())); + connect(m_deleteFingeringButton, SIGNAL(clicked()), + this, SLOT(slotDeleteFingering())); + connect(m_editFingeringButton, SIGNAL(clicked()), + this, SLOT(slotEditFingering())); + + topLayout->addWidget(new QLabel(i18n("Fingerings"), page), 0, 3); + m_fingeringsList = new QListBox(page); + topLayout->addMultiCellWidget(m_fingeringsList, 1, 2, 3, 3); + + m_fingeringBox = new FingeringBox(false, page); + topLayout->addMultiCellWidget(m_fingeringBox, 2, 2, 0, 1); + + connect(m_rootNotesList, SIGNAL(highlighted(int)), + this, SLOT(slotRootHighlighted(int))); + connect(m_chordExtList, SIGNAL(highlighted(int)), + this, SLOT(slotChordExtHighlighted(int))); + connect(m_fingeringsList, SIGNAL(highlighted(QListBoxItem*)), + this, SLOT(slotFingeringHighlighted(QListBoxItem*))); +} + +void +GuitarChordSelectorDialog::init() +{ + // populate the listboxes + // + std::vector<QString> chordFiles = getAvailableChordFiles(); + + parseChordFiles(chordFiles); + +// m_chordMap.debugDump(); + + populate(); +} + +void +GuitarChordSelectorDialog::populate() +{ + QStringList rootList = m_chordMap.getRootList(); + if (rootList.count() > 0) { + m_rootNotesList->insertStringList(rootList); + + QStringList extList = m_chordMap.getExtList(rootList.first()); + populateExtensions(extList); + + Guitar::ChordMap::chordarray chords = m_chordMap.getChords(rootList.first(), extList.first()); + populateFingerings(chords); + + m_chord.setRoot(rootList.first()); + m_chord.setExt(extList.first()); + } + + m_rootNotesList->sort(); + + m_rootNotesList->setCurrentItem(0); +} + +void +GuitarChordSelectorDialog::clear() +{ + m_rootNotesList->clear(); + m_chordExtList->clear(); + m_fingeringsList->clear(); +} + +void +GuitarChordSelectorDialog::refresh() +{ + clear(); + populate(); +} + +void +GuitarChordSelectorDialog::slotRootHighlighted(int i) +{ + NOTATION_DEBUG << "GuitarChordSelectorDialog::slotRootHighlighted " << i << endl; + + m_chord.setRoot(m_rootNotesList->text(i)); + + QStringList extList = m_chordMap.getExtList(m_chord.getRoot()); + populateExtensions(extList); + if (m_chordExtList->count() > 0) + m_chordExtList->setCurrentItem(0); + else + m_fingeringsList->clear(); // clear any previous fingerings +} + +void +GuitarChordSelectorDialog::slotChordExtHighlighted(int i) +{ + NOTATION_DEBUG << "GuitarChordSelectorDialog::slotChordExtHighlighted " << i << endl; + + Guitar::ChordMap::chordarray chords = m_chordMap.getChords(m_chord.getRoot(), m_chordExtList->text(i)); + populateFingerings(chords); + + m_fingeringsList->setCurrentItem(0); +} + +void +GuitarChordSelectorDialog::slotFingeringHighlighted(QListBoxItem* listBoxItem) +{ + NOTATION_DEBUG << "GuitarChordSelectorDialog::slotFingeringHighlighted\n"; + + FingeringListBoxItem* fingeringItem = dynamic_cast<FingeringListBoxItem*>(listBoxItem); + if (fingeringItem) { + m_chord = fingeringItem->getChord(); + m_fingeringBox->setFingering(m_chord.getFingering()); + setEditionEnabled(m_chord.isUserChord()); + } +} + +void +GuitarChordSelectorDialog::slotComplexityChanged(int) +{ + // simply repopulate the extension list box + // + QStringList extList = m_chordMap.getExtList(m_chord.getRoot()); + populateExtensions(extList); + if (m_chordExtList->count() > 0) + m_chordExtList->setCurrentItem(0); + else + m_fingeringsList->clear(); // clear any previous fingerings +} + +void +GuitarChordSelectorDialog::slotNewFingering() +{ + Guitar::Chord newChord; + newChord.setRoot(m_chord.getRoot()); + newChord.setExt(m_chord.getExt()); + + GuitarChordEditorDialog* chordEditorDialog = new GuitarChordEditorDialog(newChord, m_chordMap, this); + + if (chordEditorDialog->exec() == QDialog::Accepted) { + m_chordMap.insert(newChord); + // populate lists + // + if (!m_rootNotesList->findItem(newChord.getRoot(), Qt::ExactMatch)) { + m_rootNotesList->insertItem(newChord.getRoot()); + m_rootNotesList->sort(); + } + + if (!m_chordExtList->findItem(newChord.getExt(), Qt::ExactMatch)) { + m_chordExtList->insertItem(newChord.getExt()); + m_chordExtList->sort(); + } + } + + delete chordEditorDialog; + + refresh(); +} + +void +GuitarChordSelectorDialog::slotDeleteFingering() +{ + if (m_chord.isUserChord()) { + m_chordMap.remove(m_chord); + delete m_fingeringsList->selectedItem(); + } +} + +void +GuitarChordSelectorDialog::slotEditFingering() +{ + Guitar::Chord newChord = m_chord; + GuitarChordEditorDialog* chordEditorDialog = new GuitarChordEditorDialog(newChord, m_chordMap, this); + + if (chordEditorDialog->exec() == QDialog::Accepted) { + NOTATION_DEBUG << "GuitarChordSelectorDialog::slotEditFingering() - current map state :\n"; + m_chordMap.debugDump(); + m_chordMap.substitute(m_chord, newChord); + NOTATION_DEBUG << "GuitarChordSelectorDialog::slotEditFingering() - new map state :\n"; + m_chordMap.debugDump(); + setChord(newChord); + } + + delete chordEditorDialog; + + refresh(); +} + +void +GuitarChordSelectorDialog::slotOk() +{ + if (m_chordMap.needSave()) { + saveUserChordMap(); + m_chordMap.clearNeedSave(); + } + + KDialogBase::slotOk(); +} + +void +GuitarChordSelectorDialog::setChord(const Guitar::Chord& chord) +{ + NOTATION_DEBUG << "GuitarChordSelectorDialog::setChord " << chord << endl; + + m_chord = chord; + + // select the chord's root + // + m_rootNotesList->setCurrentItem(0); + QListBoxItem* correspondingRoot = m_rootNotesList->findItem(chord.getRoot(), Qt::ExactMatch); + if (correspondingRoot) + m_rootNotesList->setSelected(correspondingRoot, true); + + // update the dialog's complexity setting if needed, then populate the extension list + // + QString chordExt = chord.getExt(); + int complexityLevel = m_chordComplexityCombo->currentItem(); + int chordComplexity = evaluateChordComplexity(chordExt); + + if (chordComplexity > complexityLevel) { + m_chordComplexityCombo->setCurrentItem(chordComplexity); + } + + QStringList extList = m_chordMap.getExtList(chord.getRoot()); + populateExtensions(extList); + + // select the chord's extension + // + if (chordExt.isEmpty()) { + chordExt = ""; + m_chordExtList->setSelected(0, true); + } else { + QListBoxItem* correspondingExt = m_chordExtList->findItem(chordExt, Qt::ExactMatch); + if (correspondingExt) + m_chordExtList->setSelected(correspondingExt, true); + } + + // populate fingerings and pass the current chord's fingering so it is selected + // + Guitar::ChordMap::chordarray similarChords = m_chordMap.getChords(chord.getRoot(), chord.getExt()); + populateFingerings(similarChords, chord.getFingering()); +} + +void +GuitarChordSelectorDialog::populateFingerings(const Guitar::ChordMap::chordarray& chords, const Guitar::Fingering& refFingering) +{ + m_fingeringsList->clear(); + + for(Guitar::ChordMap::chordarray::const_iterator i = chords.begin(); i != chords.end(); ++i) { + const Guitar::Chord& chord = *i; + QString fingeringString = chord.getFingering().toString(); + NOTATION_DEBUG << "GuitarChordSelectorDialog::populateFingerings " << chord << endl; + QPixmap fingeringPixmap = getFingeringPixmap(chord.getFingering()); + FingeringListBoxItem *item = new FingeringListBoxItem(chord, m_fingeringsList, fingeringPixmap, fingeringString); + if (refFingering == chord.getFingering()) { + NOTATION_DEBUG << "GuitarChordSelectorDialog::populateFingerings - fingering found " << fingeringString << endl; + m_fingeringsList->setSelected(item, true); + } + } + +} + + +QPixmap +GuitarChordSelectorDialog::getFingeringPixmap(const Guitar::Fingering& fingering) const +{ + QPixmap pixmap(FINGERING_PIXMAP_WIDTH, FINGERING_PIXMAP_HEIGHT); + pixmap.fill(); + + QPainter pp(&pixmap); + QPainter *p = &pp; + + p->setViewport(FINGERING_PIXMAP_H_MARGIN, FINGERING_PIXMAP_W_MARGIN, + FINGERING_PIXMAP_WIDTH - FINGERING_PIXMAP_W_MARGIN, + FINGERING_PIXMAP_HEIGHT - FINGERING_PIXMAP_H_MARGIN); + + Guitar::NoteSymbols::drawFingeringPixmap(fingering, m_fingeringBox->getNoteSymbols(), p); + + return pixmap; +} + +void +GuitarChordSelectorDialog::populateExtensions(const QStringList& extList) +{ + m_chordExtList->clear(); + + if (m_chordComplexityCombo->currentItem() != COMPLEXITY_ALL) { + // some filtering needs to be done + int complexityLevel = m_chordComplexityCombo->currentItem(); + + QStringList filteredList; + for(QStringList::const_iterator i = extList.constBegin(); i != extList.constEnd(); ++i) { + if (evaluateChordComplexity((*i).lower().stripWhiteSpace()) <= complexityLevel) { + NOTATION_DEBUG << "GuitarChordSelectorDialog::populateExtensions - adding '" << *i << "'\n"; + filteredList.append(*i); + } + } + + m_chordExtList->insertStringList(filteredList); + + } else { + m_chordExtList->insertStringList(extList); + } +} + +int +GuitarChordSelectorDialog::evaluateChordComplexity(const QString& ext) +{ + if (ext.isEmpty() || + ext == "7" || + ext == "m" || + ext == "5") + return COMPLEXITY_BEGINNER; + + if (ext == "dim" || + ext == "dim7" || + ext == "aug" || + ext == "sus2" || + ext == "sus4" || + ext == "maj7" || + ext == "m7" || + ext == "mmaj7" || + ext == "m7b5" || + ext == "7sus4") + + return COMPLEXITY_COMMON; + + return COMPLEXITY_ALL; +} + +void +GuitarChordSelectorDialog::parseChordFiles(const std::vector<QString>& chordFiles) +{ + for(std::vector<QString>::const_iterator i = chordFiles.begin(); i != chordFiles.end(); ++i) { + parseChordFile(*i); + } +} + +void +GuitarChordSelectorDialog::parseChordFile(const QString& chordFileName) +{ + ChordXmlHandler handler(m_chordMap); + QFile chordFile(chordFileName); + bool ok = chordFile.open(IO_ReadOnly); + if (!ok) + KMessageBox::error(0, i18n("couldn't open file '%1'").arg(handler.errorString())); + + QXmlInputSource source(chordFile); + QXmlSimpleReader reader; + reader.setContentHandler(&handler); + reader.setErrorHandler(&handler); + NOTATION_DEBUG << "GuitarChordSelectorDialog::parseChordFile() parsing " << chordFileName << endl; + reader.parse(source); + if (!ok) + KMessageBox::error(0, i18n("couldn't parse chord dictionnary : %1").arg(handler.errorString())); + +} + +void +GuitarChordSelectorDialog::setEditionEnabled(bool enabled) +{ + m_deleteFingeringButton->setEnabled(enabled); + m_editFingeringButton->setEnabled(enabled); +} + +std::vector<QString> +GuitarChordSelectorDialog::getAvailableChordFiles() +{ + std::vector<QString> names; + + // Read config for default directory + QStringList chordDictFiles = KGlobal::dirs()->findAllResources("appdata", "chords/*.xml"); + + for(QStringList::iterator i = chordDictFiles.begin(); i != chordDictFiles.end(); ++i) { + NOTATION_DEBUG << "GuitarChordSelectorDialog::getAvailableChordFiles : adding file " << *i << endl; + names.push_back(*i); + } + + return names; +} + +bool +GuitarChordSelectorDialog::saveUserChordMap() +{ + // Read config for user directory + QString userDir = KGlobal::dirs()->saveLocation("appdata", "chords/"); + + QString userChordDictPath = userDir + "/user_chords.xml"; + + NOTATION_DEBUG << "GuitarChordSelectorDialog::saveUserChordMap() : saving user chord map to " << userChordDictPath << endl; + QString errMsg; + + m_chordMap.saveDocument(userChordDictPath, true, errMsg); + + return errMsg.isEmpty(); +} + + +} + + +#include "GuitarChordSelectorDialog.moc" diff --git a/src/gui/editors/guitar/GuitarChordSelectorDialog.h b/src/gui/editors/guitar/GuitarChordSelectorDialog.h new file mode 100644 index 0000000..6c8f1ad --- /dev/null +++ b/src/gui/editors/guitar/GuitarChordSelectorDialog.h @@ -0,0 +1,120 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <[email protected]>, + Chris Cannam <[email protected]>, + Richard Bown <[email protected]> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + + +#ifndef _RG_GUITARCHORDSELECTORDIALOG_H_ +#define _RG_GUITARCHORDSELECTORDIALOG_H_ + +#include "Chord.h" +#include "ChordMap.h" + +#include <kdialogbase.h> +#include <qstring.h> +#include <vector> + +class QListBox; +class QListBoxItem; +class QComboBox; +class QPushButton; + +namespace Rosegarden +{ + +class FingeringBox; + +class GuitarChordSelectorDialog : public KDialogBase +{ + Q_OBJECT + + enum { COMPLEXITY_BEGINNER, COMPLEXITY_COMMON, COMPLEXITY_ALL }; + +public: + GuitarChordSelectorDialog(QWidget *parent=0); + + void init(); + + const Guitar::Chord& getChord() const { return m_chord; } + + void setChord(const Guitar::Chord&); + +protected slots: + void slotRootHighlighted(int); + void slotChordExtHighlighted(int); + void slotFingeringHighlighted(QListBoxItem*); + void slotComplexityChanged(int); + + void slotNewFingering(); + void slotDeleteFingering(); + void slotEditFingering(); + + virtual void slotOk(); + +protected: + + void parseChordFiles(const std::vector<QString>& chordFiles); + void parseChordFile(const QString& chordFileName); + void populateFingerings(const Guitar::ChordMap::chordarray&, const Guitar::Fingering& refFingering=Guitar::Fingering(0)); + void populateExtensions(const QStringList& extList); + + /// set enabled state of edit/delete buttons + void setEditionEnabled(bool); + + void populate(); + void clear(); + void refresh(); + + bool saveUserChordMap(); + int evaluateChordComplexity(const QString& ext); + + QPixmap getFingeringPixmap(const Guitar::Fingering& fingering) const; + + /// Find all chord list files on the system + std::vector<QString> getAvailableChordFiles(); + + Guitar::ChordMap m_chordMap; + + /// current selected chord + Guitar::Chord m_chord; + + // Chord data + QListBox* m_rootNotesList; + QListBox* m_chordExtList; + QListBox* m_fingeringsList; + FingeringBox* m_fingeringBox; + + QComboBox* m_chordComplexityCombo; + QPushButton* m_newFingeringButton; + QPushButton* m_deleteFingeringButton; + QPushButton* m_editFingeringButton; + + static const unsigned int FINGERING_PIXMAP_HEIGHT = 75; + static const unsigned int FINGERING_PIXMAP_WIDTH = 75; + static const unsigned int FINGERING_PIXMAP_H_MARGIN = 5; + static const unsigned int FINGERING_PIXMAP_W_MARGIN = 5; + +}; + +} + +#endif /*_RG_GUITARCHORDSELECTORDIALOG_H_*/ diff --git a/src/gui/editors/guitar/NoteSymbols.cpp b/src/gui/editors/guitar/NoteSymbols.cpp new file mode 100644 index 0000000..14379de --- /dev/null +++ b/src/gui/editors/guitar/NoteSymbols.cpp @@ -0,0 +1,486 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <[email protected]>, + Chris Cannam <[email protected]>, + Richard Bown <[email protected]> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + This file contains code from + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "NoteSymbols.h" +#include "Fingering.h" +#include "misc/Debug.h" + +namespace Rosegarden +{ + +namespace Guitar +{ +NoteSymbols::posPair +NoteSymbols::getX ( int imgWidth, unsigned int stringNb, unsigned int nbOfStrings ) const +{ + /* + std::cout << "NoteSymbols::getX - input values" << std::endl + << " position: " << position << std::endl + << " string #: " << string_num << std::endl + << " scale: " << scale << std::endl; + */ + unsigned int lBorder = getLeftBorder( imgWidth ); + unsigned int guitarChordWidth = getGuitarChordWidth( imgWidth ); + unsigned int columnWidth = guitarChordWidth / nbOfStrings; + return std::make_pair( ( stringNb * columnWidth + lBorder ), columnWidth ); +} + +NoteSymbols::posPair +NoteSymbols::getY ( int imgHeight, unsigned int fretNb, unsigned int nbOfFrets ) const +{ + /* + std::cout << "NoteSymbols::getY - input values" << std::endl + << " position: " << fret_pos << std::endl + << " max frets: " << maxFretNum << std::endl + << " scale: " << scale << std::endl; + */ + unsigned int tBorder = getTopBorder( imgHeight ); + unsigned int guitarChordHeight = getGuitarChordHeight( imgHeight ); + unsigned int rowHeight = guitarChordHeight / nbOfFrets; + return std::make_pair( ( ( fretNb * rowHeight ) + tBorder ), rowHeight ); +} + +void +NoteSymbols::drawMuteSymbol ( QPainter* p, + unsigned int position ) const +{ + QRect v = p->viewport(); + + posPair x_pos = getX ( v.width(), position, m_nbOfStrings ); + unsigned int y_pos = getTopBorder( v.height() ) / 2; + double columnWidth = x_pos.second; + unsigned int width = static_cast<unsigned int>( columnWidth * 0.7 ); + unsigned int height = static_cast<unsigned int>( columnWidth * 0.7 ); + + //std::cout << "NoteSymbols::drawMuteSymbol - drawing Mute symbol at string #" << position + //<< std::endl; + + p->drawLine ( x_pos.first - ( width / 2 ), + y_pos - ( height / 2 ), + ( x_pos.first + ( width / 2 ) ), + y_pos + ( height / 2 ) ); + + p->drawLine( x_pos.first + ( width / 2 ), + y_pos - ( height / 2 ), + ( x_pos.first - ( width / 2 ) ), + y_pos + ( height / 2 ) ); +} + +void +NoteSymbols::drawOpenSymbol ( QPainter* p, + unsigned int position ) const +{ + QRect v = p->viewport(); + posPair x_pos = getX ( v.width(), position, m_nbOfStrings ); + unsigned int y_pos = getTopBorder( v.height() ) / 2; + double columnWidth = x_pos.second; + unsigned int radius = static_cast<unsigned int>( columnWidth * 0.7 ); + + //std::cout << "NoteSymbols::drawOpenSymbol - drawing Open symbol at string #" << position + //<< std::endl; + + p->setBrush( QBrush(p->brush().color(), Qt::NoBrush) ); + p->drawEllipse( x_pos.first - ( radius / 2 ), + y_pos - ( radius / 2 ), + radius, + radius ); +} + +void +NoteSymbols::drawNoteSymbol ( QPainter* p, + unsigned int stringNb, + int fretNb, + bool transient ) const +{ +// NOTATION_DEBUG << "NoteSymbols::drawNoteSymbol - string: " << stringNb << ", fret:" << fretNb << endl; + + QRect v = p->viewport(); + posPair x_pos = getX ( v.width(), stringNb, m_nbOfStrings ); + posPair y_pos = getY ( v.height(), fretNb, m_nbOfFrets ); + double columnWidth = x_pos.second; + unsigned int radius; + + if (transient) { + radius = static_cast<unsigned int>( columnWidth /* * 0.9 */ ); + p->setBrush( QBrush(p->brush().color(), Qt::NoBrush) ); + } else { + radius = static_cast<unsigned int>( columnWidth * 0.7 ); + p->setBrush( QBrush(p->brush().color(), Qt::SolidPattern) ); + } + + int x = x_pos.first - ( radius / 2 ), + y = y_pos.first + ( (y_pos.second - radius) / 2) - y_pos.second + TOP_GUITAR_CHORD_MARGIN; + +// y = y_pos.first - (radius / 2) - y_pos.second + TOP_GUITAR_CHORD_MARGIN; + +// RG_DEBUG << "NoteSymbols::drawNoteSymbol : rect = " << QRect(x,y, radius, radius) << endl; + + p->drawEllipse( x, + y, + radius, + radius ); + +// p->save(); +// p->setPen(Qt::red); +// p->drawRect( x, y, radius, radius ); +// p->restore(); +} + +void +NoteSymbols::drawBarreSymbol ( QPainter* p, + int fretNb, + unsigned int start, + unsigned int end ) const +{ + + //std::cout << "NoteSymbols::drawBarreSymbol - start: " << start << ", end:" << end << std::endl; + + drawNoteSymbol ( p, start, fretNb ); + + if ( ( end - start ) >= 1 ) { + QRect v = p->viewport(); + posPair startXPos = getX ( v.width(), start, m_nbOfStrings ); + posPair endXPos = getX ( v.width(), end, m_nbOfStrings ); + posPair y_pos = getY ( v.height(), fretNb, m_nbOfFrets ); + double columnWidth = startXPos.second; + unsigned int thickness = static_cast<unsigned int>( columnWidth * 0.7 ); + + p->drawRect( startXPos.first, + y_pos.first + ( y_pos.second / 4 ) + TOP_GUITAR_CHORD_MARGIN, + endXPos.first - startXPos.first, + thickness ); + } + + drawNoteSymbol ( p, end, fretNb ); +} + +void +NoteSymbols::drawFretNumber ( QPainter* p, + unsigned int fret_num ) const +{ + if ( fret_num > 1 ) { + QRect v = p->viewport(); + unsigned int imgWidth = v.width(); + unsigned int imgHeight = v.height(); + + p->save(); + QFont font; + font.setPixelSize(getFontPixelSize(v.width(), v.height())); + p->setFont(font); + + QString tmp; + tmp.setNum( fret_num ); + + // Use NoteSymbols to grab X and Y for first fret + posPair y_pos = getY( imgHeight, 0, m_nbOfFrets ); + + p->drawText( getLeftBorder( imgWidth ) / 4, + y_pos.first + ( y_pos.second / 2 ), + tmp ); + + p->restore(); + } +} + +void +NoteSymbols::drawFrets ( QPainter* p ) const +{ + /* + std::cout << "NoteSymbols::drawFretHorizontalLines" << std::endl + << " scale: " << scale << std::endl + << " frets: " << fretsDisplayed << std::endl + << " max string: " << maxStringNum << std::endl; + */ + + QRect v = p->viewport(); + unsigned int imgWidth = v.width(); + unsigned int imgHeight = v.height(); + //unsigned int endXPos = getGuitarChordWidth(imgWidth) + getLeftBorder(imgWidth); + posPair endXPos = getX ( imgWidth, m_nbOfStrings - 1, m_nbOfStrings ); + + unsigned int yGuitarChord = getGuitarChordHeight( imgHeight ); + unsigned int rowHeight = yGuitarChord / m_nbOfFrets; + + QPen pen(p->pen()); + pen.setWidth(imgHeight >= 100 ? FRET_PEN_WIDTH : FRET_PEN_WIDTH / 2); + p->save(); + p->setPen(pen); + unsigned int y_pos = (getY ( imgHeight, 0, m_nbOfFrets )).first + TOP_GUITAR_CHORD_MARGIN; + +// NOTATION_DEBUG << "NoteSymbols::drawFrets : " << m_nbOfFrets << endl; + + // Horizontal lines + for ( unsigned int i = 0; i <= m_nbOfFrets; ++i ) { + + /* This code borrowed from KGuitar 0.5 */ + p->drawLine( getLeftBorder( imgWidth ), + y_pos, + endXPos.first, + y_pos); +// NOTATION_DEBUG << "NoteSymbols::drawFrets : " << QPoint(getLeftBorder(imgWidth), y_pos) +// << " to " << QPoint(endXPos.first, y_pos) << endl; + + + y_pos += rowHeight; + } + + p->restore(); + +} + +void +NoteSymbols::drawStrings ( QPainter* p ) const +{ + // Vertical lines + QRect v = p->viewport(); + int imgHeight = v.height(); + int imgWidth = v.width(); + + unsigned int startPos = getTopBorder( imgHeight ) + TOP_GUITAR_CHORD_MARGIN; + unsigned int endPos = (getY ( imgHeight, m_nbOfFrets, m_nbOfFrets )).first + TOP_GUITAR_CHORD_MARGIN; + + unsigned int guitarChordWidth = getGuitarChordWidth( imgWidth ); + unsigned int columnWidth = guitarChordWidth / m_nbOfStrings; + + unsigned int x_pos = (getX ( imgWidth, 0, m_nbOfStrings )).first; + + QPen pen(p->pen()); + pen.setWidth(imgWidth >= 100 ? STRING_PEN_WIDTH : STRING_PEN_WIDTH / 2); + p->save(); + p->setPen(pen); + + for ( unsigned int i = 0; i < m_nbOfStrings; ++i ) { + + /* This code borrowed from KGuitar 0.5 */ + p->drawLine( x_pos, + startPos, + x_pos, + endPos ); + + x_pos += columnWidth; + } + + p->restore(); + +} + +QRect NoteSymbols::getTransientNoteSymbolRect(QSize guitarChordSize, + unsigned int stringNb, + int fretNb) const +{ + posPair x_pos = getX ( guitarChordSize.width(), stringNb, m_nbOfStrings ); + posPair y_pos = getY ( guitarChordSize.height(), fretNb, m_nbOfFrets ); + double columnWidth = x_pos.second; + unsigned int radius = static_cast<unsigned int>( columnWidth /* * 0.9 */ ); + + int x = x_pos.first - ( radius / 2 ), + y = y_pos.first + ( (y_pos.second - radius) / 2) - y_pos.second + TOP_GUITAR_CHORD_MARGIN; + + return QRect(x, y, radius, radius); +} + +unsigned int +NoteSymbols::getTopBorder ( unsigned int imgHeight ) const +{ + return static_cast<unsigned int>( TOP_BORDER_PERCENTAGE * imgHeight ); +} + +unsigned int +NoteSymbols::getBottomBorder ( unsigned int imgHeight ) const +{ + return static_cast<unsigned int>( imgHeight * BOTTOM_BORDER_PERCENTAGE ); +} + +unsigned int +NoteSymbols::getLeftBorder ( unsigned int imgWidth ) const +{ + unsigned int left = static_cast<unsigned int>( imgWidth * LEFT_BORDER_PERCENTAGE ); + if ( left < 15 ) { + left = 15; + } + return left; +} + +unsigned int +NoteSymbols::getRightBorder ( unsigned int imgWidth ) const +{ + return static_cast<unsigned int>( imgWidth * RIGHT_BORDER_PERCENTAGE ); +} + +unsigned int +NoteSymbols::getGuitarChordWidth ( int imgWidth ) const +{ + return static_cast<unsigned int>( imgWidth * GUITAR_CHORD_WIDTH_PERCENTAGE ); +} + +unsigned int +NoteSymbols::getGuitarChordHeight ( int imgHeight ) const +{ + return static_cast<unsigned int>( imgHeight * GUITAR_CHORD_HEIGHT_PERCENTAGE ); +} + +unsigned int +NoteSymbols::getFontPixelSize ( int imgWidth, int imgHeight ) const +{ + return std::max(8, imgHeight / 10); +} + +std::pair<bool, unsigned int> +NoteSymbols::getStringNumber ( int imgWidth, + unsigned int x_pos, + unsigned int maxStringNum ) const +{ + /* + std::cout << "NoteSymbols::getNumberOfStrings - input values" << std::endl + << " X position: " << x_pos << std::endl + << " string #: " << maxStringNum << std::endl + << " image width: " << imgWidth << std::endl; + */ + bool valueOk = false; + + posPair xPairPos; + unsigned int min = 0; + unsigned int max = 0; + unsigned int result = 0; + + for ( unsigned int i = 0; i < maxStringNum; ++i ) { + xPairPos = getX ( imgWidth, i, maxStringNum ); + + // If the counter equals zero then we are at the first + // string to the left + if ( i == 0 ) { + // Add 10 pixel buffer to range comparison + min = xPairPos.first - 10; + } else { + min = xPairPos.first - xPairPos.second / 2; + } + + // If the counter equals the maxString number -1 then we are at the last + // string to the right + if ( i == ( maxStringNum - 1 ) ) { + // Add 10 pixel buffer to range comparison + max = xPairPos.first + 10; + } else { + max = xPairPos.first + xPairPos.second / 2; + } + + if ( ( x_pos >= min ) && ( x_pos <= max ) ) { + result = i; + valueOk = true; + break; + } + } + + //std::cout << "NoteSymbols::getNumberOfStrings - string: #" << result << std::endl; + return std::make_pair( valueOk, result ); +} + +std::pair<bool, unsigned int> +NoteSymbols::getFretNumber ( int imgHeight, + unsigned int y_pos, + unsigned int maxFretNum ) const +{ + /* + std::cout << "NoteSymbols::getNumberOfFrets - input values" << std::endl + << " Y position: " << y_pos << std::endl + << " max frets: " << maxFretNum << std::endl + << " image height: " << imgHeight << std::endl; + */ + + bool valueOk = false; + unsigned int tBorder = getTopBorder( imgHeight ); + unsigned int result = 0; + + if ( y_pos < tBorder ) { + // User pressing above the guitar chord to mark line muted or opened + valueOk = true; + } else { + typedef std::pair<unsigned int, unsigned int> RangePair; + + posPair min_pos; + posPair max_pos; + + for ( unsigned int i = 0; i < maxFretNum; ++i ) { + min_pos = getY ( imgHeight, i, maxFretNum ); + max_pos = getY ( imgHeight, i + 1, maxFretNum ); + + if ( ( y_pos >= min_pos.first ) && y_pos <= max_pos.first - 1 ) { + result = i + 1; + valueOk = true; + break; + } + } + } + // std::cout << " fret #: " << result << std::endl; + return std::make_pair( valueOk, result ); +} + +void +NoteSymbols::drawFingeringPixmap(const Guitar::Fingering& fingering, const Guitar::NoteSymbols& noteSymbols, QPainter *p) +{ + unsigned int startFret = fingering.getStartFret(); + + noteSymbols.drawFretNumber(p, startFret); + noteSymbols.drawFrets(p); + noteSymbols.drawStrings(p); + + unsigned int stringNb = 0; + + for (Fingering::const_iterator pos = fingering.begin(); + pos != fingering.end(); + ++pos, ++stringNb) { + + switch (*pos) { + case Fingering::OPEN: + noteSymbols.drawOpenSymbol(p, stringNb); + break; + + case Fingering::MUTED: + noteSymbols.drawMuteSymbol(p, stringNb); + break; + + default: + noteSymbols.drawNoteSymbol(p, stringNb, *pos - (startFret - 1), false); + break; + } + } + +} + + +float const NoteSymbols::LEFT_BORDER_PERCENTAGE = 0.2; +float const NoteSymbols::RIGHT_BORDER_PERCENTAGE = 0.1; +float const NoteSymbols::GUITAR_CHORD_WIDTH_PERCENTAGE = 0.8; +float const NoteSymbols::TOP_BORDER_PERCENTAGE = 0.1; +float const NoteSymbols::BOTTOM_BORDER_PERCENTAGE = 0.1; +float const NoteSymbols::GUITAR_CHORD_HEIGHT_PERCENTAGE = 0.8; +int const NoteSymbols::TOP_GUITAR_CHORD_MARGIN = 5; +int const NoteSymbols::FRET_PEN_WIDTH = 2; +int const NoteSymbols::STRING_PEN_WIDTH = 2; + +} /* namespace Guitar */ + +} + diff --git a/src/gui/editors/guitar/NoteSymbols.h b/src/gui/editors/guitar/NoteSymbols.h new file mode 100644 index 0000000..f90fefb --- /dev/null +++ b/src/gui/editors/guitar/NoteSymbols.h @@ -0,0 +1,192 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <[email protected]>, + Chris Cannam <[email protected]>, + Richard Bown <[email protected]> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + This file contains code from + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + + +#ifndef _RG_SYMBOLS_H_ +#define _RG_SYMBOLS_H_ + +#include <qbrush.h> +#include <qpainter.h> + +namespace Rosegarden +{ + +/** + *---------------------------------------- + * Finding X position on guitar chord pixmap + *---------------------------------------- + * + * Originally x = position * scale + FC::BORDER + FC::CIRCBORD + FC::FRETTEXT + * + * The last three can be condense into on term called XBorder + * XBorder = FC::BORDER + FC::CIRCBORD + FC::FRETTEXT + * = 5 + 2 + 10 (see fingers.h) + * = 17 + * + * The drawable guitar chord space on the x-axis: + * XGuitarChord = pixmap width - XBorder + * = width - 17 + * + * The guitar chord x-axis is broken up into colums which represent the drawable + * space for a guitar chord component (e.g. note, barre) + * Column Width = XGuitarChord / number of strings + * + * Therefore a new x can be calculated from the position and the column width + * x = (position * Column Width) + XBorder + * + *------------------------------------------- + * Finding Y position on guitar chord pixmap + *------------------------------------------- + * + * Originally y = (FC::BORDER * scale) + (2 * FC::SPACER) + (fret * scale) + FC::CIRCBORD + * + * As with the x-axis the equation can be separated into the position plus the border. In + * this case YBorder + * YBorder = (FC::BORDER*scale) + (2*FC::SPACER) + FC::CIRCBORD + * = 17 (If we want to use the same border as the x-axis) + * + * The drawable guitar chord space on the y-axis: + * YGuitarChord = pixmap height - YBorder + * + * The guitar chord y-axis is broken up into rows which represent the drawable + * space for a guitar chord component (e.g. note, barre) + * Row Height = YGuitarChord / number of frets + * + * Therefore a new y can be calculated from the fret position and the row height + * y = fret * Row Height + **/ + +namespace Guitar +{ + +class Fingering; + + +class NoteSymbols +{ +private: + typedef std::pair<unsigned int, unsigned int> posPair; + + static float const LEFT_BORDER_PERCENTAGE; + static float const RIGHT_BORDER_PERCENTAGE; + static float const GUITAR_CHORD_WIDTH_PERCENTAGE; + static float const TOP_BORDER_PERCENTAGE; + static float const BOTTOM_BORDER_PERCENTAGE; + static float const GUITAR_CHORD_HEIGHT_PERCENTAGE; + static int const TOP_GUITAR_CHORD_MARGIN; + static int const FRET_PEN_WIDTH; + static int const STRING_PEN_WIDTH; + +public: + + NoteSymbols(unsigned int nbOfStrings, unsigned int nbOfFrets) : + m_nbOfStrings(nbOfStrings), + m_nbOfFrets(nbOfFrets) {}; + + //! Display a mute symbol in the QPainter object + void + drawMuteSymbol ( QPainter* p, + unsigned int position ) const; + + /* This code borrowed from KGuitar 0.5 */ + //! Display a open symbol in the QPainter object (KGuitar) + void drawOpenSymbol ( QPainter* p, + unsigned int position ) const; + + /* This code borrowed from KGuitar 0.5 */ + //! Display a note symbol in the QPainter object (KGuitar) + void drawNoteSymbol ( QPainter* p, + unsigned int stringNb, + int fretNb, + bool transient = false ) const; + + /* This code borrowed from KGuitar 0.5 */ + /** + * Display a bar symbol in the QPainter object (KGuitar) + * The code from the KGuitar project was modified to display a bar. This feature was not + * available in that project + */ + void drawBarreSymbol ( QPainter* p, + int fretNb, + unsigned int start, + unsigned int end ) const; + + void drawFretNumber ( QPainter* p, + unsigned int fret_num ) const; + + void drawFrets ( QPainter* p ) const; + + void drawStrings ( QPainter* p ) const; + + unsigned int getTopBorder ( unsigned int imgHeight ) const; + + unsigned int getBottomBorder ( unsigned int imgHeight ) const; + + unsigned int getLeftBorder ( unsigned int imgWidth ) const; + + unsigned int getRightBorder ( unsigned int imgWidth ) const; + + unsigned int getGuitarChordWidth ( int imgWidth ) const; + + unsigned int getGuitarChordHeight ( int imgHeight ) const; + + unsigned int getFontPixelSize ( int imgWidth, int imgHeight ) const; + + std::pair<bool, unsigned int> + getStringNumber ( int imgWidth, + unsigned int x_pos, + unsigned int string_num ) const; + + std::pair<bool, unsigned int> + getFretNumber ( int imgHeight, + unsigned int y_pos, + unsigned int maxFretNum ) const; + + QRect getTransientNoteSymbolRect(QSize guitarChordSize, + unsigned int stringNb, + int fretNb) const; + + static void drawFingeringPixmap(const Fingering& fingering, const NoteSymbols& noteSymbols, QPainter *p); + +private: + + posPair + getX ( int imgWidth, unsigned int stringNb, unsigned int nbOfStrings ) const; + + posPair + getY ( int imgHeight, unsigned int fretNb, unsigned int nbOfFrets ) const; + + + unsigned int m_nbOfStrings; + unsigned int m_nbOfFrets; + +}; + +} /* namespace Guitar */ + +} + +#endif /* SYMBOLS_H_ */ + |