cantor.cpp 23.8 KB
Newer Older
1
/*
Alexander Rieder's avatar
Alexander Rieder committed
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA  02110-1301, USA.

    ---
    Copyright (C) 2009 Alexander Rieder <alexanderrieder@gmail.com>
19
 */
Alexander Rieder's avatar
Alexander Rieder committed
20
#include "cantor.h"
21

22
#include <KActionCollection>
23
24
#include <KConfigDialog>
#include <KConfigGroup>
25
#include <KMessageBox>
26
#include <KShortcutsDialog>
27
28
#include <KStandardAction>
#include <KNS3/DownloadDialog>
29
#include <KParts/ReadWritePart>
30
#include <KRecentFilesAction>
31

32
#include <QApplication>
33
#include <QCloseEvent>
34
35
#include <QDebug>
#include <QDockWidget>
36
#include <QDir>
37
#include <QFileDialog>
38
#include <QPushButton>
39
#include <QRegularExpression>
40
#include <QGraphicsView>
41
42

#include "lib/backend.h"
43
44
#include "lib/panelpluginhandler.h"
#include "lib/panelplugin.h"
45
#include "lib/worksheetaccess.h"
46
47
48

#include "settings.h"
#include "ui_settings.h"
49
#include "backendchoosedialog.h"
50

51
CantorShell::CantorShell() : KParts::MainWindow(), m_part(nullptr)
52
53
{
    // set the shell's ui resource file
54
    setXMLFile(QLatin1String("cantor_shell.rc"));
55
56
57
58

    // then, setup our actions
    setupActions();

59
    createGUI(nullptr);
60

Filipe Saraiva's avatar
Filipe Saraiva committed
61
    m_tabWidget=new QTabWidget(this);
62
    m_tabWidget->setTabsClosable(true);
63
64
    m_tabWidget->setMovable(true);
    m_tabWidget->setDocumentMode(true);
65
66
67
    setCentralWidget(m_tabWidget);

    connect(m_tabWidget, SIGNAL(currentChanged(int)), this, SLOT(activateWorksheet(int)));
68
    connect(m_tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int)));
69
70
71
72
73

    // apply the saved mainwindow settings, if any, and ask the mainwindow
    // to automatically save settings if changed: window size, toolbar
    // position, icon size, etc.
    setAutoSaveSettings();
74
75

    setDockOptions(QMainWindow::AnimatedDocks|QMainWindow::AllowTabbedDocks|QMainWindow::VerticalTabs);
76
77

    updateNewSubmenu();
78
79
}

80
81
82
83
CantorShell::~CantorShell()
{
    if (m_recentProjectsAction)
        m_recentProjectsAction->saveEntries(KSharedConfig::openConfig()->group(QLatin1String("Recent Files")));
84
85
86
87
88
89
90
91
92
93
94

    if (!m_newBackendActions.isEmpty())
    {
        unplugActionList(QLatin1String("new_worksheet_with_backend_list"));
        qDeleteAll(m_newBackendActions);
        m_newBackendActions.clear();
    }

    unplugActionList(QLatin1String("view_show_panel_list"));
    qDeleteAll(m_panels);
    m_panels.clear();
95
96
}

97
void CantorShell::load(const QUrl &url)
98
{
Alexander Rieder's avatar
Alexander Rieder committed
99
    if (!m_part||!m_part->url().isEmpty() || m_part->isModified() )
100
    {
101
        addWorksheet(QString());
102
103
        m_tabWidget->setCurrentIndex(m_parts.size()-1);
    }
104
105
    if (!m_part->openUrl( url ))
        closeTab(m_tabWidget->currentIndex());
106
107
    if (m_recentProjectsAction)
        m_recentProjectsAction->addUrl(url);
108
109

    updateWindowTitle(m_part->url().fileName());
110
111
}

