S60: Provide a MessageBox for starting serial communication as well.

Generalize the concept of the Bluetooth starter, extract base class
BaseCommunicationStarter that can be used to start a serial communication
without further resources (listener) as well. Introduce convenience
functions for both types.

Note: This will only work for COM-ports that are not used otherwise
by the operating system.
This commit is contained in:
Friedemann Kleint
2009-10-26 15:37:09 +01:00
parent c6e9c041a7
commit 4091efb47f
13 changed files with 291 additions and 145 deletions

View File

@@ -30,6 +30,7 @@
#include "s60debuggerbluetoothstarter.h"
#include "bluetoothlistener.h"
#include "debuggermanager.h"
#include "trkoptions.h"
namespace Debugger {
namespace Internal {
@@ -48,5 +49,24 @@ trk::BluetoothListener *S60DebuggerBluetoothStarter::createListener()
return rc;
}
trk::PromptStartCommunicationResult
S60DebuggerBluetoothStarter::startCommunication(const TrkDevicePtr &trkDevice,
const QString &device,
int communicationType,
QWidget *msgBoxParent,
QString *errorMessage)
{
// Bluetooth?
if (communicationType == TrkOptions::BlueTooth) {
S60DebuggerBluetoothStarter bluetoothStarter(trkDevice);
bluetoothStarter.setDevice(device);
return trk::promptStartBluetooth(bluetoothStarter, msgBoxParent, errorMessage);
}
// Serial
BaseCommunicationStarter serialStarter(trkDevice);
serialStarter.setDevice(device);
return trk::promptStartSerial(serialStarter, msgBoxParent, errorMessage);
}
} // namespace Internal
} // namespace Debugger

View File

