[rkward/work/split_views] rkward/windows: Make window splitting work on the same foundation as workplace restoration.
Thomas Friedrichsmeier
null at kde.org
Tue Jun 20 19:40:09 UTC 2017
Git commit 367045acca4e7fdc034f886beb5265ec267c44a5 by Thomas Friedrichsmeier.
Committed on 20/06/2017 at 19:38.
Pushed by tfry into branch 'work/split_views'.
Make window splitting work on the same foundation as workplace restoration.
The original intent was to implement restoring of split views. However, this is
not quite there, yet. WIP.
M +160 -99 rkward/windows/rkworkplace.cpp
M +5 -2 rkward/windows/rkworkplace.h
M +58 -16 rkward/windows/rkworkplaceview.cpp
M +5 -1 rkward/windows/rkworkplaceview.h
https://commits.kde.org/rkward/367045acca4e7fdc034f886beb5265ec267c44a5
diff --git a/rkward/windows/rkworkplace.cpp b/rkward/windows/rkworkplace.cpp
index cea94510..ec46de60 100644
--- a/rkward/windows/rkworkplace.cpp
+++ b/rkward/windows/rkworkplace.cpp
@@ -523,7 +523,7 @@ void RKWorkplace::newRKWardGraphisWindow (RKGraphicsDevice* dev, int device_numb
addWindow (window, false);
}
-void RKWorkplace::newObjectViewer (RObject *object) {
+RKMDIWindow* RKWorkplace::newObjectViewer (RObject *object) {
RK_TRACE (APP);
RK_ASSERT (object);
@@ -533,13 +533,14 @@ void RKWorkplace::newObjectViewer (RObject *object) {
if (viewer->object () == object) {
if (view ()->windowInActivePane (viewer)) {
viewer->activate ();
- return;
+ return viewer;
}
}
}
RObjectViewer *ov = new RObjectViewer (view (), object);
addWindow (ov);
+ return ov;
}
bool RKWorkplace::canEditObject (RObject *object) {
@@ -736,6 +737,127 @@ RKMDIWindow *RKWorkplace::activeWindow (RKMDIWindow::State state) {
return (ret);
}
+QUrl checkAdjustRestoredUrl (const QString &_url, const QString old_base) {
+ QUrl url = QUrl::fromUserInput (_url, QString (), QUrl::AssumeLocalFile);
+
+ if (old_base.isEmpty ()) return (url);
+ QUrl new_base_url = RKWorkplace::mainWorkplace ()->workspaceURL ().adjusted (QUrl::RemoveFilename | QUrl::NormalizePathSegments);
+ if (new_base_url.isEmpty ()) return (url);
+ QUrl old_base_url (old_base);
+ if (old_base_url == new_base_url) return (url);
+
+ // TODO: Should we also care about non-local files? In theory: yes, but stat'ing remote files for existence can take a long time.
+ if (!(old_base_url.isLocalFile () && new_base_url.isLocalFile () && url.isLocalFile ())) return (url);
+
+ // if the file exists, unadjusted, return it.
+ if (QFileInfo (url.toLocalFile ()).exists ()) return (url);
+
+ // check whether a file exists for the adjusted url
+ QString relative = QDir (new_base_url.path ()).absoluteFilePath (QDir (old_base_url.path ()).relativeFilePath (url.path ()));
+ if (QFileInfo (relative).exists ()) return (QUrl::fromLocalFile (relative));
+ return (url);
+}
+
+QString RKWorkplace::makeItemDescription (RKMDIWindow *win) const {
+ QString type, specification;
+ QStringList params;
+ if (win->isType (RKMDIWindow::DataEditorWindow)) {
+ type = "data";
+ specification = static_cast<RKEditor*> (win)->getObject ()->getFullName ();
+ } else if (win->isType (RKMDIWindow::CommandEditorWindow)) {
+ type = "script";
+ specification = static_cast<RKCommandEditorWindow*> (win)->url ().url ();
+ } else if (win->isType (RKMDIWindow::OutputWindow)) {
+ type = "output";
+ specification = static_cast<RKHTMLWindow*> (win)->url ().url ();
+ } else if (win->isType (RKMDIWindow::HelpWindow)) {
+ type = "help";
+ specification = static_cast<RKHTMLWindow*> (win)->restorableUrl ().url ();
+ } else if (win->isToolWindow ()) {
+ type = RKToolWindowList::idOfWindow (win);
+ } else if (win->isType (RKMDIWindow::ObjectWindow)) {
+ type = "object";
+ specification = static_cast<RObjectViewer*> (win)->object ()->getFullName ();
+ }
+ if (!type.isEmpty ()) {
+ if (!win->isAttached ()) {
+ params.append (QString ("detached,") + QString::number (win->x ()) + ',' + QString::number (win->y ()) + ',' + QString::number (win->width ()) + ',' + QString::number (win->height ()));
+ }
+ if (win->isToolWindow ()) {
+ int sidebar = RKToolWindowList::Nowhere;
+ for (int i = 0; i < TOOL_WINDOW_BAR_COUNT; ++i) {
+ if (win->tool_window_bar == tool_window_bars[i]) {
+ sidebar = i;
+ break;
+ }
+ }
+ params.append (QString ("sidebar,") + QString::number (sidebar));
+ }
+ return (type + "::" + params.join (":") + "::" + specification);
+ }
+
+ return QString ();
+}
+
+struct ItemSpecification {
+ QString type;
+ QString specification;
+ QStringList params;
+};
+
+ItemSpecification parseItemDescription (const QString &description) {
+ ItemSpecification ret;
+
+ // Item format for rkward <= 0.5.4: "type:specification"
+ // Item format for rkward <= 0.5.5: "type::[optional_params1[:optional_params2[:...]]]::specification"
+ int typeend = description.indexOf (':');
+ if ((typeend < 0) || (typeend >= (description.size () - 1))) {
+ RK_ASSERT (false);
+ return ret;
+ }
+ ret.type = description.left (typeend);
+ if (description.at (typeend + 1) == ':') { // rkward 0.5.5 or later
+ int specstart = description.indexOf ("::", typeend + 2);
+ if (specstart < typeend) {
+ RK_ASSERT (false);
+ return ret;
+ }
+ ret.params = description.mid (typeend + 2, specstart - typeend - 2).split (':', QString::SkipEmptyParts);
+ ret.specification = description.mid (specstart + 2);
+ } else {
+ ret.specification = description.mid (typeend + 1);
+ }
+
+ return ret;
+}
+
+RKMDIWindow* restoreDocumentWindowInternal (RKWorkplace* wp, ItemSpecification spec, const QString &base) {
+ RK_TRACE (APP);
+
+ RKMDIWindow *win = 0;
+ if (spec.type == "data") {
+ RObject *object = RObjectList::getObjectList ()->findObject (spec.specification);
+ if (object) win = wp->editObject (object);
+ } else if (spec.type == "script") {
+ QUrl url = checkAdjustRestoredUrl (spec.specification, base);
+ win = wp->openScriptEditor (url, QString (), RKSettingsModuleCommandEditor::matchesScriptFileFilter (url.fileName()));
+ } else if (spec.type == "output") {
+ win = wp->openOutputWindow (checkAdjustRestoredUrl (spec.specification, base));
+ } else if (spec.type == "help") {
+ win = wp->openHelpWindow (checkAdjustRestoredUrl (spec.specification, base), true);
+ } else if (spec.type == "object") {
+ RObject *object = RObjectList::getObjectList ()->findObject (spec.specification);
+ if (object) win = wp->newObjectViewer (object);
+ }
+ return win;
+}
+
+bool RKWorkplace::restoreDocumentWindow (const QString &description, const QString &base) {
+ RK_TRACE (APP);
+
+ return (restoreDocumentWindowInternal (this, parseItemDescription (description), base) != 0);
+}
+
QStringList RKWorkplace::makeWorkplaceDescription () {
RK_TRACE (APP);
@@ -747,45 +869,27 @@ QStringList RKWorkplace::makeWorkplaceDescription () {
// window order in the workplace view may have changed with respect to our list. Thus we first generate a properly sorted list
RKWorkplaceObjectList list = getObjectList (RKMDIWindow::DocumentWindow, RKMDIWindow::Detached);
- for (int i=0; i < wview->count (); ++i) {
- list.append (static_cast<RKMDIWindow*> (wview->widget (i)));
- }
- list.append (getObjectList (RKMDIWindow::ToolWindow, RKMDIWindow::AnyWindowState));
foreach (RKMDIWindow *win, list) {
- QString type, specification;
- QStringList params;
- if (win->isType (RKMDIWindow::DataEditorWindow)) {
- type = "data";
- specification = static_cast<RKEditor*> (win)->getObject ()->getFullName ();
- } else if (win->isType (RKMDIWindow::CommandEditorWindow)) {
- type = "script";
- specification = static_cast<RKCommandEditorWindow*> (win)->url ().url ();
- } else if (win->isType (RKMDIWindow::OutputWindow)) {
- type = "output";
- specification = static_cast<RKHTMLWindow*> (win)->url ().url ();
- } else if (win->isType (RKMDIWindow::HelpWindow)) {
- type = "help";
- specification = static_cast<RKHTMLWindow*> (win)->restorableUrl ().url ();
- } else if (win->isToolWindow ()) {
- type = RKToolWindowList::idOfWindow (win);
- }
- if (!type.isEmpty ()) {
- if (!win->isAttached ()) {
- params.append (QString ("detached,") + QString::number (win->x ()) + ',' + QString::number (win->y ()) + ',' + QString::number (win->width ()) + ',' + QString::number (win->height ()));
- }
- if (win->isToolWindow ()) {
- int sidebar = RKToolWindowList::Nowhere;
- for (int i = 0; i < TOOL_WINDOW_BAR_COUNT; ++i) {
- if (win->tool_window_bar == tool_window_bars[i]) {
- sidebar = i;
- break;
- }
- }
- params.append (QString ("sidebar,") + QString::number (sidebar));
- }
- workplace_description.append (type + "::" + params.join (":") + "::" + specification);
+ QString desc = makeItemDescription (win);
+ if (!desc.isEmpty ()) workplace_description.append (desc);
+ }
+
+ QVariantList attached_list = wview->listContents ();
+ for (int i=0; i < attached_list.count (); ++i) {
+ if (attached_list[i].canConvert<QObject*>()) {
+ RKMDIWindow *win = static_cast<RKMDIWindow*> (attached_list[i].value<QObject*>());
+ QString desc = makeItemDescription (win);
+ if (!desc.isEmpty ()) workplace_description.append (desc);
+ } else {
+ workplace_description.append (attached_list[i].toString ());
}
}
+
+ list = getObjectList (RKMDIWindow::ToolWindow, RKMDIWindow::AnyWindowState);
+ foreach (RKMDIWindow *win, list) {
+ QString desc = makeItemDescription (win);
+ if (!desc.isEmpty ()) workplace_description.append (desc);
+ }
return workplace_description;
}
@@ -807,83 +911,40 @@ void RKWorkplace::restoreWorkplace (RCommandChain *chain, bool merge) {
RKGlobals::rInterface ()->issueCommand ("rk.restore.workplace(" + no_close_windows + ')', RCommand::App, i18n ("Restore Workplace layout"), 0, 0, chain);
}
-QUrl checkAdjustRestoredUrl (const QString &_url, const QString old_base) {
- QUrl url = QUrl::fromUserInput (_url, QString (), QUrl::AssumeLocalFile);
-
- if (old_base.isEmpty ()) return (url);
- QUrl new_base_url = RKWorkplace::mainWorkplace ()->workspaceURL ().adjusted (QUrl::RemoveFilename | QUrl::NormalizePathSegments);
- if (new_base_url.isEmpty ()) return (url);
- QUrl old_base_url (old_base);
- if (old_base_url == new_base_url) return (url);
-
- // TODO: Should we also care about non-local files? In theory: yes, but stat'ing remote files for existence can take a long time.
- if (!(old_base_url.isLocalFile () && new_base_url.isLocalFile () && url.isLocalFile ())) return (url);
-
- // if the file exists, unadjusted, return it.
- if (QFileInfo (url.toLocalFile ()).exists ()) return (url);
-
- // check whether a file exists for the adjusted url
- QString relative = QDir (new_base_url.path ()).absoluteFilePath (QDir (old_base_url.path ()).relativeFilePath (url.path ()));
- if (QFileInfo (relative).exists ()) return (QUrl::fromLocalFile (relative));
- return (url);
-}
-
void RKWorkplace::restoreWorkplace (const QStringList &description) {
RK_TRACE (APP);
RKWardMainWindow::getMain ()->lockGUIRebuild (true);
QString base;
for (int i = 0; i < description.size (); ++i) {
- // Item format for rkward <= 0.5.4: "type:specification"
- // Item format for rkward <= 0.5.5: "type::[optional_params1[:optional_params2[:...]]]::specification"
- int typeend = description[i].indexOf (':');
- if ((typeend < 0) || (typeend >= (description[i].size () - 1))) {
- RK_ASSERT (false);
- continue;
- }
- QString type, specification;
- QStringList params;
- type = description[i].left (typeend);
- if (description[i].at (typeend + 1) == ':') { // rkward 0.5.5 or later
- int specstart = description[i].indexOf ("::", typeend + 2);
- if (specstart < typeend) {
- RK_ASSERT (false);
- continue;
- }
- params = description[i].mid (typeend + 2, specstart - typeend - 2).split (':', QString::SkipEmptyParts);
- specification = description[i].mid (specstart + 2);
- } else {
- specification = description[i].mid (typeend + 1);
- }
+/* if (split_next) {
+ split_next = false;
+ view ()->restoreSplitView (split_orientation, description[i], base);
+ } */
+ ItemSpecification spec = parseItemDescription (description[i]);
RKMDIWindow *win = 0;
- if (type == "base") {
+ if (spec.type == "base") {
RK_ASSERT (i == 0);
- base = specification;
- } else if (type == "data") {
- RObject *object = RObjectList::getObjectList ()->findObject (specification);
- if (object) win = editObject (object);
- } else if (type == "script") {
- QUrl url = checkAdjustRestoredUrl (specification, base);
- win = openScriptEditor (url, QString (), RKSettingsModuleCommandEditor::matchesScriptFileFilter (url.fileName()));
- } else if (type == "output") {
- win = openOutputWindow (checkAdjustRestoredUrl (specification, base));
- } else if (type == "help") {
- win = openHelpWindow (checkAdjustRestoredUrl (specification, base), true);
+ base = spec.specification;
+ } else if (restoreDocumentWindowInternal (this, spec, base)) {
+ // it was restored. nothing else to do
+ } else if (spec.type == "split") {
+#warning implement
} else {
- win = RKToolWindowList::findToolWindowById (type);
+ win = RKToolWindowList::findToolWindowById (spec.type);
RK_ASSERT (win);
}
// apply generic window parameters
if (win) {
- for (int p = 0; p < params.size (); ++p) {
- if (params[p].startsWith ("sidebar")) {
- int position = params[p].section (',', 1).toInt ();
+ for (int p = 0; p < spec.params.size (); ++p) {
+ if (spec.params[p].startsWith ("sidebar")) {
+ int position = spec.params[p].section (',', 1).toInt ();
placeInToolWindowBar (win, position);
}
- if (params[p].startsWith ("detached")) {
- QStringList geom = params[p].split (',');
+ if (spec.params[p].startsWith ("detached")) {
+ QStringList geom = spec.params[p].split (',');
win->hide ();
win->setGeometry (geom.value (1).toInt (), geom.value (2).toInt (), geom.value (3).toInt (), geom.value (4).toInt ());
detachWindow (win);
@@ -894,7 +955,7 @@ void RKWorkplace::restoreWorkplace (const QStringList &description) {
RKWardMainWindow::getMain ()->lockGUIRebuild (false);
}
-void RKWorkplace::duplicateAndAttachWindow (RKMDIWindow* source) {
+void RKWorkplace::splitAndAttachWindow (RKMDIWindow* source) {
RK_TRACE (APP);
RK_ASSERT (source);
diff --git a/rkward/windows/rkworkplace.h b/rkward/windows/rkworkplace.h
index 046c5561..73a7e4e5 100644
--- a/rkward/windows/rkworkplace.h
+++ b/rkward/windows/rkworkplace.h
@@ -117,7 +117,7 @@ public:
void newX11Window (QWindow* window_to_embed, int device_number);
void newRKWardGraphisWindow (RKGraphicsDevice *dev, int device_number);
- void newObjectViewer (RObject *object);
+ RKMDIWindow* newObjectViewer (RObject *object);
/** @returns true if there is a known editor for this type of object, false otherwise */
bool canEditObject (RObject *object);
@@ -158,6 +158,9 @@ Has no effect, if RKSettingsModuleGeneral::workplaceSaveMode () != RKSettingsMod
void restoreWorkplace (const QStringList &description);
QStringList makeWorkplaceDescription ();
+ QString makeItemDescription (RKMDIWindow *) const;
+/** Restore a document window given its description. Returns true, if a window was restored, false otherwise (e.g. invalid/unsupported description). */
+ bool restoreDocumentWindow (const QString &description, const QString &base=QString ());
/** In the current design there is only ever one workplace. Use this static function to reference it.
@returns a pointer to the workplace */
@@ -182,7 +185,7 @@ Has no effect, if RKSettingsModuleGeneral::workplaceSaveMode () != RKSettingsMod
void addMessageWidget (KMessageWidget *message);
/** For window splitting: Copy the given window (or, if that is not possible, create a placeholder window), and attach it to the main view. */
- void duplicateAndAttachWindow (RKMDIWindow *source);
+ void splitAndAttachWindow (RKMDIWindow *source);
signals:
/** emitted when the workspace Url has changed */
void workspaceUrlChanged (const QUrl &url);
diff --git a/rkward/windows/rkworkplaceview.cpp b/rkward/windows/rkworkplaceview.cpp
index 516d3e0d..afcd6942 100644
--- a/rkward/windows/rkworkplaceview.cpp
+++ b/rkward/windows/rkworkplaceview.cpp
@@ -217,6 +217,8 @@ 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];
}
@@ -285,7 +287,8 @@ void RKWorkplaceView::pageLeft () {
RK_ASSERT (false); // action should have been disabled on an empty workplaceview
return;
}
- panes[pindex]->setCurrentIndex (panes[pindex]->count () - 1);
+ // NOTE: setCurrentIndex() is not enough, here, as the index may be current, already, while the pane is still inactive.
+ static_cast<RKMDIWindow*> (panes[pindex]->widget (panes[pindex]->count () - 1))->activate ();
}
}
@@ -304,7 +307,8 @@ void RKWorkplaceView::pageRight () {
RK_ASSERT (false); // action should have been disabled on an empty workplaceview
return;
}
- panes[pindex]->setCurrentIndex (0);
+ // NOTE: setCurrentIndex() is not enough, here, as the index may be current, already, while the pane is still inactive.
+ static_cast<RKMDIWindow*> (panes[pindex]->widget (0))->activate ();
}
}
@@ -319,21 +323,27 @@ void RKWorkplaceView::splitViewVert () {
}
// NOTE: Some of this function taken from kate's kateviewmanager.cpp
-void RKWorkplaceView::splitView (Qt::Orientation orientation, RKWorkplaceViewPane *pane) {
+RKWorkplaceViewPane* RKWorkplaceView::splitView (Qt::Orientation orientation, RKWorkplaceViewPane *pane, const QString &description, const QString &base) {
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);
+ if (!pane) pane = activePane ();
+
+ QString _description = description;
+ if (_description.isEmpty ()) {
+ RKMDIWindow *active = dynamic_cast<RKMDIWindow *> (pane->currentWidget ());
+ if (!active) {
+ RKWorkplace::mainWorkplace ()->openHelpWindow (QUrl ("rkward://page/rkward_split_views"));
+ RK_ASSERT (count () == 0);
+ active = activePage ();
+ RK_ASSERT (active);
+ }
+ _description = RKWorkplace::mainWorkplace ()->makeItemDescription (active);
}
- RK_ASSERT (pane);
+
QSplitter *splitter = qobject_cast<QSplitter *> (pane->parentWidget ());
if (!splitter) {
RK_ASSERT (splitter);
- return;
+ return 0;
}
setUpdatesEnabled (false);
@@ -372,11 +382,17 @@ void RKWorkplaceView::splitView (Qt::Orientation orientation, RKWorkplaceViewPan
}
newpane->show ();
// "copy" the "split" window to the new pane.
- // TODO: line below will only work for the main window, for the time being. We need to rethink this, if we want to enable window splitting for detached windows, too.
- RKWorkplace::mainWorkplace ()->duplicateAndAttachWindow (active);
+ // TODO: lines below will only work for the main window, for the time being. We need to rethink this, if we want to enable window splitting for detached windows, too.
+ if (!RKWorkplace::mainWorkplace ()->restoreDocumentWindow (_description, base)) {
+ RKWorkplace::mainWorkplace ()->openHelpWindow (QUrl ("rkward://page/rkward_split_views"));
+ }
+ RKWorkplaceViewPane *ret = newpane;
+ newpane = 0;
splitter->setSizes (sizes);
setUpdatesEnabled (true);
+
+ return ret;
}
void RKWorkplaceView::addWindow (RKMDIWindow *widget) {
@@ -388,10 +404,8 @@ void RKWorkplaceView::addWindow (RKMDIWindow *widget) {
if (icon.isNull ()) icon = widget->topLevelWidget ()->windowIcon ();
if (icon.isNull ()) RK_ASSERT (false);
- RKWorkplaceViewPane *pane = newpane;
- if (!pane) pane = activePane ();
+ RKWorkplaceViewPane *pane = activePane ();
RK_ASSERT (pane);
- newpane = 0; // next window will get default treatment
id = pane->addTab (widget, icon, widget->shortCaption ());
connect (widget, &RKMDIWindow::captionChanged, this, &RKWorkplaceView::childCaptionChanged);
@@ -481,4 +495,32 @@ void RKWorkplaceView::setCaption (const QString &caption) {
emit (captionChanged (caption));
}
+void listContents (const QSplitter *parent, QVariantList *ret) {
+ RK_TRACE (APP);
+ for (int i = 0; i < parent->count (); ++i) {
+ QWidget* w = parent->widget (i);
+ RKWorkplaceViewPane *pane = qobject_cast<RKWorkplaceViewPane*> (w);
+ if (pane) {
+ for (int j = 0; j < pane->count (); ++j) {
+ ret->append (QVariant::fromValue<QObject*> (pane->widget (j)));
+ }
+ } else {
+ QSplitter* sub = qobject_cast<QSplitter*> (w);
+ listContents (sub, ret);
+ }
+ if (i < parent->count () - 1) {
+ if (parent->orientation () == Qt::Vertical) ret->append (QStringLiteral ("split::::vert"));
+ else ret->append (QStringLiteral ("split::::horiz"));
+ }
+ }
+ ret->append (QStringLiteral ("split::::end"));
+}
+
+QVariantList RKWorkplaceView::listContents () const {
+ RK_TRACE (APP);
+
+ QVariantList ret;
+ ::listContents (this, &ret);
+ return ret;
+}
diff --git a/rkward/windows/rkworkplaceview.h b/rkward/windows/rkworkplaceview.h
index 2978a6fb..12be7455 100644
--- a/rkward/windows/rkworkplaceview.h
+++ b/rkward/windows/rkworkplaceview.h
@@ -88,6 +88,11 @@ public:
void setCaption (const QString &caption);
/** initialize the window left/right actions */
void initActions (KActionCollection *ac);
+
+/** List the contents of the view, recursively. Windows are returned as pointers in the list, Splits are returned as strings "split::::horiz", "split::::vert", or "split::::end" (for end of current splitter) */
+ QVariantList listContents () const;
+/** Public for use by RKWorkplace while restoring a workplace, only. @return the newly created pane */
+ RKWorkplaceViewPane* splitView (Qt::Orientation orientation, RKWorkplaceViewPane *pane, const QString &description=QString (), const QString &base=QString ());
signals:
/** a new page / window was activated
@param widget the newly activated window */
@@ -110,7 +115,6 @@ private slots:
void splitViewHoriz ();
private:
void updateActions ();
- void splitView (Qt::Orientation horiz, RKWorkplaceViewPane *pane);
RKWorkplaceViewPane *createPane ();
RKWorkplaceViewPane *findWindow (RKMDIWindow *window) const;
More information about the rkward-tracker
mailing list