Alexander Rieder's avatar
Alexander Rieder committed
112
void CantorShell::setupActions()
113
{
114
    QAction* openNew = KStandardAction::openNew(this, SLOT(fileNew()), actionCollection());
115
    openNew->setPriority(QAction::LowPriority);
116
    QAction* open = KStandardAction::open(this, SLOT(fileOpen()), actionCollection());
117
    open->setPriority(QAction::LowPriority);
118
119
120
    m_recentProjectsAction = KStandardAction::openRecent(this, &CantorShell::load, actionCollection());
    m_recentProjectsAction->setPriority(QAction::LowPriority);
    m_recentProjectsAction->loadEntries(KSharedConfig::openConfig()->group(QLatin1String("Recent Files")));
121

122
123
    KStandardAction::close (this,  SLOT(closeTab()),  actionCollection());

124
125
126
127
128
    KStandardAction::quit(qApp, SLOT(closeAllWindows()), actionCollection());

    createStandardStatusBarAction();
    //setStandardToolBarMenuEnabled(true);

129
130
    KStandardAction::keyBindings(this, SLOT(optionsConfigureKeys()), actionCollection());
    KStandardAction::configureToolbars(this, SLOT(configureToolbars()), actionCollection());
131
132

    KStandardAction::preferences(this, SLOT(showSettings()), actionCollection());
133

134
135
136
    QAction * downloadExamples = new QAction(i18n("Download Example Worksheets"), actionCollection());
    downloadExamples->setIcon(QIcon::fromTheme(QLatin1String("get-hot-new-stuff")));
    actionCollection()->addAction(QLatin1String("file_example_download"),  downloadExamples);
137
    connect(downloadExamples, SIGNAL(triggered()), this,  SLOT(downloadExamples()));
138

139
140
141
    QAction * openExample =new QAction(i18n("&Open Example"), actionCollection());
    openExample->setIcon(QIcon::fromTheme(QLatin1String("document-open")));
    actionCollection()->addAction(QLatin1String("file_example_open"), openExample);
142
    connect(openExample, SIGNAL(triggered()), this, SLOT(openExample()));
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166

    QAction* toPreviousTab = new QAction(i18n("Go to previous worksheet"), actionCollection());
    actionCollection()->addAction(QLatin1String("go_to_previous_tab"), toPreviousTab);
    actionCollection()->setDefaultShortcut(toPreviousTab, Qt::CTRL+Qt::Key_PageDown);
    connect(toPreviousTab, &QAction::triggered, toPreviousTab, [this](){
        const int index = m_tabWidget->currentIndex()-1;
        if (index >= 0)
            m_tabWidget->setCurrentIndex(index);
        else
            m_tabWidget->setCurrentIndex(m_tabWidget->count()-1);
    });
    addAction(toPreviousTab);

    QAction* toNextTab = new QAction(i18n("Go to next worksheet"), actionCollection());
    actionCollection()->addAction(QLatin1String("go_to_next_tab"), toNextTab);
    actionCollection()->setDefaultShortcut(toNextTab, Qt::CTRL+Qt::Key_PageUp);
    connect(toNextTab, &QAction::triggered, toNextTab, [this](){
        const int index = m_tabWidget->currentIndex()+1;
        if (index < m_tabWidget->count())
            m_tabWidget->setCurrentIndex(index);
        else
            m_tabWidget->setCurrentIndex(0);
    });
    addAction(toNextTab);
167
168
}

Alexander Rieder's avatar
Alexander Rieder committed
169
void CantorShell::saveProperties(KConfigGroup & /*config*/)
170
171
172
173
174
175
{
    // the 'config' object points to the session managed
    // config file.  anything you write here will be available
    // later when this app is restored
}

Alexander Rieder's avatar
Alexander Rieder committed
176
void CantorShell::readProperties(const KConfigGroup & /*config*/)
177
178
179
180
181
182
183
{
    // the 'config' object points to the session managed
    // config file.  this function is automatically called whenever
    // the app is being restored.  read in here whatever you wrote
    // in 'saveProperties'
}

184
185
186
187
188
/*!
 * called when one of the "new backend" action or the "New" action are called
 * adds a new worksheet with the backend assossiated with the called action
 * or opens the "Choose Backend" dialog, respectively.
 */
