File indexing completed on 2025-04-27 08:28:37
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"