kformulacompatibility.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2001 Ulrich Kuettler <ulrich.kuettler@mailbox.tu-dresden.de>
3
4 This file is based on the other kformula lib
5 Copyright (C) 1999 Ilya Baran (ibaran@mit.edu)
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details.
16
17 You should have received a copy of the GNU Library General Public License
18 along with this library; see the file COPYING.LIB. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21*/
22
23#include <tqvaluelist.h>
24
25#include <kdebug.h>
26#include "kformuladefs.h"
27#include "kformulacompatibility.h"
28
29KFORMULA_NAMESPACE_BEGIN
30
31const int SYMBOL_ABOVE = 20000;
32const int UNUSED_OFFSET = 1000;
33
34typedef int BoxType;
35
36//const BoxType PLUS = '+';
37//const BoxType MINUS = '-';
38//const BoxType TIMES = '*';
39const BoxType OF_DIVIDE = '\\' + UNUSED_OFFSET;
40const BoxType OF_POWER = '^' + UNUSED_OFFSET; //just a test to see if it works
41const BoxType OF_SQRT = '@' + UNUSED_OFFSET;
42//const BoxType TEXT = 't';
43//const BoxType CAT = '#' + UNUSED_OFFSET;
44const BoxType OF_SUB = '_' + UNUSED_OFFSET;
45const BoxType OF_LSUP = '6' + UNUSED_OFFSET;
46const BoxType OF_LSUB = '%' + UNUSED_OFFSET;
47//const BoxType PAREN = '(';
48//const BoxType EQUAL = '=';
49//const BoxType MORE = '>';
50//const BoxType LESS = '<';
51//const BoxType ABS = '|';
52//const BoxType BRACKET = '[';
53//const BoxType SLASH = '/';
54const BoxType OF_MATRIX = 'm' + UNUSED_OFFSET;
55const BoxType OF_SEPARATOR = '&' + UNUSED_OFFSET; // separator for matrices
56const BoxType OF_ABOVE = ')' + UNUSED_OFFSET; //something useless
57const BoxType OF_BELOW = ']' + UNUSED_OFFSET;
58const BoxType OF_SYMBOL = 's' + UNUSED_OFFSET; // whatever
59// char for keeping track of cursor position in undo/redo:
60//const BoxType CURSOR = 'c' + UNUSED_OFFSET;
61
62const int INTEGRAL = SYMBOL_ABOVE + 0; // symbols have values above that
63const int SUM = SYMBOL_ABOVE + 1;
64const int PRODUCT = SYMBOL_ABOVE + 2;
65const int ARROW = SYMBOL_ABOVE + 3;
66// elements of the symbol font are their own codes + SYMBOL_ABOVE
67
68
69Compatibility::Compatibility()
70{
71}
72
73
74TQDomDocument Compatibility::buildDOM(TQString text)
75{
76 TQDomDocument doc("KFORMULA");
77 pos = 0;
78 formulaString = text;
79 TQDomElement formula = readSequence(doc);
80 formula.setTagName("FORMULA");
81 doc.appendChild(formula);
82 return doc;
83}
84
85
86void Compatibility::appendNextSequence(const TQDomDocument& doc, TQDomElement element)
87{
88 if (hasNext() && nextToken() == '{') {
89 element.appendChild(readSequence(doc));
90 }
91 else {
92 pushback();
93 element.appendChild(doc.createElement("SEQUENCE"));
94 }
95}
96
97
98TQDomElement Compatibility::getLastSequence(const TQDomDocument& doc, TQDomElement sequence)
99{
100 if (sequence.lastChild().nodeName() == "SEQUENCE") {
101 TQDomNode child = sequence.removeChild(sequence.lastChild());
102 return child.toElement();
103 }
104 else {
105 TQDomElement newSeq = doc.createElement("SEQUENCE");
106 if (!sequence.lastChild().isNull()) {
107 TQDomNode child = sequence.removeChild(sequence.lastChild());
108 newSeq.appendChild(child);
109 }
110 return newSeq;
111 }
112}
113
114
115TQDomElement Compatibility::findIndexNode(const TQDomDocument& doc, TQDomElement sequence)
116{
117 TQDomElement element;
118 if (sequence.lastChild().nodeName() == "INDEX") {
119 element = sequence.lastChild().toElement();
120 }
121 else {
122 element = doc.createElement("INDEX");
123 TQDomElement con = doc.createElement("CONTENT");
124 element.appendChild(con);
125 con.appendChild(getLastSequence(doc, sequence));
126 sequence.appendChild(element);
127 }
128 return element;
129}
130
131
132void Compatibility::appendToSequence(TQDomElement sequence, TQDomElement element, int leftIndexSeen)
133{
134 if (leftIndexSeen > 0) {
135 if (sequence.lastChild().nodeName() == "INDEX") {
136 TQDomElement index = sequence.lastChild().toElement();
137 if ((index.firstChild().nodeName() == "CONTENT") &&
138 (index.firstChild().firstChild().nodeName() == "SEQUENCE")) {
139 TQDomElement seq = index.firstChild().firstChild().toElement();
140 if (element.nodeName() == "SEQUENCE") {
141 index.firstChild().replaceChild(element, seq);
142 }
143 else {
144 seq.appendChild(element);
145 }
146 return;
147 }
148 }
149 }
150 sequence.appendChild(element);
151}
152
153
154TQDomElement Compatibility::readMatrix(const TQDomDocument& doc)
155{
156 TQDomElement element = doc.createElement("MATRIX");
157
158 uint cols = nextToken();
159 nextToken();
160 uint rows = nextToken();
161
162 element.setAttribute("ROWS", rows);
163 element.setAttribute("COLUMNS", cols);
164
165 if ((nextToken() == '}') && (nextToken() == OF_MATRIX) && (nextToken() == '{')) {
166 TQValueList<TQDomElement> matrix;
167 for (uint c = 0; c < cols; c++) {
168 for (uint r = 0; r < rows; r++) {
169 if (hasNext() && (nextToken() == '{')) {
170 TQDomElement tmp = readSequence(doc);
171 matrix.append(tmp);
172 }
173 if (hasNext() && (nextToken() != OF_SEPARATOR)) {
174 pushback();
175 }
176 }
177 }
178 if (hasNext() && (nextToken() != '}')) {
179 pushback();
180 }
181
182 if (matrix.count() == rows*cols) {
183 for (uint r = 0; r < rows; r++) {
184 for (uint c = 0; c < cols; c++) {
185 element.appendChild(matrix[c*rows+r]);
186 }
187 }
188 }
189 }
190 else {
191 pushback();
192 }
193
194 return element;
195}
196
197
198TQDomElement Compatibility::readSequence(const TQDomDocument& doc)
199{
200 // matrizes start with something that isn't a sequence
201 if ((tokenLeft() > 6) && (lookAhead(1) == OF_SEPARATOR)) {
202 return readMatrix(doc);
203 }
204
205 int leftIndexSeen = 0;
206 TQDomElement sequence = doc.createElement("SEQUENCE");
207
208 while (hasNext()) {
209 ushort ch = nextToken();
210
211 // Debug
212 //cout << "read: " << ch << " (" << static_cast<char>(ch) << ')' << endl;
213
214 if (leftIndexSeen > 0) leftIndexSeen--;
215
216 switch (ch) {
217 case '{':
218 appendToSequence(sequence, readSequence(doc), leftIndexSeen);
219 break;
220 case '}':
221 return sequence;
222 case '(':
223 case '[':
224 case '|': {
225 // There is an empty sequence we have to remove
226 if (!sequence.lastChild().isNull()) {
227 sequence.removeChild(sequence.lastChild());
228 }
229
230 TQDomElement element = doc.createElement("BRACKET");
231 appendToSequence(sequence, element, leftIndexSeen);
232 element.setAttribute("LEFT", ch);
233 element.setAttribute("RIGHT", (ch=='(') ? ')' : ((ch=='[') ? ']' : '|'));
234
235 TQDomElement con = doc.createElement("CONTENT");
236 element.appendChild(con);
237 appendNextSequence(doc, con);
238 break;
239 }
240 case OF_DIVIDE: {
241 TQDomElement element = doc.createElement("FRACTION");
242
243 TQDomElement num = doc.createElement("NUMERATOR");
244 element.appendChild(num);
245 num.appendChild(getLastSequence(doc, sequence));
246
247 TQDomElement den = doc.createElement("DENOMINATOR");
248 element.appendChild(den);
249 appendNextSequence(doc, den);
250
251 appendToSequence(sequence, element, leftIndexSeen);
252 break;
253 }
254 case OF_SQRT: {
255 TQDomElement element = doc.createElement("ROOT");
256 TQDomElement con = doc.createElement("CONTENT");
257 element.appendChild(con);
258 appendNextSequence(doc, con);
259
260 TQDomElement ind = doc.createElement("INDEX");
261 element.appendChild(ind);
262 ind.appendChild(getLastSequence(doc, sequence));
263
264 appendToSequence(sequence, element, leftIndexSeen);
265 break;
266 }
267 case OF_POWER: {
268 TQDomElement element = findIndexNode(doc, sequence);
269 TQDomElement upperRight = doc.createElement("UPPERRIGHT");
270 element.appendChild(upperRight);
271 appendNextSequence(doc, upperRight);
272 break;
273 }
274 case OF_SUB: {
275 TQDomElement element = findIndexNode(doc, sequence);
276 TQDomElement lowerRight = doc.createElement("LOWERRIGHT");
277 element.appendChild(lowerRight);
278 appendNextSequence(doc, lowerRight);
279 break;
280 }
281 case OF_LSUP: {
282 TQDomElement upperLeft = doc.createElement("UPPERLEFT");
283 upperLeft.appendChild(getLastSequence(doc, sequence));
284 TQDomElement element;
285 if (sequence.lastChild().nodeName() == "INDEX") {
286 element = sequence.lastChild().toElement();
287 }
288 else {
289 element = doc.createElement("INDEX");
290 TQDomElement con = doc.createElement("CONTENT");
291 element.appendChild(con);
292 TQDomElement seq = doc.createElement("SEQUENCE");
293 con.appendChild(seq);
294 appendToSequence(sequence, element, leftIndexSeen);
295 }
296 element.appendChild(upperLeft);
297 leftIndexSeen = 2;
298 break;
299 }
300 case OF_LSUB: {
301 TQDomElement lowerLeft = doc.createElement("LOWERLEFT");
302 lowerLeft.appendChild(getLastSequence(doc, sequence));
303 TQDomElement element;
304 if (sequence.lastChild().nodeName() == "INDEX") {
305 element = sequence.lastChild().toElement();
306 }
307 else {
308 element = doc.createElement("INDEX");
309 TQDomElement con = doc.createElement("CONTENT");
310 element.appendChild(con);
311 TQDomElement seq = doc.createElement("SEQUENCE");
312 con.appendChild(seq);
313 appendToSequence(sequence, element, leftIndexSeen);
314 }
315 element.appendChild(lowerLeft);
316 leftIndexSeen = 2;
317 break;
318 }
319 case OF_ABOVE: {
320 if (sequence.lastChild().nodeName() == "SEQUENCE") {
321 TQDomElement seq = sequence.lastChild().toElement();
322 if ((seq.childNodes().count() == 1) &&
323 ((seq.lastChild().nodeName() == "SYMBOL") ||
324 (seq.lastChild().nodeName() == "INDEX"))) {
325 sequence.removeChild(seq);
326
327 TQDomElement element = seq.lastChild().toElement();
328 TQDomElement upper = (element.nodeName() == "SYMBOL") ?
329 doc.createElement("UPPER") :
330 doc.createElement("UPPERMIDDLE");
331 element.appendChild(upper);
332 appendNextSequence(doc, upper);
333 appendToSequence(sequence, element, leftIndexSeen);
334 break;
335 }
336 }
337 TQDomElement element = findIndexNode(doc, sequence);
338 TQDomElement upper = doc.createElement("UPPERMIDDLE");
339 element.appendChild(upper);
340 appendNextSequence(doc, upper);
341 break;
342 }
343 case OF_BELOW: {
344 if (sequence.lastChild().nodeName() == "SEQUENCE") {
345 TQDomElement seq = sequence.lastChild().toElement();
346 if ((seq.childNodes().count() == 1) &&
347 ((seq.lastChild().nodeName() == "SYMBOL") ||
348 (seq.lastChild().nodeName() == "INDEX"))) {
349 sequence.removeChild(seq);
350
351 TQDomElement element = seq.lastChild().toElement();
352 TQDomElement lower = (element.nodeName() == "SYMBOL") ?
353 doc.createElement("LOWER") :
354 doc.createElement("LOWERMIDDLE");
355 element.appendChild(lower);
356 appendNextSequence(doc, lower);
357 appendToSequence(sequence, element, leftIndexSeen);
358 break;
359 }
360 }
361 TQDomElement element = findIndexNode(doc, sequence);
362 TQDomElement lower = doc.createElement("LOWERMIDDLE");
363 element.appendChild(lower);
364 appendNextSequence(doc, lower);
365 break;
366 }
367 case OF_SYMBOL:
368 kdDebug() << "OF_SYMBOL" << endl;
369 break;
370 case INTEGRAL:
371 case SUM:
372 case PRODUCT: {
373 TQDomElement element = doc.createElement("SYMBOL");
374 element.setAttribute("TYPE",
375 (ch==INTEGRAL) ? Integral :
376 ((ch==SUM) ? Sum : Product));
377
378 TQDomElement con = doc.createElement("CONTENT");
379 element.appendChild(con);
380 con.appendChild(readSequence(doc));
381 pushback();
382 appendToSequence(sequence, element, leftIndexSeen);
383 break;
384 }
385 case ARROW: {
386 TQDomElement element = doc.createElement("TEXT");
387 element.setAttribute("CHAR", TQString(TQChar(static_cast<char>(174))));
388 element.setAttribute("SYMBOL", "1");
389 appendToSequence(sequence, element, leftIndexSeen);
390 break;
391 }
392 default: {
393 TQDomElement element = doc.createElement("TEXT");
394 element.setAttribute("CHAR", TQString(formulaString[pos-1]));
395 appendToSequence(sequence, element, leftIndexSeen);
396 }
397 }
398 }
399 return sequence;
400}
401
402KFORMULA_NAMESPACE_END
TQDomDocument buildDOM(TQString text)
Builds a kformula DOM from a old formula string.