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 }