//Author: Timothy Pearson , (C) 2012 //Copyright: See COPYING file that comes with this distribution // TDE MDI interface based on a (passable) tutorial by Andrea Bergia et al. #include "remotemdi.h" #include using namespace std; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "views/instrumentview.h" #include "dialogs/selectserverdlg.h" RemoteMDI::RemoteMDI() : KMdiMainFrm(0, "RemoteMDI", KMdi::ChildframeMode), m_children(0), m_rsvSvrSocket(NULL), connToServerConnecting(false), connToServerState(-1), connToServerTimeoutTimer(NULL) { setXMLFile("remotelabui.rc"); setIcon(SmallIcon("remote_laboratory_client")); // Create some actions KStdAction::close(this, SLOT(closeCurrent()), actionCollection()); KStdAction::quit(this, SLOT(close()), actionCollection()); KActionCollection *const ac = actionCollection(); setStandardToolBarMenuEnabled(true); KStdAction::quit(TQT_TQOBJECT(this), TQT_SLOT(close()), ac); KStdAction::configureToolbars(TQT_TQOBJECT(this), TQT_SLOT(configToolbars()), ac); KStdAction::keyBindings(TQT_TQOBJECT(this), TQT_SLOT(configKeys()), ac); connect_action = new KAction(i18n("Connect to Server"), "connect_creating", KShortcut(), TQT_TQOBJECT(this), TQT_SLOT(connectToServer()), ac, "connect_server"); disconnect_action = new KAction(i18n("Disconnect from Server"), "connect_no", KShortcut(), TQT_TQOBJECT(this), TQT_SLOT(disconnectFromServer()), ac, "disconnect_server"); // Add Window menu if ( !isFakingSDIApplication() ) { menuBar()->insertItem(i18n("&Window"), windowMenu()); } setMenuForSDIModeSysButtons(menuBar()); createGUI( 0 ); // When we change view, change the status bar text connect(this, SIGNAL(viewActivated(KMdiChildView*)), this, SLOT(currentChanged(KMdiChildView*))); ac->setHighlightingEnabled(true); connect(ac, TQT_SIGNAL(actionStatusText(const TQString&)), this, TQT_SLOT(updateStatusBarMainMessage(const TQString&) )); connect(ac, TQT_SIGNAL(clearStatusText()), statusBar(), TQT_SLOT(clear())); // Create the status bar updateStatusBarMainMessage(i18n("No active instruments")); processActions(); processLockouts(); } RemoteMDI::~RemoteMDI() { while (m_pCurrentWindow) { closeCurrent(); } if (m_rsvSvrSocket) { m_rsvSvrSocket->clearPendingData(); m_rsvSvrSocket->close(); delete m_rsvSvrSocket; m_rsvSvrSocket = NULL; } } void RemoteMDI::updateStatusBarMessage() { TQString windowStatusBarMessage; if (m_pCurrentWindow) { windowStatusBarMessage = m_windowStatusBarMapping[TQT_TQOBJECT(m_pCurrentWindow)]; } KStatusBar* sb = statusBar(); if (sb) { sb->message(m_mainStatusBarMessage + ((windowStatusBarMessage != "")?" [" + i18n("Instrument") + ": " + windowStatusBarMessage + "]":"")); } } void RemoteMDI::updateStatusBarMainMessage(const TQString& message) { m_mainStatusBarMessage = message; updateStatusBarMessage(); } void RemoteMDI::updateStatusBarWindowMessage(const TQString& message, const TQObject* window) { const TQObject* windowObject = window; if (!windowObject) { windowObject = sender(); } if (windowObject) { m_windowStatusBarMapping[windowObject] = message; } updateStatusBarMessage(); } void RemoteMDI::resizeEvent(TQResizeEvent *e) { KMdiMainFrm::resizeEvent(e); setSysButtonsAtMenuPosition(); } void RemoteMDI::processActions() { // Add dynamic actions KActionCollection *const ac = actionCollection(); KAction* action; ServiceType st; for (ServiceList::Iterator it(m_activeStation.services.begin()); it != m_activeStation.services.end(); ++it) { st = *it; action = new KAction(i18n("Launch")+" "+st.name, st.clientLibrary, KShortcut(), TQT_TQOBJECT(this), TQT_SLOT(startModule()), ac, st.clientLibrary.ascii()); m_instrumentActionList.append(action); } plugActionList("instrumentMenu_actionlist", m_instrumentActionList); plugActionList("instrumentToolBar_actionlist", m_instrumentActionList); } void RemoteMDI::startModule() { const KAction* sendingAction = dynamic_cast(sender()); if (sendingAction) { bool serviceFound = false; ServiceType st; for (ServiceList::Iterator it(m_activeStation.services.begin()); it != m_activeStation.services.end(); ++it) { st = *it; if (st.clientLibrary == sendingAction->name()) { serviceFound = true; break; } } if (!serviceFound) { KMessageBox::error(this, i18n("The active laboratory workspace does not support the requested service"), i18n("Service Unavailable")); return; } RemoteLab::InstrumentView* view = new RemoteLab::InstrumentView(st.clientLibrary, st.name, (mdiMode() == KMdi::ToplevelMode) ? 0 : this); view->setName(st.clientLibrary.ascii()); connect(view, SIGNAL(statusMessageSet(const TQString&)), this, SLOT(updateStatusBarWindowMessage(const TQString&))); if (st.singleInstance) { const_cast(sendingAction)->setEnabled(false); } openNewWindow(view); if (m_serverHost != "") { view->connectServer(m_serverHost); } } } void RemoteMDI::finishConnectingToServer() { if (!m_rsvSvrSocket) { connToServerState = -1; connToServerConnecting = false; processLockouts(); return; } if (connToServerConnecting) { switch(connToServerState) { case 0: if (!connToServerTimeoutTimer) { connToServerTimeoutTimer = new TQTimer; connToServerTimeoutTimer->start(5000, TRUE); } if ((m_rsvSvrSocket->state() == TQSocket::Connecting) || (m_rsvSvrSocket->state() == TQSocket::HostLookup)) { if (!connToServerTimeoutTimer->isActive()) { connToServerState = -3; connToServerConnecting = false; disconnectFromServer(); KMessageBox::error(this, i18n("Unable to establish connection to remote server"), i18n("Connection Failed")); } } else { if (m_rsvSvrSocket->state() == TQSocket::Connected) { printf("[DEBUG] Initial connection established...\n\r"); fflush(stdout); m_rsvSvrSocket->setDataTimeout(5000); m_rsvSvrSocket->setUsingKerberos(true); connToServerState = 1; } else { connToServerState = -1; connToServerConnecting = false; disconnectFromServer(); KMessageBox::error(this, i18n("Unable to establish connection to remote server"), i18n("Connection Failed")); } } break; case 1: if (m_rsvSvrSocket->kerberosStatus() == TDEKerberosClientSocket::KerberosInitializing) { // Do nothing } else { if (m_rsvSvrSocket->kerberosStatus() != TDEKerberosClientSocket::KerberosInUse) { connToServerState = -1; connToServerConnecting = false; disconnectFromServer(); KMessageBox::error(this, i18n("Unable to establish Kerberos protocol with remote server

