File indexing completed on 2024-05-12 16:34:16

0001 /* This file is part of the KDE project
0002  * Copyright 2007 Marijn Kruisselbrink <mkruisselbrink@kde.org>
0003  * Copyright 2011 Inge Wallin <inge@lysator.liu.se>
0004  *
0005  * This library is free software; you can redistribute it and/or
0006  * modify it under the terms of the GNU Library General Public
0007  * License as published by the Free Software Foundation; either
0008  * version 2 of the License, or (at your option) any later version.
0009  *
0010  * This library is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013  * Library General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU Library General Public License
0016  * along with this library; see the file COPYING.LIB.  If not, write to
0017  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018  * Boston, MA 02110-1301, USA.
0019  */
0020 #include "MusicShape.h"
0021 
0022 #include <limits.h>
0023 
0024 #include <QPainter>
0025 #include <QBuffer>
0026 #include <QByteArray>
0027 #include <QSvgGenerator>
0028 
0029 #include "MusicDebug.h"
0030 #include <klocalizedstring.h>
0031 
0032 #include <KoViewConverter.h>
0033 #include <KoShapeSavingContext.h>
0034 #include <KoXmlWriter.h>
0035 #include <KoXmlReader.h>
0036 #include <KoEmbeddedDocumentSaver.h>
0037 
0038 #include "core/Sheet.h"
0039 #include "core/Part.h"
0040 #include "core/Voice.h"
0041 #include "core/Staff.h"
0042 #include "core/VoiceBar.h"
0043 #include "core/Chord.h"
0044 #include "core/Note.h"
0045 #include "core/Clef.h"
0046 #include "core/Bar.h"
0047 #include "core/KeySignature.h"
0048 #include "core/TimeSignature.h"
0049 #include "core/MusicXmlWriter.h"
0050 #include "core/MusicXmlReader.h"
0051 #include "core/StaffSystem.h"
0052 
0053 #include "MusicStyle.h"
0054 #include "Engraver.h"
0055 #include "Renderer.h"
0056 
0057 using namespace MusicCore;
0058 
0059 //static MusicShape* firstShape = 0;
0060 
0061 MusicShape::MusicShape()
0062     : KoFrameShape("http://www.calligra.org/music", "shape"),
0063     m_firstSystem(0),
0064     m_style(new MusicStyle),
0065     m_engraver(new Engraver()),
0066     m_renderer(new MusicRenderer(m_style)),
0067     m_successor(0),
0068     m_predecessor(0)
0069 {
0070 /*    debugMusic << "firstShape:" << firstShape << "this:" << this;
0071 
0072     if (firstShape) {
0073         firstShape->m_successor = this;
0074         m_predecessor = firstShape;
0075         m_sheet = firstShape->m_sheet;
0076         m_firstSystem = firstShape->m_lastSystem+1;
0077         m_engraver->engraveSheet(m_sheet, m_firstSystem, QSizeF(1e9, 1e9), true, &m_lastSystem);
0078         firstShape = this;
0079     } else {
0080         firstShape = this;*/
0081         m_sheet = new Sheet();
0082         Bar* bar = m_sheet->addBar();
0083 
0084         Part* part = m_sheet->addPart(i18n("Part 1"));
0085         Staff* staff = part->addStaff();
0086         part->addVoice();
0087         bar->addStaffElement(new Clef(staff, 0, Clef::Trebble, 2, 0));
0088         bar->addStaffElement(new TimeSignature(staff, 0, 4, 4));
0089         // add some more default bars
0090         for (int i = 0; i < 9; i++) {
0091             m_sheet->addBar();
0092         }
0093 
0094         m_engraver->engraveSheet(m_sheet, 0, QSizeF(1e9, 1e9), true, &m_lastSystem);
0095 //    }
0096 }
0097 
0098 MusicShape::~MusicShape()
0099 {
0100     //debugMusic << "destroying" << this;
0101     if (!m_predecessor && !m_successor) {
0102         delete m_sheet;
0103     }
0104     delete m_style;
0105     delete m_engraver;
0106     delete m_renderer;
0107 //    if (this == firstShape) firstShape = this->m_predecessor;
0108 }
0109 
0110 void MusicShape::setSize( const QSizeF &newSize )
0111 {
0112     KoShape::setSize(newSize);
0113 
0114     engrave(false);
0115 }
0116 
0117 void MusicShape::paint( QPainter& painter, const KoViewConverter& converter, KoShapePaintingContext &)
0118 {
0119     constPaint( painter, converter );
0120 }
0121 
0122 void MusicShape::constPaint( QPainter& painter, const KoViewConverter& converter ) const
0123 {
0124     applyConversion( painter, converter );
0125 
0126     painter.setClipping(true);
0127     painter.setClipRect(QRectF(0, 0, size().width(), size().height()), Qt::IntersectClip);
0128 
0129     m_renderer->renderSheet( painter, m_sheet, m_firstSystem, m_lastSystem );
0130 }
0131 
0132 void MusicShape::saveOdf( KoShapeSavingContext & context ) const
0133 {
0134     // The name of this object in the ODF file.
0135     KoEmbeddedDocumentSaver &fileSaver = context.embeddedSaver();
0136     QString objectName = fileSaver.getFilename("Object");
0137 
0138     KoXmlWriter& writer = context.xmlWriter();
0139     writer.startElement("draw:frame");
0140     saveOdfAttributes(context, OdfAllAttributes);
0141 
0142     writer.startElement("music:shape");
0143     writer.addAttribute("xmlns:music", "http://www.calligra.org/music");
0144     MusicXmlWriter().writeSheet(writer, m_sheet, false);
0145     writer.endElement(); // music:shape
0146 
0147     const qreal previewZoom = 150 / 72.; // 150 DPI
0148     QSizeF imgSize = size(); // in points
0149     imgSize *= previewZoom;
0150     KoViewConverter converter;
0151     
0152     // Save a preview SVG image.
0153     // -------------------------
0154 
0155     // 1. Set up the svg renderer.
0156     QByteArray svgContents;           // The contents
0157     QBuffer svgBuffer(&svgContents);  // The corresponding QIODevice
0158     QSvgGenerator svg;
0159     svg.setOutputDevice(&svgBuffer);  // Write to the buffer
0160     svg.setSize(imgSize.toSize());
0161     svg.setViewBox(QRect(0, 0, boundingRect().width(), boundingRect().height()));
0162         
0163     // 2. Paint the svg preview image.
0164     //
0165     // We need to create all text as paths, because otherwise it
0166     // will be difficult for most people to preview the SVG
0167     // image. Not many people have music fonts installed.
0168     QPainter svgPainter;
0169     svgPainter.begin(&svg);
0170     svgPainter.setRenderHint(QPainter::Antialiasing);
0171     svgPainter.setRenderHint(QPainter::TextAntialiasing);
0172     m_style->setTextAsPath(true);
0173     constPaint(svgPainter, converter);
0174     m_style->setTextAsPath(false);
0175     svgPainter.end();
0176 
0177     // 3. Create the xml to embed the svg image and the contents to the file.
0178     writer.startElement("draw:image");
0179     QString name = QString("ObjectReplacements/") + objectName + ".svg";
0180     writer.addAttribute("xlink:type", "simple" );
0181     writer.addAttribute("xlink:show", "embed" );
0182     writer.addAttribute("xlink:actuate", "onLoad");
0183     writer.addAttribute("xlink:href", name);
0184     writer.endElement(); // draw:image
0185     fileSaver.saveFile(name, "image/svg+xml", svgContents);
0186 
0187     // Save a preview bitmap image.
0188     // ----------------------------
0189 
0190     // 1. Create the image.
0191     QImage img(imgSize.toSize(), QImage::Format_ARGB32);
0192     QPainter painter(&img);
0193     painter.setRenderHint(QPainter::Antialiasing);
0194     painter.setRenderHint(QPainter::TextAntialiasing);
0195     converter.setZoom(previewZoom);
0196     constPaint(painter, converter);
0197 
0198     // 2. Create the xml to embed the svg image and the contents to the file.
0199     writer.startElement("draw:image");
0200     name = context.imageHref(img);
0201     // FIXME: Find out how to save a picture using the embeddedSaver and saveFile()
0202     //name = QString("ObjectReplacements/") + objectName + ".png";
0203     writer.addAttribute("xlink:type", "simple" );
0204     writer.addAttribute("xlink:show", "embed" );
0205     writer.addAttribute("xlink:actuate", "onLoad");
0206     writer.addAttribute("xlink:href", name);
0207     writer.endElement(); // draw:image
0208 
0209     saveOdfCommonChildElements(context);
0210     writer.endElement(); // draw:frame
0211 }
0212 
0213 bool MusicShape::loadOdf( const KoXmlElement & element, KoShapeLoadingContext &context ) {
0214     loadOdfAttributes(element, context, OdfAllAttributes);
0215     return loadOdfFrame(element, context);
0216 }
0217 
0218 bool MusicShape::loadOdfFrameElement( const KoXmlElement & element, KoShapeLoadingContext & /*context*/ )
0219 {
0220     KoXmlElement score = KoXml::namedItemNS(element, "http://www.calligra.org/music", "score-partwise");
0221     if (score.isNull()) {
0222         warnMusic << "no music:score-partwise element as first child";
0223         return false;
0224     }
0225     Sheet* sheet = MusicXmlReader().loadSheet(score);
0226     if (sheet) {
0227         if (!m_predecessor && !m_successor) {
0228             delete m_sheet;
0229         }
0230         m_sheet = sheet;
0231         m_engraver->engraveSheet(m_sheet, m_firstSystem, size(), true, &m_lastSystem);
0232         return true;
0233     }
0234     return false;
0235 }
0236 
0237 Sheet* MusicShape::sheet()
0238 {
0239     return m_sheet;
0240 }
0241 
0242 void MusicShape::setSheet(Sheet* sheet, int firstSystem)
0243 {
0244     if (!m_predecessor && !m_successor) {
0245         delete m_sheet;
0246     }
0247     m_sheet = sheet;
0248     m_firstSystem = firstSystem;
0249     m_engraver->engraveSheet(m_sheet, m_firstSystem, size(), true, &m_lastSystem);
0250 }
0251 
0252 int MusicShape::firstSystem() const
0253 {
0254     return m_firstSystem;
0255 }
0256 
0257 void MusicShape::setFirstSystem(int system)
0258 {
0259     m_firstSystem = system;
0260     engrave();
0261     update();
0262 }
0263 
0264 int MusicShape::lastSystem() const
0265 {
0266     return m_lastSystem;
0267 }
0268 
0269 int MusicShape::firstBar() const
0270 {
0271     return m_sheet->staffSystem(m_firstSystem)->firstBar();    
0272 }
0273 
0274 int MusicShape::lastBar() const
0275 {
0276     int lastBar = INT_MAX;
0277     if (m_lastSystem < m_sheet->staffSystemCount()-1) {
0278         lastBar = m_sheet->staffSystem(m_lastSystem+1)->firstBar()-1;
0279     }
0280     return lastBar;
0281 }
0282 
0283 MusicRenderer* MusicShape::renderer()
0284 {
0285     return m_renderer;
0286 }
0287 
0288 void MusicShape::engrave(bool engraveBars)
0289 {
0290     m_engraver->engraveSheet(m_sheet, m_firstSystem, size(), engraveBars, &m_lastSystem);
0291     if (m_successor) {
0292         m_successor->setFirstSystem(m_lastSystem+1);
0293     }
0294 }
0295 
0296 MusicStyle* MusicShape::style()
0297 {
0298     return m_style;
0299 }
0300