File indexing completed on 2024-03-24 15:17:12
0001 /* 0002 SPDX-FileCopyrightText: 2012 Jasem Mutlaq 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "guimanager.h" 0008 0009 #include "clientmanager.h" 0010 #include "deviceinfo.h" 0011 #include "indidevice.h" 0012 #include "kstars.h" 0013 #include "Options.h" 0014 #include "fitsviewer/fitsviewer.h" 0015 #include "ksnotification.h" 0016 0017 #include <KActionCollection> 0018 #include <KMessageBox> 0019 0020 #include <QApplication> 0021 #include <QSplitter> 0022 #include <QTextEdit> 0023 #include <QPushButton> 0024 #include <QThread> 0025 #include <QAction> 0026 0027 extern const char *libindi_strings_context; 0028 0029 GUIManager *GUIManager::_GUIManager = nullptr; 0030 0031 GUIManager *GUIManager::Instance() 0032 { 0033 if (_GUIManager == nullptr) 0034 { 0035 _GUIManager = new GUIManager( 0036 Options::independentWindowINDI() ? nullptr : KStars::Instance()); 0037 } 0038 0039 return _GUIManager; 0040 } 0041 0042 void GUIManager::release() 0043 { 0044 delete _GUIManager; 0045 _GUIManager = nullptr; 0046 } 0047 0048 GUIManager::GUIManager(QWidget *parent) : QWidget(parent, Qt::Window) 0049 { 0050 #ifdef Q_OS_OSX 0051 if (Options::independentWindowINDI()) 0052 setWindowFlags(Qt::Window); 0053 else 0054 { 0055 setWindowFlags(Qt::Window | Qt::WindowStaysOnTopHint); 0056 connect(QApplication::instance(), 0057 SIGNAL(applicationStateChanged(Qt::ApplicationState)), this, 0058 SLOT(changeAlwaysOnTop(Qt::ApplicationState))); 0059 } 0060 #endif 0061 0062 mainLayout = new QVBoxLayout(this); 0063 mainLayout->setContentsMargins(10, 10, 10, 10); 0064 mainLayout->setSpacing(10); 0065 0066 mainTabWidget = new QTabWidget(this); 0067 0068 mainLayout->addWidget(mainTabWidget); 0069 0070 setWindowIcon(QIcon::fromTheme("kstars_indi")); 0071 0072 setWindowTitle(i18nc("@title:window", "INDI Control Panel")); 0073 setAttribute(Qt::WA_ShowModal, false); 0074 0075 clearB = new QPushButton(i18n("Clear")); 0076 closeB = new QPushButton(i18n("Close")); 0077 0078 QHBoxLayout *buttonLayout = new QHBoxLayout; 0079 buttonLayout->insertStretch(0); 0080 buttonLayout->addWidget(clearB, 0, Qt::AlignRight); 0081 buttonLayout->addWidget(closeB, 0, Qt::AlignRight); 0082 0083 mainLayout->addLayout(buttonLayout); 0084 0085 connect(closeB, SIGNAL(clicked()), this, SLOT(close())); 0086 connect(clearB, SIGNAL(clicked()), this, SLOT(clearLog())); 0087 0088 resize(Options::iNDIWindowWidth(), Options::iNDIWindowHeight()); 0089 0090 QAction *showINDIPanel = 0091 KStars::Instance()->actionCollection()->action("show_control_panel"); 0092 showINDIPanel->setChecked(Options::showINDIwindowInitially()); 0093 } 0094 0095 GUIManager::~GUIManager() 0096 { 0097 for (auto oneClient : qAsConst(clients)) 0098 oneClient->disconnect(this); 0099 } 0100 0101 void GUIManager::changeAlwaysOnTop(Qt::ApplicationState state) 0102 { 0103 if (isVisible()) 0104 { 0105 if (state == Qt::ApplicationActive) 0106 setWindowFlags(Qt::Window | Qt::WindowStaysOnTopHint); 0107 else 0108 setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint); 0109 show(); 0110 } 0111 } 0112 0113 void GUIManager::closeEvent(QCloseEvent * /*event*/) 0114 { 0115 KStars *ks = KStars::Instance(); 0116 0117 if (ks) 0118 { 0119 QAction *showINDIPanel = 0120 KStars::Instance()->actionCollection()->action("show_control_panel"); 0121 showINDIPanel->setChecked(false); 0122 } 0123 0124 Options::setINDIWindowWidth(width()); 0125 Options::setINDIWindowHeight(height()); 0126 } 0127 0128 void GUIManager::hideEvent(QHideEvent * /*event*/) 0129 { 0130 KStars *ks = KStars::Instance(); 0131 0132 if (ks) 0133 { 0134 QAction *a = KStars::Instance()->actionCollection()->action("show_control_panel"); 0135 a->setChecked(false); 0136 } 0137 } 0138 0139 void GUIManager::showEvent(QShowEvent * /*event*/) 0140 { 0141 QAction *a = KStars::Instance()->actionCollection()->action("show_control_panel"); 0142 a->setChecked(true); 0143 } 0144 0145 /********************************************************************* 0146 ** Traverse the drivers list, check for updated drivers and take 0147 ** appropriate action 0148 *********************************************************************/ 0149 void GUIManager::updateStatus(bool toggle_behavior) 0150 { 0151 QAction *showINDIPanel = 0152 KStars::Instance()->actionCollection()->action("show_control_panel"); 0153 0154 if (guidevices.count() == 0) 0155 { 0156 KSNotification::error(i18n("No INDI devices currently running. To run devices, " 0157 "please select devices from the " 0158 "Device Manager in the devices menu.")); 0159 showINDIPanel->setChecked(false); 0160 showINDIPanel->setEnabled(false); 0161 return; 0162 } 0163 0164 // enable the INDI button if at least one device has been recognized 0165 showINDIPanel->setEnabled(getDevices().size() > 0); 0166 0167 if (toggle_behavior) 0168 showINDIPanel->setChecked(! showINDIPanel->isChecked()); 0169 0170 if (showINDIPanel->isChecked()) 0171 { 0172 raise(); 0173 activateWindow(); 0174 showNormal(); 0175 } 0176 else 0177 { 0178 hide(); 0179 } 0180 } 0181 0182 INDI_D *GUIManager::findGUIDevice(const QString &deviceName) 0183 { 0184 for (auto oneGUIDevice : guidevices) 0185 { 0186 if (oneGUIDevice->name() == deviceName) 0187 return oneGUIDevice; 0188 } 0189 0190 return nullptr; 0191 } 0192 0193 void GUIManager::clearLog() 0194 { 0195 INDI_D *dev = findGUIDevice( 0196 mainTabWidget->tabText(mainTabWidget->currentIndex()).remove(QChar('&'))); 0197 0198 if (dev) 0199 dev->clearMessageLog(); 0200 } 0201 0202 void GUIManager::addClient(ClientManager *cm) 0203 { 0204 clients.append(cm); 0205 connect(cm, &ClientManager::newINDIDevice, this, &GUIManager::buildDevice); 0206 connect(cm, &ClientManager::removeINDIDevice, this, &GUIManager::removeDevice); 0207 } 0208 0209 void GUIManager::removeClient(ClientManager *cm) 0210 { 0211 clients.removeOne(cm); 0212 0213 QMutableListIterator<INDI_D *> it(guidevices); 0214 while (it.hasNext()) 0215 { 0216 INDI_D *gdv = it.next(); 0217 0218 if (gdv->getClientManager() == cm) 0219 { 0220 for (int i = 0; i < mainTabWidget->count(); i++) 0221 { 0222 if (mainTabWidget->tabText(i).remove('&') == QString(gdv->name())) 0223 { 0224 mainTabWidget->removeTab(i); 0225 break; 0226 } 0227 } 0228 0229 it.remove(); 0230 gdv->deleteLater(); 0231 } 0232 } 0233 0234 if (clients.size() == 0) 0235 hide(); 0236 } 0237 0238 void GUIManager::removeDevice(const QString &name) 0239 { 0240 INDI_D *dp = findGUIDevice(name); 0241 0242 if (dp == nullptr) 0243 return; 0244 0245 if (dp->getClientManager()) 0246 dp->getClientManager()->disconnect(dp); 0247 dp->disconnect(); 0248 0249 // Hack to give mainTabWidget sometime to remove its item as these calls are coming from a different thread 0250 // the clientmanager thread. Sometimes removeTab() requires sometime to be removed properly and hence the wait. 0251 if (mainTabWidget->count() != guidevices.count()) 0252 QThread::msleep(100); 0253 0254 for (int i = 0; i < mainTabWidget->count(); i++) 0255 { 0256 if (mainTabWidget->tabText(i).remove('&') == name) 0257 { 0258 mainTabWidget->removeTab(i); 0259 break; 0260 } 0261 } 0262 0263 guidevices.removeOne(dp); 0264 dp->deleteLater(); 0265 0266 if (guidevices.isEmpty()) 0267 { 0268 QAction *showINDIPanel = 0269 KStars::Instance()->actionCollection()->action("show_control_panel"); 0270 showINDIPanel->setEnabled(false); 0271 } 0272 } 0273 0274 void GUIManager::buildDevice(DeviceInfo *di) 0275 { 0276 ClientManager *cm = qobject_cast<ClientManager *>(sender()); 0277 Q_ASSERT_X(cm, __FUNCTION__, "Client manager is not valid."); 0278 0279 INDI_D *gdm = new INDI_D(mainTabWidget, di->getBaseDevice(), cm); 0280 0281 connect(cm, &ClientManager::newINDIProperty, gdm, &INDI_D::buildProperty); 0282 connect(cm, &ClientManager::removeINDIProperty, gdm, &INDI_D::removeProperty); 0283 connect(cm, &ClientManager::updateINDIProperty, gdm, &INDI_D::updateProperty); 0284 connect(cm, &ClientManager::newINDIMessage, gdm, &INDI_D::updateMessageLog); 0285 0286 // Build existing properties. 0287 for (auto &oneProperty : di->getBaseDevice().getProperties()) 0288 gdm->buildProperty(oneProperty); 0289 0290 auto deviceName = di->getDeviceName(); 0291 auto index = mainTabWidget->count(); 0292 for (int i = 0; i < mainTabWidget->count(); i++) 0293 { 0294 if (mainTabWidget->tabText(i) > deviceName) 0295 { 0296 index = i; 0297 break; 0298 } 0299 } 0300 0301 mainTabWidget->insertTab(index, gdm, di->getDeviceName()); 0302 guidevices.insert(index, gdm); 0303 0304 updateStatus(false); 0305 }