Please verify that you currently hold a valid Kerberos ticket"), i18n("Connection Failed")); } else { connect(m_rsvSvrSocket, SIGNAL(readyRead()), m_rsvSvrSocket, SLOT(processPendingData())); m_rsvSvrSocket->processPendingData(); connToServerState = 2; } } break; case 2: // Connection established! // Read magic number and proto version from server TQDataStream* ds = new TQDataStream(m_rsvSvrSocket); ds->setPrintableData(true); while (!m_rsvSvrSocket->canReadFrame()) { tqApp->processEvents(); if (!m_rsvSvrSocket) { return; } } TQ_UINT32 magicnum; TQ_UINT32 protover; *ds >> magicnum; *ds >> protover; m_rsvSvrSocket->clearFrameTail(); printf("[DEBUG] Got magic number %d and protocol version %d\n\r", magicnum, protover); fflush(stdout); delete ds; if ((magicnum == MAGIC_NUMBER) && (protover == PROTOCOL_VERSION)) { disconnect_action->setEnabled(true); promptForStationType(); } else { disconnectFromServer(); KMessageBox::error(this, i18n("The remote server is not compatible with this client"), i18n("Connection Failed")); } connToServerState = 3; connToServerConnecting = false; processLockouts(); break; } TQTimer::singleShot(0, this, SLOT(finishConnectingToServer())); } } void RemoteMDI::connectToServer() { if (m_rsvSvrSocket) { if (m_rsvSvrSocket->state() != TQSocket::Idle) { printf("[DEBUG] Not connecting because the socket is still in state %d\n\r", m_rsvSvrSocket->state()); fflush(stdout); return; } } connect_action->setEnabled(false); disconnect_action->setEnabled(true); // Connect to the central reservation/control server if (!m_rsvSvrSocket) { m_rsvSvrSocket = new TDEKerberosClientSocket(this); connect(m_rsvSvrSocket, SIGNAL(connectionClosed()), this, SLOT(connectionClosedHandler())); connect(m_rsvSvrSocket, TQT_SIGNAL(statusMessageUpdated(const TQString&)), this, TQT_SLOT(updateStatusBarMainMessage(const TQString&) )); } m_rsvSvrSocket->setServiceName("remotefpga"); if (m_serverHost != "") { m_rsvSvrSocket->setServerFQDN(m_serverHost); m_rsvSvrSocket->connectToHost(m_serverHost, 4004); // Finish connecting when appropriate connToServerState = 0; connToServerConnecting = true; TQTimer::singleShot(0, this, SLOT(finishConnectingToServer())); } } void RemoteMDI::promptForStationType() { if (!m_rsvSvrSocket) { return; } if (m_rsvSvrSocket->state() != TQSocket::Connected) { return; } TQDataStream ds(m_rsvSvrSocket); ds.setPrintableData(true); // Request list of laboratory stations StationList slist; ds << TQString("LIST"); m_rsvSvrSocket->writeEndOfFrame(); while (!m_rsvSvrSocket->canReadFrame()) { tqApp->processEvents(); if (!m_rsvSvrSocket) { return; } } ds >> slist; m_rsvSvrSocket->clearFrameTail(); printf("[RAJA DEBUG 200.2] Got list of stations, count is %d\n\r", slist.count()); fflush(stdout); SelectServerDialog select(this, 0, slist); const int ret = select.exec(); if (ret == KDialog::Accepted) { TQString result; ds << TQString("BIND"); m_rsvSvrSocket->writeEndOfFrame(); ds << select.m_selectedStation; m_rsvSvrSocket->writeEndOfFrame(); while (!m_rsvSvrSocket->canReadFrame()) { tqApp->processEvents(); if (!m_rsvSvrSocket) { return; } } ds >> result; m_rsvSvrSocket->clearFrameTail(); printf("[RAJA DEBUG 100.0] '%s'\n\r", result.ascii()); fflush(stdout); if (result == "OK") { // Success! m_activeStation = select.m_selectedStation; processActions(); } else if (result == "ERRUNAVAL") { KMessageBox::error(this, i18n("No stations of the specified type are currently available