Alexander Rieder's avatar
Alexander Rieder committed
189
void CantorShell::fileNew()
190
{
191
192
193
    QAction* a = static_cast<QAction*>(sender());
    const QString& backendName = a->data().toString();
    if (!backendName.isEmpty())
194
    {
195
196
        addWorksheet(backendName);
        return;
197
    }
198
199

    //"New" action was called -> open the "Choose Backend" dialog.
200
    addWorksheet();
201
202
}

Alexander Rieder's avatar
Alexander Rieder committed
203
void CantorShell::optionsConfigureKeys()
204
{
205
206
    KShortcutsDialog dlg( KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsDisallowed, this );
    dlg.addCollection( actionCollection(), i18n("Cantor") );
207
208
    if (m_part)
        dlg.addCollection( m_part->actionCollection(), i18n("Cantor") );
209
    dlg.configure( true );
210
211
}

Alexander Rieder's avatar
Alexander Rieder committed
212
void CantorShell::fileOpen()
213
214
215
216
{
    // this slot is called whenever the File->Open menu is selected,
    // the Open shortcut is pressed (usually CTRL+O) or the Open toolbar
    // button is clicked
217
218
219
    static const QString& filter = i18n("All supported files (*.cws *ipynb);;Cantor Worksheet (*.cws);;Jupyter Notebook (*.ipynb)");

    QUrl url = QFileDialog::getOpenFileUrl(this, i18n("Open file"), QUrl(), filter);
220
221
222

    if (url.isEmpty() == false)
    {
223
        // About this function, the style guide
224
225
226
227
228
229
230
231
232
233
        // says that it should open a new window if the document is _not_
        // in its initial state.  This is what we do here..
        /*if ( m_part->url().isEmpty() && ! m_part->isModified() )
        {
            // we open the file in this window...
            load( url );
        }
        else
        {
            // we open the file in a new window...
Alexander Rieder's avatar
Alexander Rieder committed
234
            CantorShell* newWin = new CantorShell;
235
236
237
238
239
240
241
            newWin->load( url );
            newWin->show();
            }*/
        load( url );
    }
}

Alexander Rieder's avatar
Alexander Rieder committed
242
void CantorShell::addWorksheet()
243
{
244
245
246
247
248
249
250
251
252
253
254
    bool hasBackend = false;
    for (auto* b : Cantor::Backend::availableBackends())
    {
        if(b->isEnabled())
        {
            hasBackend = true;
            break;
        }
    }

    if(hasBackend) //There is no point in asking for the backend, if no one is available
255
    {
256
257
        QString backend = Settings::self()->defaultBackend();
        if (backend.isEmpty())
258
        {
259
260
261
262
            QPointer<BackendChooseDialog> dlg=new BackendChooseDialog(this);
            if(dlg->exec())
            {
                backend = dlg->backendName();
263
                addWorksheet(backend);
264
265
266
            }

            delete dlg;
267
        }
268
269
270
271
        else
        {
            addWorksheet(backend);
        }
272

273
274
    }else
    {
275
        QTextBrowser *browser=new QTextBrowser(this);
276
        QString backendList=QLatin1String("<ul>");
Andrew Coles's avatar
Andrew Coles committed
277
        int backendListSize = 0;
278
279
        foreach(Cantor::Backend* b, Cantor::Backend::availableBackends())
        {
Yuri Chornoivan's avatar
Yuri Chornoivan committed
280
            if(!b->requirementsFullfilled()) //It's disabled because of missing dependencies, not because of some other reason(like eg. nullbackend)
Andrew Coles's avatar
Andrew Coles committed
281
            {
282
                backendList+=QString::fromLatin1("<li>%1: <a href=\"%2\">%2</a></li>").arg(b->name(), b->url());
Andrew Coles's avatar
Andrew Coles committed
283
284
                ++backendListSize;
            }
285
        }
Andrew Coles's avatar
Andrew Coles committed
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
        browser->setHtml(i18np("<h1>No Backend Found</h1>\n"             \
                               "<div>You could try:\n"                   \
                               "  <ul>"                                  \
                               "    <li>Changing the settings in the config dialog;</li>" \
                               "    <li>Installing packages for the following program:</li>" \
                               "     %2 "                                \
                               "  </ul> "                                 \
                               "</div> "
                              , "<h1>No Backend Found</h1>\n"             \
                               "<div>You could try:\n"                   \
                               "  <ul>"                                  \
                               "    <li>Changing the settings in the config dialog;</li>" \
                               "    <li>Installing packages for one of the following programs:</li>" \
                               "     %2 "                                \
                               "  </ul> "                                 \
                               "</div> "
                              , backendListSize, backendList
                              ));
304

305
        browser->setObjectName(QLatin1String("ErrorMessage"));
306
307
        m_tabWidget->addTab(browser, i18n("Error"));
    }
308
309
}

