// TODO: hdr #include "lts_global_wait_dialog.h" #include "lts_progress_dialog_helper.h" #include "lts_overlay_loading.h" #include "lts_dialog.h" #include "lts_frameless_dialog.h" #include #include //#include #ifdef Q_OS_UNIX #include // backtrace(..) #include // dladdr(..) #include // demangle #endif namespace LtsWidgets { LtsGlobalWaitDialog::~LtsGlobalWaitDialog() { delete m_progressDialog; } LtsGlobalWaitDialog::LtsGlobalWaitDialog() : m_waitingCount(0) , m_visible(true) , m_progressDialog(new LtsProgressDialogHelper(nullptr)) , m_hideTimer(new QTimer(this)) // avoids blinking windows by waiting a little until the wait window will be hidden { m_hideTimer->setSingleShot(true); m_hideTimer->setInterval(500); connect(m_hideTimer, &QTimer::timeout, this, &LtsGlobalWaitDialog::hideIt); } void LtsGlobalWaitDialog::unarm() { m_armed = false; hideIt(); // force hide it (don't worry, does nothing if now shown) } void LtsGlobalWaitDialog::rearm() { m_armed = true; setVisible(true); // show again (if some component is still waiting) } void LtsGlobalWaitDialog::setVisible(bool visible) { if (m_waitingCount > 0 && visible) showIt(); if (m_waitingCount < 1 && !visible) hideIt(); } void LtsGlobalWaitDialog::showIt() { if (m_progressDialog->isShown()) return; m_hideTimer->stop(); if (m_armed) { m_progressDialog->slotShowWaitForReplyWindow(); } } void LtsGlobalWaitDialog::hideIt() { if (!m_progressDialog->isShown()) return; m_hideTimer->stop(); m_progressDialog->slotStopWaitForReplyWindow(); } void bt() { #ifdef Q_OS_UNIX void *callstack[128]; const int nMaxFrames = sizeof(callstack) / sizeof(callstack[0]); int nFrames = backtrace(callstack, nMaxFrames); char **symbols = backtrace_symbols(callstack, nFrames); for (int i = 1; i < nFrames; i++) { QString address(QStringLiteral("0x%1").arg(QString::number(*(quintptr *)callstack[i], 16))); QString function; int offset = -1; // aka. invalid Dl_info info; if (dladdr(callstack[i], &info) && info.dli_sname) { char *demangled = nullptr; int status = -1; if (info.dli_sname[0] == '_') demangled = abi::__cxa_demangle(info.dli_sname, nullptr, 0, &status); function = QString::fromUtf8(status == 0 ? demangled : info.dli_sname == 0 ? symbols[i] : info.dli_sname); offset = (char *)callstack[i] - (char *)info.dli_saddr; free(demangled); } else { function = QString::fromUtf8(symbols[i]); } //qWarning() << "frame" << i << address << function << offset; } free(symbols); // if (nFrames == nMaxFrames) // qWarning() << "[BACKTRACE TRUNCATED]"; #endif } void LtsGlobalWaitDialog::wait(QWidget *parent, const QString &customText) { m_progressDialog->setParentWidget(parent); if (m_waitingCount < 1 && m_visible) // nothing is waiting showIt(); setCustomText(customText); m_waitingCount++; // count even if not visible ! //qWarning() << "LtsGlobalWaitDialog ++" << m_waitingCount; //bt(); } void LtsGlobalWaitDialog::setCustomText(const QString &customText) { m_progressDialog->setCustomText(customText); } void LtsGlobalWaitDialog::done() { if (m_waitingCount == 0) { //qWarning() << "LtsGlobalWaitDialog: called done() while there is nothing waiting!"; m_hideTimer->start(); return; } Q_ASSERT(m_waitingCount > 0); m_waitingCount--; //qWarning() << "LtsGlobalWaitDialog --" << m_waitingCount; if (m_waitingCount == 0) // no more waiting classes m_hideTimer->start(); } static LtsGlobalWaitDialog *s_gwdInstance = nullptr; void LtsGlobalWaitDialog::cleanupGwdInstance() { delete s_gwdInstance; s_gwdInstance = nullptr; } LtsGlobalWaitDialog *LtsGlobalWaitDialog::getInstance() { if (!s_gwdInstance) { s_gwdInstance = new LtsGlobalWaitDialog; qAddPostRoutine(&LtsGlobalWaitDialog::cleanupGwdInstance); } return s_gwdInstance; } } // namespace LtsWidgets /// Waiter Waiter::Waiter(const QString &customText) { LtsWidgets::LtsGlobalWaitDialog::getInstance()->wait(nullptr, customText); } Waiter::Waiter(LtsWidgets::LtsDialog *parent, const QString &customText) : m_parent(parent) { m_oldClosableState = m_parent->isClosable(); m_parent->setClosable(false); m_parent->setCurrentlyBlocking(true); auto *wait = new LtsWidgets::LtsOverlayLoading; wait->setText(customText.isEmpty() ? QObject::tr("Waiting for server reply...") : customText); m_parent->setOverlay(wait); m_loadingOverlay = wait; } Waiter::~Waiter() { if (m_loadingOverlay == nullptr) { LtsWidgets::LtsGlobalWaitDialog::getInstance()->done(); return; } m_parent->setClosable(m_oldClosableState); m_parent->setCurrentlyBlocking(false); m_loadingOverlay->deleteLater(); }