You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

528 lines
16 KiB

//Author: Timothy Pearson <kb9vqf@pearsoncomputing.net>, (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 <cassert>
using namespace std;
#include <kapplication.h>
#include <klocale.h>
#include <kdebug.h>
#include <kconfig.h>
#include <kmessagebox.h>
#include <tqlabel.h>
#include <tqtimer.h>
#include <tqlayout.h>
#include <kiconloader.h>
#include <kstdaction.h>
#include <kstatusbar.h>
#include <kmdichildview.h>
#include <klistbox.h>
#include <kactionclasses.h>
#include <kedittoolbar.h>
#include <kkeydialog.h>
#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");
// 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<const KAction*>(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("<qt>The active laboratory workspace does not support the requested service</qt>"), 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<KAction*>(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("<qt>Unable to establish connection to remote server</qt>"), 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("<qt>Unable to establish connection to remote server</qt>"), 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("<qt>Unable to establish Kerberos protocol with remote server<p>Please verify that you currently hold a valid Kerberos ticket</qt>"), i18n("Connection Failed"));
}
else {
connToServerState = 2;
}
}
break;
case 2:
// Connection established!
// Read magic number and proto version from server
TQDataStream* ds = new TQDataStream(m_rsvSvrSocket);
TQ_UINT32 magicnum;
TQ_UINT32 protover;
*ds >> magicnum;
*ds >> protover;
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("<qt>The remote server is not compatible with this client</qt>"), 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);
// Request list of laboratory stations
StationList slist;
ds << TQString("LIST");
ds >> slist;
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");
ds << select.m_selectedStation;
ds >> result;
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("<qt>No stations of the specified type are currently available<p>Please try again later</qt>"), i18n("Insufficient Laboratory Resources"));
disconnectFromServer();
}
else if (result == "ERRPREVCN") {
KMessageBox::error(this, i18n("<qt>You are already connected to a laboratory station<p>Please disconnect and try again</qt>"), i18n("Multiple Connections Detected"));
disconnectFromServer();
}
else {
KMessageBox::error(this, i18n("<qt>Unknown server error<p>Please reconnect and try again</qt>"), i18n("Internal Error"));
disconnectFromServer();
}
}
else {
disconnectFromServer();
}
}
void RemoteMDI::disconnectFromServer() {
connect_action->setEnabled(false);
disconnect_action->setEnabled(false);
13 years ago
m_instrumentActionList.clear();
unplugActionList("instrumentMenu_actionlist");
unplugActionList("instrumentToolBar_actionlist");
13 years ago
// Close all windows
KMdiIterator<KMdiChildView*> *it = createIterator();
while (it->currentItem()) {
KMdiChildView *c = dynamic_cast<KMdiChildView*>(it->currentItem());
if (c) {
closeSpecifiedWindow(c);
}
it->next();
}
13 years ago
deleteIterator(it);
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("<qt>The remote server has closed the connection</qt>"), 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<KAction>::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<RemoteLab::InstrumentView*>(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<RemoteLab::InstrumentView*>(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()));
13 years ago
// 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<RemoteLab::InstrumentView*>(w);
if (view) {
TQString libraryName = view->name();
for (TQPtrList<KAction>::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
13 years ago
KMdiIterator<KMdiChildView*> *it = createIterator();
while (it->currentItem()) {
KMdiChildView *c = dynamic_cast<KMdiChildView*>(it->currentItem());
if (c) {
RemoteLab::InstrumentView* iview = dynamic_cast<RemoteLab::InstrumentView*>(c);
if (iview) {
iview->closeConnections();
}
}
13 years ago
it->next();
}
13 years ago
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"