Alexander Rieder's avatar
Alexander Rieder committed
310
void CantorShell::addWorksheet(const QString& backendName)
311
312
313
314
315
316
{
    static int sessionCount=1;

    // this routine will find and load our Part.  it finds the Part by
    // name which is a bad idea usually.. but it's alright in this
    // case since our Part is made for this Shell
317
318
    KPluginLoader loader(QLatin1String("cantorpart"));
    KPluginFactory* factory = loader.factory();
319
320
    if (factory)
    {
321
        if (!backendName.isEmpty())
322
        {
323
324
            Cantor::Backend* backend = Cantor::Backend::getBackend(backendName);
            if (!backend)
325
            {
326
327
                KMessageBox::error(this, i18n("Backend %1 is not installed", backendName), i18n("Cantor"));
                return;
328
            }
329
            else
330
            {
331
332
333
334
335
                if (!backend->isEnabled())
                {
                    KMessageBox::error(this, i18n("%1 backend installed, but inactive. Please check installation and Cantor settings", backendName), i18n("Cantor"));
                    return;
                }
336
            }
337
        }
338
339
340
341
342
343
344
345
346
347
348
349
350
351

        // now that the Part is loaded, we cast it to a Part to get our hands on it
        KParts::ReadWritePart* part = factory->create<KParts::ReadWritePart>(m_tabWidget, QVariantList()<<backendName);
        if (part)
        {
            connect(part, SIGNAL(setCaption(QString,QIcon)), this, SLOT(setTabCaption(QString,QIcon)));
            connect(part, SIGNAL(worksheetSave(QUrl)), this, SLOT(onWorksheetSave(QUrl)));
            m_parts.append(part);

            int tab = m_tabWidget->addTab(part->widget(), i18n("Session %1", sessionCount++));
            m_tabWidget->setCurrentIndex(tab);
            // Setting focus on worksheet view, because Qt clear focus of added widget inside addTab
            // This fix https://bugs.kde.org/show_bug.cgi?id=395976
            part->widget()->findChild<QGraphicsView*>()->setFocus();
352
353
354

            // Force run updateCaption for getting proper backend icon
            QMetaObject::invokeMethod(part, "updateCaption");
355
        }
356
        else
357
358
359
        {
            qDebug()<<"error creating part ";
        }
360
361
362
363
364
    }
    else
    {
        // if we couldn't find our Part, we exit since the Shell by
        // itself can't do anything useful
365
        KMessageBox::error(this, i18n("Failed to find the Cantor Part with error %1", loader.errorString()));
366
367
368
369
370
371
372
373
        qApp->quit();
        // we return here, cause qApp->quit() only means "exit the
        // next time we enter the event loop...
        return;
    }

}

