File indexing completed on 2024-04-21 05:44:02

0001 /***************************************************************************
0002  *   Copyright (C) 2003-2005 by David Saxton                               *
0003  *   david@bluehaze.org                                                    *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  ***************************************************************************/
0010 
0011 #include <KXMLGUIClient>
0012 
0013 #include "circuitdocument.h"
0014 #include "componentmodellibrary.h"
0015 #include "config.h"
0016 #include "contexthelp.h"
0017 #include "docmanager.h"
0018 #include "filemetainfo.h"
0019 #include "flowcodedocument.h"
0020 #include "itemeditor.h"
0021 #include "itemgroup.h"
0022 #include "iteminterface.h"
0023 #include "itemlibrary.h"
0024 #include "ktechlab.h"
0025 #include "languagemanager.h"
0026 #include "mechanicsdocument.h"
0027 #include "microlibrary.h"
0028 #include "newfiledlg.h"
0029 #include "oscilloscope.h"
0030 #include "projectmanager.h"
0031 #include "scopescreen.h"
0032 #include "settingsdlg.h"
0033 #include "subcircuits.h"
0034 #include "symbolviewer.h"
0035 #include "textdocument.h"
0036 #include "textview.h"
0037 #include "viewcontainer.h"
0038 
0039 // #include <q3dockarea.h>
0040 #include <QTimer>
0041 #include <QToolButton>
0042 // #include <q3ptrlist.h>
0043 #include <QDesktopWidget>
0044 #include <QElapsedTimer>
0045 #include <QFileDialog>
0046 #include <QIcon>
0047 #include <QMenu>
0048 #include <QMimeData>
0049 #include <QScreen>
0050 #include <QStandardPaths>
0051 #include <QTabWidget>
0052 
0053 #include <KActionCollection>
0054 #include <KEditToolBar>
0055 #include <KSharedConfig>
0056 #include <KToolBar>
0057 
0058 //#include <kkeydialog.h>
0059 #include <KLocalizedString>
0060 #include <KMessageBox>
0061 //#include <kpopupmenu.h>
0062 //#include <kwin.h>
0063 #include <KRecentFilesAction>
0064 #include <KShortcutsDialog>
0065 #include <KStandardAction>
0066 #include <KToolBarPopupAction>
0067 #include <KWindowSystem>
0068 #include <KXMLGUIFactory>
0069 
0070 #include <ktlconfig.h>
0071 #include <ktechlab_debug.h>
0072 
0073 KTechlab *KTechlab::m_pSelf = nullptr;
0074 
0075 KTechlab::KTechlab()
0076     : KateMDI::MainWindow(nullptr)
0077 {
0078     setObjectName("KTechlab");
0079     m_pSelf = this;
0080 
0081     QElapsedTimer ct;
0082     ct.start();
0083 
0084     m_bIsShown = false;
0085     m_pContainerDropSource = nullptr;
0086     m_pContainerDropReceived = nullptr;
0087     m_pContextMenuContainer = nullptr;
0088     m_pFocusedContainer = nullptr;
0089     m_pToolBarOverlayLabel = nullptr;
0090 
0091     if (QFontInfo(m_itemFont).pixelSize() > 11) {
0092         // It has to be > 11, not > 12, as (I think) pixelSize() rounds off the actual size
0093         m_itemFont.setPixelSize(12);
0094     }
0095 
0096     m_pUpdateCaptionsTimer = new QTimer(this);
0097     connect(m_pUpdateCaptionsTimer, &QTimer::timeout, this, &KTechlab::slotUpdateCaptions);
0098 
0099     setMinimumSize(400, 400);
0100 
0101     setupTabWidget();
0102     setupToolDocks();
0103     setupActions();
0104     setupView();
0105     // readProperties( KGlobal::config() );
0106     KSharedConfigPtr cfg = KSharedConfig::openConfig();
0107     readPropertiesInConfig(cfg.data());
0108 
0109     //  qCDebug(KTL_LOG) << "Constructor time: " << ct.elapsed();
0110 }
0111 
0112 KTechlab::~KTechlab()
0113 {
0114     fileMetaInfo()->saveAllMetaInfo();
0115 
0116     for (ViewContainerList::Iterator itVc = m_viewContainerList.begin(); itVc != m_viewContainerList.end(); ++itVc) {
0117         ViewContainer *vc = itVc->data();
0118         disconnect(vc, &ViewContainer::destroyed, this, &KTechlab::slotViewContainerDestroyed);
0119     }
0120 
0121     delete fileMetaInfo();
0122     delete itemLibrary(); // This better be the last time the item library is used!
0123     delete subcircuits();
0124 }
0125 
0126 void KTechlab::show()
0127 {
0128     KateMDI::MainWindow::show();
0129     m_bIsShown = true;
0130 }
0131 
0132 void KTechlab::openFile(ViewArea *viewArea)
0133 {
0134     const QList<QUrl> files = getFileURLs(false);
0135     if (files.isEmpty())
0136         return;
0137 
0138     load(files.first(), viewArea);
0139 }
0140 
0141 void KTechlab::load(const QUrl &url, ViewArea *viewArea)
0142 {
0143     if (!url.isValid())
0144         return;
0145 
0146     loadInternal(url, false, viewArea);
0147 }
0148 
0149 void KTechlab::slotLoadRecent(const QUrl &url)
0150 {
0151     loadInternal(url, true);
0152 }
0153 
0154 void KTechlab::loadInternal(const QUrl &url, bool isRecent, ViewArea *viewArea)
0155 {
0156     if (url.fileName().endsWith(".ktechlab", Qt::CaseInsensitive)) {
0157         // This is a ktechlab project; it has to be handled separetly from a
0158         // normal file.
0159 
0160         ProjectManager::self()->slotOpenProject(url);
0161         return;
0162     }
0163 
0164     if (!isRecent)
0165         addRecentFile(url);
0166 
0167     // set our caption
0168     setCaption(url.toDisplayString(QUrl::PreferLocalFile));
0169 
0170     // load in the file (target is always local)
0171     DocManager::self()->openURL(url, viewArea);
0172 }
0173 
0174 QList<QUrl> KTechlab::recentFiles()
0175 {
0176     return m_recentFiles->urls();
0177 }
0178 
0179 void KTechlab::setupToolDocks()
0180 {
0181     setToolViewStyle(KMultiTabBar::KDEV3ICON);
0182 
0183     KateMDI::ToolView *tv = nullptr;
0184 
0185     tv = createToolView(ProjectManager::toolViewIdentifier(), KMultiTabBar::Left, QIcon::fromTheme("attach"), i18n("Project"));
0186     tv->setObjectName("ProjectManager-ToolView");
0187     ProjectManager::self(tv);
0188 
0189     tv = createToolView(ComponentSelector::toolViewIdentifier(), KMultiTabBar::Left, QIcon::fromTheme("circuit"), i18n("Components"));
0190     tv->setObjectName("ComponentSelector-ToolView");
0191     ComponentSelector::self(tv);
0192 
0193     // Create an instance of the subcircuits interface, now that we have created the component selector
0194     subcircuits();
0195     Subcircuits::loadSubcircuits();
0196 
0197     tv = createToolView(FlowPartSelector::toolViewIdentifier(), KMultiTabBar::Left, QIcon::fromTheme("flowcode"), i18n("Flow Parts"));
0198     tv->setObjectName("FlowPartSelector-ToolView");
0199     FlowPartSelector::self(tv);
0200 
0201 #ifdef MECHANICS
0202     tv = createToolView(MechanicsSelector::toolViewIdentifier(), KMultiTabBar::Left, QIcon::fromTheme("mechanics"), i18n("Mechanics"));
0203     tv->setObjectName("MechanicsSelector-ToolView");
0204     MechanicsSelector::self(tv);
0205 #endif
0206 
0207     tv = createToolView(ItemEditor::toolViewIdentifier(), KMultiTabBar::Right, QIcon::fromTheme("item"), i18n("Item Editor"));
0208     tv->setObjectName("ItemEditor-ToolView");
0209     ItemEditor::self(tv);
0210 
0211     tv = createToolView(ContextHelp::toolViewIdentifier(), KMultiTabBar::Right, QIcon::fromTheme("help-contents"), i18n("Context Help"));
0212     tv->setObjectName("ContextHelp-ToolView");
0213     ContextHelp::self(tv);
0214 
0215     tv = createToolView(LanguageManager::toolViewIdentifier(), KMultiTabBar::Bottom, QIcon::fromTheme("utilities-log-viewer"), i18n("Messages"));
0216     tv->setObjectName("LanguageManager-ToolView");
0217     LanguageManager::self(tv);
0218 
0219 #ifndef NO_GPSIM
0220     tv = createToolView(SymbolViewer::toolViewIdentifier(), KMultiTabBar::Right, QIcon::fromTheme("blockdevice"), i18n("Symbol Viewer"));
0221     tv->setObjectName("SymbolViewer-ToolView");
0222     SymbolViewer::self(tv);
0223 #endif
0224 
0225     addOscilloscopeAsToolView(this);
0226 #if 1
0227     tv = createToolView(ScopeScreen::toolViewIdentifier(), KMultiTabBar::Bottom, QIcon::fromTheme("oscilloscope"), i18n("Scope Screen (Very Rough)"));
0228     tv->setObjectName("ScopeScreen-ToolView");
0229     ScopeScreen::self(tv);
0230 #endif
0231 
0232     updateSidebarMinimumSizes();
0233 }
0234 
0235 void KTechlab::addWindow(ViewContainer *vc)
0236 {
0237     if (vc && !m_viewContainerList.contains(vc)) {
0238         m_viewContainerList << vc;
0239         connect(vc, &ViewContainer::destroyed, this, &KTechlab::slotViewContainerDestroyed);
0240     }
0241 
0242     m_viewContainerList.removeAll(static_cast<ViewContainer *>(nullptr));
0243     slotUpdateTabWidget();
0244     slotDocModifiedChanged();
0245 }
0246 
0247 void KTechlab::setupView()
0248 {
0249     setAcceptDrops(true);
0250     setStandardToolBarMenuEnabled(true);
0251     setXMLFile("ktechlabui.rc");
0252     createShellGUI(true);
0253     // 2017.09.04 - no need to explicitly add the new file selector to toolbar
0254     ////action("newfile_popup")->plug( toolBar("mainToolBar"), 0 );
0255     // actionByName("file_new")->addTo( toolBar("mainToolBar") );
0256     ////action("file_new")->unplug( toolBar("mainToolBar") );
0257     // actionByName("file_new")->removeFrom( toolBar("mainToolBar") );
0258     setupExampleActions();
0259     statusBar()->show();
0260 }
0261 
0262 void KTechlab::overlayToolBarScreenshot()
0263 {
0264     return;
0265 
0266     if (!m_pToolBarOverlayLabel) {
0267         m_pToolBarOverlayLabel = new QLabel(nullptr,
0268                                             Qt::WindowStaysOnTopHint /* | Qt::WStyle_Customize */ |
0269                                                 Qt::FramelessWindowHint
0270                                                 /*| Qt::WNoAutoErase */
0271                                                 | Qt::Popup);
0272         m_pToolBarOverlayLabel->hide();
0273         // m_pToolBarOverlayLabel->setBackgroundMode( Qt::NoBackground ); // 2018.12.02
0274         QPalette p;
0275         p.setColor(m_pToolBarOverlayLabel->backgroundRole(), Qt::transparent);
0276         m_pToolBarOverlayLabel->setPalette(p);
0277     }
0278 
0279     if (!m_bIsShown) {
0280         // The window isn't visible yet, so there's nothing to overlay (and if we tried,
0281         // it would appear as a strange floating toolbar).
0282         return;
0283     }
0284 
0285     if (m_pToolBarOverlayLabel->isVisible()) {
0286         // This is to avoid successive calls to removeGUIClient when we have
0287         // already popped it up for the first call, and don't want to take
0288         // another screenshot (as that would be without the toolbar).
0289         return;
0290     }
0291 
0292     // QPtrListIterator<KToolBar> toolBarIterator(); // unused?
0293 
0294     //  QWidget * toolsWidget = toolBar( "toolsToolBar" );
0295     //  QWidget * debugWidget = toolBar( "debugTB" );
0296 
0297     // KToolBar * toolsWidget = static_cast<KToolBar*>(child( "toolsToolBar", "KToolBar" )); // 2018.08.18 - see below
0298     // KToolBar * debugWidget = static_cast<KToolBar*>(child( "debugTB", "KToolBar" ));
0299     KToolBar *toolsWidget = findChild<KToolBar *>("toolsToolBar");
0300     KToolBar *debugWidget = findChild<KToolBar *>("debugTB");
0301 
0302     if (!toolsWidget && !debugWidget)
0303         return;
0304 
0305     QWidget *parent = static_cast<QWidget *>(toolsWidget ? toolsWidget->parent() : debugWidget->parent());
0306 
0307     QRect grabRect;
0308 
0309     // 128 is a sanity check (widget can do strange things when being destroyed)
0310 
0311     if (toolsWidget && toolsWidget->height() <= 128)
0312         grabRect |= toolsWidget->geometry();
0313     if (debugWidget && debugWidget->height() <= 128)
0314         grabRect |= debugWidget->geometry();
0315 
0316     if (!grabRect.isValid())
0317         return;
0318 
0319     QPixmap shot = parent->grab(grabRect);
0320 
0321     m_pToolBarOverlayLabel->move(parent->mapToGlobal(grabRect.topLeft()));
0322     m_pToolBarOverlayLabel->setFixedSize(grabRect.size());
0323     m_pToolBarOverlayLabel->setPixmap(shot);
0324     m_pToolBarOverlayLabel->show();
0325 
0326     QTimer::singleShot(100, this, SLOT(hideToolBarOverlay()));
0327 }
0328 
0329 void KTechlab::hideToolBarOverlay()
0330 {
0331     if (!m_pToolBarOverlayLabel)
0332         return;
0333 
0334     //  QWidget * hiddenWidget = toolBar( "toolsToolBar" );
0335     //  if ( !hiddenWidget )
0336     //      return;
0337 
0338     //  hiddenWidget->setBackgroundMode( NoBackground );
0339     //  hiddenWidget->setWFlags( WNoAutoErase );
0340     //  hiddenWidget->setUpdatesEnabled( false );
0341 
0342     m_pToolBarOverlayLabel->hide();
0343 }
0344 
0345 void KTechlab::addNoRemoveGUIClient(KXMLGUIClient *client)
0346 {
0347     if (client && !m_noRemoveGUIClients.contains(client))
0348         m_noRemoveGUIClients << client;
0349 }
0350 
0351 void KTechlab::removeGUIClients()
0352 {
0353     QList<KXMLGUIClient *> clientsToRemove;
0354 
0355     QList<KXMLGUIClient *> clients = factory()->clients();
0356     // for ( KXMLGUIClient * client = clients.first(); client; client = clients.next() )
0357     for (QList<KXMLGUIClient *>::iterator itClient = clients.begin(); itClient != clients.end(); ++itClient) {
0358         // if ( client && client != this && !m_noRemoveGUIClients.contains( client ) )
0359         if ((*itClient != this) && (!m_noRemoveGUIClients.contains(*itClient)))
0360             clientsToRemove << *itClient;
0361     }
0362 
0363     if (clients.isEmpty())
0364         return;
0365 
0366     overlayToolBarScreenshot();
0367 
0368     QList<KXMLGUIClient *>::iterator end = clientsToRemove.end();
0369     for (QList<KXMLGUIClient *>::iterator it = clientsToRemove.begin(); it != end; ++it)
0370         factory()->removeClient(*it);
0371 }
0372 
0373 void KTechlab::setupTabWidget()
0374 {
0375     m_pViewContainerTabWidget = new QTabWidget(centralWidget());
0376     if (centralWidget()->layout()) {
0377         centralWidget()->layout()->addWidget(m_pViewContainerTabWidget);
0378     } else {
0379         qCWarning(KTL_LOG) << " unexpected null layout for " << centralWidget();
0380     }
0381     connect(tabWidget(), &QTabWidget::currentChanged, this, &KTechlab::slotViewContainerActivated);
0382 
0383     // KConfig *config = kapp->config();
0384     // config->setGroup("UI");
0385     KSharedConfigPtr cfg = KSharedConfig::openConfig();
0386     KConfigGroup grUi = cfg->group("UI");
0387 
0388     bool CloseOnHover = grUi.readEntry("CloseOnHover", false);
0389     // tabWidget()->setHoverCloseButton( CloseOnHover ); // 2018.08.18 - see below
0390     tabWidget()->setTabsClosable(CloseOnHover);
0391 
0392     bool CloseOnHoverDelay = grUi.readEntry("CloseOnHoverDelay", false);
0393     // tabWidget()->setHoverCloseButtonDelayed( CloseOnHoverDelay ); // 2018.08.18 - see below
0394     tabWidget()->setTabsClosable(CloseOnHoverDelay);
0395 
0396     if (grUi.readEntry("ShowCloseTabsButton", true)) {
0397         QToolButton *but = new QToolButton(tabWidget());
0398         but->setIcon(QIcon::fromTheme("tab-close"));
0399         but->adjustSize();
0400         but->hide();
0401         connect(but, &QToolButton::clicked, this, &KTechlab::slotViewContainerClose);
0402         tabWidget()->setCornerWidget(but, Qt::TopRightCorner);
0403     }
0404 
0405     QTabBar *tabBar = tabWidget()->tabBar();
0406     tabBar->setContextMenuPolicy(Qt::CustomContextMenu);
0407     connect(tabBar, &QTabBar::customContextMenuRequested, this, &KTechlab::slotTabContext);
0408 }
0409 
0410 void KTechlab::slotUpdateTabWidget()
0411 {
0412     m_viewContainerList.removeAll(static_cast<ViewContainer *>(nullptr));
0413 
0414     bool noWindows = m_viewContainerList.isEmpty();
0415 
0416     if (QWidget *button = tabWidget()->cornerWidget(Qt::TopRightCorner))
0417         button->setHidden(noWindows);
0418 
0419     if (noWindows)
0420         setCaption(nullptr);
0421 }
0422 
0423 void KTechlab::setupActions()
0424 {
0425     KActionCollection *ac = actionCollection();
0426     // TODO move the actions from KTechLab object level to document view level for
0427     //  all types of documents; see actions marked with (1)! and TextView constructor
0428 
0429     // 2017.08.06 - using custom new action
0430     // KStandardAction::openNew(            this, SLOT(slotFileNew()),                  ac );
0431     {
0432         QAction *openAction = KStandardAction::open(this, SLOT(slotFileOpen()), ac);
0433         openAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
0434     }
0435     KStandardAction::save(this, SLOT(slotFileSave()), ac); // (1)!
0436     KStandardAction::saveAs(this, SLOT(slotFileSaveAs()), ac);
0437     KStandardAction::close(this, SLOT(slotViewClose()), ac);
0438     KStandardAction::print(this, SLOT(slotFilePrint()), ac); // (1)!
0439     KStandardAction::quit(this, SLOT(slotFileQuit()), ac);
0440     KStandardAction::undo(this, SLOT(slotEditUndo()), ac);   // (1)!
0441     KStandardAction::redo(this, SLOT(slotEditRedo()), ac);   // (1)!
0442     KStandardAction::cut(this, SLOT(slotEditCut()), ac);     // (1)!
0443     KStandardAction::copy(this, SLOT(slotEditCopy()), ac);   // (1)!
0444     KStandardAction::paste(this, SLOT(slotEditPaste()), ac); // (1)!
0445     KStandardAction::keyBindings(this, SLOT(slotOptionsConfigureKeys()), ac);
0446     KStandardAction::configureToolbars(this, SLOT(slotOptionsConfigureToolbars()), ac);
0447     KStandardAction::preferences(this, SLOT(slotOptionsPreferences()), ac);
0448 
0449     // BEGIN New file popup
0450     // KToolBarPopupAction *p = new KToolBarPopupAction( i18n("&New"), "document-new",
0451     //          KStandardShortcut::shortcut(KStandardShortcut::New), this, SLOT(slotFileNew()), ac, "newfile_popup" );
0452     KToolBarPopupAction *p = new KToolBarPopupAction(QIcon::fromTheme("document-new"), i18n("&New"), ac);
0453     p->setObjectName("file_new");
0454     p->setShortcuts(KStandardShortcut::shortcut(KStandardShortcut::New));
0455     connect(p, &KToolBarPopupAction::triggered, this, &KTechlab::slotFileNew);
0456     ac->addAction(p->objectName(), p);
0457     p->menu()->setTitle(i18n("New File"));
0458     {
0459         //(new QAction( i18n("Assembly"), "source", 0, this, SLOT(slotFileNewAssembly()), ac, "newfile_asm" ))->plug( p->menu() );
0460         QAction *a = new QAction(QIcon::fromTheme("source"), i18n("Assembly"), ac);
0461         a->setObjectName("newfile_asm");
0462         connect(a, &QAction::triggered, this, &KTechlab::slotFileNewAssembly);
0463         p->menu()->addAction(a);
0464         ac->addAction(a->objectName(), a);
0465     }
0466     {
0467         //(new QAction( i18n("C source"), "text-x-csrc", 0, this, SLOT(slotFileNewC()), ac, "newfile_c" ))->plug( p->menu() );
0468         QAction *a = new QAction(QIcon::fromTheme("text-x-csrc"), i18n("C source"), ac);
0469         a->setObjectName("newfile_c");
0470         connect(a, &QAction::triggered, this, &KTechlab::slotFileNewC);
0471         p->menu()->addAction(a);
0472         ac->addAction(a->objectName(), a);
0473     }
0474     {
0475         //(new QAction( i18n("Circuit"), "application-x-circuit", 0, this, SLOT(slotFileNewCircuit()), ac, "newfile_circuit" ))->plug( p->menu() );
0476         QAction *a = new QAction(QIcon::fromTheme("application-x-circuit"), i18n("Circuit"), ac);
0477         a->setObjectName("newfile_circuit");
0478         connect(a, &QAction::triggered, this, &KTechlab::slotFileNewCircuit);
0479         p->menu()->addAction(a);
0480         ac->addAction(a->objectName(), a);
0481     }
0482     {
0483         //(new QAction( i18n("FlowCode"), "application-x-flowcode", 0, this, SLOT(slotFileNewFlowCode()), ac, "newfile_flowcode" ))->plug( p->menu() );
0484         QAction *a = new QAction(QIcon::fromTheme("application-x-flowcode"), i18n("FlowCode"), ac);
0485         a->setObjectName("newfile_flowcode");
0486         connect(a, &QAction::triggered, this, &KTechlab::slotFileNewFlowCode);
0487         p->menu()->addAction(a);
0488         ac->addAction(a->objectName(), a);
0489     }
0490 #ifdef MECHANICS
0491     {
0492         //(new QAction( i18n("Mechanics"), "ktechlab_mechanics", 0, this, SLOT(slotFileNewMechanics()), ac, "newfile_mechanics" ))->plug( p->menu() );
0493         QAction *a = new QAction(QIcon::fromTheme("" /* "ktechlab_mechanics" -- for the future */), i18n("Mechanics"), ac);
0494         a->setObjectName("newfile_mechanics");
0495         connect(a, &QAction::triggered, this, &KTechlab::slotFileNewMechanics);
0496         p->menu()->addAction(a);
0497         ac->addAction(a->objectName(), a);
0498     }
0499 #endif
0500     {
0501         //(new QAction( "Microbe", "application-x-microbe", 0, this, SLOT(slotFileNewMicrobe()), ac, "newfile_microbe" ))->plug( p->menu() );
0502         QAction *a = new QAction(QIcon::fromTheme("application-x-microbe"), i18n("Microbe"), ac);
0503         a->setObjectName("newfile_microbe");
0504         connect(a, &QAction::triggered, this, &KTechlab::slotFileNewMicrobe);
0505         p->menu()->addAction(a);
0506         ac->addAction(a->objectName(), a);
0507     }
0508     // END New File popup
0509 
0510     m_recentFiles = KStandardAction::openRecent(this, SLOT(slotLoadRecent(QUrl)), ac);
0511 
0512     m_statusbarAction = KStandardAction::showStatusbar(this, SLOT(slotOptionsShowStatusbar()), ac);
0513     ac->addAction(m_statusbarAction->objectName(), m_statusbarAction);
0514 
0515     // BEGIN Project Actions
0516     ProjectManager *pm = ProjectManager::self();
0517     {
0518         // new QAction( i18n("New Project..."), "window-new",           0, pm, SLOT(slotNewProject()),          ac,     "project_new" );
0519         QAction *a = new QAction(QIcon::fromTheme("window-new"), i18n("New Project..."), ac);
0520         a->setObjectName("project_new");
0521         connect(a, &QAction::triggered, pm, &ProjectManager::slotNewProject);
0522         ac->addAction(a->objectName(), a);
0523     }
0524     {
0525         // new QAction( i18n("Open Project..."), "project-open",        0, pm, SLOT(slotOpenProject()),         ac,     "project_open" );
0526         QAction *a = new QAction(QIcon::fromTheme("project-open"), i18n("Open Project..."), ac);
0527         a->setObjectName("project_open");
0528         connect(a, &QAction::triggered, pm, qOverload<>(&ProjectManager::slotOpenProject));
0529         ac->addAction(a->objectName(), a);
0530     }
0531     {
0532         m_recentProjects = new KRecentFilesAction(i18n("Open &Recent Project..."), ac);
0533         m_recentProjects->setObjectName("project_open_recent");
0534         connect(m_recentProjects, SIGNAL(urlSelected(QUrl)), ProjectManager::self(), SLOT(slotOpenProject(QUrl)));
0535         ac->addAction(m_recentProjects->objectName(), m_recentProjects);
0536     }
0537     {
0538         // new QAction( i18n("Export to Makefile..."), "document-export",   0, pm, SLOT(slotExportToMakefile()),        ac, "project_export_makefile" );
0539         QAction *a = new QAction(QIcon::fromTheme("document-export"), i18n("Export to Makefile..."), ac);
0540         a->setObjectName("project_export_makefile");
0541         connect(a, &QAction::triggered, pm, &ProjectManager::slotExportToMakefile);
0542         ac->addAction(a->objectName(), a);
0543     }
0544     {
0545         // new QAction( i18n("Create Subproject..."), 0,                0, pm, SLOT(slotCreateSubproject()),        ac, "project_create_subproject" );
0546         QAction *a = new QAction(i18n("Create Subproject..."), ac);
0547         a->setObjectName("project_create_subproject");
0548         connect(a, &QAction::triggered, pm, &ProjectManager::slotCreateSubproject);
0549         ac->addAction(a->objectName(), a);
0550     }
0551     {
0552         // new QAction( i18n("Add Existing File..."), "document-open",      0, pm, SLOT(slotAddFile()),                 ac, "project_add_existing_file" );
0553         QAction *a = new QAction(QIcon::fromTheme("document-open"), i18n("Add Existing File..."), ac);
0554         a->setObjectName("project_add_existing_file");
0555         connect(a, &QAction::triggered, pm, &ProjectManager::slotAddFile);
0556         ac->addAction(a->objectName(), a);
0557     }
0558     {
0559         // new QAction( i18n("Add Current File..."), "document-import",     0, pm, SLOT(slotAddCurrentFile()),          ac, "project_add_current_file" );
0560         QAction *a = new QAction(QIcon::fromTheme("document-import"), i18n("Add Current File..."), ac);
0561         a->setObjectName("project_add_current_file");
0562         connect(a, &QAction::triggered, pm, &ProjectManager::slotAddCurrentFile);
0563         ac->addAction(a->objectName(), a);
0564     }
0565     //  new QAction( i18n("Project Options"), "configure",          0, pm, SLOT(slotProjectOptions()),          ac, "project_options" );
0566     // ^ TODO why commented?
0567     {
0568         // new QAction( i18n("Close Project"), "window-close",          0, pm, SLOT(slotCloseProject()),            ac, "project_close" );
0569         QAction *a = new QAction(QIcon::fromTheme("window-close"), i18n("Close Project"), ac);
0570         a->setObjectName("project_close");
0571         connect(a, &QAction::triggered, pm, &ProjectManager::slotCloseProject);
0572         ac->addAction(a->objectName(), a);
0573     }
0574     {
0575         // new QAction( i18n("Remove from Project"), "edit-delete",     0, pm, SLOT(slotRemoveSelected()),          ac, "project_remove_selected" );
0576         QAction *a = new QAction(QIcon::fromTheme("edit-delete"), i18n("Remove from Project"), ac);
0577         a->setObjectName("project_remove_selected");
0578         connect(a, &QAction::triggered, pm, &ProjectManager::slotRemoveSelected);
0579         ac->addAction(a->objectName(), a);
0580     }
0581     {
0582         // new QAction( i18n("Insert Existing File..."), "document-open",   0, pm, SLOT(slotSubprojectAddExistingFile()),   ac, "subproject_add_existing_file" );
0583         QAction *a = new QAction(QIcon::fromTheme("document-open"), i18n("Insert Existing File..."), ac);
0584         a->setObjectName("subproject_add_existing_file");
0585         connect(a, &QAction::triggered, pm, &ProjectManager::slotSubprojectAddExistingFile);
0586         ac->addAction(a->objectName(), a);
0587     }
0588     {
0589         // new QAction( i18n("Insert Current File..."), "document-import",  0, pm, SLOT(slotSubprojectAddCurrentFile()),ac, "subproject_add_current_file" );
0590         QAction *a = new QAction(QIcon::fromTheme("document-import"), i18n("Insert Current File..."), ac);
0591         a->setObjectName("subproject_add_current_file");
0592         connect(a, &QAction::triggered, pm, &ProjectManager::slotSubprojectAddCurrentFile);
0593         ac->addAction(a->objectName(), a);
0594     }
0595     {
0596         // new QAction( i18n("Linker Options..."), "configure",     0, pm, SLOT(slotSubprojectLinkerOptions()), ac, "project_item_linker_options" );
0597         QAction *a = new QAction(QIcon::fromTheme("configure"), i18n("Linker Options..."), ac);
0598         a->setObjectName("project_item_linker_options");
0599         connect(a, &QAction::triggered, pm, &ProjectManager::slotSubprojectLinkerOptions);
0600         ac->addAction(a->objectName(), a);
0601     }
0602     {
0603         // new QAction( i18n("Build..."), "launch",                 0, pm, SLOT(slotItemBuild()),               ac, "project_item_build" );
0604         QAction *a = new QAction(QIcon::fromTheme("run-build"), i18n("Build..."), ac);
0605         a->setObjectName("project_item_build");
0606         connect(a, &QAction::triggered, pm, &ProjectManager::slotItemBuild);
0607         ac->addAction(a->objectName(), a);
0608     }
0609     {
0610         // new QAction( i18n("Upload..."), "convert_to_pic",            0, pm, SLOT(slotItemUpload()),              ac, "project_item_upload" );
0611         QAction *a = new QAction(QIcon::fromTheme("convert_to_pic"), i18n("Upload..."), ac);
0612         a->setObjectName("project_item_upload");
0613         connect(a, &QAction::triggered, pm, &ProjectManager::slotItemUpload);
0614         ac->addAction(a->objectName(), a);
0615     }
0616     {
0617         // new QAction( i18n("Processing Options..."), "configure", 0, pm, SLOT(slotItemProcessingOptions()),   ac, "project_item_processing_options" );
0618         QAction *a = new QAction(QIcon::fromTheme("configure"), i18n("Processing Options..."), ac);
0619         a->setObjectName("project_item_processing_options");
0620         connect(a, &QAction::triggered, pm, &ProjectManager::slotItemProcessingOptions);
0621         ac->addAction(a->objectName(), a);
0622     }
0623     // END Project Actions
0624 
0625     {
0626         // new QAction( i18n("Split View Left/Right"), "view-split-left-right", Qt::CTRL|Qt::SHIFT|Qt::Key_L, this, SLOT(slotViewSplitLeftRight()), ac, "view_split_leftright" );
0627         QAction *a = new QAction(QIcon::fromTheme("view-split-left-right"), i18n("Split View Left/Right"), ac);
0628         a->setObjectName("view_split_leftright");
0629         connect(a, &QAction::triggered, this, &KTechlab::slotViewSplitLeftRight);
0630         ac->addAction(a->objectName(), a);
0631     }
0632     {
0633         // new QAction( i18n("Split View Top/Bottom"), "view-split-top-bottom", Qt::CTRL|Qt::SHIFT|Qt::Key_T, this, SLOT(slotViewSplitTopBottom()), ac, "view_split_topbottom" );
0634         QAction *a = new QAction(QIcon::fromTheme("view-split-top-bottom"), i18n("Split View Top/Bottom"), ac);
0635         a->setObjectName("view_split_topbottom");
0636         connect(a, &QAction::triggered, this, &KTechlab::slotViewSplitTopBottom);
0637         ac->addAction(a->objectName(), a);
0638     }
0639     {
0640         // KToggleAction * ta = new KToggleAction( i18n("Run Simulation"), "media-playback-start", Qt::Key_F10, 0, 0, ac, "simulation_run" );
0641         KToggleAction *ta = new KToggleAction(QIcon::fromTheme("media-playback-start"), i18n("Run Simulation"), ac);
0642         ac->setDefaultShortcut(ta, Qt::Key_F10);
0643         ta->setObjectName("simulation_run");
0644         ta->setChecked(true);
0645         connect(ta, &KToggleAction::toggled, Simulator::self(), &Simulator::slotSetSimulating);
0646         ta->setCheckedState(KGuiItem(i18n("Pause Simulation"), "media-playback-pause", nullptr));
0647         ac->addAction(ta->objectName(), ta);
0648     }
0649 
0650     // We can call slotCloseProject now that the actions have been created
0651     ProjectManager::self()->updateActions();
0652     DocManager::self()->disableContextActions();
0653 }
0654 
0655 void KTechlab::setupExampleActions()
0656 {
0657     QStringList categories;
0658     categories << "555"
0659                << "basic"
0660                << "diodes"
0661                << "jfets"
0662                << "opamps"
0663                << "matrix_disp_driver"
0664                << "mosfets"
0665                << "transistors";
0666 
0667     // A name for the example circuit can be generated from the filename (and
0668     // will be done so if the filename is not in this list). But the name
0669     // generation is not that intelligent (e.g. and.circuit should be called
0670     // "AND", not "And" - poor capitalization. And it also allows translation of
0671     // names.
0672     QStringMap filesToNames;
0673     filesToNames["internals.circuit"] = i18n("Internals");
0674     filesToNames["square-wave.circuit"] = i18n("Square Wave");
0675     filesToNames["2-way-switch.circuit"] = i18n("2-Way Switch");
0676     filesToNames["3-way-switch.circuit"] = i18n("3-Way Switch");
0677     filesToNames["capacitor.circuit"] = i18n("Capacitor");
0678     filesToNames["lrc.circuit"] = i18n("LRC");
0679     filesToNames["resistors-parallel.circuit"] = i18n("Resistors in Parallel");
0680     filesToNames["resistors-series.circuit"] = i18n("Resistors in Series");
0681     filesToNames["voltage-divider.circuit"] = i18n("Voltage Divider");
0682     filesToNames["full-wave-rectifier.circuit"] = i18n("Full Wave Rectifier");
0683     filesToNames["half-wave-rectifier.circuit"] = i18n("Half Wave Rectifier");
0684     filesToNames["inverter.circuit"] = i18n("Inverter");
0685     filesToNames["and.circuit"] = i18n("AND");
0686     filesToNames["nand.circuit"] = i18n("NAND");
0687     filesToNames["nor.circuit"] = i18n("NOR");
0688     filesToNames["or.circuit"] = i18n("OR");
0689     filesToNames["decoupler.circuit"] = i18n("Decoupler");
0690     filesToNames["inverting-amplifier.circuit"] = i18n("Inverting Amplifier");
0691     filesToNames["noninverting-amplifier.circuit"] = i18n("Noninverting Amplifier");
0692     filesToNames["instrumentation-amplifier.circuit"] = i18n("Instrumentation Amplifier");
0693     filesToNames["astable-multivibrator.circuit"] = i18n("Astable Multivibrator");
0694     filesToNames["inverter.circuit"] = i18n("Inverter");
0695     filesToNames["scmitt-trigger.circuit"] = i18n("Schmitt Trigger");
0696     filesToNames["switch.circuit"] = i18n("Switch");
0697     filesToNames["matrix_display.circuit"] = i18n("Matrix Display Driver");
0698 
0699     int at = 0;
0700 
0701     for (QString category : categories) {
0702         QDir dir(QStandardPaths::locate(QStandardPaths::AppDataLocation, "examples/" + category + "/", QStandardPaths::LocateDirectory));
0703 
0704         // K3PopupMenu * m = static_cast<K3PopupMenu*>(factory()->container( "examples_" + category, this ));
0705         QMenu *m = static_cast<QMenu *>(factory()->container("examples_" + category, this));
0706         if (!m) {
0707             qCWarning(KTL_LOG) << "failed to cast to popup menu: "
0708                        << "examples_" + category;
0709             continue;
0710         }
0711         connect(m, &QMenu::triggered, this, &KTechlab::openExample);
0712 
0713         QStringList files = dir.entryList();
0714         files.removeAll(".");
0715         files.removeAll("..");
0716 
0717         for (QString fileName : files) {
0718             QString name = filesToNames[fileName];
0719             if (name.isEmpty()) {
0720                 name = fileName;
0721                 name.remove(".circuit");
0722                 name.replace("-", " ");
0723                 name.replace("_", " ");
0724 
0725                 // Capitalize the start of each word
0726                 bool prevWasSpace = true;
0727                 for (int i = 0; i < name.length(); ++i) {
0728                     if (prevWasSpace)
0729                         name[i] = name[i].toUpper();
0730                     prevWasSpace = name[i].isSpace();
0731                 }
0732             }
0733 
0734             // m->insertItem( name, at, at ); // 2018.12.02
0735             m->addAction(name)->setData(at);
0736             m_exampleFiles[at] = dir.path() + "/" + fileName;
0737             at++;
0738         }
0739     }
0740 }
0741 
0742 void KTechlab::openExample(QAction *action)
0743 {
0744     int id = action->data().toInt();
0745     DocManager::self()->openURL(QUrl::fromLocalFile(m_exampleFiles[id]));
0746 }
0747 
0748 void KTechlab::slotViewContainerActivated(int /*index*/)
0749 {
0750     QWidget *viewContainer = m_pViewContainerTabWidget->currentWidget();
0751     if (!viewContainer) {
0752         return; // null viewContainer could be selected, when all documents are closed
0753     }
0754     m_pFocusedContainer = dynamic_cast<ViewContainer *>(viewContainer);
0755     m_pFocusedContainer->setFocus();
0756 }
0757 
0758 void KTechlab::slotViewContainerDestroyed(QObject *object)
0759 {
0760     m_viewContainerList.removeAll(static_cast<ViewContainer *>(object));
0761     m_viewContainerList.removeAll(static_cast<ViewContainer *>(nullptr));
0762     slotUpdateTabWidget();
0763 }
0764 
0765 QAction *KTechlab::actionByName(const QString &name) const
0766 {
0767     QAction *action = actionCollection()->action(name);
0768     if (!action)
0769         qCCritical(KTL_LOG) << "No such action: " << name;
0770     return action;
0771 }
0772 
0773 void KTechlab::savePropertiesInConfig(KConfig *conf)
0774 {
0775     // Dumbass KMainWindow - can't handle my width/height correctly. Whoever thought of the "+1" hack anyway?!
0776     // conf->setGroup("UI");
0777     KConfigGroup grUi = conf->group("UI");
0778     grUi.writeEntry("Width", width());
0779     grUi.writeEntry("Height", height());
0780     // grUi.writeEntry( "WinState", KWin::windowInfo( winId(), NET::WMState ).state() );
0781     grUi.writeEntry("WinState", qulonglong(KWindowInfo(winId(), NET::WMState).state()));
0782 
0783 #ifndef NO_GPSIM
0784     SymbolViewer::self()->saveProperties(conf);
0785 #endif
0786 
0787     if (ProjectManager::self()->currentProject()) {
0788         // conf->setGroup("Project");
0789         KConfigGroup grProject = conf->group("Project");
0790         grProject.writePathEntry("Open", ProjectManager::self()->currentProject()->url().toDisplayString(QUrl::PreferLocalFile));
0791     } else {
0792         conf->deleteGroup("Project");
0793     }
0794 
0795     // BEGIN Open Views State
0796     // Remvoe old entries describing the save state - we don't want a horrible mish-mash of saved states
0797     const QStringList groupList = conf->groupList();
0798     const QStringList::const_iterator groupListEnd = groupList.end();
0799     for (QStringList::const_iterator it = groupList.begin(); it != groupListEnd; ++it) {
0800         if ((*it).startsWith("ViewContainer"))
0801             conf->deleteGroup(*it);
0802     }
0803 
0804     uint viewContainerId = 1;
0805     const ViewContainerList::iterator vcEnd = m_viewContainerList.end();
0806     for (ViewContainerList::iterator it = m_viewContainerList.begin(); it != vcEnd; ++it) {
0807         if (!(*it) || !(*it)->canSaveUsefulStateInfo())
0808             continue;
0809 
0810         // To make sure the ViewContainers are restored in the right order, we must create them in alphabetical order,
0811         // as KConfig stores them as such...
0812         const QString id = QString::number(viewContainerId++).rightJustified(4, '0');
0813 
0814         // conf->setGroup( "ViewContainer " + id );
0815         KConfigGroup grViewCont = conf->group("ViewContainer " + id);
0816         //(*it)->saveState(conf);
0817         (*it)->saveState(&grViewCont);
0818     }
0819     // END Open Views State
0820 
0821     KConfigGroup grKateMdi = conf->group("KateMDI");
0822     saveSession(&grKateMdi);
0823     // Piss off KMainWindow
0824     // conf->setGroup("KateMDI");
0825     int scnum = QApplication::desktop()->screenNumber(parentWidget());
0826     //QRect desk = QApplication::desktop()->screenGeometry(scnum);
0827     QList<QScreen*> screenPtrList = QGuiApplication::screens();
0828     QScreen * screenPtr = screenPtrList.value(scnum);
0829     QRect desk(0,0,800,600);
0830     if (screenPtr) {
0831         desk = screenPtr->geometry();
0832     }
0833     grKateMdi.deleteEntry(QString::fromLatin1("Width %1").arg(desk.width()));
0834     grKateMdi.deleteEntry(QString::fromLatin1("Height %1").arg(desk.height()));
0835 
0836     conf->sync();
0837 }
0838 
0839 void KTechlab::readPropertiesInConfig(KConfig *conf)
0840 {
0841     startRestore(conf, "KateMDI");
0842 
0843     m_recentFiles->loadEntries(conf->group("Recent Files"));
0844     m_recentProjects->loadEntries(conf->group("Recent Projects"));
0845 
0846     // BEGIN Restore Open Views
0847     if (KTLConfig::restoreDocumentsOnStartup()) {
0848         // If we have a lot of views open from last time, then opening them will take a long time.
0849         // So we want to enter the qt event loop to finish drawing the window et al before adding the views.
0850         qApp->processEvents();
0851 
0852         const QStringList groupList = conf->groupList();
0853         const QStringList::const_iterator groupListEnd = groupList.end();
0854         for (QStringList::const_iterator it = groupList.begin(); it != groupListEnd; ++it) {
0855             if ((*it).startsWith("ViewContainer")) {
0856                 ViewContainer *viewContainer = new ViewContainer(*it);
0857 
0858                 // conf->setGroup(*it);
0859                 KConfigGroup grIt = conf->group(*it);
0860                 viewContainer->restoreState(&grIt, *it);
0861 
0862                 addWindow(viewContainer);
0863             }
0864         }
0865     }
0866     // END Restore Open Views
0867 
0868     // conf->setGroup("Project");
0869     KConfigGroup grProject = conf->group("Project");
0870     const QString openValue = grProject.readPathEntry("Open", QString());
0871     if (!openValue.isEmpty()) {
0872         ProjectManager::self()->slotOpenProject(QUrl::fromUserInput(openValue));
0873     }
0874 
0875 #ifndef NO_GPSIM
0876     SymbolViewer::self()->readProperties(conf);
0877 #endif
0878 
0879     finishRestore();
0880 
0881     // Dumbass KMainWindow - can't handle my width/height correctly. Whoever thought of the "+1" hack anyway?!
0882     // conf->setGroup("UI");
0883     KConfigGroup grUi = conf->group("UI");
0884     resize(grUi.readEntry("Width", 800), grUi.readEntry("Height", 500));
0885     const quint32 winStateDef = quint32(NET::Max);
0886     const quint32 winState = grUi.readEntry("WinState", winStateDef /* NET::Max */);
0887     KWindowSystem::setState(winId(), NET::States(winState));
0888     // grUi.readEntry( "WinState", (quint32) NET::Max ) );
0889 }
0890 
0891 void KTechlab::dragEnterEvent(QDragEnterEvent *event)
0892 {
0893     // accept uri drops only
0894     event->setAccepted(event->mimeData()->hasUrls());
0895 }
0896 
0897 void KTechlab::dropEvent(QDropEvent *event)
0898 {
0899     // this is a very simplistic implementation of a drop event.  we
0900     // will only accept a dropped URL.  the Qt dnd code can do *much*
0901     // much more, so please read the docs there
0902     const QList<QUrl> urls = event->mimeData()->urls();
0903 
0904     // see if we can decode a URI.. if not, just ignore it
0905     if (!urls.isEmpty()) {
0906         // okay, we have a URI.. process it
0907         const QUrl &url = urls.first();
0908 
0909         // load in the file
0910         load(url);
0911     }
0912 }
0913 
0914 void KTechlab::slotOptionsShowStatusbar()
0915 {
0916     // this is all very cut and paste code for showing/hiding the
0917     // statusbar
0918     if (m_statusbarAction->isChecked())
0919         statusBar()->show();
0920     else
0921         statusBar()->hide();
0922 }
0923 
0924 void KTechlab::slotOptionsConfigureKeys()
0925 {
0926     //     KKeyDialog::configureKeys(actionCollection(), "ktechlabui.rc");
0927     KShortcutsDialog::showDialog(actionCollection(), KShortcutsEditor::LetterShortcutsAllowed, this);
0928 }
0929 
0930 void KTechlab::slotOptionsConfigureToolbars()
0931 {
0932     KEditToolBar *dlg = new KEditToolBar(guiFactory());
0933 
0934     if (dlg->exec()) {
0935         createShellGUI(false);
0936         createShellGUI(true);
0937     }
0938 
0939     delete dlg;
0940 }
0941 
0942 void KTechlab::slotOptionsPreferences()
0943 {
0944     // An instance of your dialog could be already created and could be cached,
0945     // in which case you want to display the cached dialog instead of creating
0946     // another one
0947     if (KConfigDialog::showDialog("settings"))
0948         return;
0949 
0950     // KConfigDialog didn't find an instance of this dialog, so lets create it:
0951     SettingsDlg *dialog = new SettingsDlg(this, "settings", KTLConfig::self());
0952 
0953     // User edited the configuration - update your local copies of the
0954     // configuration data
0955     connect(dialog, &SettingsDlg::settingsChanged, this, &KTechlab::slotUpdateConfiguration);
0956     dialog->show();
0957 }
0958 
0959 void KTechlab::slotUpdateConfiguration()
0960 {
0961     emit configurationChanged();
0962 }
0963 
0964 void KTechlab::slotChangeStatusbar(const QString &text)
0965 {
0966     // Avoid flicker by repeatedly displaying the same message, as QStatusBar does not check for this
0967     if (m_lastStatusBarMessage == text)
0968         return;
0969 
0970     statusBar()->showMessage(text);
0971     m_lastStatusBarMessage = text;
0972 }
0973 
0974 void KTechlab::slotTabContext(const QPoint &pos)
0975 {
0976     QTabBar *tabBar = tabWidget()->tabBar();
0977     QWidget *widget = tabWidget()->widget(tabBar->tabAt(pos));
0978     if (!widget) {
0979         return;
0980     }
0981     const QPoint globalPos = tabBar->mapToGlobal(pos);
0982 
0983     // Shamelessly stolen from KDevelop...
0984 
0985     QMenu *tabMenu = new QMenu;
0986     tabMenu->addSection((dynamic_cast<ViewContainer *>(widget))->windowTitle());
0987 
0988     // Find the document on whose tab the user clicked
0989     m_pContextMenuContainer = nullptr;
0990 
0991     m_viewContainerList.removeAll(static_cast<ViewContainer *>(nullptr));
0992 
0993     const ViewContainerList::iterator vcEnd = m_viewContainerList.end();
0994     for (ViewContainerList::iterator it = m_viewContainerList.begin(); it != vcEnd; ++it) {
0995         ViewContainer *viewContainer = *it;
0996         if (viewContainer == widget) {
0997             m_pContextMenuContainer = viewContainer;
0998 
0999             tabMenu->addAction(QIcon::fromTheme("tab-close"), i18n("Close"))->setData(0);
1000 
1001             View *view = (viewContainer->viewCount() == 1) ? viewContainer->activeView() : nullptr;
1002 
1003             if (view && view->document()->isModified())
1004                 tabMenu->addAction(QIcon::fromTheme("document-save"), i18n("Save"))->setData(1);
1005 
1006             if (view && !view->document()->url().isEmpty())
1007                 tabMenu->addAction(QIcon::fromTheme("view-refresh"), i18n("Reload"))->setData(2);
1008 
1009             if (m_viewContainerList.count() > 1)
1010                 tabMenu->addAction(QIcon::fromTheme("tab-close-other"), i18n("Close All Others"))->setData(4);
1011         }
1012     }
1013 
1014     connect(tabMenu, &QMenu::triggered, this, &KTechlab::slotTabContextActivated);
1015 
1016     tabMenu->exec(globalPos);
1017     delete tabMenu;
1018 }
1019 
1020 void KTechlab::slotTabContextActivated(QAction *action)
1021 {
1022     // Shamelessly stolen from KDevelop...
1023 
1024     if (!m_pContextMenuContainer || !action || action->data().isNull())
1025         return;
1026 
1027     View *view = m_pContextMenuContainer->activeView();
1028     if (!view)
1029         return;
1030     QPointer<Document> document = view->document();
1031 
1032     switch (action->data().toInt()) {
1033     case 0: {
1034         m_pContextMenuContainer->closeViewContainer();
1035         break;
1036     }
1037     case 1:
1038         document->fileSave();
1039         break;
1040     case 2: {
1041         QUrl url = document->url();
1042         if (document->fileClose()) {
1043             delete document;
1044             DocManager::self()->openURL(url);
1045         }
1046         break;
1047     }
1048     case 4: {
1049         const ViewContainerList::iterator vcEnd = m_viewContainerList.end();
1050         for (ViewContainerList::iterator it = m_viewContainerList.begin(); it != vcEnd; ++it) {
1051             ViewContainer *viewContainer = *it;
1052             if (viewContainer && viewContainer != m_pContextMenuContainer) {
1053                 if (!viewContainer->closeViewContainer())
1054                     return;
1055             }
1056         }
1057         break;
1058     }
1059     default:
1060         break;
1061     }
1062 }
1063 
1064 void KTechlab::slotFileNewAssembly()
1065 {
1066     TextDocument *document = DocManager::self()->createTextDocument();
1067     if (document)
1068         document->slotInitLanguage(TextDocument::ct_asm);
1069 }
1070 void KTechlab::slotFileNewMicrobe()
1071 {
1072     TextDocument *document = DocManager::self()->createTextDocument();
1073     if (document)
1074         document->slotInitLanguage(TextDocument::ct_microbe);
1075 }
1076 void KTechlab::slotFileNewC()
1077 {
1078     TextDocument *document = DocManager::self()->createTextDocument();
1079     if (document)
1080         document->slotInitLanguage(TextDocument::ct_c);
1081 }
1082 void KTechlab::slotFileNewCircuit()
1083 {
1084     DocManager::self()->createCircuitDocument();
1085 }
1086 void KTechlab::slotFileNewFlowCode()
1087 {
1088     slotFileNew();
1089 }
1090 void KTechlab::slotFileNewMechanics()
1091 {
1092     DocManager::self()->createMechanicsDocument();
1093 }
1094 
1095 void KTechlab::slotFileNew()
1096 {
1097     NewFileDlg *newFileDlg = new NewFileDlg(this);
1098 
1099     const int accepted = newFileDlg->exec();
1100 
1101     bool addToProject = newFileDlg->addToProject();
1102     int finalType = newFileDlg->fileType();
1103     QString microID = newFileDlg->microID();
1104     int codeType = newFileDlg->codeType();
1105 
1106     delete newFileDlg;
1107     if (accepted != QDialog::Accepted)
1108         return;
1109 
1110     Document *created = nullptr;
1111 
1112     if (finalType == Document::dt_circuit)
1113         created = DocManager::self()->createCircuitDocument();
1114 
1115     else if (finalType == Document::dt_flowcode) {
1116         FlowCodeDocument *fcd = DocManager::self()->createFlowCodeDocument();
1117         fcd->setPicType(microID);
1118         created = fcd;
1119     }
1120 
1121     else if (finalType == Document::dt_mechanics)
1122         created = DocManager::self()->createMechanicsDocument();
1123 
1124     else {
1125         // Presumably a text document
1126         TextDocument *textDocument = DocManager::self()->createTextDocument();
1127 
1128         if (textDocument)
1129             textDocument->slotInitLanguage(TextDocument::CodeType(codeType));
1130 
1131         created = textDocument;
1132     }
1133 
1134     if (created && addToProject)
1135         created->setAddToProjectOnSave(true);
1136 }
1137 
1138 void KTechlab::slotFileOpen()
1139 {
1140     // this slot is called whenever the File->Open menu is selected,
1141     // the Open shortcut is pressed (usually CTRL+O) or the Open toolbar
1142     // button is clicked
1143 
1144     // standard filedialog
1145     const QList<QUrl> urls = getFileURLs();
1146     for (const QUrl &url : urls)
1147         load(url);
1148 }
1149 
1150 void KTechlab::addRecentFile(const QUrl &url)
1151 {
1152     KSharedConfigPtr config = KSharedConfig::openConfig();
1153     m_recentFiles->addUrl(url);
1154     m_recentFiles->saveEntries(config->group("Recent Files"));
1155     config->sync();
1156     emit recentFileAdded(url);
1157 }
1158 
1159 QList<QUrl> KTechlab::getFileURLs(bool allowMultiple)
1160 {
1161     QString filter;
1162     filter = QString(
1163                  "*|%1 (*)\n"
1164                  ";;*.asm *.src *.inc|%2 (*.asm, *.src, *.inc)\n"
1165                  ";;*.hex|%3 (*.hex)\n"
1166                  ";;*.circuit|%4 (*.circuit)\n"
1167                  ";;*.flowcode|FlowCode (*.flowcode)\n"
1168                  ";;*.basic *.microbe|Microbe (*.microbe, *.basic)"
1169                  /*"*.mechanics|Mechanics (*.mechanics)\n"*/)
1170                  .arg(i18n("All Files"))
1171                  .arg(i18n("Assembly Code"))
1172                  .arg(i18n("Intel Hex"))
1173                  .arg(i18n("Circuit"));
1174 
1175     if (allowMultiple)
1176         return QFileDialog::getOpenFileUrls(nullptr, i18n("Open Location"), QUrl(), filter);
1177 
1178     else {
1179         QUrl ret = QFileDialog::getOpenFileUrl(nullptr, i18n("Open Location"), QUrl(), filter);
1180         return {ret};
1181     }
1182 }
1183 
1184 void KTechlab::slotDocModifiedChanged()
1185 {
1186     // BEGIN Set tab icons
1187     const ViewContainerList::iterator vcEnd = m_viewContainerList.end();
1188     for (ViewContainerList::iterator it = m_viewContainerList.begin(); it != vcEnd; ++it) {
1189         ViewContainer *vc = *it;
1190         if (!vc || !vc->activeView() || !vc->activeView()->document())
1191             continue;
1192 
1193         QIcon icon;
1194 
1195         if (vc->activeView()->document()->isModified()) {
1196             icon = QIcon::fromTheme("document-save");
1197         } else {
1198             switch (vc->activeView()->document()->type()) {
1199             case Document::dt_circuit:
1200                 icon = QIcon::fromTheme("application-x-circuit");
1201                 break;
1202 
1203             case Document::dt_flowcode:
1204                 icon = QIcon::fromTheme("application-x-flowcode");
1205                 break;
1206 
1207             case Document::dt_mechanics:
1208                 icon = QIcon::fromTheme("ktechlab_mechanics");
1209                 break;
1210 
1211             case Document::dt_text:
1212                 icon = QIcon::fromTheme("text-x-generic");
1213                 break;
1214 
1215             case Document::dt_pinMapEditor:
1216                 break;
1217 
1218             case Document::dt_none:
1219                 icon = QIcon::fromTheme("application-x-zerosize");
1220                 break;
1221             }
1222         }
1223 
1224         tabWidget()->setTabIcon(tabWidget()->indexOf(vc), icon);
1225     }
1226     // END Set tab icons
1227 }
1228 
1229 void KTechlab::requestUpdateCaptions()
1230 {
1231     m_pUpdateCaptionsTimer->setSingleShot(true);
1232     m_pUpdateCaptionsTimer->start(0 /*, true */);
1233 }
1234 
1235 void KTechlab::slotUpdateCaptions()
1236 {
1237     // BEGIN Set KTechlab caption
1238     Document *document = DocManager::self()->getFocusedDocument();
1239     QString newCaption;
1240     if (document) {
1241         QUrl url = document->url();
1242         if (url.isEmpty())
1243             newCaption = document->caption();
1244         else {
1245             if (url.isLocalFile() && !url.hasFragment() && !url.hasQuery())
1246                 newCaption = url.toLocalFile();
1247             else
1248                 newCaption = url.toDisplayString(QUrl::PreferLocalFile);
1249         }
1250     } else
1251         newCaption = "";
1252 
1253     if (newCaption != windowTitle().remove(" - KTechlab")) {
1254         setWindowTitle(newCaption);
1255     }
1256     // END Set KTechlab caption
1257 
1258     // BEGIN Set tab captions
1259     emit needUpdateCaptions();
1260 
1261     if (document && document->activeView() && document->activeView()->viewContainer()) {
1262         document->activeView()->viewContainer()->updateCaption();
1263     }
1264     // END Set tab captions
1265 }
1266 
1267 void KTechlab::slotDocUndoRedoChanged()
1268 {
1269     Document *document = DocManager::self()->getFocusedDocument();
1270     if (!document)
1271         return;
1272 
1273     action("edit_undo")->setEnabled(document->isUndoAvailable());
1274     action("edit_redo")->setEnabled(document->isRedoAvailable());
1275 }
1276 
1277 void KTechlab::slotFileSave()
1278 {
1279     Document *document = DocManager::self()->getFocusedDocument();
1280     if (document)
1281         document->fileSave();
1282 }
1283 
1284 void KTechlab::slotFileSaveAs()
1285 {
1286     Document *document = DocManager::self()->getFocusedDocument();
1287     if (document)
1288         document->fileSaveAs();
1289 }
1290 
1291 void KTechlab::slotFilePrint()
1292 {
1293     Document *document = DocManager::self()->getFocusedDocument();
1294     if (document)
1295         document->print();
1296 }
1297 
1298 bool KTechlab::queryClose()
1299 {
1300     KSharedConfigPtr cfgPtr = KSharedConfig::openConfig();
1301     // saveProperties( KGlobal::config() );
1302     savePropertiesInConfig(cfgPtr.data());
1303 
1304     if (DocManager::self()->closeAll() && ProjectManager::self()->slotCloseProject()) {
1305         // Make ourself "deleted"
1306         m_pSelf = nullptr;
1307         return true;
1308     }
1309 
1310     return false;
1311 }
1312 
1313 void KTechlab::slotFileQuit()
1314 {
1315     // close the first window, the list makes the next one the first again.
1316     // This ensures that queryClose() is called on each window to ask for closing
1317     // KMainWindow* w;
1318     if (!memberList().isEmpty()) {
1319         QList<KMainWindow *> mainWindowList = memberList();
1320         // for( w=memberList->first(); w!=0; w=memberList->next() )
1321         for (QList<KMainWindow *>::iterator itWnd = mainWindowList.begin(); itWnd != mainWindowList.end(); ++itWnd) {
1322             // only close the window if the closeEvent is accepted. If the user presses Cancel on the saveModified() dialog,
1323             // the window and the application stay open.
1324             // if( !w->close() ) break;
1325             if (!(*itWnd)->close())
1326                 break;
1327         }
1328     }
1329 
1330     slotChangeStatusbar(i18n("Exiting..."));
1331 }
1332 
1333 void KTechlab::slotEditUndo()
1334 {
1335     Document *document = DocManager::self()->getFocusedDocument();
1336     if (document)
1337         document->undo();
1338 }
1339 
1340 void KTechlab::slotEditRedo()
1341 {
1342     Document *document = DocManager::self()->getFocusedDocument();
1343     if (document)
1344         document->redo();
1345 }
1346 
1347 void KTechlab::slotEditCut()
1348 {
1349     Document *document = DocManager::self()->getFocusedDocument();
1350     if (document)
1351         document->cut();
1352 }
1353 
1354 void KTechlab::slotEditCopy()
1355 {
1356     Document *document = DocManager::self()->getFocusedDocument();
1357     if (document)
1358         document->copy();
1359 }
1360 
1361 void KTechlab::slotEditPaste()
1362 {
1363     Document *document = DocManager::self()->getFocusedDocument();
1364     if (document)
1365         document->paste();
1366 }
1367 
1368 void KTechlab::slotViewContainerClose()
1369 {
1370     if (m_pFocusedContainer)
1371         m_pFocusedContainer->closeViewContainer();
1372 }
1373 void KTechlab::slotViewClose()
1374 {
1375     View *view = DocManager::self()->getFocusedView();
1376     if (view)
1377         view->closeView();
1378 }
1379 void KTechlab::slotViewSplitLeftRight()
1380 {
1381     View *view = DocManager::self()->getFocusedView();
1382     if (!view)
1383         return;
1384     ViewContainer *vc = view->viewContainer();
1385     uint vaId = vc->createViewArea(view->viewAreaId(), ViewArea::Right, true);
1386     //  view->document()->createView( vc, vaId );
1387     (void)vaId;
1388 }
1389 void KTechlab::slotViewSplitTopBottom()
1390 {
1391     View *view = DocManager::self()->getFocusedView();
1392     if (!view)
1393         return;
1394     ViewContainer *vc = view->viewContainer();
1395     uint vaId = vc->createViewArea(view->viewAreaId(), ViewArea::Bottom, true);
1396     //  view->document()->createView( vc, vaId );
1397     (void)vaId;
1398 }
1399 
1400 #include "moc_ktechlab.cpp"