Beispiel #1
1
bool RKObjectListViewSettings::filterAcceptsRow (int source_row, const QModelIndex& source_parent) const {
//	RK_TRACE (APP);

	// So I tried to use a KRecursiveFilterProxyModel, but
	// a) we don't really want recursion to the full depth. Thus limiting it, here.
	// b) While we don't handle insertions / removals of source indices in the presence of a filter, correctly, with KRecursiveFilterProxyModel
	//    I got crashes on this (arguably with the depth-limit in place)
	if (acceptRow (source_row, source_parent)) return true;

	RObject *parent = static_cast<RObject*> (source_parent.internalPointer ());
	if (!parent) {
		RK_ASSERT (parent);    // should have been accepted, above
		return true;
	}
	RObject *object = parent->findChildByObjectModelIndex (source_row);
	if (!object) {
		RK_ASSERT (object);    // should have been accepted, above
		RK_DEBUG (APP, DL_ERROR, "row %d of %d in %s", source_row, sourceModel ()->rowCount (source_parent), qPrintable (parent->getShortName ()));
		return false;
	}

	if (object->isType (RObject::ToplevelEnv | RObject::Workspace) || ((depth_limit > 0) && parent->isType (RObject::ToplevelEnv | RObject::Workspace))) {
		QModelIndex source_index = sourceModel ()->index (source_row, 0, source_parent);
		for (int row = 0, rows = sourceModel()->rowCount (source_index); row < rows; ++row) {
			if (filterAcceptsRow (row, source_index)) return true;
		}
	}

	return false;
}
Beispiel #2
0
quint32 RKSessionVars::parseVersionString (const QString &version, QString *suffix) {
	quint32 ret = 0;
	int pos = -1;
	int opos = 0;
	for (int i = 3; i >= 0; --i) {
		while (1) {
			++pos;
			if (!(pos < version.size () && version[pos].isDigit ())) {
				int val = version.mid (opos, pos - opos).toInt ();
				if ((val < 0) || (val > 255) || (pos == opos)) {
					RK_DEBUG (MISC, DL_ERROR, "Invalid version specification '%s'", qPrintable (version));
					if (val > 255) val = 255;
					else val = 0;
				}
				ret += val << (8 * i);
				if ((pos < version.size ()) && (version[pos] == '.')) {
					opos = pos + 1;
					break;
				}
				opos = pos;
				i = -1;
				break;
			}
		}
	}
	if (opos <= (version.size () - 1)) {
		if (suffix) *suffix = version.mid (opos);
		else RK_DEBUG (MISC, DL_WARNING, "Non numeric portion ('%s') of version specification '%s' will be ignored.", qPrintable (version.mid (opos)), qPrintable (version));
	}

	return ret;
}
SEXP RKStructureGetter::resolvePromise (SEXP from) {
	RK_TRACE (RBACKEND);

	SEXP ret = from;
	if (TYPEOF (from) == PROMSXP) {
		ret = PRVALUE(from);
		if (ret == R_UnboundValue) {
			RK_DEBUG (RBACKEND, DL_DEBUG, "temporarily resolving unbound promise");

			PROTECT (from);
			SET_PRSEEN(from, 1);
			ret = Rf_eval(PRCODE(from), PRENV(from));
			SET_PRSEEN(from, 0);
			if (keep_evalled_promises) {
				SET_PRVALUE(from, ret);
				SET_PRENV(from, R_NilValue);
			}
			UNPROTECT (1);

			RK_DEBUG (RBACKEND, DL_DEBUG, "resolved type is %d", TYPEOF (ret));
		}
	}

	return ret;
}
bool RKROutputBuffer::handleOutput (const QString &output, int buf_length, ROutput::ROutputType output_type, bool allow_blocking) {
	if (!buf_length) return false;
	RK_TRACE (RBACKEND);

	RK_DEBUG (RBACKEND, DL_DEBUG, "Output type %d: %s", output_type, qPrintable (output));

	// wait while the output buffer is exceeded to give downstream threads a chance to catch up
	while ((out_buf_len > MAX_BUF_LENGTH) && allow_blocking) {
		if (!doMSleep (10)) break;
	}

	output_buffer_mutex.lock ();
	bool previously_empty = (out_buf_len <= 0);

	ROutput *current_output = 0;
	if (!output_buffer.isEmpty ()) {
		// Merge with previous output fragment, if of the same type
		current_output = output_buffer.last ();
		if (current_output->type != output_type) current_output = 0;
	}
	if (!current_output) {
		current_output = new ROutput;
		current_output->type = output_type;
		current_output->output.reserve (OUTPUT_STRING_RESERVE);
		output_buffer.append (current_output);
	}
	current_output->output.append (output);
	out_buf_len += buf_length;

	output_buffer_mutex.unlock ();
	return previously_empty;
}
Beispiel #5
0
void RKWindowCatcher::stop (int new_cur_device) {
	RK_TRACE (MISC);
	RK_DEBUG (RBACKEND, DL_DEBUG, "Window Catcher deactivated");

	WId w = RKWardApplication::getApp ()->endWindowCreationDetection ();
	if (new_cur_device != last_cur_device) {
		if (w) {
			RKWorkplace::mainWorkplace ()->newX11Window (w, new_cur_device);
#if defined Q_WS_X11
			// All this syncing looks like a bloody hack? Absolutely. It appears to work around the occasional error "figure margins too large" from R, though.
			qApp->processEvents ();
			qApp->syncX ();
			qApp->processEvents ();
			// this appears to have the side-effect of forcing the captured window to sync with X, which is exactly, what we're trying to achieve.
			KWindowInfo wininfo = KWindowSystem::windowInfo (w, NET::WMName | NET::WMGeometry);
#endif
		} else {
#if defined Q_WS_MAC
			KMessageBox::information (0, i18n ("You have tried to embed a new R graphics device window in RKWard. However, this is not currently supported in this build of RKWard on Mac OS X. See http://rkward.kde.org/mac for more information."), i18n ("Could not embed R X11 window"), "embed_x11_device_not_supported");
#else
			RKErrorDialog::reportableErrorMessage (0, i18n ("You have tried to embed a new R graphics device window in RKWard. However, either no window was created, or RKWard failed to detect the new window. If you think RKWard should have done better, consider reporting this as a bug. Alternatively, you may want to adjust Settings->Configure RKWard->Onscreen Graphics."), QString (), i18n ("Could not embed R X11 window"), "failure_to_detect_x11_device");
#endif
		}
	}
	last_cur_device = new_cur_device;
}
Beispiel #6
0
void RKWindowCatcher::start (int prev_cur_device) {
	RK_TRACE (MISC);
	RK_DEBUG (RBACKEND, DL_DEBUG, "Window Catcher activated");

	RKWardApplication::getApp ()->startWindowCreationDetection ();
	last_cur_device = prev_cur_device;
}
Beispiel #7
0
void RObjectList::rCommandDone (RCommand *command) {
	RK_TRACE (OBJECTS);

	if (command->getFlags () == ROBJECTLIST_UPDATE_ENVIRONMENTS_COMMAND) {
		RK_ASSERT (command->getDataType () == RData::StructureVector);
		const RData::RDataStorage & data = command->structureVector ();
		RK_ASSERT (data.size () == 2);
		
		QStringList new_environments = data[0]->stringVector ();
		RK_ASSERT (new_environments.size () >= 2);

		updateEnvironments (new_environments, true);
		updateNamespaces (data[1]->stringVector ());

		RKGlobals::rInterface ()->issueCommand (QString (), RCommand::App | RCommand::Sync | RCommand::EmptyCommand, QString (), this, ROBJECTLIST_UPDATE_COMPLETE_COMMAND, update_chain);
	} else if (command->getFlags () == ROBJECTLIST_UPDATE_COMPLETE_COMMAND) {
		RK_ASSERT (update_chain);
		RKGlobals::rInterface ()->closeChain (update_chain);
		update_chain = 0;
	
		RK_DEBUG (OBJECTS, DL_DEBUG, "object list update complete");
		emit (updateComplete ());
	} else {
		RK_ASSERT (false);
	}
}
Beispiel #8
0
void RKSessionVars::setRVersion (const QString& version_string) {
	RK_TRACE (RBACKEND);

	if (!r_version_string.isEmpty ()) {
		RK_DEBUG (RBACKEND, DL_WARNING, "R version has changed during runtime, from %s to %s", qPrintable (r_version_string), qPrintable (version_string));
	}
	r_version_string = version_string;
	r_version = parseVersionString (version_string, 0);
}
Beispiel #9
0
void RObjectList::updateEnvironments (const QStringList &_env_names, bool force_globalenv_update) {
	RK_TRACE (OBJECTS);

	RObjectMap newchildmap;
	QStringList env_names = _env_names;
	if (!env_names.isEmpty ()) {
		QString dummy = env_names.takeFirst ();
		RK_ASSERT (dummy == ".GlobalEnv");
		if (force_globalenv_update) {
			// for now, we only update the .GlobalEnv. All others we assume to be static
			getGlobalEnv ()->updateFromR (update_chain);
		}
	} else {
		RK_ASSERT (!env_names.isEmpty ());
	}

	// find which items are new, and copy the old ones
	for (int i = 0; i < env_names.count (); ++i) {
		QString name = env_names[i];

		RObject *obj = findChildByName (name);
		if (obj && (i > 0) && (env_names.lastIndexOf (name, i-1) > -1)) {		// duplicate environment names can happen (e.g. if a data.frame is attached multiple times)
			obj = 0;	// only copy the old item once
		}
		if (!obj) {
			obj = createTopLevelEnvironment (name);
			RKGlobals::tracker ()->beginAddObject (obj, this, i);
			childmap.insert (i, obj);
			RKGlobals::tracker ()->endAddObject (obj, this, i);
		}
		newchildmap.insert (i, obj);
	}

	// check which envs have been removed or changed position
	for (int i = 0; i < childmap.size (); ++i) {	// do *not* cache the childmap.size ()! We may change it in the loop.
		RObject *obj = childmap[i];
		int new_pos = newchildmap.indexOf (obj);
		
		if (new_pos < 0) {	// environment is gone
			RK_DEBUG (OBJECTS, DL_INFO, "removing toplevel environment %s from list", obj->getShortName ().toLatin1 ().data ());
			if (RKGlobals::tracker ()->removeObject (obj, 0, true)) --i;
			else (newchildmap.insert (i, obj));
		} else if (new_pos != i) {
			// this call is rather expensive, all in all, but fortunately called very rarely
			moveChild (obj, i, new_pos);
		}
	}

	RK_DO (RK_ASSERT (childmap == newchildmap), OBJECTS, DL_DEBUG);	// this is an expensive assert, hence wrapping it inside RK_DO
}
Beispiel #10
0
void RObjectList::updateFromR (RCommandChain *chain) {
	RK_TRACE (OBJECTS);

	if (update_chain) {
		// gee, looks like another update is still on the way. lets schedule one for later:
		update_timer->start (UPDATE_DELAY_INTERVAL);
		RK_DEBUG (OBJECTS, DL_DEBUG, "another object-list update is already running. Rescheduling a further update for later");
		return;
	}

	emit (updateStarted ());
	update_chain = RKGlobals::rInterface ()->startChain (chain);

	RCommand *command = new RCommand ("list (search (), loadedNamespaces ())", RCommand::App | RCommand::Sync | RCommand::GetStructuredData, QString (), this, ROBJECTLIST_UPDATE_ENVIRONMENTS_COMMAND);
	RKGlobals::rInterface ()->issueCommand (command, update_chain);
}
Beispiel #11
0
void RKWindowCatcher::updateHistory (QStringList params) {
	RK_TRACE (MISC);
	RK_ASSERT (params.count () >= 1);

	int history_length = params[0].toInt ();
	QStringList labels = params.mid (1, history_length);
	RK_ASSERT (((params.count () - history_length) % 2) == 1)
	for (int i = history_length + 1; i < (params.count () - 1); i += 2) {
		RKCaughtX11Window* window = RKCaughtX11Window::getWindow (params[i].toInt ());
		if (window) {
			int position = params[i+1].toInt ();
			window->updateHistoryActions (history_length, position, labels);
		} else {
			RK_DEBUG (RBACKEND, DL_DEBUG, "Device %d is not managed, while trying to update history", params[i].toInt ());
		}
	}
}
Beispiel #12
0
void RObjectList::updateFromR (RCommandChain *chain, const QStringList &current_searchpath, const QStringList &current_namespaces) {
	RK_TRACE (OBJECTS);

// TODO: can this happen? when?
	if (update_chain) {
		// gee, looks like another update is still on the way. lets schedule one for later:
		update_timer->start (UPDATE_DELAY_INTERVAL);
		RK_DEBUG (OBJECTS, DL_DEBUG, "another object-list update is already running. Rescheduling a further update for later");
		return;
	}

	emit (updateStarted ());
	update_chain = RKGlobals::rInterface ()->startChain (chain);

	updateEnvironments (current_searchpath, false);
	updateNamespaces (current_namespaces);

	RKGlobals::rInterface ()->issueCommand (QString (), RCommand::App | RCommand::Sync | RCommand::EmptyCommand, QString (), this, ROBJECTLIST_UPDATE_COMPLETE_COMMAND, update_chain);
}
Beispiel #13
0
void RKValueSelector::availablePropertyChanged () {
	RK_TRACE (PLUGIN);

	const QStringList &vals = available->values ();
	for (int i = vals.size () - 1; i >= 1; --i) {
		if (vals.lastIndexOf (vals[i], i - 1) >= 0) {
			RK_DEBUG (PLUGIN, DL_WARNING, "Duplicate value index in value selector: %s", qPrintable (vals[i]));
		}
	}
	model->setStringList (mergeLists (labels->values (), available->values ()));

	if (!purged_selected_indexes.isEmpty ()) {
		// This is to handle the case that the "selected" property was updated externally, *before* the "available" property got the corresponding change.
		// In this case, try to re-apply any selected strings that could not be matched, before
		purged_selected_indexes.append (selected->values ());
		selected->setValueList (purged_selected_indexes);   // side effect updating selected items
		purged_selected_indexes.clear ();
	} else {
		selectionPropertyChanged ();   // To update selected items
		purged_selected_indexes.clear ();
	}
}
Beispiel #14
0
void RKContextHandler::invokeComponent (RKComponentHandle *handle) {
	RK_TRACE (PLUGIN);
	RK_ASSERT (handle);

	// create component
	RKComponent *component = handle->invoke (0, 0);

	// set context values
	for (QHash<QString, RKComponentBase*>::const_iterator it = child_map.constBegin (); it != child_map.constEnd (); ++it) {
		if (it.key () != "#noid#") {
			QString id = it.key ();
			QString remainder;
			RKComponentBase *client = component->lookupComponent (id, &remainder);

			RK_ASSERT (it.value ()->isProperty ());
			if (!(client && remainder.isEmpty () && client->isProperty () && it.value ()->isProperty ())) {
				RK_DEBUG (PLUGIN, DL_INFO, "Could not set context property %s", id.toLatin1 ().data ());
				continue;
			}

			static_cast<RKComponentPropertyBase *> (client)->connectToGovernor (static_cast<RKComponentPropertyBase *> (it.value ()), QString (), false);
		}
	}
}
Beispiel #15
0
void RKVarSelector::objectSelectionChanged () {
	RK_TRACE (PLUGIN);

	selected->setObjectList (list_view->selectedObjects ());
	RK_DEBUG (PLUGIN, DL_DEBUG, "selected in varselector: %s", qPrintable (fetchStringValue (selected)));
}
Beispiel #16
0
int main(int argc, char *argv[]) {
	options.add ("evaluate <Rcode>", ki18n ("After starting (and after loading the specified workspace, if applicable), evaluate the given R code."), 0);
	options.add ("debug-level <level>", ki18n ("Verbosity of debug messages (0-5)"), "2");
	options.add ("debug-flags <flags>", ki18n ("Mask for components to debug (see debug.h)"), QString::number (DEBUG_ALL).toLocal8Bit ());
	options.add ("debugger <command and arguments>", ki18n ("Debugger for the frontend. Specify last, or add '--' after all debugger arguments"), "");
	options.add ("backend-debugger <command>", ki18n ("Debugger for the backend. (Enclose any debugger arguments in single quotes ('') together with the command. Make sure to re-direct stdout!)"), "");
	options.add ("r-executable <command>", ki18n ("Use specified R installation, instead of the one configured at compile time (note: rkward R library must be installed to that installation of R)"), "");
	options.add ("reuse", ki18n ("Reuse a running RKWard instance (if available). If a running instance is reused, only the file arguments will be interpreted, all other options will be ignored."), 0);
	options.add ("nowarn-external", ki18n ("When used in conjunction with rkward://runplugin/-URLs specified on the command line, suppresses the warning about application-external (untrusted) links."));
	options.add ("+[Files]", ki18n ("File or files to open, typically a workspace, or an R script file. When loading several things, you should specify the workspace, first."), 0);

	KAboutData aboutData("rkward", QByteArray (), ki18n ("RKWard"), RKWARD_VERSION, ki18n ("Frontend to the R statistics language"), KAboutData::License_GPL, ki18n ("(c) 2002, 2004 - 2016"), KLocalizedString (), "http://rkward.kde.org", "*****@*****.**");
	aboutData.addAuthor (ki18n ("Thomas Friedrichsmeier"), ki18n ("Project leader / main developer"));
	aboutData.addAuthor (ki18n ("Pierre Ecochard"), ki18n ("C++ developer between 2004 and 2007"));
	aboutData.addAuthor (ki18n ("Prasenjit Kapat"), ki18n ("Many plugins, suggestions, plot history feature"));
	aboutData.addAuthor (ki18n ("Meik Michalke"), ki18n ("Many plugins, suggestions, rkwarddev package"));
	aboutData.addAuthor (ki18n ("Stefan Roediger"), ki18n ("Many plugins, suggestions, marketing, translations"));
	aboutData.addCredit (ki18n ("Contributors in alphabetical order"));
	aboutData.addCredit (ki18n ("Björn Balazs"), ki18n ("Extensive usability feedback"));
	aboutData.addCredit (ki18n ("Aaron Batty"), ki18n ("Whealth of feedback, hardware donations"));
	aboutData.addCredit (ki18n ("Jan Dittrich"), ki18n ("Extensive usability feedback"));
	aboutData.addCredit (ki18n ("Philippe Grosjean"), ki18n ("Several helpful comments and discussions"));
	aboutData.addCredit (ki18n ("Adrien d'Hardemare"), ki18n ("Plugins and patches"));
	aboutData.addCredit (ki18n ("Yves Jacolin"), ki18n ("New website"));
	aboutData.addCredit (ki18n ("Germán Márquez Mejía"), ki18n ("HP filter plugin, spanish translation"), 0);
	aboutData.addCredit (ki18n ("Marco Martin"), ki18n ("A cool icon"));
	aboutData.addCredit (ki18n ("Daniele Medri"), ki18n ("RKWard logo, many suggestions, help on wording"));
	aboutData.addCredit (ki18n ("David Sibai"), ki18n ("Several valuable comments, hints and patches"));
	aboutData.addCredit (ki18n ("Ilias Soumpasis"), ki18n ("Translation, Suggestions, plugins"));
	aboutData.addCredit (ki18n ("Ralf Tautenhahn"), ki18n ("Many comments, useful suggestions, and bug reports"));
	aboutData.addCredit (ki18n ("Jannis Vajen"), ki18n ("German Translation, bug reports"));
	aboutData.addCredit (ki18n ("Roland Vollgraf"), ki18n ("Some patches"));
	aboutData.addCredit (ki18n ("Roy Qu"), ki18n ("patches and helpful comments"));
	aboutData.addCredit (ki18n ("Many more people on [email protected]"), ki18n ("Sorry, if we forgot to list you. Please contact us to get added"));

	// before initializing the commandline args, remove the ".bin" from "rkward.bin".
	// This is so it prints "Usage rkward..." instead of "Usage rkward.bin...", etc.
	// it seems safest to keep a copy, since the shell still owns argv
	char *argv_copy[argc];
	argv_copy[0] = qstrdup (QString (argv[0]).remove (".frontend").replace (".exe", ".bat").toLocal8Bit ());
	for (int i = 1; i < argc; ++i) {
		argv_copy[i] = argv[i];
	}
	KCmdLineArgs::init (argc, argv_copy, &aboutData);
	KCmdLineArgs::addCmdLineOptions( options ); // Add our own options.

	KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
	RK_Debug_Level = DL_FATAL - QString (args->getOption ("debug-level")).toInt ();
	RK_Debug_Flags = QString (args->getOption ("debug-flags")).toInt ();
	if (!args->getOption ("debugger").isEmpty ()) {
		RK_DEBUG (DEBUG_ALL, DL_ERROR, "--debugger option should have been handled by wrapper script. Ignoring.");
	}

	if (args->count ()) {
		QStringList urls_to_open;
		for (int i = 0; i < args->count (); ++i) {
			urls_to_open.append (KCmdLineArgs::makeURL (decodeArgument (args->arg (i)).toUtf8 ()).url ());
		}
		RKGlobals::startup_options["initial_urls"] = urls_to_open;
		RKGlobals::startup_options["warn_external"] = args->isSet ("warn-external");
	}
	RKGlobals::startup_options["evaluate"] = decodeArgument (args->getOption ("evaluate"));
	RKGlobals::startup_options["backend-debugger"] = decodeArgument (args->getOption ("backend-debugger"));

	RKWardApplication app;
	// No, I do not really understand the point of separating KDE_LANG from LANGUAGE. We do honor it in so far as not
	// forcing LANGUAGE on the backend, though. Having language as LANGUAGE makes code in RKMessageCatalog much easier compared to KCatalog.
	qputenv ("LANGUAGE", QFile::encodeName (KGlobal::locale ()->language ()));
	// install message handler *after* the componentData has been initialized
	RKSettingsModuleDebug::debug_file = new QTemporaryFile (QDir::tempPath () + "/rkward.frontend");
	RKSettingsModuleDebug::debug_file->setAutoRemove (false);
	if (RKSettingsModuleDebug::debug_file->open ()) {
		RK_DEBUG (APP, DL_INFO, "Full debug output is at %s", qPrintable (RKSettingsModuleDebug::debug_file->fileName ()));
		qInstallMsgHandler (RKDebugMessageOutput);
	}

	if (app.isSessionRestored ()) {
		RESTORE(RKWardMainWindow);	// well, whatever this is supposed to do -> TODO
	} else {
		new RKWardMainWindow ();
	}
	args->clear();

	// Usually, KDE always adds the current directory to the list of prefixes.
	// However, since RKWard 0.5.6, the main binary is in KDE's libexec dir, which defies this mechanism. Therefore, RKWARD_ENSURE_PREFIX is set from the wrapper script.
	char *add_path = getenv ("RKWARD_ENSURE_PREFIX");
	if (add_path) KGlobal::dirs ()->addPrefix (QString::fromLocal8Bit (add_path));

	// do it!
	int status = app.exec ();

	qInstallMsgHandler (0);
	RKSettingsModuleDebug::debug_file->close ();

	return status;
}
RKOptionSet::RKOptionSet (const QDomElement &element, RKComponent *parent_component, QWidget *parent_widget) : RKComponent (parent_component, parent_widget) {
	RK_TRACE (PLUGIN);

	XMLHelper *xml = XMLHelper::getStaticHelper ();
	updating = false;
	last_known_status = Processing;
	n_invalid_rows = n_unfinished_rows = 0;

	min_rows = xml->getIntAttribute (element, "min_rows", 0, DL_INFO);
	min_rows_if_any = xml->getIntAttribute (element, "min_rows_if_any", 1, DL_INFO);
	max_rows = xml->getIntAttribute (element, "max_rows", INT_MAX, DL_INFO);

	// build UI framework
	QVBoxLayout *layout = new QVBoxLayout (this);
	switcher = new QStackedWidget (this);
	layout->addWidget (switcher);
	user_area = new KVBox (this);
	switcher->addWidget (user_area);
	updating_notice = new QLabel (i18n ("Updating status, please wait"), this);
	switcher->addWidget (updating_notice);
	update_timer.setInterval (0);
	update_timer.setSingleShot (true);
	connect (&update_timer, SIGNAL (timeout()), this, SLOT (slotUpdateUnfinishedRows()));

	// create some meta properties
	serialization_of_set = new RKComponentPropertyBase (this, false);
	addChild ("serialized", serialization_of_set);
	connect (serialization_of_set, SIGNAL (valueChanged(RKComponentPropertyBase*)), this, SLOT (serializationPropertyChanged(RKComponentPropertyBase*)));

	row_count = new RKComponentPropertyInt (this, false, 0);
	row_count->setInternal (true);
	addChild ("row_count", row_count);		// NOTE: read-only
	return_to_row = active_row = -1;
	current_row = new RKComponentPropertyInt (this, false, active_row);
	current_row->setInternal (true);
	addChild ("current_row", current_row);		// NOTE: read-write
	connect (current_row, SIGNAL (valueChanged(RKComponentPropertyBase*)), this, SLOT (currentRowPropertyChanged(RKComponentPropertyBase*)));

	// first build the contents, as we will need to refer to the elements inside, later
	model = 0;
	display = 0;	// will be created from the builder, on demand -> createDisplay ()
	contents_container = new RKComponent (this, user_area);
	QDomElement content_element = xml->getChildElement (element, "content", DL_ERROR);
	RKComponentBuilder *builder = new RKComponentBuilder (contents_container, content_element);
	builder->buildElement (content_element, user_area, false);	// NOTE that parent widget != parent component, here, by intention. The point is that the display should not be disabled along with the contents
	builder->parseLogic (xml->getChildElement (element, "logic", DL_INFO), false);
	builder->makeConnections ();
	addChild ("contents", contents_container);
	connect (standardComponent (), SIGNAL (standardInitializationComplete()), this, SLOT (fetchDefaults()));

	// create columns
	XMLChildList options = xml->getChildElements (element, "optioncolumn", DL_WARNING);

	QStringList visible_column_labels ("#");	// Optionally hidden first row for index
	for (int i = 0; i < options.size (); ++i) {
		const QDomElement &e = options.at (i);
		QString id = xml->getStringAttribute (e, "id", QString (), DL_ERROR);
		QString label = xml->getStringAttribute (e, "label", QString (), DL_DEBUG);
		QString governor = xml->getStringAttribute (e, "connect", QString (), DL_INFO);
		bool external = xml->getBoolAttribute (e, "external", false, DL_INFO);

		while (child_map.contains (id)) {
			RK_DEBUG (PLUGIN, DL_ERROR, "optionset already contains a property named %s. Renaming to _%s", qPrintable (id), qPrintable (id));
			id = "_" + id;
		}

		ColumnInfo col_inf;
		col_inf.column_name = id;
		col_inf.external = external;
		col_inf.governor = governor;
		if (external && e.hasAttribute ("default")) col_inf.default_value = xml->getStringAttribute (e, "default", QString (), DL_ERROR);

		RKComponentPropertyStringList *column_property = new RKComponentPropertyStringList (this, false);
		column_property->setInternal (external);	// Yes, looks strange, indeed. External properties should simply not be serialized / restored...
		addChild (id, column_property);
		connect (column_property, SIGNAL (valueChanged(RKComponentPropertyBase *)), this, SLOT (columnPropertyChanged(RKComponentPropertyBase *)));

		if (!label.isEmpty ()) {
			col_inf.display_index = visible_column_labels.size ();
			col_inf.column_label = label;
			visible_column_labels.append (label);
			visible_columns.append (column_property);
		} else {
			col_inf.display_index = -1;
		}

		column_map.insert (column_property, col_inf);
	}

	keycolumn = 0;
	QString keycol = xml->getStringAttribute (element, "keycolumn", QString (), DL_DEBUG);
	if (!keycol.isEmpty ()) {
		keycolumn = static_cast<RKComponentPropertyStringList*> (child_map.value (keycol));
		if (!column_map.contains (keycolumn)) {
			RK_DEBUG (PLUGIN, DL_ERROR, "optionset does not contain an optioncolumn named %s. Falling back to manual insertion mode", qPrintable (keycol));
			keycolumn = 0;
		} else if (!column_map[keycolumn].external) {
			RK_DEBUG (PLUGIN, DL_ERROR, "keycolumn (%s) is not marked as external. Falling back to manual insertion mode", qPrintable (keycol));
			keycolumn = 0;
		} else {
			updating = true;
			keycolumn->setValue (KEYCOLUMN_UNINITIALIZED_VALUE);
			updating = false;
		}
	}

	QMap<RKComponentPropertyStringList *, ColumnInfo>::iterator it = column_map.begin ();
	for (; it != column_map.end (); ++it) {
		ColumnInfo &ci = it.value ();
		if (!ci.governor.isEmpty ()) {		// there *can* be columns without governor for driven or connected option sets
			// Establish connections between columns and their respective governors. Since the format differs, the connection is done indirectly, through this component.
			// So, here, we set up a map of properties to columns, and connect to the change signals.
			RKComponentBase *governor = contents_container->lookupComponent (ci.governor, &ci.governor_modifier);
			if (governor && governor->isProperty ()) {
				RKComponentPropertyBase *gov_prop = static_cast<RKComponentPropertyBase*> (governor);
				if (ci.external) {
					if (!ci.governor_modifier.isEmpty ()) {
						RK_DEBUG (PLUGIN, DL_ERROR, "Cannot connect external column '%s' in optionset to property with modifier (%s).", qPrintable (ci.column_name), qPrintable (ci.governor));
						continue;
					}
				}
				columns_to_update.insertMulti (gov_prop, it.key ());
				connect (gov_prop, SIGNAL (valueChanged(RKComponentPropertyBase *)), this, SLOT (governingPropertyChanged(RKComponentPropertyBase *)));
			} else {
// TODO: split out some of the large blocks into helper functions, to make this easier to read
void RKStructureGetter::getStructureWorker (SEXP val, const QString &name, int add_type_flags, RData *storage, int nesting_depth) {
	RK_TRACE (RBACKEND);

	bool at_toplevel = (toplevel_value == val);
	bool is_function = false;
	bool is_container = false;
	bool is_environment = false;
	bool no_recurse = (nesting_depth >= 2);	// TODO: should be configurable
	unsigned int type = 0;

	RK_DEBUG (RBACKEND, DL_DEBUG, "fetching '%s': %p, s-type %d", name.toLatin1().data(), val, TYPEOF (val));

	SEXP value = val;
	PROTECT_INDEX value_index;
	PROTECT_WITH_INDEX (value, &value_index);
	// manually resolve any promises
	REPROTECT (value = resolvePromise (value), value_index);

	bool is_s4 = Rf_isS4 (value);
	SEXP baseenv = R_BaseEnv;
	if (is_s4) baseenv = R_GlobalEnv;

	// first field: get name
	RData *namedata = new RData;
	namedata->setData (QStringList (name));

	// get classes
	SEXP classes_s;

	if ((TYPEOF (value) == LANGSXP) || (TYPEOF (value) == SYMSXP)) {	// if it's a call, we should NEVER send it through eval
		extern SEXP R_data_class (SEXP, Rboolean);
		classes_s = R_data_class (value, (Rboolean) 0);

		REPROTECT (value = Rf_coerceVector (value, EXPRSXP), value_index);	// make sure the object is safe for everything to come

		PROTECT (classes_s);
	} else {
		classes_s = RKRSupport::callSimpleFun (class_fun, value, baseenv);
		PROTECT (classes_s);
	}

	QStringList classes = RKRSupport::SEXPToStringList (classes_s);
	UNPROTECT (1);	/* classes_s */

	// store classes
	RData *classdata = new RData;
	classdata->setData (classes);

	// basic classification
	for (int i = classes.size () - 1; i >= 0; --i) {
#warning: Using is.data.frame() may be more reliable (would need to be called only on List-objects, thus no major performance hit)
		if (classes[i] == "data.frame") type |= RObject::DataFrame;
	}

	if (RKRSupport::callSimpleBool (is_matrix_fun, value, baseenv)) type |= RObject::Matrix;
	if (RKRSupport::callSimpleBool (is_list_fun, value, baseenv)) type |= RObject::List;

	if (type != 0) {
		is_container = true;
		type |= RObject::Container;
	} else {
		if (RKRSupport::callSimpleBool (is_function_fun, value, baseenv)) {
			is_function = true;
			type |= RObject::Function;
		} else if (RKRSupport::callSimpleBool (is_environment_fun, value, baseenv)) {
			is_container = true;
			type |= RObject::Environment;
			is_environment = true;
		} else {
			type |= RObject::Variable;
			if (RKRSupport::callSimpleBool (is_factor_fun, value, baseenv)) type |= RObject::Factor;
			else if (RKRSupport::callSimpleBool (is_numeric_fun, value, baseenv)) type |= RObject::Numeric;
			else if (RKRSupport::callSimpleBool (is_character_fun, value, baseenv)) type |= RObject::Character;
			else if (RKRSupport::callSimpleBool (is_logical_fun, value, baseenv)) type |= RObject::Logical;

			if (RKRSupport::callSimpleBool (is_array_fun, value, baseenv)) type |= RObject::Array;
		}
	}
	type |= add_type_flags;

	if (is_container) {
		if (no_recurse) {
			type |= RObject::Incomplete;
			RK_DEBUG (RBACKEND, DL_DEBUG, "Depth limit reached. Will not recurse into %s", name.toLatin1().data ());
		}
	}

	// get meta data, if any
	RData *metadata = new RData;
	if (!Rf_isNull (Rf_getAttrib (value, meta_attrib))) {
		SEXP meta_s = RKRSupport::callSimpleFun (get_meta_fun, value, R_GlobalEnv);
		PROTECT (meta_s);
		metadata->setData (RKRSupport::SEXPToStringList (meta_s));
		UNPROTECT (1);	/* meta_s */
	} else {
		metadata->setData (QStringList ());
	}

	// get dims
	RData::IntStorage dims;
	SEXP dims_s = RKRSupport::callSimpleFun (dims_fun, value, baseenv);
	if (!Rf_isNull (dims_s)) {
		dims = RKRSupport::SEXPToIntArray (dims_s);
	} else {
		unsigned int len = Rf_length (value);
		if ((len < 2) && (!is_function)) {		// suspicious. Maybe some kind of list
			SEXP len_s = RKRSupport::callSimpleFun (length_fun, value, baseenv);
			PROTECT (len_s);
			if (Rf_isNull (len_s)) {
				dims.append (len);
			} else {
				dims = RKRSupport::SEXPToIntArray (len_s);
			}
			UNPROTECT (1); /* len_s */
		} else {
			dims.append (len);
		}
	}

	// store dims
	RData *dimdata = new RData;
	dimdata->setData (dims);

	RData *slotsdata = new RData ();
	// does it have slots?
	if (is_s4) {
		type |= RObject::S4Object;
		if (no_recurse) {
			type |= RObject::Incomplete;
			RK_DEBUG (RBACKEND, DL_DEBUG, "Depth limit reached. Will not recurse into slots of %s", name.toLatin1().data ());
		} else {
			RData::RDataStorage dummy (1, 0);
			dummy[0] = new RData ();

			SEXP slots_pseudo_object = RKRSupport::callSimpleFun (rk_get_slots_fun, value, R_GlobalEnv);
			PROTECT (slots_pseudo_object);
			getStructureSafe (slots_pseudo_object, "SLOTS", RObject::PseudoObject, dummy[0], nesting_depth);	// do not increase depth for this pseudo-object
			UNPROTECT (1);

			slotsdata->setData (dummy);
		}
	}

	// store type
	RData *typedata = new RData;
	typedata->setData (RData::IntStorage (1, type));

	// store everything we have so far
	int storage_length = RObject::StorageSizeBasicInfo;
	if (is_container) {
		storage_length = RObject::StorageSizeBasicInfo + 1;
	} else if (is_function) {
		storage_length = RObject::StorageSizeBasicInfo + 2;
	}
	RData::RDataStorage res (storage_length, 0);
	res[RObject::StoragePositionName] = namedata;
	res[RObject::StoragePositionType] = typedata;
	res[RObject::StoragePositionClass] = classdata;
	res[RObject::StoragePositionMeta] = metadata;
	res[RObject::StoragePositionDims] = dimdata;
	res[RObject::StoragePositionSlots] = slotsdata;

	// now add the extra info for containers and functions
	if (is_container) {
		bool do_env = (is_environment && (!no_recurse));
		bool do_cont = is_container && (!is_environment) && (!no_recurse);

		// fetch list of child names
		SEXP childnames_s;
		if (do_env) {
			childnames_s = R_lsInternal (value, (Rboolean) 1);
		} else if (do_cont) {
			childnames_s = RKRSupport::callSimpleFun (names_fun, value, baseenv);
		} else {
			childnames_s = R_NilValue; // dummy
		}
		PROTECT (childnames_s);
		QStringList childnames = RKRSupport::SEXPToStringList (childnames_s);
		int childcount = childnames.size ();
		if (childcount > NAMED_CHILDREN_LIMIT) {
			RK_DEBUG (RBACKEND, DL_WARNING, "object %s has %d named children. Will only retrieve the first %d", name.toLatin1().data (), childcount, NAMED_CHILDREN_LIMIT);
			childcount = NAMED_CHILDREN_LIMIT;
		}

		RData::RDataStorage children (childcount, 0);
		for (int i = 0; i < childcount; ++i) {
			children[i] = new RData ();		// NOTE: RData-ctor pre-initalizes these to empty. Thus, we're safe even if there is an error while fetching one of the children.
		}

		if (do_env) {
			RK_DEBUG (RBACKEND, DL_DEBUG, "recurse into environment %s", name.toLatin1().data ());
			if (!Rf_isEnvironment (value)) {
				// some classes (ReferenceClasses) are identified as envionments by is.environment(), but are not internally ENVSXPs.
				// For these, Rf_findVar would fail.
				REPROTECT (value = RKRSupport::callSimpleFun (as_environment_fun, value, R_GlobalEnv), value_index);
			}
			for (int i = 0; i < childcount; ++i) {
				SEXP current_childname = Rf_install(CHAR(STRING_ELT(childnames_s, i)));		// ??? Why does simply using STRING_ELT(childnames_i, i) crash?
				PROTECT (current_childname);
				SEXP child = Rf_findVar (current_childname, value);
				PROTECT (child);

				bool child_misplaced = false;
				if (at_toplevel && with_namespace && (!RKRBackend::this_pointer->RRuntimeIsVersion (2, 14, 0))) {
					if (!Rf_isNull (namespace_envir)) {
						SEXP dummy = Rf_findVarInFrame (namespace_envir, current_childname);
						if (Rf_isNull (dummy) || (dummy == R_UnboundValue)) child_misplaced = true;
					}
				}

				getStructureSafe (child, childnames[i], child_misplaced ? RObject::Misplaced : 0, children[i], nesting_depth + 1);
				UNPROTECT (2); /* current_childname, child */
			}
		} else if (do_cont) {
			RK_DEBUG (RBACKEND, DL_DEBUG, "recurse into list %s", name.toLatin1().data ());
			// fewer elements than names() can happen, although I doubt it is supposed to happen.
			// see http://sourceforge.net/tracker/?func=detail&aid=3002439&group_id=50231&atid=459007
			bool may_be_special = Rf_length (value) < childcount;
			if (Rf_isList (value) && (!may_be_special)) {		// old style list
				for (int i = 0; i < childcount; ++i) {
					SEXP child = CAR (value);
					getStructureSafe (child, childnames[i], 0, children[i], nesting_depth + 1);
					CDR (value);
				}
			} else if (Rf_isNewList (value) && (!may_be_special)) {				// new style list
				for (int i = 0; i < childcount; ++i) {
					SEXP child = VECTOR_ELT(value, i);
					getStructureSafe (child, childnames[i], 0, children[i], nesting_depth + 1);
				}
			} else {		// probably an S4 object disguised as a list
				SEXP index = Rf_allocVector(INTSXP, 1);
				PROTECT (index);
				for (int i = 0; i < childcount; ++i) {
					INTEGER (index)[0] = (i + 1);
					SEXP child = RKRSupport::callSimpleFun2 (double_brackets_fun, value, index, baseenv);
					getStructureSafe (child, childnames[i], 0, children[i], nesting_depth + 1);
				}
				UNPROTECT (1); /* index */
			}
		}
		UNPROTECT (1);   /* childnames_s */

		RData *childdata = new RData;
		childdata->setData (children);
		res[RObject::StoragePositionChildren] = childdata;

		if (is_environment && at_toplevel && with_namespace) {
			RData *namespacedata = new RData;

			if (no_recurse) {
				type |= RObject::Incomplete;
				RK_DEBUG (RBACKEND, DL_DEBUG, "Depth limit reached. Will not recurse into namespace of %s", name.toLatin1().data ());
			} else {
				RData::RDataStorage dummy (1, 0);
				dummy[0] = new RData ();

				getStructureSafe (namespace_envir, "NAMESPACE", RObject::PseudoObject, dummy[0], nesting_depth+99);	// HACK: By default, do not recurse into the children of the namespace, until dealing with the namespace object itself.

				namespacedata->setData (dummy);
			}

			res.insert (RObject::StoragePositionNamespace, namespacedata);
		}
	} else if (is_function) {
// TODO: getting the formals is still a bit of a bottleneck, but no idea, how to improve on this, any further
		SEXP formals_s;
		if (Rf_isPrimitive (value)) formals_s = FORMALS (RKRSupport::callSimpleFun (args_fun, value, baseenv));	// primitives don't have formals, internally
		else formals_s = FORMALS (value);
		PROTECT (formals_s);

		// get the default values
		QStringList formals = RKRSupport::SEXPToStringList (formals_s);
		// for the most part, the implicit as.character in SEXPToStringList does a good on the formals (and it's the fastest of many options that I have tried).
		// Only for naked strings (as in 'function (a="something")'), we're missing the quotes. So we add quotes, after conversion, as needed:
		SEXP dummy = formals_s;
		const int formals_len = Rf_length (formals_s);
		for (int i = 0; i < formals_len; ++i) {
			if (TYPEOF (CAR (dummy)) == STRSXP) formals[i] = RKRSharedFunctionality::quote (formals[i]);
			dummy = CDR (dummy);
		}
		RData *funargvaluesdata = new RData;
		funargvaluesdata->setData (formals);

		// the argument names
		SEXP names_s = Rf_getAttrib (formals_s, R_NamesSymbol);
		PROTECT (names_s);
		RData *funargsdata = new RData;
		funargsdata->setData (RKRSupport::SEXPToStringList (names_s));

		UNPROTECT (2); /* names_s, formals_s */

		res[RObject::StoragePositionFunArgs] = funargsdata;
		res[RObject::StoragePositionFunValues] = funargvaluesdata;
	}

	UNPROTECT (1); /* value */

	RK_ASSERT (!res.contains (0));
	storage->setData (res);
}
Beispiel #19
0
RKConsole::RKConsole (QWidget *parent, bool tool_window, const char *name) : RKMDIWindow (parent, RKMDIWindow::ConsoleWindow, tool_window, name), commands_history (false, false) {
	RK_TRACE (APP);

	QVBoxLayout *layout = new QVBoxLayout (this);
	layout->setContentsMargins (0, 0, 0, 0);

	// create a Kate-part as command-editor
	KTextEditor::Editor* editor = KTextEditor::editor ("katepart");
	if (!editor) {
		RK_ASSERT (false);
		KMessageBox::error (this, i18n ("The 'katepart' component could not be loaded. RKWard cannot run without katepart, and will exit, now. Please install katepart, and try again."), i18n ("'katepart' component could not be found"));
		exit (1);
	}

	doc = editor->createDocument (this);
	view = doc->createView (this);
	layout->addWidget (view);

	KTextEditor::ConfigInterface *confint = qobject_cast<KTextEditor::ConfigInterface*> (view);
	RK_ASSERT (view);
	confint->setConfigValue ("dynamic-word-wrap", false);

	KTextEditor::CodeCompletionInterface *iface = qobject_cast<KTextEditor::CodeCompletionInterface*> (view);
	if (iface) iface->setAutomaticInvocationEnabled (false);

	setFocusProxy (view);
	setFocusPolicy (Qt::StrongFocus);
	
	/* We need to disable kactions that were plugged to the KateViewInternal in kateview.cpp.
	These actions include Key_Up, Key_Down, etc. */
	kate_edit_actions = view->findChild<KActionCollection*> ("edit_actions");
	if (!kate_edit_actions) {
		kate_edit_actions=view->actionCollection();
	}
	if (kate_edit_actions) {
		// make sure these actions never get triggered by a shortcut

		QList<QKeySequence> noshort;
		noshort.append (QKeySequence ());	// no primary
		noshort.append (QKeySequence ());	// no secondary
		noshort.append (QKeySequence ());	// for good measure

		QList<QAction*> keas = kate_edit_actions->actions ();
		for (int i = 0; i < keas.size (); ++i) {
			keas[i]->setShortcuts (noshort);
		}
	} else {
		RK_DEBUG (APP, DL_ERROR, "Could not retrieve the katepart's edit action collection");
	}

	if (view->focusProxy ()) view->focusProxy()->installEventFilter(this);
	view->installEventFilter(this);

	doc->setModified (false);

	hinter = new RKFunctionArgHinter (this, view);
	
	setCaption (i18n ("R Console"));
	console_part = new RKConsolePart (this);
	setPart (console_part);
	setMetaInfo (shortCaption (), "rkward://page/rkward_console", RKSettings::PageConsole);
	initializeActivationSignals ();
	initializeActions (getPart ()->actionCollection ());

	nprefix = "> ";
	iprefix = "+ ";
	prefix = nprefix;
// KDE4: a way to do this?
//	doc->setUndoSteps (0);
	clear ();
	RKCommandHighlighter::setHighlighting (doc, RKCommandHighlighter::RInteractiveSession);

	commands_history.setHistory (RKSettingsModuleConsole::loadCommandHistory ());

	current_command = 0;
	current_command_displayed_up_to = 0;
	tab_key_pressed_before = false;
	previous_chunk_was_piped = false;

// KDE4 TODO: workaround for KDE 4 pre-release versions. Hope this gets fixed before 4.0
// see http://lists.kde.org/?l=kwrite-devel&m=119721420603507&w=2
	setMinimumHeight (50);
}