Please try again later"), i18n("Insufficient Laboratory Resources")); disconnectFromServer(); } else if (result == "ERRPREVCN") { KMessageBox::error(this, i18n("You are already connected to a laboratory station

Please disconnect and try again"), i18n("Multiple Connections Detected")); disconnectFromServer(); } else { KMessageBox::error(this, i18n("Unknown server error

Please reconnect and try again"), i18n("Internal Error")); disconnectFromServer(); } } else { disconnectFromServer(); } } void RemoteMDI::disconnectFromServer() { connect_action->setEnabled(false); disconnect_action->setEnabled(false); m_instrumentActionList.clear(); unplugActionList("instrumentMenu_actionlist"); unplugActionList("instrumentToolBar_actionlist"); // Close all windows closeAllViews(); if (m_rsvSvrSocket) { m_rsvSvrSocket->clearPendingData(); m_rsvSvrSocket->close(); delete m_rsvSvrSocket; m_rsvSvrSocket = NULL; } connect_action->setEnabled(true); processLockouts(); } void RemoteMDI::connectionClosedHandler() { disconnectFromServer(); KMessageBox::error(this, i18n("The remote server has closed the connection"), i18n("Connection Terminated")); } void RemoteMDI::processLockouts() { bool connected = false; if (m_rsvSvrSocket) { connected = ((m_rsvSvrSocket->state() == TQSocket::Connected) && (connToServerConnecting == false) && (connToServerState > 0)); } printf("[RAJA DEBUG 600.0] connected: %d\n\r", connected); fflush(stdout); connect_action->setEnabled(!connected); disconnect_action->setEnabled(connected); for (TQPtrList::Iterator it(m_instrumentActionList.begin()); it != m_instrumentActionList.end(); ++it) { (*it)->setEnabled(connected); } } void RemoteMDI::configToolbars() { KEditToolbar dialog(factory(), this); dialog.showButtonApply(false); if (dialog.exec()) { applyMainWindowSettings(kapp->config(), "window"); } } void RemoteMDI::configKeys() { KKeyDialog::configure(actionCollection(), this); } void RemoteMDI::setServerHost(TQString server) { m_serverHost = server; } void RemoteMDI::openNewWindow(KMdiChildView *view) { // Add a child view m_children++; // The child view will be our child only if we aren't in Toplevel mode if (!view) { view = new KMdiChildView(i18n("View %1").arg(m_children), (mdiMode() == KMdi::ToplevelMode) ? 0 : this); } (new TQHBoxLayout(view))->setAutoAdd( true ); // Add to the MDI and set as current if (mdiMode() == KMdi::ToplevelMode) { addWindow(view, KMdi::Detach); } else { addWindow(view); } currentChanged(view); // Handle termination connect(view, SIGNAL(childWindowCloseRequest(KMdiChildView*)), this, SLOT(childClosed(KMdiChildView*))); } void RemoteMDI::childWindowCloseRequest(KMdiChildView *pWnd) { RemoteLab::InstrumentView* iview = dynamic_cast(pWnd); if (iview) { // Give the child a chance to finish what it was doing and exit cleanly (i.e. without crashing!) iview->closeConnections(); iview->hide(); KMdiMainFrm::childWindowCloseRequest(pWnd); } } void RemoteMDI::currentChanged(KMdiChildView *current) { RemoteLab::InstrumentView* view = dynamic_cast(current); // Plug/unplug menus if (view) { unplugActionList("selectedInstrument_actionlist"); plugActionList("selectedInstrument_actionlist", view->menuActionList()); } // Update status bar and list box updateStatusBarMainMessage(i18n("Instrument %1 activated").arg(current->tabCaption())); } void RemoteMDI::closeCurrent() { // If there's a current view, close it if (m_pCurrentWindow) { closeSpecifiedWindow(m_pCurrentWindow); } } void RemoteMDI::closeSpecifiedWindow(KMdiChildView *window) { if (window) { // Notify the status bar of the removal of the window updateStatusBarWindowMessage(TQString::null, TQT_TQOBJECT(window)); updateStatusBarMainMessage(i18n("Instrument %1 removed").arg(window->tabCaption())); // Unplug menus unplugActionList("selectedInstrument_actionlist"); // We could also call removeWindowFromMdi, but it doesn't delete the // pointer. This way, we're sure that the view will get deleted. closeWindow(window); // Synchronize combo box if (m_pCurrentWindow) { currentChanged(m_pCurrentWindow); } } } void RemoteMDI::childClosed(KMdiChildView * w) { assert(w); // Set as active w->activate(); assert(w == m_pCurrentWindow); // Unplug menus unplugActionList("selectedInstrument_actionlist"); // Notify the status bar of the removal of the window updateStatusBarWindowMessage(TQString::null, TQT_TQOBJECT(w)); updateStatusBarMainMessage(i18n("Instrument %1 removed").arg(w->tabCaption())); // Re-enable associated action RemoteLab::InstrumentView* view = dynamic_cast(w); if (view) { TQString libraryName = view->name(); for (TQPtrList::Iterator it(m_instrumentActionList.begin()); it != m_instrumentActionList.end(); ++it) { if ((*it)->name() == libraryName) { (*it)->setEnabled(true); } } } // Remove status bar text m_windowStatusBarMapping.remove(TQT_TQOBJECT(w)); // Remove the view from MDI, BUT DO NOT DELETE IT! It is automatically deleted by TQt since it was closed. removeWindowFromMdi(w); } bool RemoteMDI::queryClose() { // Close all open connections KMdiIterator *it = createIterator(); while (it->currentItem()) { KMdiChildView *c = dynamic_cast(it->currentItem()); if (c) { RemoteLab::InstrumentView* iview = dynamic_cast(c); if (iview) { iview->closeConnections(); } } it->next(); } deleteIterator(it); // Save current MDI settings (window positions, etc.) KConfig *c = kapp->config(); // RAJA FIXME c->sync(); // Allow this window to close return true; } #include "remotemdi.moc"