Alexander Rieder's avatar
Alexander Rieder committed
374
void CantorShell::activateWorksheet(int index)
375
{
376
    QObject* pluginHandler=m_part->findChild<QObject*>(QLatin1String("PanelPluginHandler"));
377
378
379
380
381
382
383
384
385
386
387
388
389
390
    if (pluginHandler)
        disconnect(pluginHandler,SIGNAL(pluginsChanged()), this, SLOT(updatePanel()));

    // Save part state before change worksheet
    if (m_part)
    {
        QStringList visiblePanelNames;
        foreach (QDockWidget* doc, m_panels)
        {
            if (doc->widget() && doc->widget()->isVisible())
                visiblePanelNames << doc->objectName();
        }
        m_pluginsVisibility[m_part] = visiblePanelNames;
    }
391

392
    m_part=findPart(m_tabWidget->widget(index));
393
    if(m_part)
394
    {
395
        createGUI(m_part);
396

397
        updateWindowTitle(m_part->url().fileName());
398
        QObject* pluginHandler=m_part->findChild<QObject*>(QLatin1String("PanelPluginHandler"));
399
400
401
        connect(pluginHandler, SIGNAL(pluginsChanged()), this, SLOT(updatePanel()));
        updatePanel();
    }
402
    else
403
        qDebug()<<"selected part doesn't exist";
404
405

    m_tabWidget->setCurrentIndex(index);
406
407
}

408
void CantorShell::setTabCaption(const QString& caption, const QIcon& icon)
409
410
{
    KParts::ReadWritePart* part=dynamic_cast<KParts::ReadWritePart*>(sender());
411
412
    if (part)
    {
413
414
        if (!caption.isEmpty())
            m_tabWidget->setTabText(m_tabWidget->indexOf(part->widget()), caption);
415
416
        m_tabWidget->setTabIcon(m_tabWidget->indexOf(part->widget()), icon);
    }
417
418
}

419
420
421
422
423
424
void CantorShell::updateWindowTitle(const QString& fileName)
{
    QFileInfo info(fileName);
    setWindowTitle(info.baseName());
}

425
void CantorShell::closeTab(int index)
426
{
427
428
    if (!reallyClose(false))
    {
429
430
        return;
    }
431
432
433

    QWidget* widget = nullptr;
    if (index >= 0)
434
    {
435
436
437
438
439
440
441
442
443
444
445
        widget = m_tabWidget->widget(index);
    }
    else if (m_part)
    {
        widget = m_part->widget();
    }

    if (!widget)
    {
        qWarning() << "Could not find widget by tab index" << index;
        return;
446
    }
447

448

449
450
    m_tabWidget->removeTab(index);

451
    if(widget->objectName()==QLatin1String("ErrorMessage"))
452
453
454
455
456
457
458
459
    {
        widget->deleteLater();
    }else
    {
        KParts::ReadWritePart* part= findPart(widget);
        if(part)
        {
            m_parts.removeAll(part);
460
            m_pluginsVisibility.remove(part);
461
            delete part;
462
463
        }
    }
464
465
466

    if (m_tabWidget->count() == 0)
        setCaption(QString());
467
    updatePanel();
468
469
}

470
471
472
bool CantorShell::reallyClose(bool checkAllParts) {
    if(checkAllParts && m_parts.count() > 1) {
        bool modified = false;
473
        foreach( KParts::ReadWritePart* const part, m_parts)
474
475
476
477
478
479
480
481
        {
            if(part->isModified()) {
                modified = true;
                break;
            }
        }
        if(!modified) return true;
        int want_save = KMessageBox::warningYesNo( this,
482
            i18n("Multiple unsaved Worksheets are opened. Do you want to close them?"),
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
            i18n("Close Cantor"));
        switch (want_save) {
            case KMessageBox::Yes:
                return true;
            case KMessageBox::No:
                return false;
        }
    }
    if (m_part && m_part->isModified() ) {
        int want_save = KMessageBox::warningYesNoCancel( this,
            i18n("The current project has been modified. Do you want to save it?"),
            i18n("Save Project"));
        switch (want_save) {
            case KMessageBox::Yes:
                m_part->save();
                if(m_part->waitSaveComplete()) {
                    return true;
                } else {
                    m_part->setModified(true);
                    return false;
                }
            case KMessageBox::Cancel:
                return false;
            case KMessageBox::No:
                return true;
        }
    }
    return true;
}

void CantorShell::closeEvent(QCloseEvent* event) {
    if(!reallyClose()) {
        event->ignore();
    } else {
        KParts::MainWindow::closeEvent(event);
    }
}

