[rkward/work/split_views] rkward: Support duplication ("splitting") of editors, object viewers, output window.
Thomas Friedrichsmeier
null at kde.org
Tue Jun 20 08:59:09 UTC 2017
Git commit 12f841b54fcf3c8c3b8c30f127d0e436b251d789 by Thomas Friedrichsmeier.
Committed on 20/06/2017 at 08:57.
Pushed by tfry into branch 'work/split_views'.
Support duplication ("splitting") of editors, object viewers, output window.
Add tool button for splitting and closing panes.
Left TODO: save and restore splits.
M +6 -6 rkward/core/rkmodificationtracker.cpp
M +2 -2 rkward/core/rkmodificationtracker.h
M +2 -2 rkward/core/robject.cpp
M +1 -1 rkward/core/robject.h
M +9 -9 rkward/windows/rkhtmlwindow.cpp
M +5 -3 rkward/windows/rkhtmlwindow.h
M +48 -22 rkward/windows/rkworkplace.cpp
M +4 -1 rkward/windows/rkworkplace.h
M +59 -25 rkward/windows/rkworkplaceview.cpp
M +6 -3 rkward/windows/rkworkplaceview.h
https://commits.kde.org/rkward/12f841b54fcf3c8c3b8c30f127d0e436b251d789
diff --git a/rkward/core/rkmodificationtracker.cpp b/rkward/core/rkmodificationtracker.cpp
index bd80eff7..7d93c00d 100644
--- a/rkward/core/rkmodificationtracker.cpp
+++ b/rkward/core/rkmodificationtracker.cpp
@@ -2,7 +2,7 @@
rkmodificationtracker - description
-------------------
begin : Tue Aug 31 2004
- copyright : (C) 2004-2015 by Thomas Friedrichsmeier
+ copyright : (C) 2004-2017 by Thomas Friedrichsmeier
email : thomas.friedrichsmeier at kdemail.net
***************************************************************************/
@@ -55,9 +55,8 @@ void RKModificationTracker::lockUpdates (bool lock) {
bool RKModificationTracker::removeObject (RObject *object, RKEditor *editor, bool removed_in_workspace) {
RK_TRACE (OBJECTS);
-// TODO: allow more than one editor per object
// WARNING: This does not work, if a sub-object is being edited!
- RKEditor *ed = objectEditor (object);
+ RKEditor* ed = objectEditors (object).value (0);
RK_ASSERT (object);
RK_ASSERT (!((editor) && (!ed)));
RK_ASSERT (!(removed_in_workspace && editor));
@@ -245,19 +244,20 @@ void RKModificationTracker::sendListenerNotification (RObjectListener::Notificat
}
}
-RKEditor* RKModificationTracker::objectEditor (const RObject* object) {
+QList<RKEditor*> RKModificationTracker::objectEditors (const RObject* object) const {
RK_TRACE (OBJECTS);
+ QList<RKEditor*> ret;
QList<RObjectListener*> obj_listeners = listeners.values (const_cast<RObject*> (object));
for (int i = obj_listeners.size () - 1; i >= 0; --i) {
RObjectListener* listener = obj_listeners[i];
if (!(listener->listenerType () == RObjectListener::DataModel)) continue;
RKEditor* ed = static_cast<RKVarEditModel*> (listener)->getEditor ();
- if (ed) return ed;
+ if (ed) ret.append (ed);
}
- return 0;
+ return ret;
}
///////////////// RKObjectListModel ///////////////////////////
diff --git a/rkward/core/rkmodificationtracker.h b/rkward/core/rkmodificationtracker.h
index 344de9f5..c700f3e2 100644
--- a/rkward/core/rkmodificationtracker.h
+++ b/rkward/core/rkmodificationtracker.h
@@ -2,7 +2,7 @@
rkmodificationtracker - description
-------------------
begin : Tue Aug 31 2004
- copyright : (C) 2004, 2007, 2011 by Thomas Friedrichsmeier
+ copyright : (C) 2004 - 2017 by Thomas Friedrichsmeier
email : thomas.friedrichsmeier at kdemail.net
***************************************************************************/
@@ -135,7 +135,7 @@ public:
/** recursive! */
void lockUpdates (bool lock);
/** returns (the first) editor that is currently active for this object, or 0, if there is no editor */
- RKEditor* objectEditor (const RObject* object);
+ QList<RKEditor*> objectEditors (const RObject* object) const;
private:
int updates_locked;
/** relay change notifications to connected listeners. This is not pretty, since the arguments change their meanings depending on the type of notification, but for now this is ok */
diff --git a/rkward/core/robject.cpp b/rkward/core/robject.cpp
index b2843da5..90f4a29c 100644
--- a/rkward/core/robject.cpp
+++ b/rkward/core/robject.cpp
@@ -558,8 +558,8 @@ RObject *RObject::findChildByObjectModelIndex (int index) const {
return 0;
}
-RKEditor *RObject::editor () const {
- return (RKGlobals::tracker ()->objectEditor (this));
+QList <RKEditor*> RObject::editors () const {
+ return (RKGlobals::tracker ()->objectEditors (this));
}
void RObject::rename (const QString &new_short_name) {
diff --git a/rkward/core/robject.h b/rkward/core/robject.h
index 2725e228..f93f5116 100644
--- a/rkward/core/robject.h
+++ b/rkward/core/robject.h
@@ -145,7 +145,7 @@ public:
void markDataDirty ();
/** Returns the editor of this object, if any, or 0 */
- RKEditor* editor () const;
+ QList<RKEditor*> editors () const;
bool canWrite () const;
bool canRead () const;
bool canRename () const;
diff --git a/rkward/windows/rkhtmlwindow.cpp b/rkward/windows/rkhtmlwindow.cpp
index b6514b48..777e29eb 100644
--- a/rkward/windows/rkhtmlwindow.cpp
+++ b/rkward/windows/rkhtmlwindow.cpp
@@ -2,7 +2,7 @@
rkhtmlwindow - description
-------------------
begin : Wed Oct 12 2005
- copyright : (C) 2005-2015 by Thomas Friedrichsmeier
+ copyright : (C) 2005-2017 by Thomas Friedrichsmeier
email : thomas.friedrichsmeier at kdemail.net
***************************************************************************/
@@ -1093,18 +1093,18 @@ void RKOutputWindowManager::rewatchOutput () {
file_watcher->addFile (current_default_path);
}
-RKHTMLWindow* RKOutputWindowManager::getCurrentOutputWindow () {
+QList<RKHTMLWindow*> RKOutputWindowManager::existingOutputWindows () const {
RK_TRACE (APP);
- RKHTMLWindow *current_output = windows.value (current_default_path);
-
- if (!current_output) {
- current_output = new RKHTMLWindow (RKWorkplace::mainWorkplace ()->view (), RKHTMLWindow::HTMLOutputWindow);
+ return (windows.values (current_default_path));
+}
- current_output->openURL (QUrl::fromLocalFile (current_default_path));
+RKHTMLWindow* RKOutputWindowManager::newOutputWindow () {
+ RK_TRACE (APP);
- RK_ASSERT (current_output->url ().toLocalFile () == current_default_path);
- }
+ RKHTMLWindow* current_output = new RKHTMLWindow (RKWorkplace::mainWorkplace ()->view (), RKHTMLWindow::HTMLOutputWindow);
+ current_output->openURL (QUrl::fromLocalFile (current_default_path));
+ RK_ASSERT (current_output->url ().toLocalFile () == current_default_path);
return current_output;
}
diff --git a/rkward/windows/rkhtmlwindow.h b/rkward/windows/rkhtmlwindow.h
index 7598b100..0a0a63e5 100644
--- a/rkward/windows/rkhtmlwindow.h
+++ b/rkward/windows/rkhtmlwindow.h
@@ -2,7 +2,7 @@
rkhtmlwindow - description
-------------------
begin : Wed Oct 12 2005
- copyright : (C) 2005, 2006, 2007, 2009, 2011, 2014, 2015 by Thomas Friedrichsmeier
+ copyright : (C) 2005-2017 by Thomas Friedrichsmeier
email : thomas.friedrichsmeier at kdemail.net
***************************************************************************/
@@ -221,8 +221,10 @@ public:
void registerWindow (RKHTMLWindow *window);
/** R may produce output while no output window is active. This allows to set the file that should be monitored for such changes (called from within rk.set.html.output.file()). */
void setCurrentOutputPath (const QString &path);
-/** return a pointer to the current output. If there is no output window, one will be created (and shown) automatically */
- RKHTMLWindow* getCurrentOutputWindow ();
+/** returns a list (possibly empty) of pointers to existing output windows (for the current output path, only). */
+ QList<RKHTMLWindow*> existingOutputWindows () const;
+/** Create (and show) a new output window, and @return the pointer */
+ RKHTMLWindow* newOutputWindow ();
private:
RKOutputWindowManager ();
~RKOutputWindowManager ();
diff --git a/rkward/windows/rkworkplace.cpp b/rkward/windows/rkworkplace.cpp
index f5a0fe1a..cea94510 100644
--- a/rkward/windows/rkworkplace.cpp
+++ b/rkward/windows/rkworkplace.cpp
@@ -494,13 +494,17 @@ RKMDIWindow* RKWorkplace::openHelpWindow (const QUrl &url, bool only_once) {
RKMDIWindow* RKWorkplace::openOutputWindow (const QUrl &url) {
RK_TRACE (APP);
- RKHTMLWindow *w = RKOutputWindowManager::self ()->getCurrentOutputWindow ();
- if (!windows.contains (w)) {
- addWindow (w);
- } else {
- w->activate ();
+ QList<RKHTMLWindow*> owins = RKOutputWindowManager::self ()->existingOutputWindows ();
+ for (int i = 0; i < owins.size (); ++i) {
+ if (view ()->windowInActivePane (owins[i])) {
+ owins[i]->activate ();
+ return (owins[i]);
+ }
}
- return (w);
+
+ RKHTMLWindow* ret = RKOutputWindowManager::self ()->newOutputWindow ();
+ addWindow (ret);
+ return (ret);
}
void RKWorkplace::newX11Window (QWindow* window_to_embed, int device_number) {
@@ -524,10 +528,13 @@ void RKWorkplace::newObjectViewer (RObject *object) {
RK_ASSERT (object);
RKWorkplaceObjectList object_windows = getObjectList (RKMDIWindow::ObjectWindow, RKMDIWindow::AnyWindowState);
- for (RKWorkplaceObjectList::const_iterator it = object_windows.constBegin (); it != object_windows.constEnd (); ++it) {
- if (static_cast<RObjectViewer *> (*it)->object () == object) {
- (*it)->activate ();
- return;
+ for (int i = 0; i < object_windows.size (); ++i) {
+ RObjectViewer *viewer = static_cast<RObjectViewer *> (object_windows[i]);
+ if (viewer->object () == object) {
+ if (view ()->windowInActivePane (viewer)) {
+ viewer->activate ();
+ return;
+ }
}
}
@@ -561,17 +568,27 @@ RKEditor *RKWorkplace::editObject (RObject *object) {
RK_ASSERT (object);
RObject *iobj = object;
+ if (!iobj->isDataFrame ()) {
+ if (iobj->isVariable () && iobj->parentObject ()->isDataFrame ()) {
+ iobj = iobj->parentObject ();
+ } else {
+ return 0;
+ }
+ }
+
RKEditor *ed = 0;
- RKEditor *existing_editor = object->editor ();
- if (!existing_editor) {
- if (!iobj->isDataFrame ()) {
- if (iobj->isVariable () && iobj->parentObject ()->isDataFrame ()) {
- iobj = iobj->parentObject ();
- } else {
- return 0;
+ QList<RKEditor*> existing_editors = object->editors ();
+ for (int i = 0; i < existing_editors.size (); ++i) {
+ RObject *eobj = existing_editors[i]->getObject ();
+ if (eobj == iobj) {
+ if (view ()->windowInActivePane (existing_editors[i])) {
+ ed = existing_editors[i];
+ break;
}
}
+ }
+ if (!ed) {
unsigned long size = 1;
foreach (int dim, iobj->getDimensions ()) {
size *= dim;
@@ -584,8 +601,6 @@ RKEditor *RKWorkplace::editObject (RObject *object) {
ed = new RKEditorDataFrame (static_cast<RContainerObject*> (iobj), 0);
addWindow (ed);
- } else {
- ed = existing_editor;
}
ed->activate ();
@@ -634,10 +649,15 @@ RKWorkplace::RKWorkplaceObjectList RKWorkplace::getObjectList (int type, int sta
void RKWorkplace::closeAll (int type, int state) {
RK_TRACE (APP);
+ closeWindows (getObjectList (type, state));
+}
+
+void RKWorkplace::closeWindows (QList<RKMDIWindow*> windows) {
+ RK_TRACE (APP);
+
RKWardMainWindow::getMain ()->lockGUIRebuild (true);
- RKWorkplaceObjectList list_to_close = getObjectList (type, state);
- for (RKWorkplaceObjectList::const_iterator it = list_to_close.constBegin (); it != list_to_close.constEnd (); ++it) {
- closeWindow (*it);
+ for (int i = windows.size () - 1; i >= 0; --i) {
+ closeWindow (windows[i]);
}
RKWardMainWindow::getMain ()->lockGUIRebuild (false);
}
@@ -883,6 +903,12 @@ void RKWorkplace::duplicateAndAttachWindow (RKMDIWindow* source) {
openScriptEditor (url, QString (), RKSettingsModuleCommandEditor::matchesScriptFileFilter (url.fileName()));
} else if (source->isType (RKMDIWindow::HelpWindow)) {
openHelpWindow (static_cast<RKHTMLWindow*> (source)->url ());
+ } else if (source->isType (RKMDIWindow::OutputWindow)) {
+ openOutputWindow (static_cast<RKHTMLWindow*> (source)->url ());
+ } else if (source->isType (RKMDIWindow::DataEditorWindow)) {
+ editObject (static_cast<RKEditor*> (source)->getObject ());
+ } else if (source->isType (RKMDIWindow::ObjectWindow)) {
+ newObjectViewer (static_cast<RObjectViewer*> (source)->object ());
} else {
openHelpWindow (QUrl ("rkward://page/rkward_split_views"));
}
diff --git a/rkward/windows/rkworkplace.h b/rkward/windows/rkworkplace.h
index 2fb7a8fb..046c5561 100644
--- a/rkward/windows/rkworkplace.h
+++ b/rkward/windows/rkworkplace.h
@@ -111,7 +111,7 @@ public:
@param url URL to open
@param only_once if true, checks whether any help window already shows this URL. If so, raise it, but do not open a new window. Else show the new window */
RKMDIWindow* openHelpWindow (const QUrl &url=QUrl (), bool only_once=false);
-/** Opens a new output window. Currently only a single output window will ever be created. Subsequent calls to the function will not create additional windows right now (but will raise / refresh the output window
+/** Opens a new output window, or raise / refresh the current output window.
@param url currently ignored! */
RKMDIWindow* openOutputWindow (const QUrl &url=QUrl ());
@@ -138,6 +138,9 @@ public:
/** Close the given window, whether it is attached or detached.
@param window window to close */
void closeWindow (RKMDIWindow *window);
+/** Close the given windows, whether they are attached or detached. TODO: Be smart about asking what to save.
+ at param windows list windows to close */
+ void closeWindows (QList<RKMDIWindow*> windows);
/** Closes all windows of the given type(s). Default call (no arguments) closes all windows
@param type: A bitwise OR of RKWorkplaceObjectType
@param state: A bitwise OR of RKWorkplaceObjectState */
diff --git a/rkward/windows/rkworkplaceview.cpp b/rkward/windows/rkworkplaceview.cpp
index 2e889915..516d3e0d 100644
--- a/rkward/windows/rkworkplaceview.cpp
+++ b/rkward/windows/rkworkplaceview.cpp
@@ -42,13 +42,6 @@ RKWorkplaceViewPane::RKWorkplaceViewPane (RKWorkplaceView* parent) : QTabWidget
workplace_view = parent;
- // close button(s)
- QToolButton* close_button = new QToolButton (this);
- close_button->setIcon (QIcon::fromTheme("tab-close"));
- connect (close_button, &QToolButton::clicked, this, &RKWorkplaceViewPane::closeCurrentPage);
- close_button->adjustSize ();
- setCornerWidget (close_button, Qt::TopRightCorner);
-
setTabsClosable (true);
connect (this, &QTabWidget::tabCloseRequested, this, static_cast<void (RKWorkplaceViewPane::*)(int)>(&RKWorkplaceViewPane::closePage));
@@ -66,6 +59,34 @@ RKWorkplaceViewPane::~RKWorkplaceViewPane () {
RK_TRACE (APP);
}
+void RKWorkplaceViewPane::initActions () {
+ RK_TRACE (APP);
+
+ QToolButton *split_button = new QToolButton (this);
+ split_button->setAutoRaise (true);
+ split_button->setPopupMode (QToolButton::InstantPopup);
+ split_button->setIcon (QIcon::fromTheme (QStringLiteral("view-split-left-right")));
+ split_button->addAction (workplace_view->action_split_vert);
+ split_button->addAction (workplace_view->action_split_horiz);
+ QAction *close_all = new QAction (QIcon::fromTheme("tab-close"), i18n ("Close all"), this);
+ connect (close_all, &QAction::triggered, this, &RKWorkplaceViewPane::closeAll);
+ split_button->addAction (close_all);
+ split_button->installEventFilter (this); // on click, active this pane
+ split_button->adjustSize ();
+ setCornerWidget (split_button, Qt::TopRightCorner);
+}
+
+bool RKWorkplaceViewPane::eventFilter (QObject* obj, QEvent* event) {
+ if (event->type () == QEvent::MouseButtonPress) {
+ RKMDIWindow *current = static_cast<RKMDIWindow*> (currentWidget ());
+ if (current && !current->isActiveInsideToplevelWindow()) {
+ current->activate (); // make sure this pane is active
+ }
+ }
+ return QObject::eventFilter (obj, event);
+}
+
+
bool RKWorkplaceViewPane::isActive () {
RK_TRACE (APP);
@@ -88,10 +109,14 @@ void RKWorkplaceViewPane::showContextMenu (const QPoint &pos) {
delete m;
}
-void RKWorkplaceViewPane::closeCurrentPage () {
+void RKWorkplaceViewPane::closeAll () {
RK_TRACE (APP);
- closePage (currentWidget ());
+ QList<RKMDIWindow*> windows;
+ for (int i = count () - 1; i >= 0; --i) {
+ windows.append (static_cast<RKMDIWindow*> (widget (i)));
+ }
+ RKWorkplace::mainWorkplace ()->closeWindows (windows);
}
void RKWorkplaceViewPane::closePage (int index) {
@@ -192,8 +217,6 @@ RKWorkplaceView::~RKWorkplaceView () {
RKWorkplaceViewPane* RKWorkplaceView::activePane () const {
RK_TRACE (APP);
- if (newpane) return newpane;
-
for (int i = 0; i < panes.size (); ++i) {
if (panes[i]->isActive ()) return panes[i];
}
@@ -219,20 +242,23 @@ void RKWorkplaceView::initActions (KActionCollection *ac) {
ac->setDefaultShortcuts (action_page_right, QList<QKeySequence>() << Qt::ControlModifier + Qt::Key_Greater << Qt::ControlModifier + Qt::Key_Period);
// NOTE: Icons, shortcuts, action names for split view actions as in kate
- QAction *action = ac->addAction (QStringLiteral ("view_split_vert"));
- action->setIcon (QIcon::fromTheme(QStringLiteral ("view-split-left-right")));
- action->setText (i18n("Split Ve&rtical"));
- ac->setDefaultShortcut (action, Qt::CTRL + Qt::SHIFT + Qt::Key_L);
- connect (action, &QAction::triggered, this, &RKWorkplaceView::splitViewVert);
- action->setWhatsThis (i18n ("Split the currently active view into two views, vertically."));
-
- action = ac->addAction (QStringLiteral ("view_split_horiz"));
- action->setIcon (QIcon::fromTheme(QStringLiteral ("view-split-top-bottom")));
- action->setText (i18n ("Split &Horizontal"));
- ac->setDefaultShortcut (action, Qt::CTRL + Qt::SHIFT + Qt::Key_T);
- connect (action, &QAction::triggered, this, &RKWorkplaceView::splitViewHoriz);
- action->setWhatsThis (i18n ("Split the currently active view into two views, horizontally."));
+ action_split_vert = ac->addAction (QStringLiteral ("view_split_vert"));
+ action_split_vert->setIcon (QIcon::fromTheme(QStringLiteral ("view-split-left-right")));
+ action_split_vert->setText (i18n("Split Ve&rtical"));
+ ac->setDefaultShortcut (action_split_vert, Qt::CTRL + Qt::SHIFT + Qt::Key_L);
+ connect (action_split_vert, &QAction::triggered, this, &RKWorkplaceView::splitViewVert);
+ action_split_vert->setWhatsThis (i18n ("Split the currently active view into two views, vertically."));
+
+ action_split_horiz = ac->addAction (QStringLiteral ("view_split_horiz"));
+ action_split_horiz->setIcon (QIcon::fromTheme(QStringLiteral ("view-split-top-bottom")));
+ action_split_horiz->setText (i18n ("Split &Horizontal"));
+ ac->setDefaultShortcut (action_split_horiz, Qt::CTRL + Qt::SHIFT + Qt::Key_T);
+ connect (action_split_horiz, &QAction::triggered, this, &RKWorkplaceView::splitViewHoriz);
+ action_split_horiz->setWhatsThis (i18n ("Split the currently active view into two views, horizontally."));
+ for (int i = 0; i < panes.size (); ++i) {
+ panes[i]->initActions ();
+ }
updateActions ();
}
@@ -297,6 +323,12 @@ void RKWorkplaceView::splitView (Qt::Orientation orientation, RKWorkplaceViewPan
RK_TRACE (APP);
RKMDIWindow *active = activePage ();
+ if (!active) {
+ RKWorkplace::mainWorkplace ()->openHelpWindow (QUrl ("rkward://page/rkward_split_views"));
+ RK_ASSERT (count () == 0);
+ active = activePage ();
+ RK_ASSERT (active);
+ }
RK_ASSERT (pane);
QSplitter *splitter = qobject_cast<QSplitter *> (pane->parentWidget ());
if (!splitter) {
@@ -313,6 +345,7 @@ void RKWorkplaceView::splitView (Qt::Orientation orientation, RKWorkplaceViewPan
newpane = createPane ();
panes.insert (lindex + 1, newpane);
+ newpane->initActions ();
// If there is only one child (left) in the current splitter, we can just set the orientation as needed.
if (splitter->count () == 1) {
@@ -355,7 +388,8 @@ void RKWorkplaceView::addWindow (RKMDIWindow *widget) {
if (icon.isNull ()) icon = widget->topLevelWidget ()->windowIcon ();
if (icon.isNull ()) RK_ASSERT (false);
- RKWorkplaceViewPane *pane = activePane ();
+ RKWorkplaceViewPane *pane = newpane;
+ if (!pane) pane = activePane ();
RK_ASSERT (pane);
newpane = 0; // next window will get default treatment
id = pane->addTab (widget, icon, widget->shortCaption ());
diff --git a/rkward/windows/rkworkplaceview.h b/rkward/windows/rkworkplaceview.h
index f4394e8b..2978a6fb 100644
--- a/rkward/windows/rkworkplaceview.h
+++ b/rkward/windows/rkworkplaceview.h
@@ -37,12 +37,14 @@ private:
void closePage (QWidget* page);
/** Close a page given its index */
void closePage (int page);
-/** (Attempts to) close the current tab */
- void closeCurrentPage ();
+/** (Attempts to) close all tabs in this pane (and thus the pane itself) */
+ void closeAll ();
bool isActive ();
+ void initActions ();
protected:
void tabRemoved (int index) override;
void tabInserted (int index) override;
+ bool eventFilter (QObject* obj, QEvent* event) override;
signals:
void becameEmpty (RKWorkplaceViewPane* pane);
private slots:
@@ -111,10 +113,11 @@ private:
void splitView (Qt::Orientation horiz, RKWorkplaceViewPane *pane);
RKWorkplaceViewPane *createPane ();
RKWorkplaceViewPane *findWindow (RKMDIWindow *window) const;
- RKWorkplaceViewPane *addNewPane (int index);
QAction *action_page_left;
QAction *action_page_right;
+ QAction *action_split_horiz;
+ QAction *action_split_vert;
QList<RKWorkplaceViewPane*> panes;
RKWorkplaceViewPane *activePane () const;
More information about the rkward-tracker
mailing list