1
/* This file is part of the KDE project
2
Copyright (C) 2006 Martin Pfeiffer <hubipete@gmx.net>
3
2009 Jeremias Epperlein <jeeree@web.de>
5
This library is free software; you can redistribute it and/or
6
modify it under the terms of the GNU Library General Public
7
License as published by the Free Software Foundation; either
8
version 2 of the License, or (at your option) any later version.
10
This library is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
Library General Public License for more details.
15
You should have received a copy of the GNU Library General Public License
16
along with this library; see the file COPYING.LIB. If not, write to
17
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18
Boston, MA 02110-1301, USA.
21
#include "MultiscriptElement.h"
22
#include "AttributeManager.h"
23
#include <KoXmlWriter.h>
24
#include <KoXmlReader.h>
26
#include "FormulaCursor.h"
29
MultiscriptElement::MultiscriptElement( BasicElement* parent ) : FixedElement( parent )
31
m_baseElement = new RowElement( this );
34
MultiscriptElement::~MultiscriptElement()
37
//Delete all of the scripts
38
while (!m_preScripts.isEmpty())
39
delete m_preScripts.takeFirst();
40
while (!m_postScripts.isEmpty())
41
delete m_postScripts.takeFirst();
44
void MultiscriptElement::paint( QPainter& painter, AttributeManager* am )
48
/*do nothing as this element has no visual representation*/
51
void MultiscriptElement::ensureEvenNumberElements() {
52
if(m_postScripts.size() % 2 == 1) {
53
//Odd number - at a None element on the end
54
m_postScripts.append(NULL);
56
if(m_preScripts.size() % 2 == 1) {
57
//Odd number - at a None element on the end
58
m_preScripts.append(NULL);
62
void MultiscriptElement::layout( const AttributeManager* am )
64
// Get the minimum amount of shifting
65
double subscriptshift = am->doubleOf( "subscriptshift", this );
66
double superscriptshift = am->doubleOf( "superscriptshift", this );
67
//Add half a thin space between both sup and superscript, so there is a minimum
68
//of a whole thin space between them.
69
double halfthinspace = am->layoutSpacing( this )/2.0;
71
//First make sure that there are an even number of elements in both subscript and superscript
72
ensureEvenNumberElements();
74
// Go through all the superscripts (pre and post) and find the maximum heights;
75
// BaseLine is the distance from the top to the baseline.
76
// Depth is the distance from the baseline to the bottom
77
double maxSuperScriptDepth = 0.0;
78
double maxSuperScriptBaseLine = 0.0;
79
double maxSubScriptDepth = 0.0;
80
double maxSubScriptBaseLine = 0.0;
81
bool isSuperscript = true; //Toggle after each element time
82
foreach( BasicElement *script, m_postScripts ) {
83
isSuperscript = !isSuperscript; //Toggle each time
85
continue; // Null means no element - just a blank
87
maxSuperScriptDepth = qMax( script->height() - script->baseLine(), maxSuperScriptDepth );
88
maxSuperScriptBaseLine = qMax( script->baseLine(), maxSuperScriptBaseLine );
90
//Find out how much the subscript sticks below the baseline
91
maxSubScriptDepth = qMax( script->height() - script->baseLine(), maxSubScriptDepth );
92
maxSubScriptBaseLine = qMax( script->baseLine(), maxSubScriptBaseLine );
95
foreach( BasicElement *script, m_preScripts ) {
96
isSuperscript = !isSuperscript; //Toggle each time
98
continue; // Null means no element - just a blank
100
maxSuperScriptDepth = qMax( script->height() - script->baseLine(), maxSuperScriptDepth );
101
maxSuperScriptBaseLine = qMax( script->baseLine(), maxSuperScriptBaseLine );
103
//Find out how much the subscript sticks below the baseline
104
maxSubScriptDepth = qMax( script->height() - script->baseLine(), maxSubScriptDepth );
105
maxSubScriptBaseLine = qMax( script->baseLine(), maxSubScriptBaseLine );
108
// The yOffsetBase is the amount the base element is moved down to make
109
// room for the superscript
110
double yOffsetBase = 0;
111
if(maxSuperScriptDepth + maxSuperScriptBaseLine > 0) {
112
yOffsetBase = maxSuperScriptDepth + maxSuperScriptBaseLine - m_baseElement->height()/2.0 + halfthinspace;
113
yOffsetBase = qMax( yOffsetBase, superscriptshift );
115
// The yOffsetSub is the amount the subscript elements /baseline/ are moved down.
116
double yOffsetSub = yOffsetBase + maxSubScriptBaseLine +
117
qMax( m_baseElement->height()/2 + halfthinspace,
118
m_baseElement->height() - maxSubScriptBaseLine
121
double xOffset = 0.0; //We increment this as we go along, to keep track of where to place elements
122
double lastSuperScriptWidth= 0.0;
123
// Now we have all the information needed to start putting elements in place.
124
// We start from the far left, and work to the far right.
125
for( int i = m_preScripts.size()-1; i >= 0; i--) {
126
//We start from the end, and work in.
127
//m_preScripts[0] is subscript etc. So even i is subscript, odd i is superscript
129
// i is even, so subscript
130
if(!m_preScripts[i]) {
131
xOffset += lastSuperScriptWidth;
133
// For a given vertical line, this is processed after the superscript
134
double offset = qMax(0.0, (lastSuperScriptWidth - m_preScripts[i]->width())/2.0);
135
m_preScripts[i]->setOrigin( QPointF(
137
yOffsetSub - m_preScripts[i]->baseLine() ) );
138
xOffset += qMax(lastSuperScriptWidth, m_preScripts[i]->width());
140
if(i!=0) //No halfthinspace between the first element and the base element
141
xOffset += halfthinspace;
143
// i is odd, so superscript
144
// For a given vertical line, we process the superscript first, then
145
// the subscript. We need to look at the subscript (i-1) as well
146
// to find out how to align them
147
if( !m_preScripts[i] )
148
lastSuperScriptWidth = 0.0;
150
lastSuperScriptWidth = m_preScripts[i]->width();
152
if(m_preScripts[i-1]) //the subscript directly below us.
153
offset = qMax(0.0, (m_preScripts[i-1]->width() - lastSuperScriptWidth)/2.0);
154
m_preScripts[i]->setOrigin( QPointF(
156
maxSuperScriptBaseLine - m_preScripts[i]->baseLine()));
161
//We have placed all the prescripts now. So now place the base element
162
m_baseElement->setOrigin( QPointF( xOffset, yOffsetBase ) );
163
xOffset += m_baseElement->width();
164
double lastSubScriptWidth = 0.0;
165
//Now we can draw the post scripts. This code is very similar, but this time we will parse
166
//the subscript before the superscript
167
for( int i = 0; i < m_postScripts.size(); i++) {
168
//We start from the start, and work out.
169
//m_preScripts[0] is subscript etc. So even i is subscript, odd i is superscript
171
// i is even, so subscript
172
// For a given vertical line, we process the subscript first, then
173
// the superscript. We need to look at the superscript (i+1) as well
174
// to find out how to align them
176
if(!m_postScripts[i]) {
177
lastSubScriptWidth = 0.0;
179
lastSubScriptWidth = m_postScripts[i]->width();
180
// For a given vertical line, this is processed after the superscript
182
if(m_postScripts.size() > i+1 && m_postScripts[i+1] != NULL) //the subscript directly below us.
183
offset = qMax(0.0, (m_postScripts[i+1]->width() - lastSubScriptWidth)/2.0);
184
m_postScripts[i]->setOrigin( QPointF(
186
yOffsetSub - m_postScripts[i]->baseLine() ) );
189
// i is odd, so superscript
190
if( !m_postScripts[i] )
191
xOffset += lastSubScriptWidth;
193
double offset = qMax(0.0, (lastSubScriptWidth - m_postScripts[i]->width())/2.0);
194
m_postScripts[i]->setOrigin( QPointF(
196
maxSuperScriptBaseLine - m_postScripts[i]->baseLine()));
197
xOffset += qMax(lastSubScriptWidth, m_postScripts[i]->width());
199
if(i != m_postScripts.size()-1)
200
xOffset += halfthinspace; //Don't add an unneeded space at the very end
205
//Finally, set our boundingbox
207
setHeight( yOffsetSub + maxSubScriptDepth );
208
setBaseLine( yOffsetBase + m_baseElement->baseLine() );
211
bool MultiscriptElement::acceptCursor( const FormulaCursor& cursor )
217
const QList<BasicElement*> MultiscriptElement::childElements() const
219
QList<BasicElement*> list;
220
for (int i=m_preScripts.count()-2;i>=0; i-=2 ) {
221
if(m_preScripts[i]) list << m_preScripts[i];
222
if(m_preScripts[i+1]) list << m_preScripts[i+1];
224
list << m_baseElement;
225
foreach( BasicElement* tmp, m_postScripts ) {
234
QString MultiscriptElement::attributesDefaultValue( const QString& attribute ) const
236
Q_UNUSED( attribute )
240
ElementType MultiscriptElement::elementType() const
245
bool MultiscriptElement::readMathMLContent( const KoXmlElement& parent )
247
QString name = parent.tagName().toLower();
248
BasicElement* tmpElement = 0;
250
bool prescript = false; //When we see a mprescripts tag, we enable this
251
bool baseElement = true;
252
forEachElement( tmp, parent ) {
253
if(tmp.tagName() == "none") {
254
//In mathml, we read subscript, then superscript, etc. To skip one,
256
//To represent "none" we use a NULL pointer
258
m_preScripts.append(NULL);
260
m_postScripts.append(NULL);
262
} else if(tmp.tagName() == "mprescripts") {
264
//In mathml, when we see this tag, all the elements after it are
269
tmpElement = ElementFactory::createElement( tmp.tagName(), this );
270
if( !tmpElement->readMathML( tmp ) )
272
if( baseElement ) { //Very first element is the base
273
delete m_baseElement;
274
m_baseElement = tmpElement;
278
m_preScripts.append( tmpElement );
280
m_postScripts.append( tmpElement );
282
ensureEvenNumberElements();
283
Q_ASSERT(m_baseElement); //We should have at least a BasicElement for the base
287
void MultiscriptElement::writeMathMLContent( KoXmlWriter* writer ) const
289
m_baseElement->writeMathML( writer ); // Just save the children in
291
foreach( BasicElement* tmp, m_postScripts ) {
293
tmp->writeMathML( writer );
295
//We need to use a none element for missing elements in the super/sub scripts
296
writer->startElement("none");
297
writer->endElement();
300
if( m_preScripts.isEmpty() ) return;
301
writer->startElement("mprescripts");
302
writer->endElement();
303
foreach( BasicElement* tmp, m_preScripts ) {
305
tmp->writeMathML( writer );
307
//We need to use a none element for missing elements in the super/sub scripts
308
writer->startElement("none");
309
writer->endElement();
314
// int MultiscriptElement::length() const
316
// if (!m_postScripts.isEmpty() && m_postScripts.last()==0) {
317
// //the last element is empty, so there are no cursor positions around it
318
// return 2*(m_preScripts.count()+m_postScripts.count())-1;
320
// return 2*(m_preScripts.count()+m_postScripts.count()+1)-1;
324
bool MultiscriptElement::moveCursor ( FormulaCursor& newcursor, FormulaCursor& oldcursor )
326
// grouppositions: 1 3 1 3
330
//TODO: Fill this out
332
int childposition=newcursor.position()/2;
333
//this should be cached
334
int prescriptCount=0;
335
foreach (BasicElement* tmp, m_preScripts) {
340
if (childposition==prescriptCount) {
341
//we are in BasePosition
342
if (newcursor.direction()==MoveUp || newcursor.direction()==MoveDown) {
345
if (m_postScripts.isEmpty() && m_preScripts.isEmpty()) {
346
//this should not happen
347
return moveSingleSituation(newcursor,oldcursor,
348
childElements().indexOf(m_baseElement));
350
if (newcursor.direction()==MoveLeft) {
351
if (!m_preScripts.isEmpty()) {
352
// we search for the first non NULL element to the left
354
for (i=0; i<m_preScripts.count(); i++) {
355
if (m_preScripts[i]) {
359
if ((i<m_preScripts.count()) && m_preScripts[i]) {
360
return moveHorSituation(newcursor,oldcursor,
361
childElements().indexOf(m_preScripts[i]),
362
childElements().indexOf(m_baseElement));
365
return moveSingleSituation(newcursor,oldcursor,0);
366
} else if (newcursor.direction()==MoveRight) {
367
if (!m_postScripts.isEmpty()) {
368
// we search for the first non NULL element to the left
370
for (i=0; i<m_postScripts.count(); i++) {
371
if (m_postScripts[i]) {
375
if (m_postScripts[i]) {
376
return moveHorSituation(newcursor,oldcursor,
377
childElements().indexOf(m_baseElement),
378
childElements().indexOf(m_postScripts[i]));
381
return moveSingleSituation(newcursor,oldcursor,
382
childElements().indexOf(m_baseElement));
387
if (childposition<prescriptCount) {
388
//determine the position in the pre-/postscripts we are in
389
groupposition=m_preScripts.indexOf(childElements()[childposition]);
391
groupposition=m_postScripts.indexOf(childElements()[childposition]);
394
int pair=groupposition/2;
395
if (newcursor.direction()==MoveUp || newcursor.direction()==MoveDown) {
396
// kDebug()<<groupposition<<" - "<<prescriptCount<< "-" <<pair;
398
if (m_preScripts[pair*2] && m_preScripts[pair*2+1]) {
399
return moveVertSituation(newcursor,oldcursor,
400
childElements().indexOf(m_preScripts[pair*2+1]),
401
childElements().indexOf(m_preScripts[pair*2]));
406
if (m_postScripts[pair*2] && m_postScripts[pair*2+1]) {
407
return moveVertSituation(newcursor,oldcursor,
408
childElements().indexOf(m_postScripts[pair*2+1]),
409
childElements().indexOf(m_postScripts[pair*2]));
414
} else if (newcursor.direction()==MoveLeft) {
416
//we are in the prescripts
417
int i=groupposition+2;
418
if (!((i<m_preScripts.count()) && m_preScripts[i])) {
419
for (i=groupposition+1; i<m_preScripts.count(); i++) {
420
if (m_preScripts[i]) {
425
if ((i<m_preScripts.count()) && m_preScripts[i]) {
426
return moveHorSituation(newcursor,oldcursor,
427
childElements().indexOf(m_preScripts[i]),
428
childElements().indexOf(m_preScripts[groupposition]));
430
return moveSingleSituation(newcursor,oldcursor,
431
childElements().indexOf(m_preScripts[groupposition]));
434
//we are in the postscripts
435
int i=groupposition-1;
436
if (!(i>=0) && m_postScripts[i]) {
437
for (i=groupposition-2; i>=0; i--) {
438
if (m_postScripts[i]) {
443
if ((i>=0) && m_postScripts[i]) {
444
return moveHorSituation(newcursor,oldcursor,
445
childElements().indexOf(m_postScripts[i]),
446
childElements().indexOf(m_postScripts[groupposition]));
448
return moveHorSituation(newcursor,oldcursor,
449
childElements().indexOf(m_baseElement),
450
childElements().indexOf(elementNext(newcursor.position())));
453
} else if (newcursor.direction()==MoveRight) {
455
//we are in the prescripts
456
int i=groupposition-2;
457
if (!((i>=0) && m_preScripts[i])) {
458
for (i=groupposition-1; i>=0; i--) {
459
if (m_preScripts[i]) {
464
if ((i>=0) && m_preScripts[i]) {
465
// kDebug()<<"Going from "<< groupposition <<" to " <<i;
466
return moveHorSituation(newcursor,oldcursor,
467
childElements().indexOf(m_preScripts[groupposition]),
468
childElements().indexOf(m_preScripts[i]));
470
return moveHorSituation(newcursor,oldcursor,
471
childElements().indexOf(elementNext(newcursor.position())),
472
childElements().indexOf(m_baseElement));
475
//we are in the postscripts
476
int i=groupposition+2;
477
if (!((i<m_postScripts.count()) && m_postScripts[i])) {
478
for (i=groupposition+1; i<m_postScripts.count(); i++) {
479
if (m_postScripts[i]) {
484
if ((i<m_postScripts.count()) && m_postScripts[i]) {
485
return moveHorSituation(newcursor,oldcursor,
486
childElements().indexOf(m_postScripts[groupposition]),
487
childElements().indexOf(m_postScripts[i]));
489
return moveSingleSituation(newcursor,oldcursor,
490
childElements().indexOf(m_preScripts[groupposition]));
498
bool MultiscriptElement::setCursorTo ( FormulaCursor& cursor, QPointF point )
500
if (cursor.isSelecting()) {
503
foreach (BasicElement* tmp, childElements()) {
504
if (tmp->boundingRect().contains(point)) {
505
return tmp->setCursorTo(cursor,point-tmp->origin());
508
return m_baseElement->setCursorTo(cursor,point-m_baseElement->origin());