Alexander Rieder's avatar
Alexander Rieder committed
521
void CantorShell::showSettings()
522
{
523
    KConfigDialog *dialog = new KConfigDialog(this,  QLatin1String("settings"), Settings::self());
524
525
526
    QWidget *generalSettings = new QWidget;
    Ui::SettingsBase base;
    base.setupUi(generalSettings);
Alexander Rieder's avatar
Alexander Rieder committed
527
    base.kcfg_DefaultBackend->addItems(Cantor::Backend::listAvailableBackends());
528

529
    dialog->addPage(generalSettings, i18n("General"), QLatin1String("preferences-other"));
Alexander Rieder's avatar
Alexander Rieder committed
530
    foreach(Cantor::Backend* backend, Cantor::Backend::availableBackends())
531
532
533
534
535
536
537
    {
        if (backend->config()) //It has something to configure, so add it to the dialog
            dialog->addPage(backend->settingsWidget(dialog), backend->config(), backend->name(),  backend->icon());
    }

    dialog->show();
}
538

Alexander Rieder's avatar
Alexander Rieder committed
539
void CantorShell::downloadExamples()
540
{
541
542
543
    KNS3::DownloadDialog dialog;
    dialog.exec();
    foreach (const KNS3::Entry& e,  dialog.changedEntries())
544
    {
545
        qDebug() << "Changed Entry: " << e.name();
546
547
    }
}
548

Alexander Rieder's avatar
Alexander Rieder committed
549
void CantorShell::openExample()
550
{
551
    QString dir = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1String("/examples");
552
    if (dir.isEmpty()) return;
553
    QDir().mkpath(dir);
554
555

    QStringList files=QDir(dir).entryList(QDir::Files);
556
    QPointer<QDialog> dlg=new QDialog(this);
557
    QListWidget* list=new QListWidget(dlg);
558
559
560
    foreach(const QString& file, files)
    {
        QString name=file;
561
        name.remove(QRegularExpression(QStringLiteral("-.*\\.hotstuff-access$")));
562
563
564
        list->addItem(name);
    }

565
566
567
568
569
570
571
572
573
574
575
576
577
    QVBoxLayout *mainLayout = new QVBoxLayout;
    dlg->setLayout(mainLayout);
    mainLayout->addWidget(list);

    QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);

    mainLayout->addWidget(buttonBox);

    buttonBox->button(QDialogButtonBox::Ok)->setIcon(QApplication::style()->standardIcon(QStyle::SP_DialogOkButton));
    buttonBox->button(QDialogButtonBox::Cancel)->setIcon(QApplication::style()->standardIcon(QStyle::SP_DialogCancelButton));

    connect(buttonBox, SIGNAL(accepted()), dlg, SLOT(accept()) );
    connect(buttonBox, SIGNAL(rejected()), dlg, SLOT(reject()) );
578

579
    if (dlg->exec()==QDialog::Accepted&&list->currentRow()>=0)
580
581
    {
        const QString& selectedFile=files[list->currentRow()];
582
        QUrl url = QUrl::fromLocalFile(QDir(dir).absoluteFilePath(selectedFile));
583

584
        qDebug()<<"loading file "<<url;
585
        load(url);
586
    }
587
588

    delete dlg;
589
}
590
591
592
593
594
595
596
597

KParts::ReadWritePart* CantorShell::findPart(QWidget* widget)
{
    foreach( KParts::ReadWritePart* const part, m_parts)
    {
        if(part->widget()==widget)
            return part;
    }
598
    return nullptr;
599
}
600
601
602

