1 /***************************************************************************
2 rkvariable - description
3 -------------------
4 begin : Thu Aug 12 2004
5 copyright : (C) 2004, 2007, 2008, 2010, 2011, 2012 by Thomas Friedrichsmeier
6 email : thomas.friedrichsmeier@kdemail.net
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17 #include "rkvariable.h"
18
19 #include <qstringlist.h>
20 #include "float.h"
21 #include <cmath>
22
23 #include "rcontainerobject.h"
24 #include "robjectlist.h"
25
26 #include "../rbackend/rkrinterface.h"
27 #include "../rkglobals.h"
28 #include "rkmodificationtracker.h"
29
30 #define GET_DATA_COMMAND 11
31
32 #define MAX_PRECISION DBL_DIG
33
34 #include "../debug.h"
35
RKVariable(RContainerObject * parent,const QString & name)36 RKVariable::RKVariable (RContainerObject *parent, const QString &name) : RObject (parent, name) {
37 RK_TRACE (OBJECTS);
38 type = Variable;
39 data = 0;
40 setDataType (RObject::DataNumeric);
41 }
42
~RKVariable()43 RKVariable::~RKVariable () {
44 RK_TRACE (OBJECTS);
45
46 RK_ASSERT (!data); // endEdit() should have been called
47 }
48
setVarType(RObject::RDataType new_type,bool sync)49 void RKVariable::setVarType (RObject::RDataType new_type, bool sync) {
50 RK_TRACE (OBJECTS);
51
52 if (getDataType () == new_type) return;
53 if ((new_type < RObject::MinKnownDataType) || (new_type > RObject::MaxKnownDataType)) {
54 new_type = RObject::DataCharacter;
55 }
56
57 // if the variable is currently opened for editing, all values need to be rechecked / resynced
58 if (data) {
59 // quick and dirty approach! TODO: make more efficient
60 QStringList list;
61 list.reserve (getLength ());
62 bool labelled = (new_type == DataCharacter);
63 for (int i=0; i < getLength (); ++i) list.append (getText (i, labelled));
64
65 // all pending changes are moot
66 discardUnsyncedChanges ();
67
68 // store what we want to keep of the edit data
69 int num_listeners = data->num_listeners;
70 ValueLabels *value_labels = data->value_labels;
71 data->value_labels = 0; // prevent destruction
72 FormattingOptions formatting_options = data->formatting_options;
73
74 // destroy and re-allocate edit data
75 data->num_listeners = 0; // to avoid the otherwise useful assert in discardEditData
76 bool pending = isPending ();
77 discardEditData ();
78 if (pending) type |= Pending; // flag is cleared in discardEditData()
79 setDataType (new_type);
80 allocateEditData ();
81
82 // re-set presistent aspects of the edit data
83 data->value_labels = value_labels;
84 data->formatting_options = formatting_options;
85 data->num_listeners = num_listeners;
86
87 // re-set all data
88 lockSyncing (true);
89 for (int i = list.size () - 1; i >= 0; --i) setText (i, list[i]);
90
91 if (sync) {
92 QString command = ".rk.set.vector.mode(" + getFullName () + ", ";
93 if (new_type == RObject::DataCharacter) command += "as.character";
94 else if (new_type == RObject::DataNumeric) command += "as.numeric";
95 else if (new_type == RObject::DataLogical) command += "as.logical";
96 else if (new_type == RObject::DataFactor) command += "as.factor";
97 command += ')';
98 RKGlobals::rInterface ()->issueCommand (command, RCommand::App | RCommand::Sync, QString ());
99 if (new_type == RObject::DataFactor) updateValueLabels (); // as.factor resets the "levels"-attribute!
100
101 syncDataToR ();
102 } else discardUnsyncedChanges ();
103 lockSyncing (false);
104 } else {
105 setDataType (new_type);
106 }
107 }
108
writeMetaData(RCommandChain * chain)109 void RKVariable::writeMetaData (RCommandChain *chain) {
110 RK_TRACE (OBJECTS);
111
112 writeValueLabels (chain);
113 RObject::writeMetaData (chain);
114 }
115
rCommandDone(RCommand * command)116 void RKVariable::rCommandDone (RCommand *command) {
117 RK_TRACE (OBJECTS);
118
119 if (command->getFlags () == ROBJECT_UDPATE_STRUCTURE_COMMAND) {
120 RObject::rCommandDone (command);
121 } else if (command->getFlags () == GET_DATA_COMMAND) {
122 if (!data) return; // this can happen, if the editor is closed while a data update is still queued.
123
124 // prevent resyncing of data
125 lockSyncing (true);
126
127 RK_ASSERT (command->getDataType () == RData::StructureVector);
128 RK_ASSERT (command->getDataLength () == 3);
129
130 RData::RDataStorage top = command->structureVector ();
131 RData *cdata = top.at (0);
132 RData *levels = top.at (1);
133 RData *invalids = top.at (2);
134
135 // set factor levels first
136 RK_ASSERT (levels->getDataType () == RData::StringVector);
137 QStringList new_levels = levels->stringVector ();
138 int levels_len = new_levels.size ();
139 RK_ASSERT (levels_len >= 1);
140 delete data->value_labels;
141 data->value_labels = new RObject::ValueLabels;
142 if ((levels_len == 1) && new_levels.at (0).isEmpty ()) {
143 // no levels
144 } else {
145 for (int i=0; i < levels_len; ++i) {
146 data->value_labels->insert (QString::number (i+1), new_levels.at (i));
147 }
148 }
149
150 // now set the data
151 RK_ASSERT (cdata->getDataLength () == (unsigned int) getLength ()); // not really a problem due to the line below, I'd still like to know if / when this happens.
152 extendToLength (cdata->getDataLength ());
153 if (cdata->getDataType () == RData::StringVector) {
154 setCharacterFromR (0, getLength () - 1, cdata->stringVector ());
155 } else if (cdata->getDataType () == RData::RealVector) {
156 setNumericFromR (0, getLength () - 1, cdata->realVector ());
157 } else if (cdata->getDataType () == RData::IntVector) {
158 RData::IntStorage int_data = cdata->intVector ();
159 unsigned int len = getLength ();
160 QVector<double> dd;
161 dd.reserve (len);
162 for (unsigned int i = 0; i < len; ++i) {
163 if (RInterface::isNaInt (int_data.at (i))) dd.append (NAN);
164 else dd.append ((double) int_data.at (i));
165 }
166 setNumericFromR (0, getLength () - 1, dd);
167 }
168
169 // now set the invalid fields (only if they are still NAs in the R data)
170 data->invalid_fields.clear ();
171 if (invalids->getDataLength () <= 1) {
172 // no invalids
173 } else {
174 RK_ASSERT (invalids->getDataType () == RData::StringVector);
175 QStringList invalids_list = invalids->stringVector ();
176 int invalids_length = invalids_list.size ();
177 RK_ASSERT ((invalids_length % 2) == 0);
178 int invalids_count = invalids_length / 2;
179 for (int i=0; i < invalids_count; ++i) {
180 int row = invalids_list.at (i).toInt () - 1;
181 if (data->cell_states[row] & RKVarEditData::NA) { // NOTE: Do *not* use setText(), here. It tries too hard to set a valid value.
182 data->invalid_fields.insert (row, invalids_list.at (invalids_count + i));
183 data->cell_states[row] = RKVarEditData::Invalid;
184 }
185 }
186 }
187 data->previously_valid = data->invalid_fields.isEmpty ();
188 data->formatting_options = parseFormattingOptionsString (getMetaProperty ("format"));
189
190 ChangeSet *set = new ChangeSet (0, getLength (), true);
191 RKGlobals::tracker ()->objectDataChanged (this, set);
192 RKGlobals::tracker ()->objectMetaChanged (this);
193 type -= (type & NeedDataUpdate);
194 discardUnsyncedChanges ();
195 lockSyncing (false);
196 } else {
197 RK_ASSERT (false);
198 }
199 }
200
201
202 ////////////////////// BEGIN: data-handling //////////////////////////////
203 #define ALLOC_STEP 2
204 #define INITIAL_ALLOC 100
205
setLength(int len)206 void RKVariable::setLength (int len) {
207 RK_TRACE (OBJECTS);
208 RK_ASSERT (!getLength ()); // should only be called once
209 RK_ASSERT (!dimensions.isEmpty ());
210
211 dimensions[0] = len;
212 }
213
updateType(RData * new_data)214 bool RKVariable::updateType (RData *new_data) {
215 RK_TRACE (OBJECTS);
216
217 if (data) {
218 int old_type = type;
219 bool ret = RObject::updateType (new_data);
220 int new_type = type;
221
222 // Convert old values to the new data type.
223 // TODO: This is quite inefficient, as we will update the data from R in a second, anyway.
224 // Still it is a quick, dirty, and safe way to keep the data representation in a suitable format
225 type = old_type; // needed to read out the old data
226 setVarType (typeToDataType (new_type), false);
227 type = new_type;
228 return ret;
229 }
230 return RObject::updateType (new_data);
231 }
232
233 // virtual
beginEdit()234 void RKVariable::beginEdit () {
235 RK_TRACE (OBJECTS);
236
237 if (!data) {
238 allocateEditData ();
239 if (!(isPending () || (parentObject () && parentObject ()->isPending ()))) updateDataFromR (0);
240 }
241 ++(data->num_listeners);
242 }
243
244 // virtual
endEdit()245 void RKVariable::endEdit () {
246 RK_TRACE (OBJECTS);
247
248 RK_ASSERT (data);
249 RK_ASSERT (data->num_listeners > 0);
250 --(data->num_listeners);
251 if (!data->num_listeners) discardEditData ();
252 }
253
allocateEditData()254 void RKVariable::allocateEditData () {
255 RK_TRACE (OBJECTS);
256
257 // edit data should only be allocated once, even if there are multiple editors
258 RK_ASSERT (!data);
259
260 data = new RKVarEditData;
261 data->sync_locks = 0;
262 data->value_labels = 0;
263 data->formatting_options.alignment = FormattingOptions::AlignDefault;
264 data->formatting_options.precision_mode = FormattingOptions::PrecisionDefault;
265 data->formatting_options.precision = 0;
266 data->previously_valid = true;
267 data->num_listeners = 0;
268 discardUnsyncedChanges (); // initialize
269
270 // initialization hack
271 int length = getLength ();
272 dimensions[0] = -1;
273 extendToLength (length);
274 RK_ASSERT (data->cell_states.size () >= getLength ());
275
276 for (int i = 0; i < getLength (); ++i) {
277 data->cell_states[i] = RKVarEditData::NA;
278 }
279 }
280
discardEditData()281 void RKVariable::discardEditData () {
282 RK_TRACE (OBJECTS);
283
284 RK_ASSERT (data);
285 RK_ASSERT (!(data->num_listeners));
286 RK_ASSERT (data->changes.from_index == -1);
287
288 delete data->value_labels;
289 delete data;
290 data = 0;
291
292 if (isPending ()) (type -= Pending);
293 }
294
updateDataFromR(RCommandChain * chain)295 void RKVariable::updateDataFromR (RCommandChain *chain) {
296 RK_TRACE (OBJECTS);
297 if (!data) return;
298
299 RKGlobals::rInterface ()->issueCommand (".rk.get.vector.data (" + getFullName () + ')', RCommand::App | RCommand::Sync | RCommand::GetStructuredData, QString (), this, GET_DATA_COMMAND, chain);
300 }
301
lockSyncing(bool lock)302 void RKVariable::lockSyncing (bool lock) {
303 RK_TRACE (OBJECTS);
304 RK_ASSERT (data);
305
306 if (lock) data->sync_locks++;
307 else data->sync_locks--;
308 RK_ASSERT (data->sync_locks >= 0);
309
310 if (!(data->sync_locks)) {
311 syncDataToR ();
312 discardUnsyncedChanges ();
313 }
314 }
315
discardUnsyncedChanges()316 void RKVariable::discardUnsyncedChanges () {
317 RK_TRACE (OBJECTS);
318
319 RK_ASSERT (data);
320 data->changes.from_index = data->changes.to_index = -1;
321 }
322
syncDataToR()323 void RKVariable::syncDataToR () {
324 RK_TRACE (OBJECTS);
325 if (data->changes.from_index == -1) return;
326
327 // TODO
328 writeData (data->changes.from_index, data->changes.to_index);
329 discardUnsyncedChanges ();
330 }
331
restore(RCommandChain * chain)332 void RKVariable::restore (RCommandChain *chain) {
333 RK_TRACE (OBJECTS);
334 RK_ASSERT (data);
335
336 writeData (0, getLength () - 1, chain);
337 discardUnsyncedChanges ();
338 writeMetaData (chain);
339 }
340
writeInvalidFields(QList<int> rows,RCommandChain * chain)341 void RKVariable::writeInvalidFields (QList<int> rows, RCommandChain *chain) {
342 RK_TRACE (OBJECTS);
343
344 if (rows.isEmpty ()) return;
345
346 QString set;
347 QString values;
348 QString clear;
349
350 for (int i = 0; i < rows.size (); ++i) {
351 int row = rows[i];
352
353 if (data->invalid_fields.contains (row)) {
354 if (!set.isEmpty ()) {
355 set.append (", ");
356 values.append (", ");
357 }
358 set.append (QString::number (row+1));
359 values.append (rQuote (data->invalid_fields[row]));
360 } else {
361 if (!clear.isEmpty ()) clear.append (", ");
362 clear.append (QString::number (row+1));
363 }
364
365 data->cell_states[row] -= (data->cell_states[row] & RKVarEditData::UnsyncedInvalidState);
366 }
367
368 if (!set.isEmpty ()) {
369 set = "set=c(" + set + "), ";
370 values = "values=c(" + values + ')';
371 }
372 if (!clear.isEmpty ()) {
373 clear = "clear=c(" + clear + ')';
374 if (!values.isEmpty ()) values.append (",");
375 }
376
377 RKGlobals::rInterface ()->issueCommand (".rk.set.invalid.fields (" + getFullName () + ", " + set + values + clear + ')', RCommand::App | RCommand::Sync, QString (), 0,0, chain);
378
379 if (data->previously_valid != data->invalid_fields.isEmpty ()) {
380 data->previously_valid = data->invalid_fields.isEmpty ();
381 RKGlobals::tracker ()->objectMetaChanged (this);
382 }
383 }
384
writeData(int from_row,int to_row,RCommandChain * chain)385 void RKVariable::writeData (int from_row, int to_row, RCommandChain *chain) {
386 RK_TRACE (OBJECTS);
387 if (from_row == -1) return;
388
389 QList<int> changed_invalids;
390
391 // TODO: try to sync in correct storage mode
392 if (from_row == to_row) {
393 RKGlobals::rInterface ()->issueCommand (getFullName () + '[' + QString::number (from_row+1) + "] <- " + getRText (from_row), RCommand::App | RCommand::Sync, QString (), 0,0, chain);
394 if (data->cell_states[from_row] & RKVarEditData::UnsyncedInvalidState) changed_invalids.append (from_row);
395 } else {
396 QString data_string = "c (";
397 for (int row = from_row; row <= to_row; ++row) {
398 // TODO: use getCharacter and direct setting of vectors.
399 data_string.append (getRText (row));
400 if (row != to_row) {
401 data_string.append (", ");
402 }
403 if (data->cell_states[row] & RKVarEditData::UnsyncedInvalidState) changed_invalids.append (row);
404 }
405 data_string.append (")");
406 RKGlobals::rInterface ()->issueCommand (getFullName () + '[' + QString::number (from_row + 1) + ':' + QString::number (to_row + 1) + "] <- " + data_string, RCommand::App | RCommand::Sync, QString (), 0,0, chain);
407 }
408
409 if (!changed_invalids.isEmpty ()) writeInvalidFields (changed_invalids, chain);
410
411 ChangeSet *set = new ChangeSet (from_row, to_row);
412 RKGlobals::tracker ()->objectDataChanged (this, set);
413 }
414
cellsChanged(int from_row,int to_row)415 void RKVariable::cellsChanged (int from_row, int to_row) {
416 RK_TRACE (OBJECTS);
417 if (!data->sync_locks) {
418 writeData (from_row, to_row);
419 } else {
420 if ((data->changes.from_index > from_row) || (data->changes.from_index == -1)) data->changes.from_index = from_row;
421 if (data->changes.to_index < to_row) data->changes.to_index = to_row;
422 }
423 }
424
extendToLength(int length)425 void RKVariable::extendToLength (int length) {
426 if (!data) return;
427 RK_TRACE (OBJECTS);
428
429 if (length <= 0) length = 0;
430 int old_length = getLength ();
431 if (length <= old_length) return;
432
433 // pad storage to required list with "unknown" data
434 for (int i=old_length; i < length; ++i) {
435 if (getDataType () == DataCharacter) data->cell_strings.append (QString ());
436 else data->cell_doubles.append (0.0);
437 data->cell_states.append (RKVarEditData::Unknown);
438 }
439
440 dimensions[0] = length;
441 }
442
hasInvalidFields() const443 bool RKVariable::hasInvalidFields () const {
444 if (!data) return false; // should not ever happen, though
445 return (!data->invalid_fields.isEmpty ());
446 }
447
getText(int row,bool pretty) const448 QString RKVariable::getText (int row, bool pretty) const {
449 if (row >= getLength ()) {
450 RK_ASSERT (false);
451 return (QString ());
452 }
453
454 if (data->cell_states[row] & RKVarEditData::Invalid) {
455 RK_ASSERT (data->invalid_fields.contains (row));
456 return (data->invalid_fields.value (row));
457 }
458
459 if (data->cell_states[row] & RKVarEditData::NA) return (QString ());
460
461 QString ret;
462 if (getDataType () == DataCharacter) {
463 RK_ASSERT (!data->cell_strings.isEmpty ());
464 ret = data->cell_strings[row];
465 } else {
466 RK_ASSERT (!data->cell_doubles.isEmpty ());
467 if (pretty && (data->formatting_options.precision_mode != FormattingOptions::PrecisionDefault) && (getDataType () != DataLogical)) {
468 if (data->formatting_options.precision_mode == FormattingOptions::PrecisionRequired) {
469 ret = QString::number (data->cell_doubles[row], 'g', MAX_PRECISION);
470 } else {
471 ret = QString::number (data->cell_doubles[row], 'f', data->formatting_options.precision);
472 }
473 } else {
474 ret = QString::number (data->cell_doubles[row], 'g', MAX_PRECISION);
475 }
476 }
477
478 if (pretty) {
479 if (getDataType () == DataLogical) {
480 if (ret == "0") return "FALSE";
481 else if (ret == "1") return "TRUE";
482 } else if (data->value_labels) {
483 if (data->value_labels->contains (ret)) {
484 return (*(data->value_labels))[ret];
485 }
486 }
487 }
488 return ret;
489 }
490
getRText(int row) const491 QString RKVariable::getRText (int row) const {
492 RK_TRACE (OBJECTS);
493
494 Status cell_state = cellStatus (row);
495
496 if ((cell_state == ValueUnused) || (cell_state == ValueInvalid)) {
497 return ("NA");
498 } else if (getDataType () == DataFactor) {
499 return (rQuote (getText (row, true)));
500 } else if (getDataType () == DataCharacter) {
501 return (rQuote (getText (row)));
502 } else if (getDataType () == DataLogical) {
503 RK_ASSERT (!data->cell_doubles.isEmpty ());
504 if (data->cell_doubles[row] == 0) return ("FALSE");
505 else return ("TRUE");
506 } else {
507 RK_ASSERT (!data->cell_doubles.isEmpty ());
508 return (QString::number (data->cell_doubles[row], 'g', MAX_PRECISION));
509 }
510 }
511
setText(int row,const QString & text)512 void RKVariable::setText (int row, const QString &text) {
513 RK_TRACE (OBJECTS);
514 RK_ASSERT (row < getLength ());
515
516 // clear previous state
517 if (data->cell_states[row] & RKVarEditData::Invalid) {
518 data->cell_states[row] = RKVarEditData::UnsyncedInvalidState;
519 data->invalid_fields.remove (row);
520 } else {
521 data->cell_states[row] = 0;
522 }
523
524 bool valid = true;
525 if (text.isNull ()) {
526 data->cell_states[row] |= RKVarEditData::NA;
527 } else if (text.isEmpty () && getDataType () != DataCharacter) {
528 data->cell_states[row] |= RKVarEditData::NA;
529 } else {
530 if (getDataType () == DataCharacter) {
531 data->cell_strings[row] = text;
532 } else if (getDataType () == DataFactor) {
533 if (data->value_labels) {
534 QString realtext = data->value_labels->key (text); // first, attempt to set by level
535 if (!realtext.isEmpty ()) data->cell_doubles[row] = realtext.toInt ();
536 else { // if this failed, try to set by index, instead.
537 if (data->value_labels->contains (text)) data->cell_doubles[row] = text.toInt ();
538 else valid = false;
539 }
540 } else valid = false;
541 } else if (getDataType () == DataLogical) {
542 if (text == "0" || text == "F" || text == "FALSE") data->cell_doubles[row] = 0;
543 else if (text == "1" || text == "T" || text == "TRUE") data->cell_doubles[row] = 1;
544 else valid = false;
545 } else {
546 data->cell_doubles[row] = text.toDouble (&valid);
547 }
548 }
549
550 if (valid) {
551 if (!(data->cell_states[row] & RKVarEditData::NA)) data->cell_states[row] |= RKVarEditData::Valid;
552 } else {
553 data->invalid_fields.insert (row, text);
554 data->cell_states[row] |= RKVarEditData::Invalid | RKVarEditData::UnsyncedInvalidState;
555 }
556
557 cellsChanged (row, row);
558 }
559
setNumericFromR(int from_row,int to_row,const QVector<double> & numdata)560 void RKVariable::setNumericFromR (int from_row, int to_row, const QVector<double> &numdata) {
561 RK_TRACE (OBJECTS);
562 RK_ASSERT (to_row < getLength ());
563 RK_ASSERT ((to_row - from_row) < numdata.size ());
564
565 if (getDataType () == DataCharacter) {
566 RK_ASSERT (false); // asserting false to catch cases of this use for now. it's not really a problem, though
567 int i = 0;
568 for (int row=from_row; row <= to_row; ++row) {
569 setText (row, QString::number (numdata[i++], 'g', MAX_PRECISION));
570 }
571 } else if (getDataType () == DataFactor) {
572 int i = 0;
573 for (int row=from_row; row <= to_row; ++row) {
574 if (data->cell_states[row] & RKVarEditData::Invalid) data->cell_states[row] = RKVarEditData::UnsyncedInvalidState;
575 else data->cell_states[row] = 0;
576
577 if (std::isnan (numdata[i]) || (!data->value_labels) || (!data->value_labels->contains (QString::number (numdata[i])))) {
578 data->cell_states[row] |= RKVarEditData::NA;
579 } else {
580 data->cell_states[row] |= RKVarEditData::Valid;
581 data->cell_doubles[row] = numdata[i];
582 }
583 ++i;
584 }
585 } else {
586 int i = 0;
587 for (int row=from_row; row <= to_row; ++row) {
588 if (data->cell_states[row] & RKVarEditData::Invalid) data->cell_states[row] = RKVarEditData::UnsyncedInvalidState;
589 else data->cell_states[row] = 0;
590
591 if (std::isnan (numdata[i])) {
592 data->cell_states[row] |= RKVarEditData::NA;
593 } else {
594 data->cell_states[row] |= RKVarEditData::Valid;
595 data->cell_doubles[row] = numdata[i];
596 }
597 ++i;
598 }
599 }
600 cellsChanged (from_row, to_row);
601 }
602
getCharacter(int from_row,int to_row) const603 QString *RKVariable::getCharacter (int from_row, int to_row) const {
604 RK_TRACE (OBJECTS);
605 if (to_row >= getLength ()) {
606 RK_ASSERT (false);
607 return 0;
608 }
609 RK_ASSERT (from_row <= to_row);
610
611 QString *ret = new QString[(to_row - from_row) + 1];
612
613 int i = 0;
614 for (int row = from_row; row <= to_row; ++row) {
615 ret[i] = getText (row);
616 i++;
617 }
618
619 return ret;
620 }
621
setCharacterFromR(int from_row,int to_row,const QStringList & txtdata)622 void RKVariable::setCharacterFromR (int from_row, int to_row, const QStringList &txtdata) {
623 RK_TRACE (OBJECTS);
624 RK_ASSERT (to_row < getLength ());
625 RK_ASSERT ((to_row - from_row) < txtdata.size ());
626
627 lockSyncing (true);
628 int i=0;
629 for (int row=from_row; row <= to_row; ++row) {
630 setText (row, txtdata[i++]);
631 }
632 lockSyncing (false);
633 }
634
cellStatus(int row) const635 RKVariable::Status RKVariable::cellStatus (int row) const {
636 if (row >= getLength ()) return ValueUnknown;
637 if (data->cell_states[row] == RKVarEditData::Unknown) return ValueUnknown;
638 if (data->cell_states[row] & RKVarEditData::NA) return ValueUnused;
639 if (data->cell_states[row] & RKVarEditData::Invalid) return ValueInvalid;
640 return ValueValid;
641 }
642
removeRows(int from_row,int to_row)643 void RKVariable::removeRows (int from_row, int to_row) {
644 RK_TRACE (OBJECTS);
645
646 QList<int> changed_invalids;
647 int offset = (to_row - from_row) + 1;
648
649 for (int row = from_row; row < getLength (); ++row) {
650 if (data->invalid_fields.contains (row)) {
651 QString inv = data->invalid_fields.take (row);
652 changed_invalids.append (row);
653 if (row > to_row) {
654 changed_invalids.append (row - offset);
655 data->invalid_fields.insert (row - offset, inv);
656 }
657 }
658 }
659
660 for (int row = to_row; row >= from_row; --row) {
661 data->cell_states.removeAt (row);
662 if (getDataType () == DataCharacter) data->cell_strings.removeAt (row);
663 else data->cell_doubles.removeAt (row);
664 }
665
666 if (!changed_invalids.isEmpty ()) writeInvalidFields (changed_invalids);
667
668 dimensions[0] -= offset;
669 }
670
insertRows(int row,int count)671 void RKVariable::insertRows (int row, int count) {
672 RK_TRACE (OBJECTS);
673
674 for (int i=row; i < row+count; ++i) {
675 data->cell_states.insert (i, RKVarEditData::NA);
676 if (getDataType () == DataCharacter) data->cell_strings.insert (i, QString ());
677 else data->cell_doubles.insert (i, 0.0);
678 }
679
680 QList<int> changed_invalids;
681 for (int i = getLength () - 1; i >= row; --i) {
682 if (data->invalid_fields.contains (i)) {
683 QString dummy = data->invalid_fields.take (i);
684 changed_invalids.append (i);
685 changed_invalids.append (i + count);
686 data->invalid_fields.insert (i + count, dummy);
687 }
688 }
689
690 if (!changed_invalids.isEmpty ()) writeInvalidFields (changed_invalids);
691
692 dimensions[0] += count;
693 }
694
getValueLabels() const695 RObject::ValueLabels RKVariable::getValueLabels () const {
696 RK_ASSERT (data);
697
698 if (!data->value_labels) return RObject::ValueLabels ();
699 return (*(data->value_labels));
700 }
701
setValueLabels(const ValueLabels & labels)702 void RKVariable::setValueLabels (const ValueLabels& labels) {
703 RK_TRACE (OBJECTS);
704 RK_ASSERT (data);
705
706 if (labels.isEmpty ()) {
707 if (!data->value_labels) return; // no change: was empty, is empty
708
709 delete data->value_labels;
710 data->value_labels = 0;
711 } else {
712 if (!(data->value_labels)) data->value_labels = new RObject::ValueLabels;
713 else {
714 if (*(data->value_labels) == labels) return; // old and new lists are the same
715 }
716 *(data->value_labels) = labels;
717 }
718
719 updateValueLabels ();
720 }
721
updateValueLabels()722 void RKVariable::updateValueLabels () {
723 RK_TRACE (OBJECTS);
724
725 writeValueLabels (0);
726 RKGlobals::tracker ()->objectMetaChanged (this);
727
728 ValueLabels *labels = data->value_labels;
729
730 lockSyncing (true);
731 // find out which values got valid / invalid and change those
732 for (int i=0; i < getLength (); ++i) {
733 if (cellStatus (i) == ValueInvalid) {
734 if (labels && labels->contains (getText (i))) {
735 setText (i, getText (i));
736 }
737 } else {
738 if (!(labels && labels->contains (getText (i)))) {
739 setText (i, getText (i));
740 }
741 }
742 }
743 lockSyncing (false);
744
745 // also update display of all values:
746 ChangeSet *set = new ChangeSet (0, getLength () - 1);
747 RKGlobals::tracker ()->objectDataChanged (this, set);
748
749 // TODO: find out whether the object is valid after the operation and update accordingly!
750 }
751
writeValueLabels(RCommandChain * chain) const752 void RKVariable::writeValueLabels (RCommandChain *chain) const {
753 RK_TRACE (OBJECTS);
754 RK_ASSERT (data);
755
756 QString level_string;
757 if (data->value_labels && (!data->value_labels->isEmpty())) {
758 int i = 1;
759 level_string = "c (";
760 while (data->value_labels->contains (QString::number (i))) {
761 level_string.append (rQuote ((*(data->value_labels))[QString::number (i)]));
762 if (data->value_labels->contains (QString::number (++i))) {
763 level_string.append (", ");
764 }
765 }
766 level_string.append (")");
767 } else {
768 level_string = "NULL";
769 }
770
771 RKGlobals::rInterface ()->issueCommand (".rk.set.levels (" + getFullName () + ", " + level_string + ')', RCommand::App | RCommand::Sync, QString (), 0, 0, chain);
772 }
773
getValueLabelString() const774 QString RKVariable::getValueLabelString () const {
775 RK_TRACE (OBJECTS);
776 RK_ASSERT (data);
777
778 if (data->value_labels) {
779 int i = 1;
780 QString level_string;
781 while (data->value_labels->contains (QString::number (i))) {
782 level_string.append ((*(data->value_labels))[QString::number (i)]);
783 if (data->value_labels->contains (QString::number (++i))) {
784 level_string.append ("#,#");
785 }
786 }
787
788 return level_string;
789 } else {
790 return QString ();
791 }
792 }
793
setValueLabelString(const QString & string)794 void RKVariable::setValueLabelString (const QString &string) {
795 RK_TRACE (OBJECTS);
796 RK_ASSERT (data);
797
798 ValueLabels new_labels;
799 QStringList list = string.split ("#,#");
800
801 int i = 1;
802 for (QStringList::const_iterator it = list.constBegin (); it != list.constEnd (); ++it) {
803 new_labels.insert (QString::number (i), *it);
804 ++i;
805 }
806 setValueLabels (new_labels);
807 }
808
getFormattingOptions() const809 RKVariable::FormattingOptions RKVariable::getFormattingOptions () const {
810 RK_TRACE (OBJECTS);
811 RK_ASSERT (data);
812
813 return data->formatting_options;
814 }
815
setFormattingOptions(const FormattingOptions new_options)816 void RKVariable::setFormattingOptions (const FormattingOptions new_options) {
817 RK_TRACE (OBJECTS);
818 RK_ASSERT (data);
819
820 if ((new_options.alignment == data->formatting_options.alignment) && (new_options.precision_mode == data->formatting_options.precision_mode) && (new_options.precision == data->formatting_options.precision)) return;
821
822 data->formatting_options = new_options;
823 setMetaProperty ("format", formattingOptionsToString (new_options));
824
825 // also update display of all values:
826 ChangeSet *set = new ChangeSet (0, getLength () -1);
827 RKGlobals::tracker ()->objectDataChanged (this, set);
828 }
829
getFormattingOptionsString() const830 QString RKVariable::getFormattingOptionsString () const {
831 RK_TRACE (OBJECTS);
832 RK_ASSERT (data);
833
834 return getMetaProperty ("format");
835 }
836
setFormattingOptionsString(const QString & string)837 void RKVariable::setFormattingOptionsString (const QString &string) {
838 RK_TRACE (OBJECTS);
839 RK_ASSERT (data);
840
841 setFormattingOptions (parseFormattingOptionsString (string));
842 }
843
844 // static
formattingOptionsToString(const FormattingOptions & options)845 QString RKVariable::formattingOptionsToString (const FormattingOptions& options) {
846 RK_TRACE (OBJECTS);
847
848 QString format_string;
849 if (options.alignment != (int) FormattingOptions::AlignDefault) {
850 format_string.append ("align:" + QString::number (options.alignment));
851 }
852
853 if (options.precision_mode != (int) FormattingOptions::PrecisionDefault) {
854 if (!format_string.isEmpty ()) format_string.append ("#");
855 format_string.append ("prec:");
856 if (options.precision_mode == (int) FormattingOptions::PrecisionRequired) {
857 format_string.append ("v");
858 } else {
859 format_string.append (QString::number (options.precision));
860 }
861 }
862
863 return format_string;
864 }
865
866 // static
parseFormattingOptionsString(const QString & string)867 RKVariable::FormattingOptions RKVariable::parseFormattingOptionsString (const QString &string) {
868 RK_TRACE (OBJECTS);
869
870 FormattingOptions formatting_options;
871 formatting_options.alignment = FormattingOptions::AlignDefault;
872 formatting_options.precision_mode = FormattingOptions::PrecisionDefault;
873 formatting_options.precision = 0;
874
875 QStringList list = string.split ('#', QString::SkipEmptyParts);
876 QString option, parameter;
877 for (QStringList::const_iterator it = list.constBegin (); it != list.constEnd (); ++it) {
878 option = (*it).section (':', 0, 0);
879 parameter = (*it).section (':', 1, 1);
880
881 if (parameter.isEmpty ()) continue;
882
883 if (option == "align") {
884 int al = parameter.toInt ();
885 if ((al >= (int) FormattingOptions::AlignDefault) && (al <= (int) FormattingOptions::AlignRight)) {
886 formatting_options.alignment = (FormattingOptions::Alignment) al;
887 }
888 } else if (option == "prec") {
889 if (parameter == "d") {
890 formatting_options.precision_mode = FormattingOptions::PrecisionDefault;
891 } else if (parameter == "v") {
892 formatting_options.precision_mode = FormattingOptions::PrecisionRequired;
893 } else {
894 int digits = parameter.toInt ();
895 if ((digits >= 0) && (digits <= 15)) {
896 formatting_options.precision_mode = FormattingOptions::PrecisionFixed;
897 formatting_options.precision = digits;
898 }
899 }
900 } else {
901 RK_ASSERT (false);
902 }
903 }
904
905 return formatting_options;
906 }
907
getAlignment() const908 RKVariable::CellAlign RKVariable::getAlignment () const {
909 RK_ASSERT (data);
910
911 if (data->formatting_options.alignment != FormattingOptions::AlignDefault) {
912 if (data->formatting_options.alignment == FormattingOptions::AlignLeft) return AlignCellLeft;
913 return AlignCellRight;
914 } else {
915 // TODO: use global (configurable) defaults, if not specified
916 if ((getDataType () == DataCharacter) || (getDataType () == DataFactor)) {
917 return AlignCellLeft;
918 } else {
919 return AlignCellRight;
920 }
921 }
922 }
923
924 /////////////////// END: data-handling ///////////////////////////
925