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