void CantorShell::updatePanel()
{
Minh Ngo's avatar
Minh Ngo committed
603
    unplugActionList(QLatin1String("view_show_panel_list"));
604

605
606
607
608
    //remove all of the previous panels (but do not delete the widgets)
    foreach(QDockWidget* dock, m_panels)
    {
        QWidget* widget=dock->widget();
609
        if(widget!=nullptr)
610
611
612
613
614
615
616
617
        {
            widget->setParent(this);
            widget->hide();
        }
        dock->deleteLater();
    }
    m_panels.clear();

618
619
    QList<QAction*> panelActions;

620
    Cantor::PanelPluginHandler* handler=m_part->findChild<Cantor::PanelPluginHandler*>(QLatin1String("PanelPluginHandler"));
621
622
    if(!handler)
    {
623
        qDebug()<<"no PanelPluginHandle found for this part";
624
625
626
        return;
    }

627
    QDockWidget* last=nullptr;
628
629

    QList<Cantor::PanelPlugin*> plugins=handler->plugins();
630
    const bool isNewWorksheet = !m_pluginsVisibility.contains(m_part);
631
632
    foreach(Cantor::PanelPlugin* plugin, plugins)
    {
633
        if(plugin==nullptr)
634
        {
635
            qDebug()<<"somethings wrong";
636
637
638
            continue;
        }

639
        qDebug()<<"adding panel for "<<plugin->name();
640
641
642
643
644
645
646
        plugin->setParentWidget(this);

        QDockWidget* docker=new QDockWidget(plugin->name(), this);
        docker->setObjectName(plugin->name());
        docker->setWidget(plugin->widget());
        addDockWidget ( Qt::RightDockWidgetArea,  docker );

647
        // Set visibility for dock from saved info
648
649
650
        if (isNewWorksheet)
        {
            if (plugin->showOnStartup())
651
652
653
                docker->show();
            else
                docker->hide();
654
        }
655
        else
656
657
658
659
660
661
        {
            if (m_pluginsVisibility[m_part].contains(plugin->name()))
                docker->show();
            else
                docker->hide();
        }
662

663
        if(last!=nullptr)
664
665
666
            tabifyDockWidget(last, docker);
        last=docker;

667
        connect(plugin, &Cantor::PanelPlugin::visibilityRequested, this, &CantorShell::pluginVisibilityRequested);
668

669
        m_panels.append(docker);
670
671
672
673

        //Create the action to show/hide this panel
        panelActions<<docker->toggleViewAction();

674
    }
675

676
    plugActionList(QLatin1String("view_show_panel_list"), panelActions);
677

678
679
680
681
682
    updateNewSubmenu();
}

void CantorShell::updateNewSubmenu()
{
683
    unplugActionList(QLatin1String("new_worksheet_with_backend_list"));
684
685
686
    qDeleteAll(m_newBackendActions);
    m_newBackendActions.clear();

687
688
689
690
    foreach (Cantor::Backend* backend, Cantor::Backend::availableBackends())
    {
        if (!backend->isEnabled())
            continue;
691
        QAction * action = new QAction(QIcon::fromTheme(backend->icon()), backend->name(), nullptr);
692
693
        action->setData(backend->name());
        connect(action, SIGNAL(triggered()), this, SLOT(fileNew()));
694
        m_newBackendActions << action;
695
    }
696
    plugActionList(QLatin1String("new_worksheet_with_backend_list"), m_newBackendActions);
697
}
698
699
700
701
702
703
704
705
706
707

Cantor::WorksheetAccessInterface* CantorShell::currentWorksheetAccessInterface()
{
    Cantor::WorksheetAccessInterface* wa=m_part->findChild<Cantor::WorksheetAccessInterface*>(Cantor::WorksheetAccessInterface::Name);

    if (!wa)
        qDebug()<<"failed to access worksheet access interface for current part";

    return wa;
}
708
709
710
711
712
713
714
715
716
717
718
719
720
721

void CantorShell::pluginVisibilityRequested()
{
    Cantor::PanelPlugin* plugin = static_cast<Cantor::PanelPlugin*>(sender());
    for (QDockWidget* docker: m_panels)
    {
        if (plugin->name() == docker->windowTitle())
        {
            if (docker->isHidden())
                docker->show();
            docker->raise();
        }
    }
}
722
723
724
725
726

void CantorShell::onWorksheetSave(const QUrl& url)
{
    if (m_recentProjectsAction)
        m_recentProjectsAction->addUrl(url);
727
728

    updateWindowTitle(m_part->url().fileName());
729
}