Files
miniCashAll/miniCashConnect/mcconnectmainwindow.cpp
2025-08-05 22:37:51 +02:00

385 lines
12 KiB
C++

/***************************************************************************
miniCashConnect
Copyright © 2022 christoph holzheuer
c.holzheuer@sourceworx.org
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
***************************************************************************/
#include <QtGui>
#include <QWidget>
#include <QMessageBox>
#include <QShortcut>
#include <QInputDialog>
#include <QDir>
#include <QPalette>
#include <QTimer>
#include <miniCashConnect.h>
#include <mcconnectmainwindow.h>
#include <mcsetupdialog.h>
#include <mcnetworkdialog.h>
#include <mcceditview.h>
#include <mcbillingview.h>
#include <mccaboutme.h>
#include <ui_mcconnectmainwindow.h>
#include <mcnetworkwidget.h>
/**
* @brief MCConnectMainWindow::MCConnectMainWindow
* @param parent
*/
MCConnectMainWindow::MCConnectMainWindow( QWidget* parent )
: MCMainWindowBase{ parent }
{
setupUi( this );
/// kommt aus der miniCash library
setupDefaults();
_billingView->setupDefaults( _dataFileName, &_mainSettings );
/// das ist ein hack, der der Oberklasse 'MCMainwindowbase' den Zugriff ermöglicht.
_inputViewProxy = _inputView;
_inputView->setupDefaults( this, &_salesModel );
_editView->setupDefaults( this );
statusBar()->setFont( QFont( "Arial", 8 ) );
statusBar()->showMessage( QString( miniCash::copyConnect ) + miniCash::versionConnect );
//_helpViewer = new MCHelpViewer();
//_helpViewer->load( "file:///C:/HandbuchMiniCash.html" );
/// actions
connect( _actionSetup, SIGNAL( triggered() ), this, SLOT( onSetup() ) );
connect( _actionSetupNetwork, SIGNAL( triggered() ), this, SLOT( onShowNetworkDialog() ) );
connect( _actionInputTransactions, SIGNAL( triggered() ), this, SLOT( onInputTransactions() ) );
connect( _actionViewTransactions, SIGNAL( triggered() ), this, SLOT( onViewTransactions() ) );
connect( _actionEditTransactions, SIGNAL( triggered() ), this, SLOT( onEditTransactions() ) );
connect( _actionCopySaleData, SIGNAL( triggered() ), this, SLOT( onCopyTransactions() ) );
connect( _actionStartBilling, SIGNAL( triggered() ), this, SLOT( onStartBilling() ) );
connect( _actionExit, SIGNAL( triggered() ), this, SLOT( onExit() ) );
connect( _actionExitConfirmed, SIGNAL( triggered() ), this, SLOT( onExitConfirmed() ) );
connect( _actionHelpAbout, SIGNAL( triggered() ), this, SLOT( onHelpAbout() ) );
connect( _actionHelpContents, SIGNAL( triggered() ), this, SLOT( onHelpManual() ) );
/// senden & empfangen sind asynchron, also schieben wir die in
/// eigenen Threads
_tcpSender.moveToThread( &_senderThread );
_tcpReceiver.moveToThread( &_receiverThread );
//connect( this, SIGNAL( stopNetwork() ), &_tcpSender, SLOT( onDiscardConnection() ) );
//connect( this, SIGNAL( stopNetwork() ), &_tcpReceiver, SLOT( onDiscardConnection() ) );
connect( this, SIGNAL( startNetwork() ), &_tcpSender, SLOT( onCreateConnection() ) );
connect( this, SIGNAL( startNetwork() ), &_tcpReceiver, SLOT( onCreateConnection() ) );
qRegisterMetaType<miniCash::CState>("miniCash::CState");
qRegisterMetaType<QAbstractSocket::SocketError>("QAbstractSocket::SocketError");
connect( &_tcpSender, SIGNAL( connectionChanged(miniCash::CState) ), this, SLOT( onStateChanged(miniCash::CState) ) );
connect( &_tcpReceiver, SIGNAL( connectionChanged(miniCash::CState) ), this, SLOT( onStateChanged(miniCash::CState) ) );
/// das SIG transactionCreated() stammt aus der Basisklass MCMainWindowBase, nette Möglichkleit, eine
/// abstrakte Methode 'sendTransaction() = 0' zu ersetzen.
connect( this, SIGNAL( transactionCreated(QString) ), &_tcpSender, SLOT( onSendTransaction(QString) ) );
connect( &_tcpReceiver, SIGNAL( newTransaction(QString) ), this, SLOT( onTransactionReceived(QString) ) );
/// catch network errors
connect( &_tcpSender, SIGNAL( errorOccurred(QAbstractSocket::SocketError) ), this, SLOT( onConnectionError(QAbstractSocket::SocketError) ) );
connect( &_tcpReceiver, SIGNAL( acceptError(QAbstractSocket::SocketError) ), this, SLOT( onConnectionError(QAbstractSocket::SocketError) ) );
/// erstmal die Basis ...
/// wenn wir das erste mal hier sind, defaults laden.
if( !_mainSettings.contains( miniCash::keyReceiverHost ) )
{
_mainSettings.setValue( miniCash::keyIsTcpReceiver, miniCash::isTcpReceiver );
_mainSettings.setValue( miniCash::keyIsTcpSender, miniCash::isTcpSender );
_mainSettings.setValue( miniCash::keyReceiverHost, "" );
_mainSettings.setValue( miniCash::keyReceiverPort, miniCash::receiverPort );
}
connect( _netWidget, SIGNAL( showNetworkDialog() ), this, SLOT( onShowNetworkDialog() ) );
/// gleich beim Start NetDlg zeigen?
//_isSender = _mainSettings.value( miniCash::keyIsTcpSender ).toBool();
//_host = _mainSettings.value( miniCash::keyReceiverHost ).toString();
//if( _isSender && _host.isEmpty() )
/// Ist schon Ok, den DLG immer anzuzeigen
_showDlg = true;
_isReceiver = _mainSettings.value( miniCash::keyIsTcpReceiver ).toBool();
_sideBar->appendAction( _actionInputTransactions );
if( _isReceiver )
_sideBar->appendAction( _actionViewTransactions );
_sideBar->appendAction( _actionEditTransactions );
_sideBar->appendAction( _actionStartBilling );
_sideBar->setCheckedAction( _actionInputTransactions );
}
/**
* @brief Destruktor
*/
MCConnectMainWindow::~MCConnectMainWindow()
{
_senderThread.exit();
_receiverThread.exit();
if( !_senderThread.wait( 3000 ) ) //Wait until it actually has terminated (max. 3 sec)
{
_senderThread.terminate(); //Thread didn't exit in time, probably deadlocked, terminate it!
_senderThread.wait(); //We have to wait again here!
}
if( !_receiverThread.wait( 3000 ) ) //Wait until it actually has terminated (max. 3 sec)
{
_receiverThread.terminate(); //Thread didn't exit in time, probably deadlocked, terminate it!
_receiverThread.wait(); //We have to wait again here!
}
}
/**
* @brief Überschreibt 'QMainWindow::showEvent', um ggf. einen NetworkSetup Dialog
* einzuschmuggleln.
* @param event
*/
void MCConnectMainWindow::showEvent( QShowEvent* event )
{
/// 'onShowNetworkDialog' gehört eigentlich in den Konstruktor (falls der Hostname nicht gesetzt ist,
/// wenn aber dort der NetworkSetup-Dialog aufgerufen wird, dann erscheint dieser _vor_ dem
/// Hauptfenster. Um das zu vermeiden, schmuggeln wir das per 'showEvent' ein.
QMainWindow::showEvent( event );
/// guard: der DLG soll nur einmal beim Start erscheinen
if( !_showDlg )
return;
/// Call slot via queued connection so it's called from the UI thread after this method has returned and the window has been shown
QMetaObject::invokeMethod( this, &MCConnectMainWindow::onShowNetworkDialog, Qt::ConnectionType::QueuedConnection );
_showDlg = false;
}
void MCConnectMainWindow::onStateChanged( miniCash::CState state )
{
/// Serverstate schlägt Clientstate, ist deswege numerisch höher,
/// Errorstates werden woanders gesetzt
if( state > _netWidget->connectionState() )
_netWidget->setConnectionState( state );
}
/**
* @brief setup dialog anzeigen
*
* Maske mit den Programmeinstellungen anzeigen: Ziellaufwerk etc.
*
*/
void MCConnectMainWindow::onSetup()
{
MCSetupDialog( this, &_mainSettings ).exec();
/// alles kann geändert worden sein -> also nochmal los
setupDefaults();
}
void MCConnectMainWindow::onShowNetworkDialog()
{
int result = MCNetworkDialog( this, &_mainSettings ).exec();
if( QDialog::Rejected == result )
return;
setupNetwork();
}
/**
* @brief Netzwerksettings initialisieren
*
* Wird direkt beim Programmstart aufgerufen, setzt die Vorgabewerte
* fürs Networking. Das ist eine Ergänzung zu @see setupDefaults in der Basisklasse.
*/
void MCConnectMainWindow::setupNetwork()
{
//emit stopNetwork();
_isSender = _mainSettings.value( miniCash::keyIsTcpSender ).toBool();
_isReceiver = _mainSettings.value( miniCash::keyIsTcpReceiver ).toBool();
_host = _mainSettings.value( miniCash::keyReceiverHost ).toString();
_port = _mainSettings.value( miniCash::keyReceiverPort ).toInt();
bool useNetwork = _isSender || _isReceiver;
/// gar kein Netz?
if( !useNetwork )
return _netWidget->setConnectionState( miniCash::Disabled );
/*
/// ... und nochmal Prüfen: Host, default ist ja leer
if( _host.isEmpty() )
{
int result = MCNetworkDialog( this, &_mainSettings ).exec();
if( QDialog::Rejected == result )
return;
}
*/
/// Netz wird verwendet, verrbinden ...
_netWidget->setConnectionState( miniCash::UnConnected );
/// bin ich Server, also Empfänger
if( _isReceiver )
{
/// (Re-)init server
_tcpReceiver.setupConnection( _port );
if( !_receiverThread.isRunning() )
_receiverThread.start();
}
/// bin ich Client, also Sender?
if( _isSender )
{
_tcpSender.setupConnection( _host, _port );
if( !_senderThread.isRunning() )
_senderThread.start();
}
emit startNetwork();
}
void MCConnectMainWindow::onConnectionError( QAbstractSocket::SocketError socketError )
{
qDebug() << "socketError:" << socketError;
switch( socketError )
{
case QAbstractSocket::RemoteHostClosedError:
QMessageBox::information(this, "QTCPServer", "RemoteHostClosedError:");
break;
case QAbstractSocket::HostNotFoundError:
QMessageBox::information(this, "QTCPServer", "The host was not found. Please check the host name and port settings.");
break;
case QAbstractSocket::ConnectionRefusedError:
QMessageBox::information(this, "QTCPServer", "The connection was refused by the peer. Make sure QTCPServer is running, and check that the host name and port settings are correct.");
break;
default:
QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender());
QMessageBox::information(this, "QTCPServer", QString("The following error occurred: %1.").arg(socket->errorString()));
break;
}
}
/**
* @brief Eingabemaske (wieder) einblenden, in den
* Eingabemodus schalten
*/
void MCConnectMainWindow::onInputTransactions()
{
_contentWidget->setCurrentWidget( _inputView );
}
/**
* @brief TransactionView einblenden: Eingehende Transaktionen werden angezeigt
*/
void MCConnectMainWindow::onViewTransactions()
{
// Anim??
_contentWidget->setCurrentWidget( _transactionView );
}
/**
* @brief Transaktionsdaten korrigieren, z.B. Zahlendreher beheben oder Stornos
* einbuchen.
*/
void MCConnectMainWindow::onEditTransactions()
{
qDebug() << " -- Edit Transactions: " << _dataFilePath;
_editView->loadTransactions( _dataFilePath );
_contentWidget->setCurrentWidget( _editView );
}
/**
* @brief MCConnectMainWindow::onTransactionReceived
* @param transaction: Transaktionen als String
*/
void MCConnectMainWindow::onTransactionReceived( const QString& transaction )
{
_transactionView->onTransactionReceived( transaction );
}
/**
* @brief In den Abrechungsmodus schalten
*
* Abrechnung starten: Dazu wird das Hauptfenster auf die Abrechungsmaske
* umgeschaltet
*/
void MCConnectMainWindow::onStartBilling()
{
_contentWidget->setCurrentWidget( _billingView );
}
/**
* @brief Kurzinfo anzeigen
*
*/
void MCConnectMainWindow::onHelpAbout()
{
MCCAboutMe().exec();
}