1 /***************************************************************************
2                           robjectbrowser  -  description
3                              -------------------
4     begin                : Thu Aug 19 2004
5     copyright            : (C) 2004 - 2017 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 "robjectbrowser.h"
18 
19 #include <qlayout.h>
20 #include <qpushbutton.h>
21 #include <QFocusEvent>
22 #include <QVBoxLayout>
23 #include <QMenu>
24 #include <QInputDialog>
25 
26 #include <KLocalizedString>
27 #include <kmessagebox.h>
28 
29 #include "../rkward.h"
30 #include "rkhelpsearchwindow.h"
31 #include "../rkglobals.h"
32 #include "../core/robjectlist.h"
33 #include "../core/renvironmentobject.h"
34 #include "../core/rkmodificationtracker.h"
35 #include "../rbackend/rkrinterface.h"
36 #include "../misc/rkobjectlistview.h"
37 #include "../misc/rkdummypart.h"
38 #include "../misc/rkstandardicons.h"
39 #include "../misc/rkstandardactions.h"
40 #include "rkworkplace.h"
41 #include "../dataeditor/rkeditor.h"
42 
43 #include "../debug.h"
44 
45 // static
46 RObjectBrowser* RObjectBrowser::object_browser = 0;
47 
RObjectBrowser(QWidget * parent,bool tool_window,const char * name)48 RObjectBrowser::RObjectBrowser (QWidget *parent, bool tool_window, const char *name) : RKMDIWindow (parent, WorkspaceBrowserWindow, tool_window, name) {
49 	RK_TRACE (APP);
50 
51 	internal = 0;
52 	locked = true;
53 
54 	QVBoxLayout *layout = new QVBoxLayout (this);
55 	layout->setContentsMargins (0, 0, 0, 0);
56 	layout_widget = new QWidget (this);
57 	layout->addWidget (layout_widget);
58 	layout_widget->setFocusPolicy (Qt::StrongFocus);
59 
60 	RKDummyPart *part = new RKDummyPart (this, layout_widget);
61 	setPart (part);
62 	setMetaInfo (i18n ("R workspace browser"), QUrl ("rkward://page/rkward_workspace_browser"), RKSettings::PageObjectBrowser);
63 	initializeActivationSignals ();
64 
65 	setCaption (i18n ("R Workspace"));
66 }
67 
~RObjectBrowser()68 RObjectBrowser::~RObjectBrowser () {
69 	RK_TRACE (APP);
70 }
71 
unlock()72 void RObjectBrowser::unlock () {
73 	RK_TRACE (APP);
74 
75 	locked = false;
76 	if (!isHidden ()) {
77 		initialize ();
78 	}
79 }
80 
showEvent(QShowEvent * e)81 void RObjectBrowser::showEvent (QShowEvent *e) {
82 	RK_TRACE (APP);
83 
84 	initialize ();
85 	RKMDIWindow::showEvent (e);
86 }
87 
initialize()88 void RObjectBrowser::initialize () {
89 	RK_TRACE (APP);
90 
91 	if (internal) return;
92 	if (locked) return;
93 
94 	RK_DEBUG (APP, DL_INFO, "creating workspace browser");
95 
96 	internal = new RObjectBrowserInternal (layout_widget, this);
97 	QVBoxLayout *l = new QVBoxLayout (layout_widget);
98 	l->setContentsMargins (0, 0, 0, 0);
99 	l->addWidget (internal);
100 
101 	setFocusProxy (internal);
102 	setMinimumSize (internal->minimumSize ());
103 }
104 
105 
106 ///////////////////////// RObjectBrowserInternal /////////////////////////////
RObjectBrowserInternal(QWidget * parent,RObjectBrowser * browser)107 RObjectBrowserInternal::RObjectBrowserInternal (QWidget *parent, RObjectBrowser *browser) : QWidget (parent) {
108 	RK_TRACE (APP);
109 	setFocusPolicy (Qt::ClickFocus);
110 
111 	QVBoxLayout *vbox = new QVBoxLayout (this);
112 	vbox->setContentsMargins (0, 0, 0, 0);
113 
114 	list_view = new RKObjectListView (true, this);
115 	vbox->addWidget (list_view->getSettings ()->filterWidget (this));
116 	vbox->addWidget (list_view);
117 
118 	update_button = new QPushButton (i18n ("Update"), this);
119 	vbox->addWidget (update_button);
120 
121 	actions.insert (Help, RKStandardActions::functionHelp (browser, this));
122 	actions.insert (SearchOnline, RKStandardActions::onlineHelp (browser, this));
123 	actions.insert (Edit, new QAction (i18n ("Edit"), this));
124 	connect (actions[Edit], &QAction::triggered, this, &RObjectBrowserInternal::popupEdit);
125 	actions.insert (View, new QAction (i18n ("View"), this));
126 	connect (actions[View], &QAction::triggered, this, &RObjectBrowserInternal::popupView);
127 	actions.insert (Rename, new QAction (i18n ("Rename"), this));
128 	connect (actions[Rename], &QAction::triggered, this, &RObjectBrowserInternal::popupRename);
129 	actions.insert (Copy, new QAction (i18n ("Copy to new symbol"), this));
130 	connect (actions[Copy], &QAction::triggered, this, &RObjectBrowserInternal::popupCopy);
131 	actions.insert (CopyToGlobalEnv, new QAction (i18n ("Copy to .GlobalEnv"), this));
132 	connect (actions[CopyToGlobalEnv], &QAction::triggered, this, &RObjectBrowserInternal::popupCopyToGlobalEnv);
133 	actions.insert (Delete, new QAction (i18n ("Delete"), this));
134 	connect (actions[Delete], &QAction::triggered, this, &RObjectBrowserInternal::popupDelete);
135 	actions.insert (Unload, new QAction (i18n ("Unload Package"), this));
136 	connect (actions[Unload], &QAction::triggered, this, &RObjectBrowserInternal::popupUnload);
137 	actions.insert (LoadUnloadPackages, new QAction (i18n ("Load / Unload Packages"), this));
138 	connect (actions[LoadUnloadPackages], &QAction::triggered, RKWardMainWindow::getMain(), &RKWardMainWindow::slotFileLoadLibs);
139 
140 	QAction* sep = list_view->contextMenu ()->insertSeparator (list_view->contextMenu ()->actions ().value (0));
141 	list_view->contextMenu ()->insertActions (sep, actions);
142 
143 	connect (list_view, &RKObjectListView::aboutToShowContextMenu, this, &RObjectBrowserInternal::contextMenuCallback);
144 
145 	connect (list_view, &QAbstractItemView::doubleClicked, this, &RObjectBrowserInternal::doubleClicked);
146 
147 	resize (minimumSizeHint ().expandedTo (QSize (400, 480)));
148 
149 	list_view->initialize ();
150 	connect (update_button, &QPushButton::clicked, this, &RObjectBrowserInternal::updateButtonClicked);
151 }
152 
~RObjectBrowserInternal()153 RObjectBrowserInternal::~RObjectBrowserInternal () {
154 	RK_TRACE (APP);
155 }
156 
focusInEvent(QFocusEvent * e)157 void RObjectBrowserInternal::focusInEvent (QFocusEvent *e) {
158 	RK_TRACE (APP);
159 
160 	list_view->getSettings ()->filterWidget (this)->setFocus ();
161 	if (e->reason () != Qt::MouseFocusReason) {
162 		list_view->setObjectCurrent (RObjectList::getGlobalEnv (), true);
163 	}
164 }
165 
updateButtonClicked()166 void RObjectBrowserInternal::updateButtonClicked () {
167 	RK_TRACE (APP);
168 	RObjectList::getObjectList ()->updateFromR (0);
169 }
170 
currentHelpContext(QString * symbol,QString * package)171 void RObjectBrowserInternal::currentHelpContext (QString* symbol, QString* package) {
172 	RK_TRACE (APP);
173 
174 	RObject *object = list_view->menuObject ();
175 	if (!object) return;
176 	*symbol = object->getShortName ();
177 	*package = object->isInGlobalEnv () ? QString () : object->toplevelEnvironment ()->packageName ();
178 }
179 
popupEdit()180 void RObjectBrowserInternal::popupEdit () {
181 	RK_TRACE (APP);
182 	if (list_view->menuObject ()) RKWorkplace::mainWorkplace ()->editObject (list_view->menuObject ());
183 }
184 
popupCopy()185 void RObjectBrowserInternal::popupCopy () {
186 	RK_TRACE (APP);
187 
188 	bool ok;
189 	RObject *object = list_view->menuObject ();
190 	QString suggested_name = RObjectList::getGlobalEnv ()->validizeName (object->getShortName ());
191 	QString name = QInputDialog::getText (this, i18n ("Copy object"), i18n ("Enter the name to copy to"), QLineEdit::Normal, suggested_name, &ok);
192 
193 	if (ok) {
194 		QString valid = RObjectList::getGlobalEnv ()->validizeName (name);
195 		if (valid != name) KMessageBox::sorry (this, i18n ("The name you specified was already in use or not valid. Renamed to %1", valid), i18n ("Invalid Name"));
196 		RKGlobals::rInterface ()->issueCommand (RObject::rQuote (valid) + " <- " + object->getFullName (), RCommand::App | RCommand::ObjectListUpdate);
197 	}
198 }
199 
popupCopyToGlobalEnv()200 void RObjectBrowserInternal::popupCopyToGlobalEnv () {
201 	RK_TRACE (APP);
202 
203 	RObject *object = list_view->menuObject ();
204 	QString name = object->getShortName ();
205 
206 	QString valid = RObjectList::getGlobalEnv ()->validizeName (name);
207 	if (valid != name) KMessageBox::sorry (this, i18n ("An object named '%1' already exists in the GlobalEnv. Created the copy as '%2' instead.", name, valid), i18n ("Name already in use"));
208 	RKGlobals::rInterface ()->issueCommand (RObject::rQuote (valid) + " <- " + object->getFullName (), RCommand::App | RCommand::ObjectListUpdate);
209 }
210 
popupView()211 void RObjectBrowserInternal::popupView () {
212 	RK_TRACE (APP);
213 	RKWorkplace::mainWorkplace ()->flushAllData ();
214 	RKWorkplace::mainWorkplace ()->newObjectViewer (list_view->menuObject ());
215 }
216 
popupDelete()217 void RObjectBrowserInternal::popupDelete () {
218 	RK_TRACE (APP);
219 	RKGlobals::tracker ()->removeObject (list_view->menuObject ());
220 }
221 
popupUnload()222 void RObjectBrowserInternal::popupUnload () {
223 	RK_TRACE (APP);
224 
225 	RObject *object = list_view->menuObject ();
226 	RK_ASSERT (object);
227 	RK_ASSERT (object->isType (RObject::PackageEnv));
228 
229 	QStringList messages = RObjectList::getObjectList ()->detachPackages (QStringList (object->getShortName ()));
230 
231 	if (!messages.isEmpty ()) KMessageBox::sorry (this, messages.join ("\n"));
232 }
233 
popupRename()234 void RObjectBrowserInternal::popupRename () {
235 	RK_TRACE (APP);
236 	bool ok;
237 	QString name = QInputDialog::getText (this, i18n ("Rename object"), i18n ("Enter the new name"), QLineEdit::Normal, list_view->menuObject ()->getShortName (), &ok);
238 
239 	if (ok) {
240 		QString valid = static_cast<RContainerObject*> (list_view->menuObject ()->parentObject ())->validizeName (name);
241 		if (valid != name) KMessageBox::sorry (this, i18n ("The name you specified was already in use or not valid. Renamed to %1", valid), i18n ("Invalid Name"));
242 		RKGlobals::tracker ()->renameObject (list_view->menuObject (), valid);
243 	}
244 }
245 
contextMenuCallback(RObject *,bool *)246 void RObjectBrowserInternal::contextMenuCallback (RObject *, bool *) {
247 	RK_TRACE (APP);
248 	RObject *object = list_view->menuObject ();
249 
250 	if (!object) {
251 		RK_ASSERT (actions.size () == ActionCount);
252 		for (int i = 0; i < ActionCount; ++i) {
253 			actions[i]->setVisible (false);
254 		}
255 		actions[LoadUnloadPackages]->setVisible (true);
256 		return;
257 	}
258 
259 	actions[Help]->setVisible (!(object->isType (RObject::ToplevelEnv) || object->isInGlobalEnv ()));
260 	actions[Edit]->setText (object->canWrite () ? i18n ("Edit") : i18n ("View in editor (read-only)"));
261 	actions[Edit]->setVisible (RKWorkplace::mainWorkplace ()->canEditObject (object));
262 	actions[View]->setVisible (object->canRead ());
263 	actions[Rename]->setVisible (object->canRename ());
264 	actions[Copy]->setVisible (object->canRead () && (!object->isType (RObject::ToplevelEnv)));
265 	actions[CopyToGlobalEnv]->setVisible (object->canRead () && (!object->isInGlobalEnv()) && (!object->isType (RObject::ToplevelEnv)));
266 	actions[Delete]->setVisible (object->canRemove ());
267 	actions[Unload]->setVisible (object->isType (RObject::PackageEnv));
268 	actions[LoadUnloadPackages]->setVisible (object == RObjectList::getObjectList ());
269 }
270 
doubleClicked(const QModelIndex & index)271 void RObjectBrowserInternal::doubleClicked (const QModelIndex& index) {
272 	RK_TRACE (APP);
273 
274 	RObject *object = list_view->objectAtIndex (index);
275 	if (!object) return;
276 	if (object == RObjectList::getObjectList ()) return;
277 
278 	if (object->isInGlobalEnv ()) {
279 		if (RKWorkplace::mainWorkplace ()->canEditObject (object)) {
280 			RKWorkplace::mainWorkplace ()->editObject (object);
281 		} else {
282 			RKWorkplace::mainWorkplace ()->newObjectViewer (object);
283 		}
284 	} else {
285 		RKHelpSearchWindow::mainHelpSearch ()->getFunctionHelp (object->getShortName (), object->toplevelEnvironment ()->packageName ());
286 	}
287 }
288