Mercurial > hg > svgui
changeset 376:e1a9e478b7f2
* juggle some files around in order to free audioio, base, and system libraries
from dependency on QtGui
line wrap: on
line diff
--- a/layer/Colour3DPlotLayer.cpp Wed Mar 12 14:40:18 2008 +0000 +++ b/layer/Colour3DPlotLayer.cpp Wed Mar 12 17:42:56 2008 +0000 @@ -18,7 +18,7 @@ #include "view/View.h" #include "base/Profiler.h" #include "base/LogRange.h" -#include "base/ColourMapper.h" +#include "ColourMapper.h" #include <QPainter> #include <QImage>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layer/ColourDatabase.cpp Wed Mar 12 17:42:56 2008 +0000 @@ -0,0 +1,207 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2007 QMUL. + + 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 "ColourDatabase.h" +#include "base/XmlExportable.h" + +#include <QPainter> + +ColourDatabase +ColourDatabase::m_instance; + +ColourDatabase * +ColourDatabase::getInstance() +{ + return &m_instance; +} + +ColourDatabase::ColourDatabase() +{ +} + +int +ColourDatabase::getColourCount() const +{ + return m_colours.size(); +} + +QString +ColourDatabase::getColourName(int c) const +{ + if (c < 0 || size_t(c) >= m_colours.size()) return ""; + return m_colours[c].name; +} + +QColor +ColourDatabase::getColour(int c) const +{ + if (c < 0 || size_t(c) >= m_colours.size()) return Qt::black; + return m_colours[c].colour; +} + +QColor +ColourDatabase::getColour(QString name) const +{ + for (ColourList::const_iterator i = m_colours.begin(); + i != m_colours.end(); ++i) { + if (i->name == name) return i->colour; + } + + return Qt::black; +} + +int +ColourDatabase::getColourIndex(QString name) const +{ + int index = 0; + for (ColourList::const_iterator i = m_colours.begin(); + i != m_colours.end(); ++i) { + if (i->name == name) return index; + ++index; + } + + return -1; +} + +int +ColourDatabase::getColourIndex(QColor c) const +{ + int index = 0; + for (ColourList::const_iterator i = m_colours.begin(); + i != m_colours.end(); ++i) { + if (i->colour == c) return index; + ++index; + } + + return -1; +} + +bool +ColourDatabase::useDarkBackground(int c) const +{ + if (c < 0 || size_t(c) >= m_colours.size()) return false; + return m_colours[c].darkbg; +} + +void +ColourDatabase::setUseDarkBackground(int c, bool dark) +{ + if (c < 0 || size_t(c) >= m_colours.size()) return; + if (m_colours[c].darkbg != dark) { + m_colours[c].darkbg = dark; + emit colourDatabaseChanged(); + } +} + +int +ColourDatabase::addColour(QColor c, QString name) +{ + int index = 0; + for (ColourList::iterator i = m_colours.begin(); + i != m_colours.end(); ++i) { + if (i->name == name) { + i->colour = c; + return index; + } + ++index; + } + + ColourRec rec; + rec.colour = c; + rec.name = name; + rec.darkbg = false; + m_colours.push_back(rec); + emit colourDatabaseChanged(); + return index; +} + +void +ColourDatabase::removeColour(QString name) +{ + for (ColourList::iterator i = m_colours.begin(); + i != m_colours.end(); ++i) { + if (i->name == name) { + m_colours.erase(i); + return; + } + } +} + +void +ColourDatabase::getStringValues(int index, + QString &colourName, + QString &colourSpec, + QString &darkbg) const +{ + colourName = ""; + colourSpec = ""; + if (index < 0 || size_t(index) >= m_colours.size()) return; + + colourName = getColourName(index); + QColor c = getColour(index); + colourSpec = XmlExportable::encodeColour(c.red(), c.green(), c.blue()); + darkbg = useDarkBackground(index) ? "true" : "false"; +} + +int +ColourDatabase::putStringValues(QString colourName, + QString colourSpec, + QString darkbg) +{ + int index = -1; + if (colourSpec != "") { + QColor colour(colourSpec); + index = getColourIndex(colour); + if (index < 0) { + index = addColour(colour, + colourName == "" ? colourSpec : colourName); + } + } else if (colourName != "") { + index = getColourIndex(colourName); + } + if (index >= 0) { + setUseDarkBackground(index, darkbg == "true"); + } + return index; +} + +void +ColourDatabase::getColourPropertyRange(int *min, int *max) const +{ + ColourDatabase *db = getInstance(); + if (min) *min = 0; + if (max) { + *max = 0; + if (db->getColourCount() > 0) *max = db->getColourCount()-1; + } +} + +QPixmap +ColourDatabase::getExamplePixmap(int index, QSize size) const +{ + QPixmap pmap(size); + pmap.fill(useDarkBackground(index) ? Qt::black : Qt::white); + QPainter paint(&pmap); + QColor colour(getColour(index)); + paint.setPen(colour); + paint.setBrush(colour); + int margin = 2; + if (size.width() < 4 || size.height() < 4) margin = 0; + else if (size.width() < 8 || size.height() < 8) margin = 1; + paint.drawRect(margin, margin, + size.width() - margin*2 - 1, size.height() - margin*2 - 1); + return pmap; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layer/ColourDatabase.h Wed Mar 12 17:42:56 2008 +0000 @@ -0,0 +1,84 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2007 QMUL. + + 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 _COLOUR_DATABASE_H_ +#define _COLOUR_DATABASE_H_ + +#include <QObject> +#include <QString> +#include <QColor> +#include <QSize> +#include <QPixmap> +#include <vector> + +class ColourDatabase : public QObject +{ + Q_OBJECT + +public: + static ColourDatabase *getInstance(); + + int getColourCount() const; + QString getColourName(int c) const; + QColor getColour(int c) const; + QColor getColour(QString name) const; + int getColourIndex(QString name) const; // -1 -> not found + int getColourIndex(QColor c) const; // returns first index of possibly many + bool haveColour(QColor c) const; + + bool useDarkBackground(int c) const; + void setUseDarkBackground(int c, bool dark); + + int addColour(QColor, QString); // returns index + void removeColour(QString); + + // returned colour is not necessarily in database + QColor getContrastingColour(int c) const; + + // for use in XML export + void getStringValues(int index, + QString &colourName, + QString &colourSpec, + QString &darkbg) const; + + // for use in XML import + int putStringValues(QString colourName, + QString colourSpec, + QString darkbg); + + // for use by PropertyContainer getPropertyRangeAndValue methods + void getColourPropertyRange(int *min, int *max) const; + + QPixmap getExamplePixmap(int index, QSize size) const; + +signals: + void colourDatabaseChanged(); + +protected: + ColourDatabase(); + + struct ColourRec { + QColor colour; + QString name; + bool darkbg; + }; + + typedef std::vector<ColourRec> ColourList; + ColourList m_colours; + + static ColourDatabase m_instance; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layer/ColourMapper.cpp Wed Mar 12 17:42:56 2008 +0000 @@ -0,0 +1,254 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006-2007 Chris Cannam and QMUL. + + 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 "ColourMapper.h" + +#include <iostream> + +#include <cmath> + +ColourMapper::ColourMapper(int map, float min, float max) : + QObject(), + m_map(map), + m_min(min), + m_max(max) +{ + if (m_min == m_max) { + std::cerr << "WARNING: ColourMapper: min == max (== " << m_min + << "), adjusting" << std::endl; + m_max = m_min + 1; + } +} + +ColourMapper::~ColourMapper() +{ +} + +int +ColourMapper::getColourMapCount() +{ + return 11; +} + +QString +ColourMapper::getColourMapName(int n) +{ + if (n >= getColourMapCount()) return tr("<unknown>"); + StandardMap map = (StandardMap)n; + + switch (map) { + case DefaultColours: return tr("Default"); + case WhiteOnBlack: return tr("White on Black"); + case BlackOnWhite: return tr("Black on White"); + case RedOnBlue: return tr("Red on Blue"); + case YellowOnBlack: return tr("Yellow on Black"); + case BlueOnBlack: return tr("Blue on Black"); + case Sunset: return tr("Sunset"); + case FruitSalad: return tr("Fruit Salad"); + case Banded: return tr("Banded"); + case Highlight: return tr("Highlight"); + case Printer: return tr("Printer"); + } + + return tr("<unknown>"); +} + +QColor +ColourMapper::map(float value) const +{ + float norm = (value - m_min) / (m_max - m_min); + if (norm < 0.f) norm = 0.f; + if (norm > 1.f) norm = 1.f; + + float h = 0.f, s = 0.f, v = 0.f, r = 0.f, g = 0.f, b = 0.f; + bool hsv = true; + +// float red = 0.f, green = 0.3333f; + float blue = 0.6666f, pieslice = 0.3333f; + + if (m_map >= getColourMapCount()) return Qt::black; + StandardMap map = (StandardMap)m_map; + + switch (map) { + + case DefaultColours: + h = blue - norm * 2.f * pieslice; + s = 0.5f + norm/2.f; + v = norm; + break; + + case WhiteOnBlack: + r = g = b = norm; + hsv = false; + break; + + case BlackOnWhite: + r = g = b = 1.f - norm; + hsv = false; + break; + + case RedOnBlue: + h = blue - pieslice/4.f + norm * (pieslice + pieslice/4.f); + s = 1.f; + v = norm; + break; + + case YellowOnBlack: + h = 0.15f; + s = 1.f; + v = norm; + break; + + case BlueOnBlack: + h = blue; + s = 1.f; + v = norm * 2.f; + if (v > 1.f) { + v = 1.f; + s = 1.f - (sqrtf(norm) - 0.707f) * 3.413f; + if (s < 0.f) s = 0.f; + if (s > 1.f) s = 1.f; + } + break; + + case Sunset: + r = (norm - 0.24f) * 2.38f; + if (r > 1.f) r = 1.f; + if (r < 0.f) r = 0.f; + g = (norm - 0.64f) * 2.777f; + if (g > 1.f) g = 1.f; + if (g < 0.f) g = 0.f; + b = (3.6f * norm); + if (norm > 0.277f) b = 2.f - b; + if (b > 1.f) b = 1.f; + if (b < 0.f) b = 0.f; + hsv = false; + break; + + case FruitSalad: + h = blue + (pieslice/6.f) - norm; + if (h < 0.f) h += 1.f; + s = 1.f; + v = 1.f; + break; + + case Banded: + if (norm < 0.125) return Qt::darkGreen; + else if (norm < 0.25) return Qt::green; + else if (norm < 0.375) return Qt::darkBlue; + else if (norm < 0.5) return Qt::blue; + else if (norm < 0.625) return Qt::darkYellow; + else if (norm < 0.75) return Qt::yellow; + else if (norm < 0.875) return Qt::darkRed; + else return Qt::red; + break; + + case Highlight: + if (norm > 0.99) return Qt::white; + else return Qt::darkBlue; + + case Printer: + if (norm > 0.8) { + r = 1.f; + } else if (norm > 0.7) { + r = 0.9f; + } else if (norm > 0.6) { + r = 0.8f; + } else if (norm > 0.5) { + r = 0.7f; + } else if (norm > 0.4) { + r = 0.6f; + } else if (norm > 0.3) { + r = 0.5f; + } else if (norm > 0.2) { + r = 0.4f; + } else { + r = 0.f; + } + r = g = b = 1.f - r; + hsv = false; + break; + } + + if (hsv) { + return QColor::fromHsvF(h, s, v); + } else { + return QColor::fromRgbF(r, g, b); + } +} + +QColor +ColourMapper::getContrastingColour() const +{ + if (m_map >= getColourMapCount()) return Qt::white; + StandardMap map = (StandardMap)m_map; + + switch (map) { + + case DefaultColours: + return QColor(255, 150, 50); + + case WhiteOnBlack: + return Qt::red; + + case BlackOnWhite: + return Qt::darkGreen; + + case RedOnBlue: + return Qt::green; + + case YellowOnBlack: + return QColor::fromHsv(240, 255, 255); + + case BlueOnBlack: + return Qt::red; + + case Sunset: + return Qt::white; + + case FruitSalad: + return Qt::white; + + case Banded: + return Qt::cyan; + + case Highlight: + return Qt::red; + + case Printer: + return Qt::red; + } + + return Qt::white; +} + +bool +ColourMapper::hasLightBackground() const +{ + if (m_map >= getColourMapCount()) return false; + StandardMap map = (StandardMap)m_map; + + switch (map) { + + case BlackOnWhite: + case Printer: + return true; + + default: + return false; + } +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layer/ColourMapper.h Wed Mar 12 17:42:56 2008 +0000 @@ -0,0 +1,68 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006-2007 Chris Cannam and QMUL. + + 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 _COLOUR_MAPPER_H_ +#define _COLOUR_MAPPER_H_ + +#include <QObject> +#include <QColor> +#include <QString> + +/** + * A class for mapping intensity values onto various colour maps. + */ + +class ColourMapper : public QObject +{ + Q_OBJECT + +public: + ColourMapper(int map, float minValue, float maxValue); + virtual ~ColourMapper(); + + enum StandardMap { + DefaultColours, + Sunset, + WhiteOnBlack, + BlackOnWhite, + RedOnBlue, + YellowOnBlack, + BlueOnBlack, + FruitSalad, + Banded, + Highlight, + Printer + }; + + int getMap() const { return m_map; } + float getMinValue() const { return m_min; } + float getMaxValue() const { return m_max; } + + static int getColourMapCount(); + static QString getColourMapName(int n); + + QColor map(float value) const; + + QColor getContrastingColour() const; // for cursors etc + bool hasLightBackground() const; + +protected: + int m_map; + float m_min; + float m_max; +}; + +#endif +
--- a/layer/ImageLayer.cpp Wed Mar 12 14:40:18 2008 +0000 +++ b/layer/ImageLayer.cpp Wed Mar 12 17:42:56 2008 +0000 @@ -571,7 +571,7 @@ m_editingPoint = ImageModel::Point(frame, "", ""); m_originalPoint = m_editingPoint; - if (m_editingCommand) m_editingCommand->finish(); + if (m_editingCommand) finish(m_editingCommand); m_editingCommand = new ImageModel::EditCommand(m_model, "Add Image"); m_editingCommand->addPoint(m_editingPoint); @@ -616,7 +616,7 @@ m_editingCommand->deletePoint(m_editingPoint); } - m_editingCommand->finish(); + finish(m_editingCommand); m_editingCommand = 0; m_editing = false; } @@ -635,7 +635,7 @@ ImageModel::EditCommand *command = new ImageModel::EditCommand(m_model, "Add Image"); command->addPoint(point); - command->finish(); + finish(command); return true; } @@ -654,7 +654,7 @@ m_originalPoint = m_editingPoint; if (m_editingCommand) { - m_editingCommand->finish(); + finish(m_editingCommand); m_editingCommand = 0; } @@ -688,7 +688,7 @@ if (!m_model || !m_editing) return; if (m_editingCommand) { - m_editingCommand->finish(); + finish(m_editingCommand); } m_editingCommand = 0; @@ -746,7 +746,7 @@ } } - command->finish(); + finish(command); } void @@ -780,7 +780,7 @@ } } - command->finish(); + finish(command); } void @@ -799,7 +799,7 @@ if (s.contains(i->frame)) command->deletePoint(*i); } - command->finish(); + finish(command); } void @@ -885,7 +885,7 @@ command->addPoint(newPoint); } - command->finish(); + finish(command); return true; }
--- a/layer/ImageLayer.h Wed Mar 12 14:40:18 2008 +0000 +++ b/layer/ImageLayer.h Wed Mar 12 17:42:56 2008 +0000 @@ -131,6 +131,11 @@ ImageModel::Point m_originalPoint; ImageModel::Point m_editingPoint; ImageModel::EditCommand *m_editingCommand; + + void finish(ImageModel::EditCommand *command) { + Command *c = command->finish(); + if (c) CommandHistory::getInstance()->addCommand(c, false); + } }; #endif
--- a/layer/Layer.cpp Wed Mar 12 14:40:18 2008 +0000 +++ b/layer/Layer.cpp Wed Mar 12 17:42:56 2008 +0000 @@ -16,7 +16,7 @@ #include "Layer.h" #include "view/View.h" #include "data/model/Model.h" -#include "base/CommandHistory.h" +#include "widgets/CommandHistory.h" #include <iostream>
--- a/layer/Layer.h Wed Mar 12 14:40:18 2008 +0000 +++ b/layer/Layer.h Wed Mar 12 17:42:56 2008 +0000 @@ -20,6 +20,8 @@ #include "base/XmlExportable.h" #include "base/Selection.h" +#include "widgets/CommandHistory.h" + #include <QObject> #include <QRect> #include <QXmlAttributes>
--- a/layer/NoteLayer.cpp Wed Mar 12 14:40:18 2008 +0000 +++ b/layer/NoteLayer.cpp Wed Mar 12 17:42:56 2008 +0000 @@ -20,7 +20,7 @@ #include "base/Profiler.h" #include "base/Pitch.h" #include "base/LogRange.h" -#include "base/ColourDatabase.h" +#include "ColourDatabase.h" #include "view/View.h" #include "data/model/NoteModel.h" @@ -623,7 +623,7 @@ m_editingPoint = NoteModel::Point(frame, value, 0, 0.8, tr("New Point")); m_originalPoint = m_editingPoint; - if (m_editingCommand) m_editingCommand->finish(); + if (m_editingCommand) finish(m_editingCommand); m_editingCommand = new NoteModel::EditCommand(m_model, tr("Draw Point")); m_editingCommand->addPoint(m_editingPoint); @@ -665,7 +665,7 @@ { // std::cerr << "NoteLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl; if (!m_model || !m_editing) return; - m_editingCommand->finish(); + finish(m_editingCommand); m_editingCommand = 0; m_editing = false; } @@ -681,7 +681,7 @@ m_editingPoint = *points.begin(); if (m_editingCommand) { - m_editingCommand->finish(); + finish(m_editingCommand); m_editingCommand = 0; } @@ -710,7 +710,7 @@ m_editingCommand->deletePoint(m_editingPoint); - m_editingCommand->finish(); + finish(m_editingCommand); m_editingCommand = 0; m_editing = false; } @@ -729,7 +729,7 @@ m_originalPoint = m_editingPoint; if (m_editingCommand) { - m_editingCommand->finish(); + finish(m_editingCommand); m_editingCommand = 0; } @@ -781,7 +781,7 @@ } m_editingCommand->setName(newName); - m_editingCommand->finish(); + finish(m_editingCommand); } m_editingCommand = 0; @@ -823,7 +823,7 @@ (m_model, tr("Edit Point")); command->deletePoint(note); command->addPoint(newNote); - command->finish(); + finish(command); } delete dialog; @@ -852,7 +852,7 @@ } } - command->finish(); + finish(command); } void @@ -891,7 +891,7 @@ } } - command->finish(); + finish(command); } void @@ -913,7 +913,7 @@ } } - command->finish(); + finish(command); } void @@ -1011,7 +1011,7 @@ command->addPoint(newPoint); } - command->finish(); + finish(command); return true; }
--- a/layer/NoteLayer.h Wed Mar 12 14:40:18 2008 +0000 +++ b/layer/NoteLayer.h Wed Mar 12 17:42:56 2008 +0000 @@ -117,6 +117,11 @@ NoteModel::Point m_editingPoint; NoteModel::EditCommand *m_editingCommand; VerticalScale m_verticalScale; + + void finish(NoteModel::EditCommand *command) { + Command *c = command->finish(); + if (c) CommandHistory::getInstance()->addCommand(c, false); + } }; #endif
--- a/layer/SingleColourLayer.cpp Wed Mar 12 14:40:18 2008 +0000 +++ b/layer/SingleColourLayer.cpp Wed Mar 12 17:42:56 2008 +0000 @@ -14,7 +14,7 @@ */ #include "SingleColourLayer.h" -#include "base/ColourDatabase.h" +#include "ColourDatabase.h" #include "view/View.h" #include <iostream> @@ -106,7 +106,10 @@ int value) const { if (name == "Colour") { - return Layer::getPropertyValueLabel(name, value); + ColourDatabase *db = ColourDatabase::getInstance(); + if (value >= 0 && size_t(value) < db->getColourCount()) { + return db->getColourName(value); + } } return tr("<unknown>"); }
--- a/layer/SliceLayer.cpp Wed Mar 12 14:40:18 2008 +0000 +++ b/layer/SliceLayer.cpp Wed Mar 12 17:42:56 2008 +0000 @@ -20,8 +20,8 @@ #include "base/AudioLevel.h" #include "base/RangeMapper.h" #include "base/RealTime.h" -#include "base/ColourMapper.h" -#include "base/ColourDatabase.h" +#include "ColourMapper.h" +#include "ColourDatabase.h" #include "PaintAssistant.h"
--- a/layer/SpectrogramLayer.cpp Wed Mar 12 14:40:18 2008 +0000 +++ b/layer/SpectrogramLayer.cpp Wed Mar 12 17:42:56 2008 +0000 @@ -23,8 +23,8 @@ #include "base/Preferences.h" #include "base/RangeMapper.h" #include "base/LogRange.h" -#include "base/CommandHistory.h" -#include "base/ColourMapper.h" +#include "widgets/CommandHistory.h" +#include "ColourMapper.h" #include "ImageRegionFinder.h" #include <QPainter>
--- a/layer/SpectrumLayer.cpp Wed Mar 12 14:40:18 2008 +0000 +++ b/layer/SpectrumLayer.cpp Wed Mar 12 17:42:56 2008 +0000 @@ -21,7 +21,7 @@ #include "base/Preferences.h" #include "base/RangeMapper.h" #include "base/Pitch.h" -#include "base/ColourMapper.h" +#include "ColourMapper.h" #include <QPainter> #include <QTextStream>
--- a/layer/TextLayer.cpp Wed Mar 12 14:40:18 2008 +0000 +++ b/layer/TextLayer.cpp Wed Mar 12 17:42:56 2008 +0000 @@ -18,7 +18,7 @@ #include "data/model/Model.h" #include "base/RealTime.h" #include "base/Profiler.h" -#include "base/ColourDatabase.h" +#include "ColourDatabase.h" #include "view/View.h" #include "data/model/TextModel.h" @@ -398,7 +398,7 @@ m_editingPoint = TextModel::Point(frame, height, ""); m_originalPoint = m_editingPoint; - if (m_editingCommand) m_editingCommand->finish(); + if (m_editingCommand) finish(m_editingCommand); m_editingCommand = new TextModel::EditCommand(m_model, "Add Label"); m_editingCommand->addPoint(m_editingPoint); @@ -443,7 +443,7 @@ m_editingCommand->deletePoint(m_editingPoint); } - m_editingCommand->finish(); + finish(m_editingCommand); m_editingCommand = 0; m_editing = false; } @@ -459,7 +459,7 @@ m_editingPoint = *points.begin(); if (m_editingCommand) { - m_editingCommand->finish(); + finish(m_editingCommand); m_editingCommand = 0; } @@ -488,7 +488,7 @@ m_editingCommand->deletePoint(m_editingPoint); - m_editingCommand->finish(); + finish(m_editingCommand); m_editingCommand = 0; m_editing = false; } @@ -508,7 +508,7 @@ m_originalPoint = m_editingPoint; if (m_editingCommand) { - m_editingCommand->finish(); + finish(m_editingCommand); m_editingCommand = 0; } @@ -563,7 +563,7 @@ } m_editingCommand->setName(newName); - m_editingCommand->finish(); + finish(m_editingCommand); } m_editingCommand = 0; @@ -615,7 +615,7 @@ } } - command->finish(); + finish(command); } void @@ -649,7 +649,7 @@ } } - command->finish(); + finish(command); } void @@ -668,7 +668,7 @@ if (s.contains(i->frame)) command->deletePoint(*i); } - command->finish(); + finish(command); } void @@ -767,7 +767,7 @@ command->addPoint(newPoint); } - command->finish(); + finish(command); return true; }
--- a/layer/TextLayer.h Wed Mar 12 14:40:18 2008 +0000 +++ b/layer/TextLayer.h Wed Mar 12 17:42:56 2008 +0000 @@ -102,6 +102,11 @@ TextModel::Point m_originalPoint; TextModel::Point m_editingPoint; TextModel::EditCommand *m_editingCommand; + + void finish(TextModel::EditCommand *command) { + Command *c = command->finish(); + if (c) CommandHistory::getInstance()->addCommand(c, false); + } }; #endif
--- a/layer/TimeInstantLayer.cpp Wed Mar 12 14:40:18 2008 +0000 +++ b/layer/TimeInstantLayer.cpp Wed Mar 12 17:42:56 2008 +0000 @@ -20,7 +20,7 @@ #include "view/View.h" #include "base/Profiler.h" #include "base/Clipboard.h" -#include "base/ColourDatabase.h" +#include "ColourDatabase.h" #include "data/model/SparseOneDimensionalModel.h" @@ -456,7 +456,7 @@ m_editingPoint = SparseOneDimensionalModel::Point(frame, tr("New Point")); - if (m_editingCommand) m_editingCommand->finish(); + if (m_editingCommand) finish(m_editingCommand); m_editingCommand = new SparseOneDimensionalModel::EditCommand(m_model, tr("Draw Point")); m_editingCommand->addPoint(m_editingPoint); @@ -489,7 +489,7 @@ m_model->getSampleRate()) .toText(false).c_str()); m_editingCommand->setName(newName); - m_editingCommand->finish(); + finish(m_editingCommand); m_editingCommand = 0; m_editing = false; } @@ -505,7 +505,7 @@ m_editingPoint = *points.begin(); if (m_editingCommand) { - m_editingCommand->finish(); + finish(m_editingCommand); m_editingCommand = 0; } @@ -533,7 +533,7 @@ m_editingCommand->deletePoint(m_editingPoint); - m_editingCommand->finish(); + finish(m_editingCommand); m_editingCommand = 0; m_editing = false; } @@ -551,7 +551,7 @@ m_editingPoint = *points.begin(); if (m_editingCommand) { - m_editingCommand->finish(); + finish(m_editingCommand); m_editingCommand = 0; } @@ -590,7 +590,7 @@ m_model->getSampleRate()) .toText(false).c_str()); m_editingCommand->setName(newName); - m_editingCommand->finish(); + finish(m_editingCommand); } m_editingCommand = 0; m_editing = false; @@ -624,7 +624,7 @@ new SparseOneDimensionalModel::EditCommand(m_model, tr("Edit Point")); command->deletePoint(point); command->addPoint(newPoint); - command->finish(); + finish(command); } delete dialog; @@ -654,7 +654,7 @@ } } - command->finish(); + finish(command); } void @@ -689,7 +689,7 @@ } } - command->finish(); + finish(command); } void @@ -709,7 +709,7 @@ if (s.contains(i->frame)) command->deletePoint(*i); } - command->finish(); + finish(command); } void @@ -796,7 +796,7 @@ command->addPoint(newPoint); } - command->finish(); + finish(command); return true; }
--- a/layer/TimeInstantLayer.h Wed Mar 12 14:40:18 2008 +0000 +++ b/layer/TimeInstantLayer.h Wed Mar 12 17:42:56 2008 +0000 @@ -112,6 +112,11 @@ SparseOneDimensionalModel::Point m_editingPoint; SparseOneDimensionalModel::EditCommand *m_editingCommand; PlotStyle m_plotStyle; + + void finish(SparseOneDimensionalModel::EditCommand *command) { + Command *c = command->finish(); + if (c) CommandHistory::getInstance()->addCommand(c, false); + } }; #endif
--- a/layer/TimeRulerLayer.cpp Wed Mar 12 14:40:18 2008 +0000 +++ b/layer/TimeRulerLayer.cpp Wed Mar 12 17:42:56 2008 +0000 @@ -19,7 +19,7 @@ #include "data/model/Model.h" #include "base/RealTime.h" -#include "base/ColourDatabase.h" +#include "ColourDatabase.h" #include "view/View.h" #include <QPainter>
--- a/layer/TimeValueLayer.cpp Wed Mar 12 14:40:18 2008 +0000 +++ b/layer/TimeValueLayer.cpp Wed Mar 12 17:42:56 2008 +0000 @@ -19,7 +19,7 @@ #include "base/RealTime.h" #include "base/Profiler.h" #include "base/LogRange.h" -#include "base/ColourDatabase.h" +#include "ColourDatabase.h" #include "view/View.h" #include "data/model/SparseTimeValueModel.h" @@ -29,7 +29,7 @@ #include "widgets/ListInputDialog.h" #include "SpectrogramLayer.h" // for optional frequency alignment -#include "base/ColourMapper.h" +#include "ColourMapper.h" #include <QPainter> #include <QPainterPath> @@ -875,7 +875,7 @@ m_originalPoint = m_editingPoint; - if (m_editingCommand) m_editingCommand->finish(); + if (m_editingCommand) finish(m_editingCommand); m_editingCommand = new SparseTimeValueModel::EditCommand(m_model, tr("Draw Point")); if (!havePoint) { @@ -942,7 +942,7 @@ { // std::cerr << "TimeValueLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl; if (!m_model || !m_editing) return; - m_editingCommand->finish(); + finish(m_editingCommand); m_editingCommand = 0; m_editing = false; } @@ -958,7 +958,7 @@ m_editingPoint = *points.begin(); if (m_editingCommand) { - m_editingCommand->finish(); + finish(m_editingCommand); m_editingCommand = 0; } @@ -987,7 +987,7 @@ m_editingCommand->deletePoint(m_editingPoint); - m_editingCommand->finish(); + finish(m_editingCommand); m_editingCommand = 0; m_editing = false; } @@ -1006,7 +1006,7 @@ m_originalPoint = m_editingPoint; if (m_editingCommand) { - m_editingCommand->finish(); + finish(m_editingCommand); m_editingCommand = 0; } @@ -1058,7 +1058,7 @@ } m_editingCommand->setName(newName); - m_editingCommand->finish(); + finish(m_editingCommand); } m_editingCommand = 0; @@ -1097,7 +1097,7 @@ new SparseTimeValueModel::EditCommand(m_model, tr("Edit Point")); command->deletePoint(point); command->addPoint(newPoint); - command->finish(); + finish(command); } delete dialog; @@ -1127,7 +1127,7 @@ } } - command->finish(); + finish(command); } void @@ -1162,7 +1162,7 @@ } } - command->finish(); + finish(command); } void @@ -1185,7 +1185,7 @@ } } - command->finish(); + finish(command); } void @@ -1401,7 +1401,7 @@ command->addPoint(newPoint); } - command->finish(); + finish(command); return true; }
--- a/layer/TimeValueLayer.h Wed Mar 12 14:40:18 2008 +0000 +++ b/layer/TimeValueLayer.h Wed Mar 12 17:42:56 2008 +0000 @@ -142,6 +142,11 @@ int m_colourMap; PlotStyle m_plotStyle; VerticalScale m_verticalScale; + + void finish(SparseTimeValueModel::EditCommand *command) { + Command *c = command->finish(); + if (c) CommandHistory::getInstance()->addCommand(c, false); + } }; #endif
--- a/layer/WaveformLayer.cpp Wed Mar 12 14:40:18 2008 +0000 +++ b/layer/WaveformLayer.cpp Wed Mar 12 17:42:56 2008 +0000 @@ -19,7 +19,7 @@ #include "view/View.h" #include "base/Profiler.h" #include "base/RangeMapper.h" -#include "base/ColourDatabase.h" +#include "ColourDatabase.h" #include <QPainter> #include <QPixmap>
--- a/layer/layer.pro Wed Mar 12 14:40:18 2008 +0000 +++ b/layer/layer.pro Wed Mar 12 17:42:56 2008 +0000 @@ -15,6 +15,8 @@ # Input HEADERS += Colour3DPlotLayer.h \ + ColourDatabase.h \ + ColourMapper.h \ ImageLayer.h \ ImageRegionFinder.h \ Layer.h \ @@ -32,6 +34,8 @@ TimeValueLayer.h \ WaveformLayer.h SOURCES += Colour3DPlotLayer.cpp \ + ColourDatabase.cpp \ + ColourMapper.cpp \ ImageLayer.cpp \ ImageRegionFinder.cpp \ Layer.cpp \
--- a/view/Pane.cpp Wed Mar 12 14:40:18 2008 +0000 +++ b/view/Pane.cpp Wed Mar 12 17:42:56 2008 +0000 @@ -20,8 +20,8 @@ #include "base/RealTime.h" #include "base/Profiler.h" #include "ViewManager.h" -#include "base/CommandHistory.h" -#include "base/TextAbbrev.h" +#include "widgets/CommandHistory.h" +#include "widgets/TextAbbrev.h" #include "base/Preferences.h" #include "layer/WaveformLayer.h"
--- a/view/ViewManager.cpp Wed Mar 12 14:40:18 2008 +0000 +++ b/view/ViewManager.cpp Wed Mar 12 17:42:56 2008 +0000 @@ -16,7 +16,7 @@ #include "ViewManager.h" #include "base/AudioPlaySource.h" #include "data/model/Model.h" -#include "base/CommandHistory.h" +#include "widgets/CommandHistory.h" #include "View.h" #include <QSettings>
--- a/view/ViewManager.h Wed Mar 12 14:40:18 2008 +0000 +++ b/view/ViewManager.h Wed Mar 12 17:42:56 2008 +0000 @@ -22,6 +22,7 @@ #include <map> +#include "base/ViewManagerBase.h" #include "base/Selection.h" #include "base/Command.h" #include "base/Clipboard.h" @@ -46,7 +47,7 @@ * correctly whether they are supplied with a ViewManager or not. */ -class ViewManager : public QObject +class ViewManager : public ViewManagerBase { Q_OBJECT
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/CommandHistory.cpp Wed Mar 12 17:42:56 2008 +0000 @@ -0,0 +1,487 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + 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. +*/ + +/* + This is a modified version of a source file from the Rosegarden + MIDI and audio sequencer and notation editor, copyright 2000-2006 + Chris Cannam, distributed under the GNU General Public License. + + This file contains traces of the KCommandHistory class from the KDE + project, copyright 2000 Werner Trobin and David Faure and + distributed under the GNU Lesser General Public License. +*/ + +#include "CommandHistory.h" + +#include "base/Command.h" + +#include <QRegExp> +#include <QMenu> +#include <QToolBar> +#include <QString> +#include <QTimer> +#include <QAction> + +#include <iostream> + +CommandHistory *CommandHistory::m_instance = 0; + +CommandHistory::CommandHistory() : + m_undoLimit(50), + m_redoLimit(50), + m_menuLimit(15), + m_savedAt(0), + m_currentCompound(0), + m_executeCompound(false), + m_currentBundle(0), + m_bundleTimer(0), + m_bundleTimeout(5000) +{ + m_undoAction = new QAction(QIcon(":/icons/undo.png"), tr("&Undo"), this); + m_undoAction->setShortcut(tr("Ctrl+Z")); + m_undoAction->setStatusTip(tr("Undo the last editing operation")); + connect(m_undoAction, SIGNAL(triggered()), this, SLOT(undo())); + + m_undoMenuAction = new QAction(QIcon(":/icons/undo.png"), tr("&Undo"), this); + connect(m_undoMenuAction, SIGNAL(triggered()), this, SLOT(undo())); + + m_undoMenu = new QMenu(tr("&Undo")); + m_undoMenuAction->setMenu(m_undoMenu); + connect(m_undoMenu, SIGNAL(triggered(QAction *)), + this, SLOT(undoActivated(QAction*))); + + m_redoAction = new QAction(QIcon(":/icons/redo.png"), tr("Re&do"), this); + m_redoAction->setShortcut(tr("Ctrl+Shift+Z")); + m_redoAction->setStatusTip(tr("Redo the last operation that was undone")); + connect(m_redoAction, SIGNAL(triggered()), this, SLOT(redo())); + + m_redoMenuAction = new QAction(QIcon(":/icons/redo.png"), tr("Re&do"), this); + connect(m_redoMenuAction, SIGNAL(triggered()), this, SLOT(redo())); + + m_redoMenu = new QMenu(tr("Re&do")); + m_redoMenuAction->setMenu(m_redoMenu); + connect(m_redoMenu, SIGNAL(triggered(QAction *)), + this, SLOT(redoActivated(QAction*))); +} + +CommandHistory::~CommandHistory() +{ + m_savedAt = -1; + clearStack(m_undoStack); + clearStack(m_redoStack); + + delete m_undoMenu; + delete m_redoMenu; +} + +CommandHistory * +CommandHistory::getInstance() +{ + if (!m_instance) m_instance = new CommandHistory(); + return m_instance; +} + +void +CommandHistory::clear() +{ +// std::cerr << "CommandHistory::clear()" << std::endl; + closeBundle(); + m_savedAt = -1; + clearStack(m_undoStack); + clearStack(m_redoStack); + updateActions(); +} + +void +CommandHistory::registerMenu(QMenu *menu) +{ + menu->addAction(m_undoAction); + menu->addAction(m_redoAction); +} + +void +CommandHistory::registerToolbar(QToolBar *toolbar) +{ + toolbar->addAction(m_undoMenuAction); + toolbar->addAction(m_redoMenuAction); +} + +void +CommandHistory::addCommand(Command *command) +{ + if (!command) return; + + if (m_currentCompound) { + addToCompound(command, m_executeCompound); + return; + } + + addCommand(command, true); +} + +void +CommandHistory::addCommand(Command *command, bool execute, bool bundle) +{ + if (!command) return; + + if (m_currentCompound) { + addToCompound(command, execute); + return; + } + + if (bundle) { + addToBundle(command, execute); + return; + } else if (m_currentBundle) { + closeBundle(); + } + +// std::cerr << "CommandHistory::addCommand: " << command->getName().toLocal8Bit().data() << " at " << command << std::endl; + + // We can't redo after adding a command +// std::cerr << "CommandHistory::clearing redo stack" << std::endl; + clearStack(m_redoStack); + + // can we reach savedAt? + if ((int)m_undoStack.size() < m_savedAt) m_savedAt = -1; // nope + + m_undoStack.push(command); + clipCommands(); + + if (execute) { + command->execute(); + } + + // Emit even if we aren't executing the command, because + // someone must have executed it for this to make any sense + emit commandExecuted(); + emit commandExecuted(command); + + updateActions(); +} + +void +CommandHistory::addToBundle(Command *command, bool execute) +{ + if (m_currentBundle) { + if (!command || (command->getName() != m_currentBundleName)) { + closeBundle(); + } + } + + if (!command) return; + + if (!m_currentBundle) { + // need to addCommand before setting m_currentBundle, as addCommand + // with bundle false will reset m_currentBundle to 0 + MacroCommand *mc = new MacroCommand(command->getName()); + addCommand(mc, false); + m_currentBundle = mc; + m_currentBundleName = command->getName(); + } + + if (execute) command->execute(); + m_currentBundle->addCommand(command); + + delete m_bundleTimer; + m_bundleTimer = new QTimer(this); + connect(m_bundleTimer, SIGNAL(timeout()), this, SLOT(bundleTimerTimeout())); + m_bundleTimer->start(m_bundleTimeout); +} + +void +CommandHistory::closeBundle() +{ + m_currentBundle = 0; + m_currentBundleName = ""; +} + +void +CommandHistory::bundleTimerTimeout() +{ + closeBundle(); +} + +void +CommandHistory::addToCompound(Command *command, bool execute) +{ +// std::cerr << "CommandHistory::addToCompound: " << command->getName().toLocal8Bit().data() << std::endl; + if (!m_currentCompound) { + std::cerr << "CommandHistory::addToCompound: ERROR: no compound operation in progress!" << std::endl; + return; + } + + if (execute) command->execute(); + m_currentCompound->addCommand(command); +} + +void +CommandHistory::startCompoundOperation(QString name, bool execute) +{ + if (m_currentCompound) { + std::cerr << "CommandHistory::startCompoundOperation: ERROR: compound operation already in progress!" << std::endl; + std::cerr << "(name is " << m_currentCompound->getName().toLocal8Bit().data() << ")" << std::endl; + return; + } + + closeBundle(); + + m_currentCompound = new MacroCommand(name); + m_executeCompound = execute; +} + +void +CommandHistory::endCompoundOperation() +{ + if (!m_currentCompound) { + std::cerr << "CommandHistory::endCompoundOperation: ERROR: no compound operation in progress!" << std::endl; + return; + } + + MacroCommand *toAdd = m_currentCompound; + m_currentCompound = 0; + + if (toAdd->haveCommands()) { + + // We don't execute the macro command here, because we have + // been executing the individual commands as we went along if + // m_executeCompound was true. + addCommand(toAdd, false); + } +} + +void +CommandHistory::addExecutedCommand(Command *command) +{ + addCommand(command, false); +} + +void +CommandHistory::addCommandAndExecute(Command *command) +{ + addCommand(command, true); +} + +void +CommandHistory::undo() +{ + if (m_undoStack.empty()) return; + + closeBundle(); + + Command *command = m_undoStack.top(); + command->unexecute(); + emit commandExecuted(); + emit commandUnexecuted(command); + + m_redoStack.push(command); + m_undoStack.pop(); + + clipCommands(); + updateActions(); + + if ((int)m_undoStack.size() == m_savedAt) emit documentRestored(); +} + +void +CommandHistory::redo() +{ + if (m_redoStack.empty()) return; + + closeBundle(); + + Command *command = m_redoStack.top(); + command->execute(); + emit commandExecuted(); + emit commandExecuted(command); + + m_undoStack.push(command); + m_redoStack.pop(); + // no need to clip + + updateActions(); + + if ((int)m_undoStack.size() == m_savedAt) emit documentRestored(); +} + +void +CommandHistory::setUndoLimit(int limit) +{ + if (limit > 0 && limit != m_undoLimit) { + m_undoLimit = limit; + clipCommands(); + } +} + +void +CommandHistory::setRedoLimit(int limit) +{ + if (limit > 0 && limit != m_redoLimit) { + m_redoLimit = limit; + clipCommands(); + } +} + +void +CommandHistory::setMenuLimit(int limit) +{ + m_menuLimit = limit; + updateActions(); +} + +void +CommandHistory::setBundleTimeout(int ms) +{ + m_bundleTimeout = ms; +} + +void +CommandHistory::documentSaved() +{ + closeBundle(); + m_savedAt = m_undoStack.size(); +} + +void +CommandHistory::clipCommands() +{ + if ((int)m_undoStack.size() > m_undoLimit) { + m_savedAt -= (m_undoStack.size() - m_undoLimit); + } + + clipStack(m_undoStack, m_undoLimit); + clipStack(m_redoStack, m_redoLimit); +} + +void +CommandHistory::clipStack(CommandStack &stack, int limit) +{ + int i; + + if ((int)stack.size() > limit) { + + CommandStack tempStack; + + for (i = 0; i < limit; ++i) { +// Command *command = stack.top(); +// std::cerr << "CommandHistory::clipStack: Saving recent command: " << command->getName().toLocal8Bit().data() << " at " << command << std::endl; + tempStack.push(stack.top()); + stack.pop(); + } + + clearStack(stack); + + for (i = 0; i < m_undoLimit; ++i) { + stack.push(tempStack.top()); + tempStack.pop(); + } + } +} + +void +CommandHistory::clearStack(CommandStack &stack) +{ + while (!stack.empty()) { + Command *command = stack.top(); + // Not safe to call getName() on a command about to be deleted +// std::cerr << "CommandHistory::clearStack: About to delete command " << command << std::endl; + delete command; + stack.pop(); + } +} + +void +CommandHistory::undoActivated(QAction *action) +{ + int pos = m_actionCounts[action]; + for (int i = 0; i <= pos; ++i) { + undo(); + } +} + +void +CommandHistory::redoActivated(QAction *action) +{ + int pos = m_actionCounts[action]; + for (int i = 0; i <= pos; ++i) { + redo(); + } +} + +void +CommandHistory::updateActions() +{ + m_actionCounts.clear(); + + for (int undo = 0; undo <= 1; ++undo) { + + QAction *action(undo ? m_undoAction : m_redoAction); + QAction *menuAction(undo ? m_undoMenuAction : m_redoMenuAction); + QMenu *menu(undo ? m_undoMenu : m_redoMenu); + CommandStack &stack(undo ? m_undoStack : m_redoStack); + + if (stack.empty()) { + + QString text(undo ? tr("Nothing to undo") : tr("Nothing to redo")); + + action->setEnabled(false); + action->setText(text); + + menuAction->setEnabled(false); + menuAction->setText(text); + + } else { + + action->setEnabled(true); + menuAction->setEnabled(true); + + QString commandName = stack.top()->getName(); + commandName.replace(QRegExp("&"), ""); + + QString text = (undo ? tr("&Undo %1") : tr("Re&do %1")) + .arg(commandName); + + action->setText(text); + menuAction->setText(text); + } + + menu->clear(); + + CommandStack tempStack; + int j = 0; + + while (j < m_menuLimit && !stack.empty()) { + + Command *command = stack.top(); + tempStack.push(command); + stack.pop(); + + QString commandName = command->getName(); + commandName.replace(QRegExp("&"), ""); + + QString text; + if (undo) text = tr("&Undo %1").arg(commandName); + else text = tr("Re&do %1").arg(commandName); + + QAction *action = menu->addAction(text); + m_actionCounts[action] = j++; + } + + while (!tempStack.empty()) { + stack.push(tempStack.top()); + tempStack.pop(); + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/CommandHistory.h Wed Mar 12 17:42:56 2008 +0000 @@ -0,0 +1,226 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + 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. +*/ + +/* + This is a modified version of a source file from the Rosegarden + MIDI and audio sequencer and notation editor, copyright 2000-2006 + Chris Cannam, distributed under the GNU General Public License. + + This file contains traces of the KCommandHistory class from the KDE + project, copyright 2000 Werner Trobin and David Faure and + distributed under the GNU Lesser General Public License. +*/ + +#ifndef _MULTI_VIEW_COMMAND_HISTORY_H_ +#define _MULTI_VIEW_COMMAND_HISTORY_H_ + +#include <QObject> +#include <QString> + +#include <stack> +#include <set> +#include <map> + +class Command; +class MacroCommand; +class QAction; +class QMenu; +class QToolBar; +class QTimer; + +/** + * The CommandHistory class stores a list of executed commands and + * maintains Undo and Redo actions synchronised with those commands. + * + * CommandHistory allows you to associate more than one Undo and Redo + * menu or toolbar with the same command history, and it keeps them + * all up-to-date at once. This makes it effective in systems where + * multiple views may be editing the same data. + */ + +class CommandHistory : public QObject +{ + Q_OBJECT + +public: + virtual ~CommandHistory(); + + static CommandHistory *getInstance(); + + void clear(); + + void registerMenu(QMenu *menu); + void registerToolbar(QToolBar *toolbar); + + /** + * Add a command to the command history. + * + * The command will normally be executed before being added; but + * if a compound operation is in use (see startCompoundOperation + * below), the execute status of the compound operation will + * determine whether the command is executed or not. + */ + void addCommand(Command *command); + + /** + * Add a command to the command history. + * + * If execute is true, the command will be executed before being + * added. Otherwise it will be assumed to have been already + * executed -- a command should not be added to the history unless + * its work has actually been done somehow! + * + * If a compound operation is in use (see startCompoundOperation + * below), the execute value passed to this method will override + * the execute status of the compound operation. In this way it's + * possible to have a compound operation mixing both to-execute + * and pre-executed commands. + * + * If bundle is true, the command will be a candidate for bundling + * with any adjacent bundleable commands that have the same name, + * into a single compound command. This is useful for small + * commands that may be executed repeatedly altering the same data + * (e.g. type text, set a parameter) whose number and extent is + * not known in advance. The bundle parameter will be ignored if + * a compound operation is already in use. + */ + void addCommand(Command *command, bool execute, bool bundle = false); + + /// Return the maximum number of items in the undo history. + int getUndoLimit() const { return m_undoLimit; } + + /// Set the maximum number of items in the undo history. + void setUndoLimit(int limit); + + /// Return the maximum number of items in the redo history. + int getRedoLimit() const { return m_redoLimit; } + + /// Set the maximum number of items in the redo history. + void setRedoLimit(int limit); + + /// Return the maximum number of items visible in undo and redo menus. + int getMenuLimit() const { return m_menuLimit; } + + /// Set the maximum number of items in the menus. + void setMenuLimit(int limit); + + /// Return the time after which a bundle will be closed if nothing is added. + int getBundleTimeout() const { return m_bundleTimeout; } + + /// Set the time after which a bundle will be closed if nothing is added. + void setBundleTimeout(int msec); + + /// Start recording commands to batch up into a single compound command. + void startCompoundOperation(QString name, bool execute); + + /// Finish recording commands and store the compound command. + void endCompoundOperation(); + +public slots: + /** + * Checkpoint function that should be called when the document is + * saved. If the undo/redo stack later returns to the point at + * which the document was saved, the documentRestored signal will + * be emitted. + */ + virtual void documentSaved(); + + /** + * Add a command to the history that has already been executed, + * without executing it again. Equivalent to addCommand(command, false). + */ + void addExecutedCommand(Command *); + + /** + * Add a command to the history and also execute it. Equivalent + * to addCommand(command, true). + */ + void addCommandAndExecute(Command *); + + void undo(); + void redo(); + +protected slots: + void undoActivated(QAction *); + void redoActivated(QAction *); + void bundleTimerTimeout(); + +signals: + /** + * Emitted whenever a command has just been executed or + * unexecuted, whether by addCommand, undo, or redo. + */ + void commandExecuted(); + + /** + * Emitted whenever a command has just been executed, whether by + * addCommand or redo. + */ + void commandExecuted(Command *); + + /** + * Emitted whenever a command has just been unexecuted, whether by + * addCommand or undo. + */ + void commandUnexecuted(Command *); + + /** + * Emitted when the undo/redo stack has reached the same state at + * which the documentSaved slot was last called. + */ + void documentRestored(); + +protected: + CommandHistory(); + static CommandHistory *m_instance; + + QAction *m_undoAction; + QAction *m_redoAction; + QAction *m_undoMenuAction; + QAction *m_redoMenuAction; + QMenu *m_undoMenu; + QMenu *m_redoMenu; + + std::map<QAction *, int> m_actionCounts; + + typedef std::stack<Command *> CommandStack; + CommandStack m_undoStack; + CommandStack m_redoStack; + + int m_undoLimit; + int m_redoLimit; + int m_menuLimit; + int m_savedAt; + + MacroCommand *m_currentCompound; + bool m_executeCompound; + void addToCompound(Command *command, bool execute); + + MacroCommand *m_currentBundle; + QString m_currentBundleName; + QTimer *m_bundleTimer; + int m_bundleTimeout; + void addToBundle(Command *command, bool execute); + void closeBundle(); + + void updateActions(); + + void clipCommands(); + + void clipStack(CommandStack &stack, int limit); + void clearStack(CommandStack &stack); +}; + + +#endif
--- a/widgets/PluginParameterDialog.cpp Wed Mar 12 14:40:18 2008 +0000 +++ b/widgets/PluginParameterDialog.cpp Wed Mar 12 17:42:56 2008 +0000 @@ -18,7 +18,7 @@ #include "PluginParameterBox.h" #include "WindowTypeSelector.h" -#include "base/TextAbbrev.h" +#include "TextAbbrev.h" #include "vamp-sdk/Plugin.h" #include "vamp-sdk/PluginHostAdapter.h"
--- a/widgets/PropertyBox.cpp Wed Mar 12 14:40:18 2008 +0000 +++ b/widgets/PropertyBox.cpp Wed Mar 12 17:42:56 2008 +0000 @@ -19,8 +19,8 @@ #include "base/PropertyContainer.h" #include "base/PlayParameters.h" #include "layer/Layer.h" +#include "layer/ColourDatabase.h" #include "base/UnitDatabase.h" -#include "base/ColourDatabase.h" #include "base/RangeMapper.h" #include "plugin/RealTimePluginFactory.h" @@ -607,12 +607,14 @@ PropertyContainer::PropertyType type = m_container->getPropertyType(name); + Command *c = 0; + if (type == PropertyContainer::UnitsProperty) { NotifyingComboBox *cb = dynamic_cast<NotifyingComboBox *>(obj); if (cb) { QString unit = cb->currentText(); - m_container->setPropertyWithCommand + c = m_container->getSetPropertyCommand (name, UnitDatabase::getInstance()->getUnitId(unit)); } @@ -625,12 +627,14 @@ return; } } - m_container->setPropertyWithCommand(name, value); + c = m_container->getSetPropertyCommand(name, value); } else if (type != PropertyContainer::InvalidProperty) { - m_container->setPropertyWithCommand(name, value); + c = m_container->getSetPropertyCommand(name, value); } + + if (c) CommandHistory::getInstance()->addCommand(c, true, true); updateContextHelp(obj); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/TextAbbrev.cpp Wed Mar 12 17:42:56 2008 +0000 @@ -0,0 +1,271 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006-2007 Chris Cannam and QMUL. + + 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 "TextAbbrev.h" + +#include <QFontMetrics> +#include <QApplication> + +#include <iostream> + +QString +TextAbbrev::getDefaultEllipsis() +{ + return "..."; +} + +int +TextAbbrev::getFuzzLength(QString ellipsis) +{ + int len = ellipsis.length(); + if (len < 3) return len + 3; + else if (len > 5) return len + 5; + else return len * 2; +} + +int +TextAbbrev::getFuzzWidth(const QFontMetrics &metrics, QString ellipsis) +{ + int width = metrics.width(ellipsis); + return width * 2; +} + +QString +TextAbbrev::abbreviateTo(QString text, int characters, Policy policy, + QString ellipsis) +{ + switch (policy) { + + case ElideEnd: + case ElideEndAndCommonPrefixes: + text = text.left(characters) + ellipsis; + break; + + case ElideStart: + text = ellipsis + text.right(characters); + break; + + case ElideMiddle: + if (characters > 2) { + text = text.left(characters/2 + 1) + ellipsis + + text.right(characters - (characters/2 + 1)); + } else { + text = text.left(characters) + ellipsis; + } + break; + } + + return text; +} + +QString +TextAbbrev::abbreviate(QString text, int maxLength, Policy policy, bool fuzzy, + QString ellipsis) +{ + if (ellipsis == "") ellipsis = getDefaultEllipsis(); + int fl = (fuzzy ? getFuzzLength(ellipsis) : 0); + if (maxLength <= ellipsis.length()) maxLength = ellipsis.length() + 1; + if (text.length() <= maxLength + fl) return text; + + int truncated = maxLength - ellipsis.length(); + return abbreviateTo(text, truncated, policy, ellipsis); +} + +QString +TextAbbrev::abbreviate(QString text, + const QFontMetrics &metrics, int &maxWidth, + Policy policy, QString ellipsis) +{ + if (ellipsis == "") ellipsis = getDefaultEllipsis(); + + int tw = metrics.width(text); + + if (tw <= maxWidth) { + maxWidth = tw; + return text; + } + + int truncated = text.length(); + QString original = text; + + while (tw > maxWidth && truncated > 1) { + + truncated--; + + if (truncated > ellipsis.length()) { + text = abbreviateTo(original, truncated, policy, ellipsis); + } else { + break; + } + + tw = metrics.width(text); + } + + maxWidth = tw; + return text; +} + +QStringList +TextAbbrev::abbreviate(const QStringList &texts, int maxLength, + Policy policy, bool fuzzy, QString ellipsis) +{ + if (policy == ElideEndAndCommonPrefixes && + texts.size() > 1) { + + if (ellipsis == "") ellipsis = getDefaultEllipsis(); + int fl = (fuzzy ? getFuzzLength(ellipsis) : 0); + if (maxLength <= ellipsis.length()) maxLength = ellipsis.length() + 1; + + int maxOrigLength = 0; + for (int i = 0; i < texts.size(); ++i) { + int len = texts[i].length(); + if (len > maxOrigLength) maxOrigLength = len; + } + if (maxOrigLength <= maxLength + fl) return texts; + + return abbreviate(elidePrefixes + (texts, maxOrigLength - maxLength, ellipsis), + maxLength, ElideEnd, fuzzy, ellipsis); + } + + QStringList results; + for (int i = 0; i < texts.size(); ++i) { + results.push_back + (abbreviate(texts[i], maxLength, policy, fuzzy, ellipsis)); + } + return results; +} + +QStringList +TextAbbrev::abbreviate(const QStringList &texts, const QFontMetrics &metrics, + int &maxWidth, Policy policy, QString ellipsis) +{ + if (policy == ElideEndAndCommonPrefixes && + texts.size() > 1) { + + if (ellipsis == "") ellipsis = getDefaultEllipsis(); + + int maxOrigWidth = 0; + for (int i = 0; i < texts.size(); ++i) { + int w = metrics.width(texts[i]); + if (w > maxOrigWidth) maxOrigWidth = w; + } + + return abbreviate(elidePrefixes(texts, metrics, + maxOrigWidth - maxWidth, ellipsis), + metrics, maxWidth, ElideEnd, ellipsis); + } + + QStringList results; + int maxAbbrWidth = 0; + for (int i = 0; i < texts.size(); ++i) { + int width = maxWidth; + QString abbr = abbreviate(texts[i], metrics, width, policy, ellipsis); + if (width > maxAbbrWidth) maxAbbrWidth = width; + results.push_back(abbr); + } + maxWidth = maxAbbrWidth; + return results; +} + +QStringList +TextAbbrev::elidePrefixes(const QStringList &texts, + int targetReduction, + QString ellipsis) +{ + if (texts.empty()) return texts; + int plen = getPrefixLength(texts); + int fl = getFuzzLength(ellipsis); + if (plen < fl) return texts; + + QString prefix = texts[0].left(plen); + int truncated = plen; + if (plen >= targetReduction + fl) { + truncated = plen - targetReduction; + } else { + truncated = fl; + } + prefix = abbreviate(prefix, truncated, ElideEnd, false, ellipsis); + + QStringList results; + for (int i = 0; i < texts.size(); ++i) { + results.push_back + (prefix + texts[i].right(texts[i].length() - plen)); + } + return results; +} + +QStringList +TextAbbrev::elidePrefixes(const QStringList &texts, + const QFontMetrics &metrics, + int targetWidthReduction, + QString ellipsis) +{ + if (texts.empty()) return texts; + int plen = getPrefixLength(texts); + int fl = getFuzzLength(ellipsis); + if (plen < fl) return texts; + + QString prefix = texts[0].left(plen); + int pwid = metrics.width(prefix); + int twid = pwid - targetWidthReduction; + if (twid < metrics.width(ellipsis) * 2) twid = metrics.width(ellipsis) * 2; + prefix = abbreviate(prefix, metrics, twid, ElideEnd, ellipsis); + + QStringList results; + for (int i = 0; i < texts.size(); ++i) { + results.push_back + (prefix + texts[i].right(texts[i].length() - plen)); + } + return results; +} + +static bool +havePrefix(QString prefix, const QStringList &texts) +{ + for (int i = 1; i < texts.size(); ++i) { + if (!texts[i].startsWith(prefix)) return false; + } + return true; +} + +int +TextAbbrev::getPrefixLength(const QStringList &texts) +{ + QString reference = texts[0]; + + if (reference == "" || havePrefix(reference, texts)) { + return reference.length(); + } + + int candidate = reference.length(); + QString splitChars(";:,./#-!()$_+=[]{}\\"); + + while (--candidate > 1) { + if (splitChars.contains(reference[candidate])) { + if (havePrefix(reference.left(candidate), texts)) { + break; + } + } + } + +// std::cerr << "TextAbbrev::getPrefixLength: prefix length is " << candidate << std::endl; +// for (int i = 0; i < texts.size(); ++i) { +// std::cerr << texts[i].left(candidate).toStdString() << "|" << texts[i].right(texts[i].length() - candidate).toStdString() << std::endl; +// } + + return candidate; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/TextAbbrev.h Wed Mar 12 17:42:56 2008 +0000 @@ -0,0 +1,108 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006-2007 Chris Cannam and QMUL. + + 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 _TEXT_ABBREV_H_ +#define _TEXT_ABBREV_H_ + +#include <QString> +#include <QStringList> + +class QFontMetrics; + +class TextAbbrev +{ +public: + enum Policy { + ElideEnd, + ElideEndAndCommonPrefixes, + ElideStart, + ElideMiddle + }; + + /** + * Abbreviate the given text to the given maximum length + * (including ellipsis), using the given abbreviation policy. If + * fuzzy is true, the text will be left alone if it is "not much + * more than" the maximum length. + * + * If ellipsis is non-empty, it will be used to show elisions in + * preference to the default (which is "..."). + */ + static QString abbreviate(QString text, int maxLength, + Policy policy = ElideEnd, + bool fuzzy = true, + QString ellipsis = ""); + + /** + * Abbreviate the given text to the given maximum painted width, + * using the given abbreviation policy. maxWidth is also modified + * so as to return the painted width of the abbreviated text. + * + * If ellipsis is non-empty, it will be used to show elisions in + * preference to the default (which is tr("...")). + */ + static QString abbreviate(QString text, + const QFontMetrics &metrics, + int &maxWidth, + Policy policy = ElideEnd, + QString ellipsis = ""); + + /** + * Abbreviate all of the given texts to the given maximum length, + * using the given abbreviation policy. If fuzzy is true, texts + * that are "not much more than" the maximum length will be left + * alone. + * + * If ellipsis is non-empty, it will be used to show elisions in + * preference to the default (which is tr("...")). + */ + static QStringList abbreviate(const QStringList &texts, int maxLength, + Policy policy = ElideEndAndCommonPrefixes, + bool fuzzy = true, + QString ellipsis = ""); + + /** + * Abbreviate all of the given texts to the given maximum painted + * width, using the given abbreviation policy. maxWidth is also + * modified so as to return the maximum painted width of the + * abbreviated texts. + * + * If ellipsis is non-empty, it will be used to show elisions in + * preference to the default (which is tr("...")). + */ + static QStringList abbreviate(const QStringList &texts, + const QFontMetrics &metrics, + int &maxWidth, + Policy policy = ElideEndAndCommonPrefixes, + QString ellipsis = ""); + +protected: + static QString getDefaultEllipsis(); + static int getFuzzLength(QString ellipsis); + static int getFuzzWidth(const QFontMetrics &metrics, QString ellipsis); + static QString abbreviateTo(QString text, int characters, + Policy policy, QString ellipsis); + static QStringList elidePrefixes(const QStringList &texts, + int targetReduction, + QString ellipsis); + static QStringList elidePrefixes(const QStringList &texts, + const QFontMetrics &metrics, + int targetWidthReduction, + QString ellipsis); + static int getPrefixLength(const QStringList &texts); +}; + +#endif +
--- a/widgets/widgets.pro Wed Mar 12 14:40:18 2008 +0000 +++ b/widgets/widgets.pro Wed Mar 12 17:42:56 2008 +0000 @@ -16,6 +16,7 @@ # Input HEADERS += AudioDial.h \ ColourNameDialog.h \ + CommandHistory.h \ Fader.h \ IconLoader.h \ ImageDialog.h \ @@ -37,12 +38,14 @@ PropertyStack.h \ RangeInputDialog.h \ SubdividingMenu.h \ + TextAbbrev.h \ Thumbwheel.h \ TipDialog.h \ WindowShapePreview.h \ WindowTypeSelector.h SOURCES += AudioDial.cpp \ ColourNameDialog.cpp \ + CommandHistory.cpp \ Fader.cpp \ IconLoader.cpp \ ImageDialog.cpp \ @@ -64,6 +67,7 @@ PropertyStack.cpp \ RangeInputDialog.cpp \ SubdividingMenu.cpp \ + TextAbbrev.cpp \ Thumbwheel.cpp \ TipDialog.cpp \ WindowShapePreview.cpp \