@@ -31,6 +31,7 @@
#define S60DEBUGGERBLUETOOTHSTARTER_H
#include "communicationstarter.h"
#include "bluetoothlistener_gui.h"
namespace Debugger {
namespace Internal {
@@ -38,15 +39,24 @@ namespace Internal {
/* S60DebuggerBluetoothStarter: Creates a listener in 'Listen' mode
* parented on the Debugger manager which outputs to the debugger window.
* Note: This is a "last resort" starter, normally, the run configuration
* should have already started a listener. */
* should have already started a listener.
* Provides a static convenience to prompt for both connection types. */
class S60DebuggerBluetoothStarter : public trk::AbstractBluetoothStarter
{
public:
explicit S60DebuggerBluetoothStarter(const TrkDevicePtr& trkDevice, QObject *parent = 0);
static trk::PromptStartCommunicationResult
startCommunication(const TrkDevicePtr &trkDevice,
const QString &device,
int communicationType,
QWidget *msgBoxParent,
QString *errorMessage);
protected:
virtual trk::BluetoothListener *createListener();
private:
explicit S60DebuggerBluetoothStarter(const TrkDevicePtr& trkDevice, QObject *parent = 0);
};
} // namespace Internal

View File

@@ -1512,30 +1512,22 @@ void TrkGdbAdapter::startAdapter()
debugMessage(_("TRYING TO START ADAPTER"));
logMessage(QLatin1String("### Starting TrkGdbAdapter"));
m_trkDevice->setSerialFrame(effectiveTrkDeviceType() != TrkOptions::BlueTooth);
// Prompt the user for a bluetooth connection
const QString device = effectiveTrkDevice();
// Prompt the user to start communication
QString message;
if (effectiveTrkDeviceType() == TrkOptions::BlueTooth) {
S60DebuggerBluetoothStarter starter(m_trkDevice);
starter.setDevice(device);
const trk::StartBluetoothGuiResult src = trk::startBluetoothGui(starter, 0, &message);
const trk::PromptStartCommunicationResult src =
S60DebuggerBluetoothStarter::startCommunication(m_trkDevice,
effectiveTrkDevice(),
effectiveTrkDeviceType(),
0, &message);
switch (src) {
case trk::BluetoothGuiConnected:
case trk::PromptStartCommunicationConnected:
break;
case trk::BluetoothGuiCanceled:
case trk::PromptStartCommunicationCanceled:
emit adapterStartFailed(message, QString());
return;
case trk::BluetoothGuiError:
case trk::PromptStartCommunicationError:
emit adapterStartFailed(message, TrkOptionsPage::settingsId());
return;
};
} else {
if (!m_trkDevice->isOpen() && !m_trkDevice->open(device, &message)) {
message = tr("Failed to connect to %1: %2\nCheck whether TRK is running.").arg(device).arg(message);
logMessage(message);
emit adapterStartFailed(message, TrkOptionsPage::settingsId());
return;
}
}
QTC_ASSERT(m_gdbServer == 0, delete m_gdbServer);

View File

@@ -46,6 +46,7 @@ namespace Internal {
struct TrkOptions
{
// Matches the communication enumeration from the S60 devices listener.
enum Mode { Serial, BlueTooth };
TrkOptions();

View File

@@ -583,20 +583,23 @@ void S60DeviceRunControlBase::signsisProcessFinished()
emit addToOutputWindow(this, tr("Package: %1\nDeploying application to '%2'...").arg(lsFile(copySrc), m_serialPortFriendlyName));
QString errorMessage;
// Prompt the user to start up the Blue tooth connection
if (m_communicationType == BlueToothCommunication) {
S60RunConfigBluetoothStarter starter(m_launcher->trkDevice());
switch (trk::startBluetoothGui(starter, 0, &errorMessage)) {
case trk::BluetoothGuiConnected:
const trk::PromptStartCommunicationResult src =
S60RunConfigBluetoothStarter::startCommunication(m_launcher->trkDevice(),
m_serialPortName,
m_communicationType, 0,
&errorMessage);
switch (src) {
case trk::PromptStartCommunicationConnected:
break;
case trk::BluetoothGuiCanceled:
case trk::BluetoothGuiError:
case trk::PromptStartCommunicationCanceled:
case trk::PromptStartCommunicationError:
delete m_launcher;
m_launcher = 0;
error(this, errorMessage);
emit finished();
return;
};
}
if (!m_launcher->startServer(&errorMessage)) {
delete m_launcher;
m_launcher = 0;

View File

@@ -302,22 +302,22 @@ bool S60DeviceRunConfigurationWidget::getDeviceInfo(QString *message)
const CommunicationDevice commDev = currentDevice();
launcher->setSerialFrame(commDev.type == SerialPortCommunication);
launcher->setTrkServerName(commDev.portName);
// Prompt the user to start
if (commDev.type == BlueToothCommunication) {
S60RunConfigBluetoothStarter starter(launcher->trkDevice());
starter.setDevice(launcher->trkServerName());
const trk::StartBluetoothGuiResult src = trk::startBluetoothGui(starter, this, message);
// Prompt user
const trk::PromptStartCommunicationResult src =
S60RunConfigBluetoothStarter::startCommunication(launcher->trkDevice(),
commDev.portName,
commDev.type, this,
message);
switch (src) {
case trk::BluetoothGuiConnected:
case trk::PromptStartCommunicationConnected:
break;
case trk::BluetoothGuiCanceled:
case trk::PromptStartCommunicationCanceled:
launcher->deleteLater();
return true;
case trk::BluetoothGuiError:
case trk::PromptStartCommunicationError:
launcher->deleteLater();
return false;
};
}
if (!launcher->startServer(message)) {
launcher->deleteLater();
return false;

View File

@@ -29,6 +29,7 @@
#include "s60runconfigbluetoothstarter.h"
#include "bluetoothlistener.h"
#include "serialdevicelister.h"
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
@@ -49,5 +50,25 @@ trk::BluetoothListener *S60RunConfigBluetoothStarter::createListener()
connect(rc, SIGNAL(message(QString)), core->messageManager(), SLOT(printToOutputPane(QString)));
return rc;
}
trk::PromptStartCommunicationResult
S60RunConfigBluetoothStarter::startCommunication(const TrkDevicePtr &trkDevice,
const QString &device,
int communicationType,
QWidget *msgBoxParent,
QString *errorMessage)
{
// Bluetooth?
if (communicationType == BlueToothCommunication) {
S60RunConfigBluetoothStarter bluetoothStarter(trkDevice);
bluetoothStarter.setDevice(device);
return trk::promptStartBluetooth(bluetoothStarter, msgBoxParent, errorMessage);
}
// Serial
BaseCommunicationStarter serialStarter(trkDevice);
serialStarter.setDevice(device);
return trk::promptStartSerial(serialStarter, msgBoxParent, errorMessage);
}
} // namespace Internal
} // namespace Qt4ProjectManager

View File

@@ -31,20 +31,34 @@
#define S60RUNCONFIGBLUETOOTHSTARTER_H
#include "communicationstarter.h"
#include "bluetoothlistener_gui.h"
namespace Qt4ProjectManager {
namespace Internal {
/* S60RunConfigBluetoothStarter: Creates a listener in 'Listen' mode
* parented on the Qt Creator core which outputs to the message manager. */
* parented on the Qt Creator core which outputs to the message manager.
* Provides a static convenience to prompt for both connection types. */
class S60RunConfigBluetoothStarter : public trk::AbstractBluetoothStarter
{
public:
explicit S60RunConfigBluetoothStarter(const TrkDevicePtr& trkDevice, QObject *parent = 0);
typedef trk::AbstractBluetoothStarter::TrkDevicePtr TrkDevicePtr;
// Convenience function to start communication depending on type,
// passing on the right messages.
static trk::PromptStartCommunicationResult
startCommunication(const TrkDevicePtr &trkDevice,
const QString &device,
int communicationType,
QWidget *msgBoxParent,
QString *errorMessage);
protected:
virtual trk::BluetoothListener *createListener();
private:
explicit S60RunConfigBluetoothStarter(const TrkDevicePtr& trkDevice, QObject *parent = 0);
};
} // namespace Internal

View File

@@ -38,38 +38,62 @@
namespace trk {
StartBluetoothGuiResult
startBluetoothGui(AbstractBluetoothStarter &starter,
PromptStartCommunicationResult
promptStartCommunication(BaseCommunicationStarter &starter,
const QString &msgBoxTitle,
const QString &msgBoxText,
QWidget *msgBoxParent,
QString *errorMessage)
{
errorMessage->clear();
// Initial connection attempt.
switch (starter.start()) {
case AbstractBluetoothStarter::Started:
case BaseCommunicationStarter::Started:
break;
case AbstractBluetoothStarter::ConnectionSucceeded:
return BluetoothGuiConnected;
case AbstractBluetoothStarter::StartError:
case BaseCommunicationStarter::ConnectionSucceeded:
return PromptStartCommunicationConnected;
case BaseCommunicationStarter::StartError:
*errorMessage = starter.errorString();
return BluetoothGuiError;
return PromptStartCommunicationError;
}
// Run the starter with the event loop of a message box, close it
// with the finished signals.
const QString title = QCoreApplication::translate("trk::startBluetoothGui", "Waiting for Bluetooth Connection");
const QString message = QCoreApplication::translate("trk::startBluetoothGui", "Connecting to %1...").arg(starter.device());
QMessageBox messageBox(QMessageBox::Information, title, message, QMessageBox::Cancel, msgBoxParent);
// Run the starter with the event loop of a message box, have the box
// closed by the signals of the starter.
QMessageBox messageBox(QMessageBox::Information, msgBoxTitle, msgBoxText, QMessageBox::Cancel, msgBoxParent);
QObject::connect(&starter, SIGNAL(connected()), &messageBox, SLOT(close()));
QObject::connect(&starter, SIGNAL(timeout()), &messageBox, SLOT(close()));
messageBox.exec();
// Only starter.state() is reliable here.
if (starter.state() == AbstractBluetoothStarter::Running) {
*errorMessage = QCoreApplication::translate("trk::startBluetoothGui", "Connection on %1 canceled.").arg(starter.device());
return BluetoothGuiCanceled;
}
if (starter.state() != AbstractBluetoothStarter::Connected) {
// Only starter.state() is reliable here to obtain the state.
switch (starter.state()) {
case AbstractBluetoothStarter::Running:
*errorMessage = QCoreApplication::translate("trk::promptStartCommunication", "Connection on %1 canceled.").arg(starter.device());
return PromptStartCommunicationCanceled;
case AbstractBluetoothStarter::TimedOut:
*errorMessage = starter.errorString();
return BluetoothGuiError;
return PromptStartCommunicationError;
case AbstractBluetoothStarter::Connected:
break;
}
return BluetoothGuiConnected;
return PromptStartCommunicationConnected;
}
PromptStartCommunicationResult
promptStartSerial(BaseCommunicationStarter &starter,
QWidget *msgBoxParent,
QString *errorMessage)
{
const QString title = QCoreApplication::translate("trk::promptStartCommunication", "Waiting for TRK");
const QString message = QCoreApplication::translate("trk::promptStartCommunication", "Waiting for TRK to start on %1...").arg(starter.device());
return promptStartCommunication(starter, title, message, msgBoxParent, errorMessage);
}
PromptStartCommunicationResult
promptStartBluetooth(BaseCommunicationStarter &starter,
QWidget *msgBoxParent,
QString *errorMessage)
{
const QString title = QCoreApplication::translate("trk::promptStartCommunication", "Waiting for Bluetooth Connection");
const QString message = QCoreApplication::translate("trk::promptStartCommunication", "Connecting to %1...").arg(starter.device());
return promptStartCommunication(starter, title, message, msgBoxParent, errorMessage);
}
} // namespace trk

View File

@@ -37,20 +37,37 @@ class QWidget;
QT_END_NAMESPACE
namespace trk {
class AbstractBluetoothStarter;
class BaseCommunicationStarter;
/* startBluetoothGui(): Prompt the user to start a Bluetooth
* connection with a message box he can cancel. Pass in
* the starter with device and parameters set up. */
/* promptStartCommunication(): Convenience functions that
* prompt the user to start a communication (launching or
* connecting TRK) using a modal message box in which they can cancel.
* Pass in the starter with device and parameters set up. */
enum StartBluetoothGuiResult {
BluetoothGuiConnected,
BluetoothGuiCanceled,
BluetoothGuiError
};
enum PromptStartCommunicationResult {
PromptStartCommunicationConnected,
PromptStartCommunicationCanceled,
PromptStartCommunicationError
};
StartBluetoothGuiResult
startBluetoothGui(AbstractBluetoothStarter &starter,
PromptStartCommunicationResult
promptStartCommunication(BaseCommunicationStarter &starter,
const QString &msgBoxTitle,
const QString &msgBoxText,
QWidget *msgBoxParent,
QString *errorMessage);
// Convenience to start a serial connection (messages prompting
// to launch Trk).
PromptStartCommunicationResult
promptStartSerial(BaseCommunicationStarter &starter,
QWidget *msgBoxParent,
QString *errorMessage);
// Convenience to start blue tooth connection (messages
// prompting to connect).
PromptStartCommunicationResult
promptStartBluetooth(BaseCommunicationStarter &starter,
QWidget *msgBoxParent,
QString *errorMessage);
} // namespace trk

View File

@@ -35,11 +35,12 @@
#include <QtCore/QEventLoop>
namespace trk {
// --------------- AbstractBluetoothStarter
struct AbstractBluetoothStarterPrivate {
explicit AbstractBluetoothStarterPrivate(const AbstractBluetoothStarter::TrkDevicePtr &d);
const AbstractBluetoothStarter::TrkDevicePtr trkDevice;
// --------------- AbstractBluetoothStarter
struct BaseCommunicationStarterPrivate {
explicit BaseCommunicationStarterPrivate(const BaseCommunicationStarter::TrkDevicePtr &d);
const BaseCommunicationStarter::TrkDevicePtr trkDevice;
BluetoothListener *listener;
QTimer *timer;
int intervalMS;
@@ -47,11 +48,10 @@ struct AbstractBluetoothStarterPrivate {
int n;
QString device;
QString errorString;
AbstractBluetoothStarter::State state;
BaseCommunicationStarter::State state;
};
AbstractBluetoothStarterPrivate::AbstractBluetoothStarterPrivate(const AbstractBluetoothStarter::TrkDevicePtr &d) :
BaseCommunicationStarterPrivate::BaseCommunicationStarterPrivate(const BaseCommunicationStarter::TrkDevicePtr &d) :
trkDevice(d),
listener(0),
timer(0),
@@ -59,32 +59,38 @@ AbstractBluetoothStarterPrivate::AbstractBluetoothStarterPrivate(const AbstractB
attempts(-1),
n(0),
device(QLatin1String("/dev/rfcomm0")),
state(AbstractBluetoothStarter::TimedOut)
state(BaseCommunicationStarter::TimedOut)
{
}
AbstractBluetoothStarter::AbstractBluetoothStarter(const TrkDevicePtr &trkDevice, QObject *parent) :
BaseCommunicationStarter::BaseCommunicationStarter(const TrkDevicePtr &trkDevice, QObject *parent) :
QObject(parent),
d(new AbstractBluetoothStarterPrivate(trkDevice))
d(new BaseCommunicationStarterPrivate(trkDevice))
{
}
AbstractBluetoothStarter::~AbstractBluetoothStarter()
BaseCommunicationStarter::~BaseCommunicationStarter()
{
stopTimer();
delete d;
}
void AbstractBluetoothStarter::stopTimer()
void BaseCommunicationStarter::stopTimer()
{
if (d->timer && d->timer->isActive())
d->timer->stop();
}
AbstractBluetoothStarter::StartResult AbstractBluetoothStarter::start()
bool BaseCommunicationStarter::initializeStartupResources(QString *errorMessage)
{
errorMessage->clear();
return true;
}
BaseCommunicationStarter::StartResult BaseCommunicationStarter::start()
{
if (state() == Running) {
d->errorString = QLatin1String("Internal error, attempt to re-start AbstractBluetoothStarter.\n");
d->errorString = QLatin1String("Internal error, attempt to re-start BaseCommunicationStarter.\n");
return StartError;
}
// Before we instantiate timers, and such, try to open the device,
@@ -92,10 +98,9 @@ AbstractBluetoothStarter::StartResult AbstractBluetoothStarter::start()
// 'Watch' mode
if (d->trkDevice->open(d->device , &(d->errorString)))
return ConnectionSucceeded;
// Fire up the listener
// Pull up resources for next attempt
d->n = 0;
d->listener = createListener();
if (!d->listener->start(d->device, &(d->errorString)))
if (!initializeStartupResources(&(d->errorString)))
return StartError;
// Start timer
if (!d->timer) {
@@ -109,49 +114,49 @@ AbstractBluetoothStarter::StartResult AbstractBluetoothStarter::start()
return Started;
}
AbstractBluetoothStarter::State AbstractBluetoothStarter::state() const
BaseCommunicationStarter::State BaseCommunicationStarter::state() const
{
return d->state;
}
int AbstractBluetoothStarter::intervalMS() const
int BaseCommunicationStarter::intervalMS() const
{
return d->intervalMS;
}
void AbstractBluetoothStarter::setIntervalMS(int i)
void BaseCommunicationStarter::setIntervalMS(int i)
{
d->intervalMS = i;
if (d->timer)
d->timer->setInterval(i);
}
int AbstractBluetoothStarter::attempts() const
int BaseCommunicationStarter::attempts() const
{
return d->attempts;
}
void AbstractBluetoothStarter::setAttempts(int a)
void BaseCommunicationStarter::setAttempts(int a)
{
d->attempts = a;
}
QString AbstractBluetoothStarter::device() const
QString BaseCommunicationStarter::device() const
{
return d->device;
}
void AbstractBluetoothStarter::setDevice(const QString &dv)
void BaseCommunicationStarter::setDevice(const QString &dv)
{
d->device = dv;
}
QString AbstractBluetoothStarter::errorString() const
QString BaseCommunicationStarter::errorString() const
{
return d->errorString;
}
void AbstractBluetoothStarter::slotTimer()
void BaseCommunicationStarter::slotTimer()
{
++d->n;
// Check for timeout
@@ -166,17 +171,32 @@ void AbstractBluetoothStarter::slotTimer()
if (d->trkDevice->open(d->device , &(d->errorString))) {
stopTimer();
const QString msg = tr("%1: Connection attempt %2 succeeded.").arg(d->device).arg(d->n);
d->listener->emitMessage(msg);
emit message(msg);
d->state = Connected;
emit connected();
} else {
const QString msg = tr("%1: Connection attempt %2 failed: %3 (retrying)...")
.arg(d->device).arg(d->n).arg(d->errorString);
d->listener->emitMessage(msg);
emit message(msg);
}
}
}
// --------------- AbstractBluetoothStarter
AbstractBluetoothStarter::AbstractBluetoothStarter(const TrkDevicePtr &trkDevice, QObject *parent) :
BaseCommunicationStarter(trkDevice, parent)
{
}
bool AbstractBluetoothStarter::initializeStartupResources(QString *errorMessage)
{
// Create the listener and forward messages to it.
BluetoothListener *listener = createListener();
connect(this, SIGNAL(message(QString)), listener, SLOT(emitMessage(QString)));
return listener->start(device(), errorMessage);
}
// -------- ConsoleBluetoothStarter
ConsoleBluetoothStarter::ConsoleBluetoothStarter(const TrkDevicePtr &trkDevice,
QObject *listenerParent,

View File

@@ -36,25 +36,28 @@
namespace trk {
class TrkDevice;
class BluetoothListener;
struct AbstractBluetoothStarterPrivate;
struct BaseCommunicationStarterPrivate;
/* AbstractBluetoothStarter: Repeatedly tries to open a trk device
* until a connection succeeds, allowing to do something else in the
* foreground (local event loop or asynchronous operation).
* Note that in case a Listener is already running in watch mode, it might
* also happen that connection succeeds immediately.
* Implementations must provide a factory function that creates and sets up the
* listener (mode, message connection, etc). */
/* BaseCommunicationStarter: A QObject that repeatedly tries to open a
* trk device until a connection succeeds or a timeout occurs (emitting
* signals), allowing to do something else in the foreground (local event loop
* [say QMessageBox] or some asynchronous operation). If the initial
* connection attempt in start() fails, the
* virtual initializeStartupResources() is called to initialize resources
* required to pull up the communication (namely Bluetooth listeners).
* The base class can be used as is to prompt the user to launch TRK for a serial
* communication as this requires no further resource setup. */
class AbstractBluetoothStarter : public QObject {
class BaseCommunicationStarter : public QObject {
Q_OBJECT
Q_DISABLE_COPY(AbstractBluetoothStarter)
Q_DISABLE_COPY(BaseCommunicationStarter)
public:
typedef QSharedPointer<TrkDevice> TrkDevicePtr;
enum State { Running, Connected, TimedOut };
virtual ~AbstractBluetoothStarter();
explicit BaseCommunicationStarter(const TrkDevicePtr& trkDevice, QObject *parent = 0);
virtual ~BaseCommunicationStarter();
int intervalMS() const;
void setIntervalMS(int i);
@@ -80,19 +83,40 @@ public:
signals:
void connected();
void timeout();
void message(const QString &);
private slots:
void slotTimer();
protected:
explicit AbstractBluetoothStarter(const TrkDevicePtr& trkDevice, QObject *parent = 0);
// Overwrite to create and parametrize the listener.
virtual BluetoothListener *createListener() = 0;
virtual bool initializeStartupResources(QString *errorMessage);
private:
inline void stopTimer();
AbstractBluetoothStarterPrivate *d;
BaseCommunicationStarterPrivate *d;
};
/* AbstractBluetoothStarter: Repeatedly tries to open a trk Bluetooth
* device. Note that in case a Listener is already running mode, the
* connection will succeed immediately.
* initializeStartupResources() is implemented to fire up the listener.
* Introduces a new virtual createListener() that derived classes must
* implement as a factory function that creates and sets up the
* listener (mode, message connection, etc). */
class AbstractBluetoothStarter : public BaseCommunicationStarter {
Q_OBJECT
Q_DISABLE_COPY(AbstractBluetoothStarter)
public:
protected:
explicit AbstractBluetoothStarter(const TrkDevicePtr& trkDevice, QObject *parent = 0);
// Implemented to fire up the listener.
virtual bool initializeStartupResources(QString *errorMessage);
// New virtual: Overwrite to create and parametrize the listener.
virtual BluetoothListener *createListener() = 0;
};
/* ConsoleBluetoothStarter: Convenience class for console processes. Creates a
@@ -102,7 +126,6 @@ class ConsoleBluetoothStarter : public AbstractBluetoothStarter {
Q_OBJECT
Q_DISABLE_COPY(ConsoleBluetoothStarter)
public:
static bool startBluetooth(const TrkDevicePtr& trkDevice,
QObject *listenerParent,
const QString &device,

View File

@@ -14,6 +14,7 @@ SOURCES += $$PWD/trkutils.cpp \
$$PWD/bluetoothlistener.cpp \
$$PWD/communicationstarter.cpp
# Tests/trklauncher is a console application
contains(QT, gui) {
HEADERS += $$PWD/bluetoothlistener_gui.h
SOURCES += $$PWD/bluetoothlistener_gui.cpp