Warning, file /sdk/ktechlab/src/ktechlab.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

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