summaryrefslogtreecommitdiff
path: root/noncore/apps/oxygen/kmolcalc.cpp
Unidiff
Diffstat (limited to 'noncore/apps/oxygen/kmolcalc.cpp') (more/less context) (show whitespace changes)
-rw-r--r--noncore/apps/oxygen/kmolcalc.cpp235
1 files changed, 235 insertions, 0 deletions
diff --git a/noncore/apps/oxygen/kmolcalc.cpp b/noncore/apps/oxygen/kmolcalc.cpp
new file mode 100644
index 0000000..7257c4a
--- a/dev/null
+++ b/noncore/apps/oxygen/kmolcalc.cpp
@@ -0,0 +1,235 @@
1/*
2 * kmolcalc.cpp
3 *
4 * Copyright (C) 2000 Tomislav Gountchev <tomi@idiom.com>
5 * Copyright (C) 2002 Carsten Niehaus <cniehaus@handhelds.org>
6 */
7
8/**
9 * KMOLCALC is the calculation engine. It knows about a hashtable of user defined atomic
10 * weights and group definitions ELSTABLE, and the currently processed formula, stored
11 * as a list of elements and their coefficients, ELEMENTS.
12 */
13
14#include "kmolcalc.h"
15#include <qdict.h>
16#include <qdir.h>
17#include <qfile.h>
18#include <iostream.h>
19
20
21/**
22 * Construct a new calculator object.
23 */
24KMolCalc::KMolCalc() {
25 elements = new ElementList;
26 elstable = NULL;
27 readElstable();
28}
29
30KMolCalc::~KMolCalc() {
31 delete elements;
32}
33
34void KMolCalc::readElstable() {
35 weight = -1; // not calculated yet
36 if (elstable) delete elstable;
37 elstable = new QDict<SubUnit> (197, TRUE);
38 elstable->setAutoDelete(TRUE);
39 QStringList files = "/home/opie/opie/noncore/apps/oxigen/kmolweights";
40 mwfile = "/home/opie/opie/noncore/apps/oxigen/kmolweights";
41 QFile f(mwfile);
42 QString* latest_f = &mwfile;
43 for (uint i=0; i<files.count(); i++) {
44 if (QFileInfo(QFile(files[i])).lastModified() > QFileInfo(QFile(*latest_f)).lastModified()) {
45 latest_f = &files[i];
46 }
47 }
48 QFile lf(*latest_f);
49 if (f.exists()) readMwfile(f);
50 if (!f.exists()) {
51 readMwfile(lf);
52 writeElstable();
53 } else if (QFileInfo(f).lastModified() < QFileInfo(lf).lastModified()) {
54 // announce
55 QMessageBox::information
56 (0, "Warning:", "Found new global Mw file.\nLocal definitions will be updated.", QMessageBox::Ok);
57 readMwfile(lf);
58 writeElstable();
59 }
60
61}
62
63
64/**
65 * Parse a string S and construct the ElementList this->ELEMENTS, representing the
66 * composition of S. Returns 0 if successful, or an error code (currently -1) if
67 * parsing failed.
68 * The elements is S must be valid element or group symbols, as stored in this->ELSTABLE.
69 * See help files for correct formula syntax.
70 */
71QString KMolCalc::readFormula(const QString& s) {
72 weight = -1;
73 if (elements) delete elements;
74 elements = new ElementList;
75 return KMolCalc::readGroup(s, elements);
76}
77
78// read a formula group recursively. Called by readFormula.
79QString KMolCalc::readGroup(const QString& s, ElementList* els) {
80 if (s.isEmpty()) return QString ("Enter a formula."); //ERROR
81 int sl = s.length();
82 int i = 0;
83 QString errors ("OK");
84 bool ok = TRUE;
85 while (i < sl && ((s[i] <= '9' && s[i] >= '0') || s[i] == '.')) i++;
86 double prefix = (i == 0 ? 1 : s.left(i).toDouble(&ok));
87 if (! ok || i == sl || prefix == 0) return QString ("Bad formula."); // ERROR
88 ElementList* elstemp = new ElementList;
89 while (i < sl) {
90 int j = i;
91 if (s[i] == '(') {
92 ElementList* inner = new ElementList;
93 int level = 1; // count levels of nested ( ).
94 while (1) {
95 if (i++ == sl) {
96 delete inner;
97 delete elstemp;
98 return QString ("Bad formula."); //ERROR
99 }
100 if (s[i] == '(') level++;
101 if (s[i] == ')') level--;
102 if (level == 0) break;
103 }
104 errors = KMolCalc::readGroup(s.mid(j+1, i-j-1), inner);
105 j = ++i;
106 while (i < sl && ((s[i] <= '9' && s[i] >= '0') || s[i] == '.')) i++;
107 double suffix = (i == j ? 1 : s.mid(j, i-j).toDouble(&ok));
108 if (! ok || suffix == 0) {
109 delete inner;
110 delete elstemp;
111 return QString ("Bad formula."); // ERROR
112 }
113 inner->addTo(*elstemp, suffix);
114 delete inner;
115 inner = NULL;
116 } else if ((s[i] >= 'A' && s[i] <= 'Z') || (s[i] >= 'a' && s[i] <= 'z')) {
117 while (++i < sl && ((s[i] >= 'a' && s[i] <= 'z') || s[i] == '*' ||
118 s[i] == '\''));
119 QString elname = s.mid(j, i-j);
120 j = i;
121 while (i < sl && ((s[i] <= '9' && s[i] >= '0') || s[i] == '.')) i++;
122 double suffix = (i == j ? 1 : s.mid(j, i-j).toDouble(&ok));
123 if (! ok || suffix == 0) {
124 delete elstemp;
125 return QString ("Bad formula."); // ERROR
126 }
127 SubUnit* group = elstable->find(elname);
128 if (group == 0) {
129 delete elstemp;
130 return QString ("Undefined symbol: ") + elname; //ERROR
131 }
132 group->addTo(*elstemp, suffix);
133 } else if (s[i] == '+') {
134 if (elstemp->isEmpty()) {
135 delete elstemp;
136 return QString ("Bad formula."); //ERROR
137 }
138 elstemp->addTo(*els, prefix);
139 delete elstemp;
140 errors = KMolCalc::readGroup(s.mid(i+1, sl-i-1), els);
141 return errors;
142 } else {
143 delete elstemp;
144 return QString ("Bad formula."); //ERROR
145 }
146 }
147 elstemp->addTo(*els, prefix);
148 delete elstemp;
149 return errors;
150}
151
152/**
153 * Calculate and return the molecular weight of the current chemical formula.
154 */
155double KMolCalc::getWeight() {
156 if (weight == -1) weight = elements->getWeight(elstable);
157 return weight;
158}
159
160/**
161 * Return the elemental composition of the current formula, as a string of tab-separated
162 * element - percentage pairs, separated by newlines.
163 */
164QString KMolCalc::getEA() {
165 if (weight == -1) weight = elements->getWeight(elstable);
166 if (weight == -1) return QString("ERROR: Couldn't get Mw..."); // ERROR
167 return elements->getEA(elstable, weight);
168}
169
170/**
171 * Return the empirical formula of the current compound as a QString.
172 */
173QString KMolCalc::getEmpFormula() {
174 return elements->getEmpFormula();
175}
176
177// Read the element definition file.
178void KMolCalc::readMwfile(QFile& f) {
179 if (! f.open(IO_ReadOnly)) return; //ERROR
180 QTextStream fs (&f);
181 QString line;
182 while (! fs.eof()) {
183 line = fs.readLine();
184 SubUnit* s = SubUnit::makeSubUnit(line);
185 elstable->replace(s->getName(), s);
186 }
187 f.close();
188}
189
190/**
191 * Save the element definitions file.
192 */
193void KMolCalc::writeElstable() {
194 QFile f(mwfile);
195 if (! f.open(IO_WriteOnly)) return; //ERROR
196 QTextStream fs (&f);
197 QString line;
198 QDictIterator<SubUnit> it(*elstable);
199 while (it.current()) {
200 it.current()->writeOut(line);
201 fs << line << endl;
202 ++it;
203 }
204 f.close();
205}
206
207/**
208 * Remove a group or element definition from ELSTABLE.
209 */
210void KMolCalc::undefineGroup (const QString& name) {
211 elstable->remove (name);
212}
213
214/**
215 * Add a new element name - atomic weight record to the ELSTABLE hashtable. Assumes
216 * NAME has valid syntax.
217
218 */
219void KMolCalc::defineElement (const QString& name, double weight) {
220 Element* el = new Element(name, weight);
221 elstable->replace(name, el);
222}
223
224/**
225 * Add a new group definition to the ELSTABLE. Returns 0 if OK, -1 if parsing FORMULA
226 * fails. Assumes the syntax of grpname is correct.
227 */
228QString KMolCalc::defineGroup (const QString& grpname, const QString& formula) {
229 ElementList* els = new ElementList(grpname);
230 QString error = readGroup(formula, els);
231 if (error != "OK") return error;
232 if (els->contains(grpname)) return QString("Can't define a group recursively!\n");
233 elstable->replace(grpname, els);
234 return QString("OK");
235}