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