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.
QtTermTCP/QtTermTCP.cpp

7956 lines
192 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

// Qt Version of BPQTermTCP
// Application icon design by Red PE1RRR
#define VersionString "0.0.0.77"
// .12 Save font weight
// .13 Display incomplete lines (ie without CR)
// .14 Add YAPP and Listen Mode
// .15 Reuse windows in Listen Mode
// .17 MDI Version 7/1/20
// .18 Fix input window losing focus when data arrives on other window
// .19 Fix Scrollback
// .20 WinXP compatibility changes
// .21 Save Window Positions
// .22 Open a window on first start
// .23 Add Tabbed display option
// .24 Fix crash when setting Monitor flags in Tabbed mode
// .25 Add AGW mode
// .26 Add sending CTRL/C CTRL/D and CTRL/Z)
// .27 Limit size of windows to 10000 lines
// Fix YAPP in Tabbed Mode
// .28 Add AGW Beacon
// .29 Fix allocating Listem sessions to tabs connected using AGW
// .30 Fix allocationd AGW Monitor Sessions
// .31 Fix output being written to the wrong place when window is scrolled back
// .32 Fix connect with digis
// .33 Add StripLF option
// .34 Improvements for Andriod
// Option to change Menu Fonts
// Fix receiving part lines when scrolled back
// .35 Fix PE if not in Tabbed Mode
// .36 Improved dialogs mainly for Android
// Try to make sure sessions are closed before exiting
// .37 Replace VT with LF (for pasting from BPQ32 Terminal Window)
// Dont show AGW status line if AGW interface is disabled
// Don't show Window Menu in Single Mode
// .38 Protect against connect to normal Telnet Port.
// Send CTEXT and Beep for inward AGW Connects
// Make sending Idle and Connect beeps configurable
// Change displayed Monitor flags when active window changed.
// Fix duplicate text on long lines
// .39 Add option to Convert non-utf8 charaters
// .40 Prevent crash if AGW monitor Window closed
// .41 Allow AGW Config and Connect dialogs to be resized with scrollbars
// Fix disabling connect flag on current session when AGW connects
// .42 Fix some bugs in AGW session handling
// .43 Include Ross KD5LPB's fixes for MAC
// .44 Ctrl/[ sends ESC (0x1b)
// .45 Add VARA support Nov 2021
// .46 Fix dialog size on VARA setup
// .47 Move VARA Status indicator.
// Add FLRIG PTT for VARA
// .48 Check that YAPP Receive Directory has been set
// Save separate configs for VARA, VARAFM and VARASAT
// .49 Add BBS Monitor Dec 2021
// .50 Make Serial port support optional for Android Version
// .51 Add KISS TNC Support May 2022
// .52 Save partially typed lines on cursor up May 2022
// .53 Add UI Mode for KISS Sessions May 2022
// Fix saving KISS Paclen (b)
// Fix Keepalive (c)
// .54 Add option to change screen colours June 2022
// .55 Build without optimisation
// .56 Add Teletext mode Nov 2022
// .57 Fix KISS mode incoming call handling
// .58 Add method to toggle Normal/Teletext Mode
// Fix KISS multiple session protection
// .59 Add Teletext double height mode
// .60 Add option to name sessions Jan 2023
// .61 Add VARA Init Script Feb 2023
// .62 Fix running AGW in single session mode Feb 2023
// .63 Fix handling split monitor frame (no fe in buffer) Apr 2023
// .64 Add Clear Screen command to context menu Apr 2023
// .65 Fixes for 63 port version of BPQ May 2023
// .66 Colour Tab of incoming calls June 2023
// .67 Add config Yapp RX Size dialog July 2023
// Fix 63 port montoring
// .68 Sept 2023
// Remember last used host on restart
// Add AutoConnect in Tabbed Mode
// Fix auto copy when QtTerm not active window
// .69 October 2023
// Options related to sound alerts moved to a separte menu in Setup
// Allow use of WAV files instead of Beep sound for sound alerts
// Enable an alarm to be sounded when one of a list of words or phrases is received.
// .70 October 2023
// Include some .wav files in resources
// Add Test button to Alert setup dialog
// .71 October 2023
// Add option to use local time
// Fixes for Mac OS
// .72 November 2023
// Don't display "Enable Monitor" on startup
// .73 November 2023
// Raise RTS on KISS serial port
// .74 April 2024
// Support BPQKISS TNCs with CHECKSUM and/or ACKMODE enabled
// .75 May 2024
// Flush Monitor log file every minute
// .76 August 2024
// Fix using digs in KISS UI mode.
// Add option to send TXDELAY to KISS TNC
// .77
// Support multichannel KISS TNCs (Beta 1)
// Fix using AGW listen in single terminal mode
#define _CRT_SECURE_NO_WARNINGS
#define UNUSED(x) (void)(x)
#define USESERIAL
#include "QtTermTCP.h"
#include "TabDialog.h"
#include <time.h>
#include <QVBoxLayout>
#include <QListWidgetItem>
#include <QToolButton>
#include <QInputMethod>
#include "QTreeWidget"
#include <QToolBar>
#include <QLabel>
#include <QActionGroup>
#include <QtWidgets>
#include <QColor>
#ifdef USESERIAL
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
#endif
#ifndef WIN32
#define strtok_s strtok_r
#endif
#include <fcntl.h>
#include <errno.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include "ax25.h"
#define UNREFERENCED_PARAMETER(P) (P)
void DecodeTeleText(Ui_ListenSession * Sess, char * text);
char Host[MAXHOSTS + 1][100] = { "" };
int Port[MAXHOSTS + 1] = { 0 };
char UserName[MAXHOSTS + 1][80] = { "" };
char Password[MAXHOSTS + 1][80] = { "" };
char MonParams[MAXHOSTS + 1][80] = { "" };
char SessName[MAXHOSTS + 1][80] = { "" };
int ListenPort = 8015;
// Session Type Equates
#define Term 1
#define Mon 2
#define Listen 4
// Presentation - Single Window, MDI or Tabbed
int TermMode = 0;
#define Single 0
#define MDI 1
#define Tabbed 2
int singlemodeFormat = Mon + Term;
char monStyleSheet[128] = "background-color: rgb(0,255,255)";
char termStyleSheet[128] = "background-color: rgb(255,0,255);";
char inputStyleSheet[128] = "color: rgb(255, 0, 0); background-color: rgb(255,255,0);";
QColor monBackground = qRgb(0, 255, 255);
QColor monRxText = qRgb(0, 0, 255);
QColor monTxText = qRgb(255, 0, 0);
QColor monOtherText = qRgb(0, 0, 0);
QColor termBackground = qRgb(255, 0, 255);
QColor outputText = qRgb(0, 0, 0);
QColor EchoText = qRgb(0, 0, 255);
QColor WarningText = qRgb(255, 0, 0);
QColor inputBackground = qRgb(255, 255, 0);
QColor inputText = qRgb(0, 0, 255);
QColor newTabText = qRgb(255, 0, 0); // Red
QColor oldTabText = qRgb(0, 0, 0); // Black
// There is something odd about this. It doesn't match BPQTERMTCP though it looks the same
// Chat uses these (+ 10)
//{ 0, 4, 9, 11, 13, 16, 17, 42, 45, 50, 61, 64, 66, 72, 81, 84, 85, 86, 87, 89 };
// As we have a white background we need dark colours
// 0 is black. 23 is normal output (blue) 81 is red (used by AGW Mon)
// for monitor in colour, host sends 17 for RX Text, 81 for TX Text but code converts to monRx/TxText qrgb values
QRgb Colours[256] = { 0,
qRgb(0,0,0), qRgb(0,0,128), qRgb(0,0,192), qRgb(0,0,255), // 1 - 4
qRgb(0,64,0), qRgb(0,64,128), qRgb(0,64,192), qRgb(0,64,255), // 5 - 8
qRgb(0,128,0), qRgb(0,128,128), qRgb(0,128,192), qRgb(0,128,255), // 9 - 12
qRgb(0,192,0), qRgb(0,192,128), qRgb(0,192,192), qRgb(0,192,255), // 13 - 16
qRgb(0,255,0), qRgb(0,255,128), qRgb(0,255,192), qRgb(0,255,255), // 17 - 20
qRgb(64,0,0), qRgb(64,0,128), qRgb(64,0,192), qRgb(0,0,255), // 21
qRgb(64,64,0), qRgb(64,64,128), qRgb(64,64,192), qRgb(64,64,255),
qRgb(64,128,0), qRgb(64,128,128), qRgb(64,128,192), qRgb(64,128,255),
qRgb(64,192,0), qRgb(64,192,128), qRgb(64,192,192), qRgb(64,192,255),
qRgb(64,255,0), qRgb(64,255,128), qRgb(64,255,192), qRgb(64,255,255),
qRgb(128,0,0), qRgb(128,0,128), qRgb(128,0,192), qRgb(128,0,255), // 41
qRgb(128,64,0), qRgb(128,64,128), qRgb(128,64,192), qRgb(128,64,255),
qRgb(128,128,0), qRgb(128,128,128), qRgb(128,128,192), qRgb(128,128,255),
qRgb(128,192,0), qRgb(128,192,128), qRgb(128,192,192), qRgb(128,192,255),
qRgb(128,255,0), qRgb(128,255,128), qRgb(128,255,192), qRgb(128,255,255),
qRgb(192,0,0), qRgb(192,0,128), qRgb(192,0,192), qRgb(192,0,255), // 61 - 80
qRgb(192,64,0), qRgb(192,64,128), qRgb(192,64,192), qRgb(192,64,255),
qRgb(192,128,0), qRgb(192,128,128), qRgb(192,128,192), qRgb(192,128,255),
qRgb(192,192,0), qRgb(192,192,128), qRgb(192,192,192), qRgb(192,192,255),
qRgb(192,255,0), qRgb(192,255,128), qRgb(192,255,192), qRgb(192,255,255),
qRgb(255,0,0), qRgb(255,0,128), qRgb(255,0,192), qRgb(255,0,255), // 81 - 100
qRgb(255,64,0), qRgb(255,64,128), qRgb(255,64,192), qRgb(255,64,255),
qRgb(255,128,0), qRgb(255,128,128), qRgb(255,128,192), qRgb(255,128,255),
qRgb(255,192,0), qRgb(255,192,128), qRgb(255,192,192), qRgb(255,192,255),
qRgb(255,255,0), qRgb(255,255,128), qRgb(255,255,192), qRgb(255,255,255)
};
int SavedHost = 0; // from config
char * sessionList = NULL; // Saved sessions
extern int ChatMode;
extern int Bells;
extern int StripLF;
extern int convUTF8;
extern time_t LastWrite;
extern int AlertInterval;
extern int AlertBeep;
extern int AlertFreq;
extern int AlertDuration;
extern int ConnectBeep;
bool useBeep; // use wav files if not set
extern int UseKeywords;
extern QString KeyWordsFile;
QString ConnectWAV("");
QString AlertWAV("");
QString BellWAV("");
QString IntervalWAV("");
extern int MaxRXSize;
extern int AutoTeletext;
// AGW Host Interface stuff
int AGWEnable = 0;
int AGWMonEnable = 0;
int AGWLocalTime = 0;
int AGWMonNodes = 0;
char AGWTermCall[12] = "";
char AGWBeaconDest[12] = "";
char AGWBeaconPath[80] = "";
int AGWBeaconInterval = 0;
char AGWBeaconPorts[80];
char AGWBeaconMsg[260] = "";
char AGWHost[128] = "127.0.0.1";
int AGWPortNum = 8000;
int AGWPaclen = 80;
extern char * AGWPortList;
extern myTcpSocket * AGWSock;
typedef struct AGWUser_t
{
QTcpSocket *socket;
unsigned char data_in[8192];
int data_in_len;
unsigned char AGW_frame_buf[512];
int Monitor;
int Monitor_raw;
Ui_ListenSession * MonSess; // Window for Monitor info
} AGWUser;
extern AGWUser *AGWUsers; // List of currently connected clients
void Send_AGW_m_Frame(QTcpSocket* socket);
QStringList AGWToCalls;
// KISS Interface
int KISSEnable = 0;
extern "C" int KISSMonEnable;
extern "C" int KISSLocalTime;
extern "C" int KISSMonNodes;
extern "C" int KISSListen;
extern "C" int KISSChecksum;
extern "C" int KISSAckMode;
extern "C" short txtail[5];
extern "C" short txdelay[5];
extern "C" int sendTXDelay[4];
char SerialPort[80] = "";
char KISSHost[128] = "127.0.0.1";
int KISSPortNum = 1000;
int KISSBAUD = 19200;
char KISSMYCALL[32];
char KISSVia[128]; // Digi String
extern "C" UCHAR axMYCALL[7]; // Mycall in ax.25
int KISSMode = 0; // Connected (0) or UI (1)
myTcpSocket * KISSSock;
extern "C" void * KISSSockCopy[4];
int KISSConnected = 0;
int KISSConnecting = 0;
Ui_ListenSession * KISSMonSess = nullptr;
QSerialPort * m_serial = nullptr;
// VARA Interface
int VARAEnable = 0;
char VARAHost[128] = "127.0.0.1";
char VARAHostHF[128] = "127.0.0.1";
char VARAHostFM[128] = "127.0.0.1";
char VARAHostSAT[128] = "127.0.0.1";
int VARAPortNum = 8000;
int VARAPortHF = 8000;
int VARAPortFM = 8000;
int VARAPortSAT = 8000;
char VARATermCall[12] = "";
char VARAPath[256] = "";
char VARAInit[256] = "";
char VARAPathHF[256] = "";
char VARAPathFM[256] = "";
char VARAPathSAT[256] = "";
int VARA500 = 0;
int VARA2300 = 1;
int VARA2750 = 0;
int VARAHF = 1;
int VARAFM = 0;
int VARASAT = 0;
myTcpSocket * VARASock;
myTcpSocket * VARADataSock;
int VARAConnected = 0;
int VARAConnecting = 0;
extern char YAPPPath[256];
void menuChecked(QAction * Act);
// PTT Stuff
#define PTTRTS 1
#define PTTDTR 2
#define PTTCAT 4
#define PTTCM108 8
#define PTTHAMLIB 16
#define PTTFLRIG 32
#ifdef USESERIAL
QSerialPort * hPTTDevice = 0;
#else
#endif
char PTTPort[80] = ""; // Port for Hardware PTT - may be same as control port.
int PTTBAUD = 19200;
int PTTMode = PTTRTS; // PTT Control Flags.
char PTTOnString[128] = "";
char PTTOffString[128] = "";
int CATHex = 1;
unsigned char PTTOnCmd[64];
int PTTOnCmdLen = 0;
unsigned char PTTOffCmd[64];
int PTTOffCmdLen = 0;
int pttGPIOPin = 17; // Default
int pttGPIOPinR = 17;
bool pttGPIOInvert = false;
bool useGPIO = false;
bool gotGPIO = false;
int HamLibPort = 4532;
char HamLibHost[32] = "192.168.1.14";
int FLRigPort = 12345;
char FLRigHost[32] = "127.0.0.1";
char CM108Addr[80] = "";
int VID = 0;
int PID = 0;
// CM108 Code
char * CM108Device = NULL;
QProcess *process = NULL;
void GetSettings();
// These widgets defined here as they are accessed from outside the framework
QLabel * Status1;
QLabel * Status2;
QLabel * Status3;
QLabel * Status4;
QAction *actHost[19];
QAction *actSetup[16];
QAction * TabSingle = NULL;
QAction * TabBoth = NULL;
QAction * TabMon = NULL;
QMenu *monitorMenu;
QMenu * YAPPMenu;
QMenu *connectMenu;
QMenu *disconnectMenu;
QAction *EnableMonitor;
QAction *EnableMonLog;
QAction *MonLocalTime;
QAction *MonTX;
QAction *MonSup;
QAction *MonNodes;
QAction *MonUI;
QAction *MonColour;
QAction *MonPort[65];
QAction *actChatMode;
QAction *actAutoTeletext;
QAction *actBells;
QAction *actStripLF;
QAction *actIntervalBeep;
QAction *actConnectBeep;
QAction *actAuto;
QAction *actnoConv;
QAction *actCP1251;
QAction *actCP1252;
QAction *actCP437;
QAction *actFonts;
QAction *actmenuFont;
QAction *actColours;
QAction *actmyFonts;
QAction *YAPPSend;
QAction *YAPPSetRX;
QAction *YAPPSetSize;
QAction *singleAct;
QAction *singleAct2;
QAction *MDIAct;
QAction *tabbedAct;
QAction *discAction;
QFont * menufont;
QMenu *windowMenu;
QMenu *setupMenu;
QAction *ListenAction;
QTabWidget *tabWidget;
QMdiArea *mdiArea;
QWidget * mythis;
QStatusBar * myStatusBar;
QTcpServer * _server;
QMenuBar *mymenuBar;
QToolBar *toolBar;
QToolButton * but1;
QToolButton * but2;
QToolButton * but3;
QToolButton * but4;
QToolButton * but5;
QList<Ui_ListenSession *> _sessions;
// Session Type Equates
#define Term 1
#define Mon 2
#define Listen 4
int TabType[10] = { Term, Term + Mon, Term, Term, Term, Term, Mon, Mon, Mon };
int AutoConnect[10] = {0, 0 ,0, 0, 0, 0, 0, 0, 0, 0};
int currentHost[10] = {0, 0 ,0, 0, 0, 0, 0, 0, 0, 0};
int listenPort = 8015;
extern "C" int listenEnable;
char listenCText[4096] = "";
int ConfigHost = 0;
int Split = 50; // Mon/Term size split
int termX;
bool Cascading = false; // Set to stop size being saved when cascading
QMdiSubWindow * ActiveSubWindow = NULL;
Ui_ListenSession * ActiveSession = NULL;
Ui_ListenSession * newWindow(QObject * parent, int Type, const char * Label = nullptr);
void Send_AGW_C_Frame(Ui_ListenSession * Sess, int Port, char * CallFrom, char * CallTo, char * Data, int DataLen);
void AGW_AX25_data_in(void * AX25Sess, unsigned char * data, int Len);
void AGWMonWindowClosing(Ui_ListenSession *Sess);
void AGWWindowClosing(Ui_ListenSession *Sess);
extern "C" void KISSDataReceived(void * socket, unsigned char * data, int length);
void closeSerialPort();
extern void initUTF8();
int checkUTF8(unsigned char * Msg, int Len, unsigned char * out);
QDialog * deviceUI;
#include <QStandardPaths>
#include <sys/stat.h>
#include <sys/types.h>
void EncodeSettingsLine(int n, char * String)
{
sprintf(String, "%s|%d|%s|%s|%s|%s", Host[n], Port[n], UserName[n], Password[n], MonParams[n], SessName[n]);
return;
}
// This is used for placing discAction in the preference menu
#ifdef __APPLE__
bool is_mac = true;
#else
bool is_mac = false;
#endif
QString GetConfPath()
{
std::string conf_path = "QtTermTCP.ini"; // Default conf file stored alongside application.
#ifdef __APPLE__
// Get configuration path for MacOS.
// This will usually be placed in /Users/USER/Library/Application Support/QtTermTCP
std::string directory = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation).at(0).toStdString();
conf_path = directory + "/QtTermTCP.ini";
mkdir(directory.c_str(), 0775);
#endif
return QString::fromUtf8(conf_path.c_str());
}
void DecodeSettingsLine(int n, char * String)
{
char * Param = strdup(String);
char * Rest;
char * Save = Param; // for Free
Rest = strlop(Param, '|');
if (Rest == NULL)
return;
strcpy(Host[n], Param);
Param = Rest;
Rest = strlop(Param, '|');
Port[n] = atoi(Param);
Param = Rest;
Rest = strlop(Param, '|');
strcpy(UserName[n], Param);
Param = Rest;
Rest = strlop(Param, '|');
strcpy(Password[n], Param);
Param = Rest;
Rest = strlop(Param, '|');
strcpy(MonParams[n], Param);
if (Rest)
strcpy(SessName[n], Rest);
free(Save);
return;
}
//#ifdef ANDROID
//#define inputheight 60
//#else
#define inputheight 25
//#endif
extern "C" void SendtoAX25(void * conn, unsigned char * Msg, int Len);
void DoTermResize(Ui_ListenSession * Sess)
{
QRect r = Sess->rect();
int H, Mid, monHeight, termHeight, Width, Border = 3;
Width = r.width();
H = r.height();
if (TermMode == Tabbed)
{
// H -= 20;
Width -= 7;
}
else if (TermMode == Single)
{
Width -= 7;
// H += 20;
}
if (Sess->SessionType == Listen || Sess->SessionType == Term)
{
// Term and Input
// Calc Positions of window
termHeight = H - (inputheight + 3 * Border);
Sess->termWindow->setGeometry(QRect(Border, Border, Width, termHeight));
if (Sess->TTActive)
{
Sess->TTLabel->setGeometry(QRect(Border, Border, 600, 475));
// Sess->termWindow->setVisible(false);
Sess->TTLabel->setVisible(true);
}
else
{
// Sess->termWindow->setVisible(true);
Sess->TTLabel->setVisible(false);
}
Sess->inputWindow->setGeometry(QRect(Border, H - (inputheight + Border), Width, inputheight));
}
else if (Sess->SessionType == (Term | Mon))
{
// All 3
// Calc Positions of window
Mid = (H * Split) / 100;
monHeight = Mid - Border;
termX = Mid;
termHeight = H - Mid - (inputheight + 3 * Border);
Sess->monWindow->setGeometry(QRect(Border, Border, Width, monHeight));
Sess->termWindow->setGeometry(QRect(Border, Mid + Border, Width, termHeight));
if (Sess->TTActive)
{
Sess->TTLabel->setGeometry(QRect(3, Mid + Border, 600, 475));
// Sess->termWindow->setVisible(false);
Sess->TTLabel->setVisible(true);
}
else
{
// Sess->termWindow->setVisible(true);
Sess->TTLabel->setVisible(false);
}
Sess->inputWindow->setGeometry(QRect(Border, H - (inputheight + Border), Width, inputheight));
}
else
{
// Should just be Mon only
Sess->monWindow->setGeometry(QRect(Border, Border, Width, H - 2 * Border));
}
}
bool QtTermTCP::eventFilter(QObject* obj, QEvent *event)
{
// See if from a Listening Session
Ui_ListenSession * Sess;
for (int i = 0; i < _sessions.size(); ++i)
{
Sess = _sessions.at(i);
// Sess = _sessions.at(i); for (Ui_ListenSession * Sess : _sessions)
// {
if (Sess == NULL)
continue;
if (Sess == obj)
{
if (event->type() == QEvent::Close)
{
// Window closing
// This only closed mon sessinos
if (Sess->AGWMonSession)
// AGWMonWindowClosing(Sess);
AGWWindowClosing(Sess);
if (Sess->AGWSession)
AGWWindowClosing(Sess);
if (Sess->clientSocket)
{
int loops = 100;
Sess->clientSocket->disconnectFromHost();
while (Sess->clientSocket && loops-- && Sess->clientSocket->state() != QAbstractSocket::UnconnectedState)
QThread::msleep(10);
}
_sessions.removeOne(Sess);
return QMainWindow::eventFilter(obj, event);
}
if (event->type() == QEvent::Resize)
{
DoTermResize(Sess);
}
}
if (Sess->inputWindow == obj)
{
if (event->type() == QEvent::KeyPress)
{
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
int key = keyEvent->key();
Qt::KeyboardModifiers modifier = keyEvent->modifiers();
if (modifier == Qt::ControlModifier)
{
char Msg[] = "\0\r";
if (key == Qt::Key_BracketLeft)
Msg[0] = 0x1b;
if (key == Qt::Key_Z)
Msg[0] = 0x1a;
else if (key == Qt::Key_C)
Msg[0] = 3;
else if (key == Qt::Key_D)
Msg[0] = 4;
if (Msg[0])
{
if (Sess->KISSSession)
{
// Send to ax.25 code
SendtoAX25(Sess->KISSSession, (unsigned char *)Msg, (int)strlen(Msg));
}
else if (Sess->AGWSession)
{
// Terminal is in AGWPE mode - send as AGW frame
AGW_AX25_data_in(Sess->AGWSession, (unsigned char *)Msg, (int)strlen(Msg));
}
else if (Sess->clientSocket && Sess->clientSocket->state() == QAbstractSocket::ConnectedState)
{
Sess->clientSocket->write(Msg);
}
return true;
}
}
if (key == Qt::Key_Up)
{
// Scroll up
if (Sess->KbdStack[Sess->StackIndex] == NULL)
return true;
// If anything typed, stack part command
QByteArray stringData = Sess->inputWindow->text().toUtf8();
if (stringData.length() && Sess->StackIndex == 0)
{
if (Sess->KbdStack[49])
free(Sess->KbdStack[49]);
for (int i = 48; i >= 0; i--)
{
Sess->KbdStack[i + 1] = Sess->KbdStack[i];
}
Sess->KbdStack[0] = qstrdup(stringData.data());
Sess->StackIndex++;
}
Sess->inputWindow->setText(Sess->KbdStack[Sess->StackIndex]);
Sess->inputWindow->cursorForward(strlen(Sess->KbdStack[Sess->StackIndex]));
Sess->StackIndex++;
if (Sess->StackIndex == 50)
Sess->StackIndex = 49;
return true;
}
else if (key == Qt::Key_Down)
{
// Scroll down
if (Sess->StackIndex == 0)
{
Sess->inputWindow->setText("");
return true;
}
Sess->StackIndex--;
if (Sess->StackIndex && Sess->KbdStack[Sess->StackIndex - 1])
{
Sess->inputWindow->setText(Sess->KbdStack[Sess->StackIndex - 1]);
Sess->inputWindow->cursorForward(strlen(Sess->KbdStack[Sess->StackIndex - 1]));
}
else
Sess->inputWindow->setText("");
return true;
}
else if (key == Qt::Key_Return || key == Qt::Key_Enter)
{
LreturnPressed(Sess);
return true;
}
return false;
}
if (event->type() == QEvent::MouseButtonPress)
{
QMouseEvent *k = static_cast<QMouseEvent *> (event);
// Paste on Right Click
if (k->button() == Qt::RightButton)
{
Sess->inputWindow->paste();
return true;
}
return QMainWindow::eventFilter(obj, event);
}
}
}
return QMainWindow::eventFilter(obj, event);
}
QAction * setupMenuLine(QMenu * Menu, char * Label, QObject * parent, int State)
{
QAction * Act = new QAction(Label, parent);
if (Menu)
Menu->addAction(Act);
Act->setCheckable(true);
if (State)
Act->setChecked(true);
parent->connect(Act, SIGNAL(triggered()), parent, SLOT(menuChecked()));
Act->setFont(*menufont);
return Act;
}
// This now creates all window types - Term, Mon, Combined, Listen
int xCount = 0;
int sessNo = 0;
Ui_ListenSession * newWindow(QObject * parent, int Type, const char * Label)
{
Ui_ListenSession * Sess = new(Ui_ListenSession);
// Need to explicity initialise on Qt4
Sess->termWindow = NULL;
Sess->monWindow = NULL;
Sess->inputWindow = NULL;
for (int i = 0; i < 50; i++)
Sess->KbdStack[i] = NULL;
Sess->StackIndex = 0;
Sess->InputMode = 0;
Sess->SlowTimer = 0;
Sess->MonData = 0;
Sess->OutputSaveLen = 0;
Sess->MonSaveLen = 0;
Sess->PortMonString[0] = 0;
Sess->portmask = 0;
Sess->portmask = 1;
Sess->mtxparam = 1;
Sess->mlocaltime = 0;
Sess->mcomparam = 1;
Sess->monUI = 0;
Sess->MonitorNODES = 0;
Sess->MonitorColour = 1;
Sess->CurrentHost = 0;
Sess->SessionType = Type;
Sess->clientSocket = NULL;
Sess->AGWSession = NULL;
Sess->AGWMonSession = NULL;
Sess->KISSSession = NULL;
Sess->KISSMode = 0;
Sess->TTActive = 0;
Sess->TTFlashToggle = 0;
Sess->pageBuffer[0] = 0;
Sess->Tab = 0;
Sess->LogMonitor = false;
Sess->monSpan = (char *) "<span style=white-space:pre>";
Sess->monLogfile = nullptr;
Sess->sessNo = sessNo++;
_sessions.append(Sess);
// Sess->TT = new Ui_TeleTextDialog();
// Sess->TT->setupUi(&Sess->TTUI);
// Sess->TTUI.show();
// Sess->TTUI.raise();
// Sess->TTUI.activateWindow();
// Sess->setObjectName(QString::fromUtf8("Sess"));
if (TermMode == MDI)
{
Sess->sw = mdiArea->addSubWindow(Sess);
// Sess->installEventFilter(parent);
Sess->sw->resize(800, 600);
}
else if (TermMode == Tabbed)
{
Sess->Tab = xCount++;
if (Type == Mon)
tabWidget->addTab(Sess, "Monitor");
else
tabWidget->addTab(Sess, Label);
}
Sess->installEventFilter(parent);
QSettings settings(GetConfPath(), QSettings::IniFormat);
#ifdef ANDROID
QFont font = QFont(settings.value("FontFamily", "Driod Sans Mono").value<QString>(),
settings.value("PointSize", 12).toInt(),
settings.value("Weight", 50).toInt());
#else
QFont font = QFont(settings.value("FontFamily", "Courier New").value<QString>(),
settings.value("PointSize", 10).toInt(),
settings.value("Weight", 50).toInt());
#endif
if ((Type & Mon))
{
Sess->monWindow = new QTextEdit(Sess);
Sess->monWindow->setReadOnly(1);
Sess->monWindow->document()->setMaximumBlockCount(10000);
Sess->monWindow->setFont(font);
Sess->monWindow->setStyleSheet(monStyleSheet);
mythis->connect(Sess->monWindow, SIGNAL(selectionChanged()), parent, SLOT(onTEselectionChanged()));
}
if ((Type & (Listen | Term)))
{
Sess->termWindow = new QTextEdit(Sess);
Sess->termWindow->setReadOnly(1);
Sess->termWindow->document()->setMaximumBlockCount(10000);
Sess->termWindow->setFont(font);
Sess->termWindow->setStyleSheet(termStyleSheet);
Sess->TTLabel = new QLabel(Sess);
Sess->TTLabel->setGeometry(QRect(0,0 ,500, 500));
Sess->TTLabel->setVisible(false);
Sess->TTBitmap = new QImage(40 * 15, 25 * 19, QImage::Format_RGB32);
Sess->TTBitmap->fill(Qt::black);
/*
char Page[4096];
QFile file("/savepage.txt");
file.open(QIODevice::ReadOnly);
file.read(Page, 4096);
file.close();
Sess->TTActive = 1;
strcpy(Sess->pageBuffer, Page);
DecodeTeleText(Sess, Sess->pageBuffer);
*/
Sess->TTLabel->setPixmap(QPixmap::fromImage(*Sess->TTBitmap));
Sess->TTLabel->setContextMenuPolicy(Qt::CustomContextMenu);
mythis->connect(Sess->TTLabel, SIGNAL(customContextMenuRequested(const QPoint&)),
parent, SLOT(showContextMenuL()));
mythis->connect(Sess->termWindow, SIGNAL(selectionChanged()), parent, SLOT(onTEselectionChanged()));
Sess->inputWindow = new QLineEdit(Sess);
Sess->inputWindow->installEventFilter(parent);
Sess->inputWindow->setFont(font);
Sess->inputWindow->setStyleSheet(inputStyleSheet);
Sess->inputWindow->setContextMenuPolicy(Qt::PreventContextMenu);
mythis->connect(Sess->inputWindow, SIGNAL(selectionChanged()), parent, SLOT(onLEselectionChanged()));
}
if (Type == Term || Type == Listen)
{
// Term Only or Listen Only
Sess->termWindow->setContextMenuPolicy(Qt::CustomContextMenu);
mythis->connect(Sess->termWindow, SIGNAL(customContextMenuRequested(const QPoint&)),
parent, SLOT(showContextMenuT(const QPoint &)));
}
if (Type == Mon)
{
// Monitor Only
Sess->monWindow->setContextMenuPolicy(Qt::CustomContextMenu);
mythis->connect(Sess->monWindow, SIGNAL(customContextMenuRequested(const QPoint&)),
parent, SLOT(showContextMenuMOnly(const QPoint &)));
}
if (Type == (Term | Mon))
{
// Combined Term and Mon. Add Custom Menu to set Mon/Term Split with Right Click
Sess->monWindow->setContextMenuPolicy(Qt::CustomContextMenu);
Sess->termWindow->setContextMenuPolicy(Qt::CustomContextMenu);
mythis->connect(Sess->monWindow, SIGNAL(customContextMenuRequested(const QPoint&)),
parent, SLOT(showContextMenuM(const QPoint &)));
mythis->connect(Sess->termWindow, SIGNAL(customContextMenuRequested(const QPoint&)),
parent, SLOT(showContextMenuMT(const QPoint &)));
}
if (Sess->SessionType == Mon) // Mon Only
Sess->setWindowTitle("Monitor Session Disconnected");
else if (Sess->SessionType == Listen) // Mon Only
Sess->setWindowTitle("Listen Window Disconnected");
else
Sess->setWindowTitle("Disconnected");
Sess->installEventFilter(mythis);
Sess->show();
int pos = (_sessions.size() - 1) * 20;
if (TermMode == MDI)
{
Sess->sw->move(pos, pos);
}
QSize Size(800, 602); // Not actually used, but Event constructor needs it
QResizeEvent event(Size, Size);
QApplication::sendEvent(Sess, &event); // Resize Widgets to fix Window
return Sess;
}
QByteArray timeLoaded = QDateTime::currentDateTime().toString("yymmdd_hhmmss").toUtf8();
QtTermTCP::QtTermTCP(QWidget *parent) : QMainWindow(parent)
{
int i;
char Title[80];
// Get configuration path for MacOS.
std::string directory = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation).at(0).toStdString();
std::string conf_path = directory + "/QtTermTCP.ini";
//mkdir(directory.c_str(), 0775);
_server = new QTcpServer();
mythis = this;
ui.setupUi(this);
initUTF8();
mymenuBar = new QMenuBar(this);
mymenuBar->setGeometry(QRect(0, 0, 781, 26));
setMenuBar(mymenuBar);
toolBar = new QToolBar(this);
toolBar->setObjectName("mainToolbar");
addToolBar(Qt::BottomToolBarArea, toolBar);
QSettings mysettings(GetConfPath(), QSettings::IniFormat);
restoreGeometry(mysettings.value("geometry").toByteArray());
restoreState(mysettings.value("windowState").toByteArray());
GetSettings();
GetKeyWordFile();
// Set background colours
sprintf(monStyleSheet, "background-color: rgb(%d, %d, %d);",
monBackground.red(), monBackground.green(), monBackground.blue());
sprintf(termStyleSheet, "background-color: rgb(%d, %d, %d);",
termBackground.red(), termBackground.green(), termBackground.blue());
sprintf(inputStyleSheet, "color: rgb(%d, %d, %d); background-color: rgb(%d, %d, %d);",
inputText.red(), inputText.green(), inputText.blue(),
inputBackground.red(), inputBackground.green(), inputBackground.blue());
#ifdef ANDROID
menufont = new QFont(mysettings.value("MFontFamily", "Driod Sans").value<QString>(),
mysettings.value("MPointSize", 12).toInt(),
mysettings.value("MWeight", 50).toInt());
#else
menufont = new QFont(mysettings.value("MFontFamily", "Aerial").value<QString>(),
mysettings.value("MPointSize", 10).toInt(),
mysettings.value("MWeight", 50).toInt());
#endif
if (TermMode == MDI)
{
mdiArea = new QMdiArea(ui.centralWidget);
mdiArea->setGeometry(QRect(0, 0, 771, 571));
mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(xon_mdiArea_changed()));
setCentralWidget(mdiArea);
}
else if (TermMode == Tabbed)
{
Ui_ListenSession * Sess;
int index = 0;
tabWidget = new QTabWidget(this);
QTabBar* tabBar = tabWidget->tabBar();
// QString style1 = "QTabWidget::tab-bar{left:0;}"; // for Mac
// tabWidget->setStyleSheet(style1);
QString style = "QTabBar::tab:selected{background-color: #d0d0d0;} QTabBar::tab:!selected{background-color: #f0f0f0;}";
tabBar->setStyleSheet(style);
tabWidget->setContextMenuPolicy(Qt::CustomContextMenu);
connect(tabWidget, SIGNAL(customContextMenuRequested(const QPoint &)), SLOT(showContextMenu(const QPoint &)));
ui.verticalLayout->addWidget(tabWidget);
tabWidget->setTabPosition(QTabWidget::South);
Sess = newWindow(this, TabType[0], "Sess 1");
Sess->CurrentHost = currentHost[index++];
Sess = newWindow(this, TabType[1], "Sess 2");
Sess->CurrentHost = currentHost[index++];
Sess = newWindow(this, TabType[2], "Sess 3");
Sess->CurrentHost = currentHost[index++];
Sess = newWindow(this, TabType[3], "Sess 4");
Sess->CurrentHost = currentHost[index++];
Sess = newWindow(this, TabType[4], "Sess 5");
Sess->CurrentHost = currentHost[index++];
Sess = newWindow(this, TabType[5], "Sess 6");
Sess->CurrentHost = currentHost[index++];
Sess = newWindow(this, TabType[6], "Sess 7");
Sess->CurrentHost = currentHost[index++];
Sess = newWindow(this, TabType[7], "Monitor");
Sess->CurrentHost = currentHost[index++];
Sess = newWindow(this, TabType[8], "Monitor");
Sess->CurrentHost = currentHost[index++];
ActiveSession = _sessions.at(0);
connect(tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabSelected(int)));
tabWidget->setFont(*menufont);
}
sprintf(Title, "QtTermTCP Version %s", VersionString);
this->setWindowTitle(Title);
//#ifdef ANDROID
// mymymenuBar->setVisible(false);
//#endif
if (TermMode == Single)
windowMenu = mymenuBar->addMenu("");
else
windowMenu = mymenuBar->addMenu(tr("&Window"));
connect(windowMenu, SIGNAL(aboutToShow()), this, SLOT(updateWindowMenu()));
windowMenu->setFont(*menufont);
newTermAct = new QAction(tr("New Terminal Window"), this);
connect(newTermAct, SIGNAL(triggered()), this, SLOT(doNewTerm()));
newMonAct = new QAction(tr("New Monitor Window"), this);
connect(newMonAct, SIGNAL(triggered()), this, SLOT(doNewMon()));
newCombinedAct = new QAction(tr("New Combined Term/Mon Window"), this);
connect(newCombinedAct, SIGNAL(triggered()), this, SLOT(doNewCombined()));
if (TermMode == MDI)
{
closeAct = new QAction(tr("Cl&ose"), this);
closeAct->setStatusTip(tr("Close the active window"));
connect(closeAct, SIGNAL(triggered()), mdiArea, SLOT(closeActiveSubWindow()));
closeAllAct = new QAction(tr("Close &All"), this);
closeAllAct->setStatusTip(tr("Close all the windows"));
connect(closeAllAct, SIGNAL(triggered()), mdiArea, SLOT(closeAllSubWindows()));
tileAct = new QAction(tr("&Tile"), this);
tileAct->setStatusTip(tr("Tile the windows"));
connect(tileAct, SIGNAL(triggered()), mdiArea, SLOT(tileSubWindows()));
cascadeAct = new QAction(tr("&Cascade"), this);
cascadeAct->setStatusTip(tr("Cascade the windows"));
connect(cascadeAct, SIGNAL(triggered()), this, SLOT(doCascade()));
nextAct = new QAction(tr("Ne&xt"), this);
nextAct->setShortcuts(QKeySequence::NextChild);
nextAct->setStatusTip(tr("Move the focus to the next window"));
connect(nextAct, SIGNAL(triggered()), mdiArea, SLOT(activateNextSubWindow()));
previousAct = new QAction(tr("Pre&vious"), this);
previousAct->setShortcuts(QKeySequence::PreviousChild);
previousAct->setStatusTip(tr("Move the focus to the previous window"));
connect(previousAct, SIGNAL(triggered()), mdiArea, SLOT(activatePreviousSubWindow()));
}
quitAction = new QAction(tr("&Quit"), this);
connect(quitAction, SIGNAL(triggered()), this, SLOT(doQuit()));
windowMenuSeparatorAct = new QAction(this);
windowMenuSeparatorAct->setSeparator(true);
updateWindowMenu();
connectMenu = mymenuBar->addMenu(tr("&Connect"));
actHost[16] = new QAction("AGW Connect", this);
actHost[16]->setFont(*menufont);
actHost[16]->setVisible(0);
connectMenu->addAction(actHost[16]);
connect(actHost[16], SIGNAL(triggered()), this, SLOT(Connect()));
actHost[17] = new QAction("VARA Connect", this);
actHost[17]->setFont(*menufont);
actHost[17]->setVisible(0);
connectMenu->addAction(actHost[17]);
connect(actHost[17], SIGNAL(triggered()), this, SLOT(Connect()));
actHost[18] = new QAction("KISS Connect", this);
actHost[18]->setFont(*menufont);
actHost[18]->setVisible(KISSEnable);
actHost[18]->setEnabled(0);
connectMenu->addAction(actHost[18]);
connect(actHost[18], SIGNAL(triggered()), this, SLOT(Connect()));
for (i = 0; i < MAXHOSTS; i++)
{
if (SessName[i][0])
{
char Lable[256];
sprintf(Lable, "%s(%s)", Host[i], SessName[i]);
actHost[i] = new QAction(Lable, this);
}
else
actHost[i] = new QAction(Host[i], this);
actHost[i]->setFont(*menufont);
connectMenu->addAction(actHost[i]);
connect(actHost[i], SIGNAL(triggered()), this, SLOT(Connect()));
}
discAction = mymenuBar->addAction("&Disconnect");
// Place discAction in mac app menu, otherwise it doesn't appear
if (is_mac == true) {
QMenu * app_menu = mymenuBar->addMenu("App Menu");
discAction->setMenuRole(QAction::ApplicationSpecificRole);
app_menu->addAction(discAction);
}
connect(discAction, SIGNAL(triggered()), this, SLOT(Disconnect()));
discAction->setEnabled(false);
toolBar->setFont(*menufont);
#ifndef ANDROID
toolBar->setVisible(false);
#endif
but4 = new QToolButton();
but4->setPopupMode(QToolButton::InstantPopup);
but4->setText("Window");
but4->addAction(windowMenu->menuAction());
but4->setFont(*menufont);
toolBar->addWidget(but4);
but5 = new QToolButton();
but5->setPopupMode(QToolButton::InstantPopup);
but5->setText("Connect");
but5->addAction(connectMenu->menuAction());
but5->setFont(*menufont);
toolBar->addWidget(but5);
toolBar->addAction(discAction);
setupMenu = mymenuBar->addMenu(tr("&Setup"));
hostsubMenu = setupMenu->addMenu("Hosts");
hostsubMenu->setFont(*menufont);
for (i = 0; i < MAXHOSTS; i++)
{
if (Host[i][0])
{
char Label[256];
if (SessName[i][0])
sprintf(Label, "%s(%s)", Host[i], SessName[i]);
else
strcpy(Label, Host[i]);
actSetup[i] = new QAction(Label, this);
}
else
actSetup[i] = new QAction("New Host", this);
hostsubMenu->addAction(actSetup[i]);
connect(actSetup[i], SIGNAL(triggered()), this, SLOT(SetupHosts()));
actSetup[i]->setFont(*menufont);
}
// Setup Presentation Options
setupMenu->addSeparator()->setText(tr("Presentation"));
QActionGroup * termGroup = new QActionGroup(this);
singleAct = setupMenuLine(nullptr, (char *)"Single Window (Term + Mon)", this, (TermMode == Single) && (singlemodeFormat == Term + Mon));
singleAct2 = setupMenuLine(nullptr, (char *)"Single Window (Term only)", this, (TermMode == Single) && (singlemodeFormat == Term));
MDIAct = setupMenuLine(nullptr, (char *)"MDI", this, TermMode == MDI);
tabbedAct = setupMenuLine(nullptr, (char *)"Tabbed", this, TermMode == Tabbed);
termGroup->addAction(singleAct);
termGroup->addAction(singleAct2);
termGroup->addAction(MDIAct);
termGroup->addAction(tabbedAct);
setupMenu->addAction(singleAct);
setupMenu->addAction(singleAct2);
setupMenu->addAction(MDIAct);
setupMenu->addAction(tabbedAct);
setupMenu->addSeparator();
AlertAction = new QAction("Sound Alerts Setup", this);
setupMenu->addAction(AlertAction);
connect(AlertAction, SIGNAL(triggered()), this, SLOT(AlertSlot()));
AlertAction->setFont(*menufont);
setupMenu->addSeparator();
actFonts = new QAction("Terminal Font", this);
setupMenu->addAction(actFonts);
connect(actFonts, SIGNAL(triggered()), this, SLOT(doFonts()));
actFonts->setFont(*menufont);
actmenuFont = new QAction("Menu Font", this);
setupMenu->addAction(actmenuFont);
connect(actmenuFont, SIGNAL(triggered()), this, SLOT(doMFonts()));
actmenuFont->setFont(*menufont);
actColours = new QAction("Choose Colours", this);
setupMenu->addAction(actColours);
connect(actColours, SIGNAL(triggered()), this, SLOT(doColours()));
actColours->setFont(*menufont);
setupMenu->addSeparator();
AGWAction = new QAction("AGW Setup", this);
setupMenu->addAction(AGWAction);
connect(AGWAction, SIGNAL(triggered()), this, SLOT(AGWSlot()));
AGWAction->setFont(*menufont);
VARAAction = new QAction("VARA Setup", this);
setupMenu->addAction(VARAAction);
connect(VARAAction, SIGNAL(triggered()), this, SLOT(VARASlot()));
VARAAction->setFont(*menufont);
KISSAction = new QAction("KISS Setup", this);
setupMenu->addAction(KISSAction);
connect(KISSAction, SIGNAL(triggered()), this, SLOT(KISSSlot()));
KISSAction->setFont(*menufont);
setupMenu->addSeparator();
actChatMode = setupMenuLine(setupMenu, (char *)"Chat Terminal Mode (Send Keepalives)", this, ChatMode);
actAutoTeletext = setupMenuLine(setupMenu, (char *)"Auto switch to Teletext", this, AutoTeletext);
actStripLF = setupMenuLine(setupMenu, (char *)"Strip Line Feeds", this, StripLF);
setupMenu->addSeparator();
setupMenu->addAction(new QAction("Interpret non-UTF8 input as:", this));
setupMenu->setFont(*menufont);
QActionGroup * cpGroup = new QActionGroup(this);
actnoConv = setupMenuLine(nullptr, (char *)" Don't Convert (assume is UTF-8)", this, convUTF8 == -1);
actAuto = setupMenuLine(nullptr, (char *)" Auto", this, convUTF8 == 0);
actCP1251 = setupMenuLine(nullptr, (char *)" CP1251 (Cyrillic)", this, convUTF8 == 1251);
actCP1252 = setupMenuLine(nullptr, (char *)" CP1252 (Western Europe)", this, convUTF8 == 1252);
actCP437 = setupMenuLine(nullptr, (char *)" CP437 (Windows Line Draw)", this, convUTF8 == 437);
cpGroup->addAction(actnoConv);
cpGroup->addAction(actAuto);
cpGroup->addAction(actCP1251);
cpGroup->addAction(actCP1252);
cpGroup->addAction(actCP437);
setupMenu->addAction(actnoConv);
setupMenu->addAction(actAuto);
setupMenu->addAction(actCP1251);
setupMenu->addAction(actCP1252);
setupMenu->addAction(actCP437);
monitorMenu = mymenuBar->addMenu(tr("&Monitor"));
EnableMonitor = setupMenuLine(monitorMenu, (char *)"Enable Monitoring", this, 0);
EnableMonitor->setVisible(0);
EnableMonLog = setupMenuLine(monitorMenu, (char *)"Log to File", this, 0);
MonLocalTime = setupMenuLine(monitorMenu, (char *)"Use local time", this, 0);
MonTX = setupMenuLine(monitorMenu, (char *)"Monitor TX", this, 1);
MonSup = setupMenuLine(monitorMenu, (char *)"Monitor Supervisory", this, 1);
MonUI = setupMenuLine(monitorMenu, (char *)"Only Monitor UI Frames", this, 0);
MonNodes = setupMenuLine(monitorMenu, (char *)"Monitor NODES Broadcasts", this, 0);
MonColour = setupMenuLine(monitorMenu, (char *)"Enable Colour", this, 1);
for (i = 0; i < MAXPORTS + 1; i++)
{
MonPort[i] = setupMenuLine(monitorMenu, (char *)"Port", this, 0);
MonPort[i]->setVisible(false);
}
but1 = new QToolButton();
but1->setPopupMode(QToolButton::InstantPopup);
but1->setText("Monitor");
but1->addAction(monitorMenu->menuAction());
toolBar->addWidget(but1);
but2 = new QToolButton();
but2->setPopupMode(QToolButton::InstantPopup);
but2->setText("Setup");
but2->addAction(setupMenu->menuAction());
toolBar->addWidget(but2);
ListenAction = mymenuBar->addAction("&Listen");
connect(ListenAction, SIGNAL(triggered()), this, SLOT(ListenSlot()));
// Place Listen Action in mac app menu, otherwise it doesn't appear
if (is_mac == true)
{
QMenu * app_menu = mymenuBar->addMenu("App Menu");
ListenAction->setMenuRole(QAction::ApplicationSpecificRole);
app_menu->addAction(ListenAction);
}
toolBar->addAction(ListenAction);
YAPPMenu = mymenuBar->addMenu(tr("&YAPP"));
YAPPSend = new QAction("Send File", this);
YAPPSetRX = new QAction("Set Receive Directory", this);
YAPPSetSize = new QAction("Set Max Size", this);
YAPPSend->setFont(*menufont);
YAPPSetRX->setFont(*menufont);
YAPPSetSize->setFont(*menufont);
YAPPMenu->addAction(YAPPSend);
YAPPMenu->addAction(YAPPSetRX);
YAPPMenu->addAction(YAPPSetSize);
YAPPSend->setEnabled(false);
connect(YAPPSend, SIGNAL(triggered()), this, SLOT(doYAPPSend()));
connect(YAPPSetRX, SIGNAL(triggered()), this, SLOT(doYAPPSetRX()));
connect(YAPPSetSize, SIGNAL(triggered()), this, SLOT(doYAPPSetSize()));
but3 = new QToolButton();
but3->setPopupMode(QToolButton::InstantPopup);
but3->setText("YAPP");
but3->addAction(YAPPMenu->menuAction());
but3->setFont(*menufont);
toolBar->addWidget(but3);
toolBar->setFont(*menufont);
// Set up Status Bar
setStyleSheet("QStatusBar{border-top: 1px outset black;} QStatusBar::item { border: 1px solid black; border-radius: 3px;}");
// setStyleSheet("QStatusBar{border-top: 1px outset black;}");
Status4 = new QLabel("");
Status3 = new QLabel(" ");
Status2 = new QLabel(" ");
Status1 = new QLabel(" Disconnected ");
Status1->setMinimumWidth(100);
Status2->setMinimumWidth(100);
Status3->setMinimumWidth(100);
Status4->setMinimumWidth(100);
myStatusBar = statusBar();
statusBar()->addPermanentWidget(Status4);
statusBar()->addPermanentWidget(Status3);
statusBar()->addPermanentWidget(Status2);
statusBar()->addPermanentWidget(Status1);
statusBar()->setVisible(AGWEnable | VARAEnable | KISSEnable);
// Restore saved sessions
if (TermMode == Single)
{
Ui_ListenSession * Sess = newWindow(this, singlemodeFormat);
ActiveSession = Sess;
Sess->CurrentHost = currentHost[0];
ui.verticalLayout->addWidget(Sess);
connectMenu->setEnabled(true);
discAction->setEnabled(false);
YAPPSend->setEnabled(false);
}
if (TermMode == MDI)
{
int n = atoi(sessionList);
int index = 0;
char *p, *Context;
char delim[] = "|";
int type = 0, host = 0, topx, leftx, bottomx, rightx;
p = strtok_s((char *)sessionList, delim, &Context);
while (n--)
{
p = strtok_s(NULL, delim, &Context);
sscanf(p, "%d,%d,%d,%d,%d,%d", &type, &host, &topx, &leftx, &bottomx, &rightx);
Ui_ListenSession * Sess = newWindow(this, type);
Sess->CurrentHost = currentHost[index++];;
QRect r(leftx, topx, rightx - leftx, bottomx - topx);
Sess->sw->setGeometry(r);
Sess->sw->move(leftx, topx);
}
}
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(MyTimerSlot()));
timer->start(10000);
QTimer *timer2 = new QTimer(this);
connect(timer2, SIGNAL(timeout()), this, SLOT(KISSTimerSlot()));
timer2->start(100);
QTimer *timer3 = new QTimer(this);
connect(timer3, SIGNAL(timeout()), this, SLOT(SlowTimerSlot()));
timer3->start(60000);
// Run timer now to connect to AGW if configured
MyTimerSlot();
if (listenEnable)
_server->listen(QHostAddress::Any, listenPort);
connect(_server, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
setFonts();
if (VARAEnable)
OpenPTTPort();
memset(axMYCALL, 0, 7);
ConvToAX25(KISSMYCALL, axMYCALL);
// Do any autoconnects
for (int i = 0; i < _sessions.size(); ++i)
{
if (AutoConnect[i] > 0)
{
Ui_ListenSession * Sess = _sessions.at(i);
Sess->clientSocket = new myTcpSocket();
Sess->clientSocket->Sess = Sess;
connect(Sess->clientSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(displayError(QAbstractSocket::SocketError)));
connect(Sess->clientSocket, SIGNAL(readyRead()), this, SLOT(readyRead()));
connect(Sess->clientSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onSocketStateChanged(QAbstractSocket::SocketState)));
Sess->clientSocket->connectToHost(&Host[Sess->CurrentHost][0], Port[Sess->CurrentHost]);
}
}
}
void QtTermTCP::showContextMenu(const QPoint &point)
{
if (point.isNull())
return;
QTabBar* tabBar = tabWidget->tabBar();
QRect Wrect = tabWidget->rect();
QRect Brect = tabBar->rect();
QPoint myPoint = point;
// Get x coordinate of first tab (on Mac tabs are centre aligned)
QRect rect = tabWidget->tabBar()->geometry();
int left = rect.left();
int n = myPoint.y() - (Wrect.height() - Brect.height());
myPoint.setY(n);
n = myPoint.x() - left;
myPoint.setX(n);
int tabIndex = tabBar->tabAt(myPoint);
QMenu menu(this);
QAction * Act = new QAction("AutoConnect on load", this);
Act->setObjectName(QString::number(tabIndex));
menu.addAction(Act);
Act->setCheckable(true);
if (AutoConnect[tabIndex] <= 0)
Act->setChecked(false);
else
Act->setChecked(true);
connect(Act, SIGNAL(triggered()), this, SLOT(autoConnectChecked()));
menu.exec(tabWidget->mapToGlobal(point));
}
void QtTermTCP::autoConnectChecked()
{
QAction * Act = static_cast<QAction*>(QObject::sender());
QString Tab = Act->objectName();
int tabNo = Tab.toInt();
tabWidget->setCurrentIndex(tabNo);
Ui_ListenSession *Sess = (Ui_ListenSession *)tabWidget->currentWidget();
int state = Act->isChecked();
if (state == 0)
AutoConnect[tabNo] = 0;
else
{
if (Sess->clientSocket) // Connected
AutoConnect[tabNo] = 1;
else
AutoConnect[tabNo] = 0;
}
}
void QtTermTCP::setFonts()
{
windowMenu->menuAction()->setFont(*menufont);
connectMenu->menuAction()->setFont(*menufont);
setupMenu->menuAction()->setFont(*menufont);
monitorMenu->menuAction()->setFont(*menufont);
YAPPMenu->menuAction()->setFont(*menufont);
if (tabWidget)
tabWidget->setFont(*menufont);
mymenuBar->setFont(*menufont);
toolBar->setFont(*menufont);
but1->setFont(*menufont);
but2->setFont(*menufont);
but3->setFont(*menufont);
but4->setFont(*menufont);
but5->setFont(*menufont);
discAction->setFont(*menufont);
ListenAction->setFont(*menufont);
for (int i = 0; i < MAXHOSTS; i++)
{
actHost[i]->setFont(*menufont);
actSetup[i]->setFont(*menufont);
}
actHost[16]->setFont(*menufont); // AGW Host
}
void QtTermTCP::doQuit()
{
if (_server->isListening())
_server->close();
SaveSettings();
QCoreApplication::quit();
}
void FlashifNotActive()
{
if (!mythis->isActiveWindow())
QApplication::alert(mythis, 0);
}
// "Copy on select" Code
void QtTermTCP::onTEselectionChanged()
{
QTextEdit * x = static_cast<QTextEdit*>(QObject::sender());
if (isActiveWindow())
x->copy();
}
void QtTermTCP::onLEselectionChanged()
{
QLineEdit * x = static_cast<QLineEdit*>(QObject::sender());
if (isActiveWindow())
x->copy();
}
void QtTermTCP::setVDMode()
{
QAction * sender = static_cast<QAction*>(QObject::sender());
QTextEdit * window = static_cast<QTextEdit*>(sender->parentWidget());
// Need to find session with this window as owner
Ui_ListenSession * Sess = NULL;
for (int i = 0; i < _sessions.size(); ++i)
{
Sess = _sessions.at(i);
if (Sess->termWindow == window)
{
Sess->TTActive ^= 1;
DoTermResize(Sess);
break;
}
}
}
int splitY; // Value when menu added
void QtTermTCP::setSplit()
{
QAction * sender = static_cast<QAction*>(QObject::sender());
QWidget * Parent = sender->parentWidget();
if (Parent)
Parent = Parent->parentWidget();
int y = Parent->rect().height() - 50;
// y is height of whole windom. splitX is new split position
// So split = 100 * splitx/x
Split = (splitY * 100) / y;
if (Split < 10)
Split = 10;
else if (Split > 90)
Split = 90;
QSize size(800, 602);
QResizeEvent event(size, size);
eventFilter(Parent, &event);
}
void QtTermTCP::ClearScreen()
{
QAction * sender = static_cast<QAction*>(QObject::sender());
QTextEdit * window = static_cast<QTextEdit*>(sender->parentWidget());
window->clear();
}
void QtTermTCP::showContextMenuMT(const QPoint &pt) // Term Window
{
// Monitor and Terminal (Term Half)
QTextEdit* sender = static_cast<QTextEdit*>(QObject::sender());
QMenu *menu = sender->createStandardContextMenu();
QString style = "QMenu {border-radius:15px; background-color: white;margin: 2px; border: 1px solid rgb(58, 80, 116); color: rgb(58, 80, 116);}QMenu::separator {height: 2px;background: rgb(58, 80, 116);margin-left: 10px;margin-right: 5px;}";
menu->setStyleSheet(style);
QAction * actSplit = new QAction("Set Monitor/Output Split", sender);
QAction * actVDMode = new QAction("Toggle Viewdata Mode", sender);
QAction * actClear = new QAction("Clear Screen Buffer", sender);
menu->addAction(actSplit);
menu->addAction(actVDMode);
menu->addAction(actClear);
splitY = pt.y() + termX;
connect(actSplit, SIGNAL(triggered()), this, SLOT(setSplit()));
connect(actVDMode, SIGNAL(triggered()), this, SLOT(setVDMode()));
connect(actClear, SIGNAL(triggered()), this, SLOT(ClearScreen()));
menu->exec(sender->mapToGlobal(pt));
delete menu;
}
void QtTermTCP::showContextMenuMOnly(const QPoint &pt)
{
// Monitor only
QTextEdit* sender = static_cast<QTextEdit*>(QObject::sender());
QMenu *menu = sender->createStandardContextMenu();
QString style = "QMenu {border-radius:15px; background-color: white;margin: 2px; border: 1px solid rgb(58, 80, 116); color: rgb(58, 80, 116);}QMenu::separator {height: 2px;background: rgb(58, 80, 116);margin-left: 10px;margin-right: 5px;}";
menu->setStyleSheet(style);
QAction * actClear = new QAction("Clear Screen Buffer", sender);
menu->addAction(actClear);
connect(actClear, SIGNAL(triggered()), this, SLOT(ClearScreen()));
menu->exec(sender->mapToGlobal(pt));
delete menu;
}
void QtTermTCP::showContextMenuT(const QPoint &pt) // Term Window
{
// Just Terminal
QTextEdit* sender = static_cast<QTextEdit*>(QObject::sender());
QMenu *menu = sender->createStandardContextMenu();
QString style = "QMenu {border-radius:15px; background-color: white;margin: 2px; border: 1px solid rgb(58, 80, 116); color: rgb(58, 80, 116);}";
menu->setStyleSheet(style);
QAction * actVDMode = new QAction("Toggle Viewdata Mode", sender);
QAction * actClear = new QAction("Clear Screen Buffer", sender);
menu->addAction(actVDMode);
menu->addAction(actClear);
connect(actVDMode, SIGNAL(triggered()), this, SLOT(setVDMode()));
connect(actClear, SIGNAL(triggered()), this, SLOT(ClearScreen()));
menu->exec(sender->mapToGlobal(pt));
delete menu;
}
void QtTermTCP::showContextMenuL() // Term Window
{
// Teletext Label Right Clicked - cancel TT Mode
QLabel * sender = static_cast<QLabel*>(QObject::sender());
// Need to find session with this label as owner
Ui_ListenSession * Sess = NULL;
for (int i = 0; i < _sessions.size(); ++i)
{
Sess = _sessions.at(i);
if (Sess->TTLabel == sender)
{
Sess->TTActive ^= 1;
DoTermResize(Sess);
break;
}
}
}
void QtTermTCP::showContextMenuM(const QPoint &pt) // Mon Window
{
QTextEdit* sender = static_cast<QTextEdit*>(QObject::sender());
QMenu *menu = sender->createStandardContextMenu();
QString style = "QMenu {border-radius:15px; background-color: white;margin: 2px; border: 1px solid rgb(58, 80, 116); color: rgb(58, 80, 116);}";
menu->setStyleSheet(style);
QAction * actSplit = new QAction("Set Monitor/Output Split", sender);
QAction * actClear = new QAction("Clear Screen Buffer", sender);
menu->addAction(actSplit);
menu->addAction(actClear);
splitY = pt.y();
connect(actSplit, SIGNAL(triggered()), this, SLOT(setSplit()));
connect(actClear, SIGNAL(triggered()), this, SLOT(ClearScreen()));
menu->exec(sender->mapToGlobal(pt));
delete menu;
}
extern "C" void setMenus(int State)
{
// Sets Connect, Disconnect and YAPP Send enable flags to match connection state
if (State)
{
connectMenu->setEnabled(false);
discAction->setEnabled(true);
YAPPSend->setEnabled(true);
}
else
{
connectMenu->setEnabled(true);
discAction->setEnabled(false);
YAPPSend->setEnabled(false);
}
}
extern "C" void SetSessLabel(Ui_ListenSession * Sess, char * label)
{
if (TermMode == MDI)
Sess->setWindowTitle(label);
else if (TermMode == Tabbed)
tabWidget->setTabText(Sess->Tab, label);
else if (TermMode == Single)
mythis->setWindowTitle(label);
}
extern "C" void ClearSessLabel(Ui_ListenSession * Sess)
{
if (TermMode == MDI)
{
if (Sess->SessionType == Mon) // Mon Only
Sess->setWindowTitle("Monitor Session Disconnected");
else if (Sess->SessionType == Listen) // Mon Only
Sess->setWindowTitle("Listen Window Disconnected");
else
Sess->setWindowTitle("Disconnected");
}
else if (TermMode == Tabbed)
{
if (Sess->SessionType == Mon) // Mon Only
tabWidget->setTabText(Sess->Tab, "Monitor");
else
{
char Label[16];
sprintf(Label, "Sess %d", Sess->Tab + 1);
tabWidget->setTabText(Sess->Tab, Label);
}
}
else if (TermMode == Single)
{
if (Sess->SessionType == Mon) // Mon Only
mythis->setWindowTitle("Monitor Session Disconnected");
else
mythis->setWindowTitle("Disconnected");
}
}
void QtTermTCP::tabSelected(int Current)
{
Ui_ListenSession * Sess = NULL;
if (Current < _sessions.size())
{
Sess = _sessions.at(Current);
if (Sess == nullptr)
return;
ActiveSession = Sess;
tabWidget->tabBar()->setTabTextColor(tabWidget->currentIndex(), oldTabText);
if (Sess->clientSocket || Sess->AGWSession || Sess->KISSSession || Sess->KISSMode)
{
connectMenu->setEnabled(false);
discAction->setEnabled(true);
YAPPSend->setEnabled(true);
}
else
{
connectMenu->setEnabled(true);
discAction->setEnabled(false);
YAPPSend->setEnabled(false);
}
// If a monitor Window, change Monitor config settings
EnableMonLog->setChecked(Sess->LogMonitor);
if (AGWUsers && Sess == AGWUsers->MonSess) // AGW Monitor
{
for (int i = 0; i < 64; i++)
SetPortMonLine(i, (char *)"", 0, 0); // Set all hidden
connectMenu->setEnabled(false);
MonTX->setVisible(0);
MonSup->setVisible(0);
MonUI->setVisible(0);
MonColour->setVisible(0);
EnableMonitor->setVisible(1);
EnableMonitor->setChecked(AGWMonEnable);
MonLocalTime->setChecked(Sess->mlocaltime);
MonNodes->setChecked(Sess->MonitorNODES);
}
else if (Sess == KISSMonSess) // KISS Monitor
{
for (int i = 0; i < 64; i++)
SetPortMonLine(i, (char *)"", 0, 0); // Set all hidden
connectMenu->setEnabled(false);
MonTX->setVisible(0);
MonSup->setVisible(0);
MonUI->setVisible(0);
MonColour->setVisible(0);
EnableMonitor->setVisible(1);
EnableMonitor->setChecked(KISSMonEnable);
MonLocalTime->setChecked(Sess->mlocaltime);
MonNodes->setChecked(Sess->MonitorNODES);
}
else
{
EnableMonitor->setVisible(0);
MonTX->setVisible(1);
MonSup->setVisible(1);
MonUI->setVisible(1);
MonColour->setVisible(1);
}
if (Sess->PortMonString[0])
{
char * ptr = (char *)malloc(2048);
memcpy(ptr, Sess->PortMonString, 2048);
int NumberofPorts = atoi((char *)&ptr[2]);
char *p, *Context;
char msg[80];
int portnum, m;
char delim[] = "|";
// Remove old Monitor menu
for (int i = 0; i < 64; i++)
{
SetPortMonLine(i, (char *)"", 0, 0); // Set all hidden
}
p = strtok_s((char *)&ptr[2], delim, &Context);
while (NumberofPorts--)
{
p = strtok_s(NULL, delim, &Context);
if (p == NULL)
break;
m = portnum = atoi(p);
sprintf(msg, "Port %s", p);
if (m == 0)
m = 64;
if (Sess->portmask & ((uint64_t)1 << (m - 1)))
SetPortMonLine(portnum, msg, 1, 1);
else
SetPortMonLine(portnum, msg, 1, 0);
}
free(ptr);
MonLocalTime->setChecked(Sess->mlocaltime);
MonTX->setChecked(Sess->mtxparam);
MonSup->setChecked(Sess->mcomparam);
MonUI->setChecked(Sess->monUI);
MonNodes->setChecked(Sess->MonitorNODES);
MonColour->setChecked(Sess->MonitorColour);
}
return;
}
}
void QtTermTCP::SetupHosts()
{
QAction * Act = static_cast<QAction*>(QObject::sender());
int i;
for (i = 0; i < MAXHOSTS; i++)
{
if (Act == actSetup[i])
break;
}
if (i > 15)
return;
ConfigHost = i;
TabDialog tabdialog(0);
tabdialog.exec();
}
void QtTermTCP::Connect()
{
QMdiSubWindow * UI;
Ui_ListenSession * Sess = nullptr;
QAction * Act = static_cast<QAction*>(QObject::sender());
int i;
for (i = 0; i < MAXHOSTS; i++)
{
if (Act == actHost[i])
break;
}
SavedHost = i; // Last used
if (TermMode == MDI)
{
UI = mdiArea->activeSubWindow();
for (i = 0; i < _sessions.size(); ++i)
{
Sess = _sessions.at(i);
if (Sess->sw == UI)
break;
}
if (i == _sessions.size())
return;
currentHost[i] = SavedHost;
}
else if (TermMode == Tabbed)
{
Sess = (Ui_ListenSession *)tabWidget->currentWidget();
}
else if (TermMode == Single)
Sess = _sessions.at(0);
if ((Sess == nullptr || Sess->SessionType & Listen))
return;
Sess->CurrentHost = SavedHost;
currentHost[Sess->Tab] = SavedHost;
if (Act == actHost[16])
{
// This runs the AGW Connection dialog
Sess->CurrentHost = 16; // Iast used
AGWConnect dialog(0);
dialog.exec();
return;
}
if (Act == actHost[17])
{
// This runs the VARA Connection dialog
Sess->CurrentHost = 17; // Iast used
VARADataSock->Sess = Sess;
VARASock->Sess = Sess;
VARAConnect dialog(0);
dialog.exec();
WritetoOutputWindow(Sess, (unsigned char *)"Connecting...\r", 14);
return;
}
if (Act == actHost[18])
{
// This runs the KISS Connection dialog
Sess->CurrentHost = 18; // Iast used
KISSConnect dialog(0);
dialog.exec();
return;
}
// Set Monitor Params for this host
sscanf(MonParams[Sess->CurrentHost], "%llx %x %x %x %x %x",
&Sess->portmask, &Sess->mtxparam, &Sess->mcomparam,
&Sess->MonitorNODES, &Sess->MonitorColour, &Sess->monUI);
Sess->mlocaltime = (Sess->mtxparam >> 7);
Sess->mtxparam &= 1;
MonLocalTime->setChecked(Sess->mlocaltime);
MonTX->setChecked(Sess->mtxparam);
MonSup->setChecked(Sess->mcomparam);
MonUI->setChecked(Sess->monUI);
MonNodes->setChecked(Sess->MonitorNODES);
MonColour->setChecked(Sess->MonitorColour);
// Remove old Monitor menu
for (i = 0; i < 64; i++)
{
SetPortMonLine(i, (char *)"", 0, 0); // Set all hidden
}
delete(Sess->clientSocket);
Sess->clientSocket = new myTcpSocket();
Sess->clientSocket->Sess = Sess;
connect(Sess->clientSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(displayError(QAbstractSocket::SocketError)));
connect(Sess->clientSocket, SIGNAL(readyRead()), this, SLOT(readyRead()));
connect(Sess->clientSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onSocketStateChanged(QAbstractSocket::SocketState)));
Sess->clientSocket->connectToHost(&Host[Sess->CurrentHost][0], Port[Sess->CurrentHost]);
return;
}
extern "C" void rst_timer(TAX25Port * AX25Sess);
extern "C" void set_unlink(TAX25Port * AX25Sess, Byte * path);
void QtTermTCP::Disconnect()
{
QMdiSubWindow * UI;
Ui_ListenSession * Sess = nullptr;
if (TermMode == MDI)
{
int i;
UI = mdiArea->activeSubWindow();
for (i = 0; i < _sessions.size(); ++i)
{
Sess = _sessions.at(i);
if (Sess->sw == UI)
break;
}
if (i == _sessions.size())
return;
}
else if (TermMode == Tabbed)
{
Sess = (Ui_ListenSession *)tabWidget->currentWidget();
}
else if (TermMode == Single)
Sess = _sessions.at(0);
// if AGW send a d frame else disconnect TCP session
if (Sess->AGWSession)
Send_AGW_Ds_Frame(Sess->AGWSession);
else if (VARASock && VARASock->Sess == Sess)
VARASock->write("DISCONNECT\r");
else if (Sess->KISSSession)
{
if (Sess->KISSMode == 0)
{
TAX25Port * Port = (TAX25Port *)Sess->KISSSession;
rst_timer(Port);
set_unlink(Port, Port->Path);
}
else
{
// KISS UI Mode
char Msg[128];
int Len = sprintf(Msg, "Disconnected\r");
Sess->KISSMode = 0;
SendtoTerm(Sess, Msg, Len);
ClearSessLabel(Sess);
setMenus(0);
Sess->KISSSession = NULL;
}
}
else
{
if (Sess && Sess->clientSocket)
Sess->clientSocket->disconnectFromHost();
}
return;
}
void QtTermTCP::doYAPPSend()
{
QFileDialog dialog(this);
QStringList fileNames;
dialog.setFileMode(QFileDialog::AnyFile);
QMdiSubWindow * UI;
Ui_ListenSession * Sess = nullptr;
int i;
if (TermMode == MDI)
{
UI = mdiArea->activeSubWindow();
for (i = 0; i < _sessions.size(); ++i)
{
Sess = _sessions.at(i);
if (Sess->sw == UI)
break;
}
if (i == _sessions.size())
return;
}
else if (TermMode == Tabbed)
{
Sess = (Ui_ListenSession *)tabWidget->currentWidget();
}
else if (TermMode == Single)
Sess = _sessions.at(0);
if (Sess == nullptr)
return;
if ((Sess->SessionType & Mon))
{
// Turn off monitoring
setTraceOff(Sess);
}
if (dialog.exec())
{
char FN[256];
fileNames = dialog.selectedFiles();
if (fileNames[0].length() < 256)
{
strcpy(FN, fileNames[0].toUtf8());
YAPPSendFile(Sess, FN);
}
}
}
void QtTermTCP::doYAPPSetRX()
{
QFileDialog dialog(this);
QStringList fileNames;
dialog.setFileMode(QFileDialog::Directory);
dialog.setDirectory(YAPPPath);
if (dialog.exec())
{
fileNames = dialog.selectedFiles();
if (fileNames[0].length() < 256)
strcpy(YAPPPath, fileNames[0].toUtf8());
SaveSettings();
}
}
Ui_SizeDialog * YappSize;
void QtTermTCP::doYAPPSetSize()
{
// This runs the VARA Configuration dialog
char valChar[80];
YappSize = new(Ui_SizeDialog);
QDialog UI;
YappSize->setupUi(&UI);
UI.setFont(*menufont);
deviceUI = &UI;
sprintf(valChar, "%d", MaxRXSize);
YappSize->maxSize->setText(valChar);
QObject::connect(YappSize->okButton, SIGNAL(clicked()), this, SLOT(sizeaccept()));
QObject::connect(YappSize->cancelButton, SIGNAL(clicked()), this, SLOT(sizereject()));
UI.exec();
}
void QtTermTCP::sizeaccept()
{
QVariant Q;
Q = YappSize->maxSize->text();
MaxRXSize = Q.toInt();
SaveSettings();
delete(YappSize);
}
void QtTermTCP::sizereject()
{
delete(YappSize);
deviceUI->reject();
}
void QtTermTCP::menuChecked()
{
QAction * Act = static_cast<QAction*>(QObject::sender());
int state = Act->isChecked();
int newTermMode = TermMode;
int newSingleMode = singlemodeFormat;
if (Act == TabSingle || Act == TabBoth || Act == TabMon)
{
// Tabbed Window format had changed
int i = tabWidget->currentIndex();
if (Act == TabSingle)
TabType[i] = Term;
else if (Act == TabBoth)
TabType[i] = Term + Mon;
else
TabType[i] = Mon;
QMessageBox msgBox;
msgBox.setText("Tab Types will change next time program is started.");
msgBox.exec();
}
if (Act == singleAct || Act == singleAct2 || Act == MDIAct || Act == tabbedAct)
{
// Term Mode had changed
if (singleAct->isChecked())
{
newTermMode = Single;
newSingleMode = Mon + Term;
}
else if (singleAct2->isChecked())
{
newTermMode = Single;
newSingleMode = Term;
}
else if (MDIAct->isChecked())
newTermMode = MDI;
else if (tabbedAct->isChecked())
newTermMode = Tabbed;
if (newTermMode != TermMode || newSingleMode != singlemodeFormat)
{
QSettings settings(GetConfPath(), QSettings::IniFormat);
settings.setValue("TermMode", newTermMode);
settings.setValue("singlemodeFormat", newSingleMode);
QMessageBox msgBox;
msgBox.setText("Presentation Mode will change next time program is started.");
msgBox.exec();
}
return;
}
if (Act == EnableMonitor)
ActiveSession->EnableMonitor = state;
else if (Act == EnableMonLog)
ActiveSession->LogMonitor = state;
else if (Act == MonLocalTime)
ActiveSession->mlocaltime = state;
else if (Act == MonTX)
ActiveSession->mtxparam = state;
else if (Act == MonSup)
ActiveSession->mcomparam = state;
else if (Act == MonUI)
ActiveSession->monUI = state;
else if (Act == MonNodes)
ActiveSession->MonitorNODES = state;
else if (Act == MonColour)
ActiveSession->MonitorColour = state;
else if (Act == actChatMode)
ChatMode = state;
else if (Act == actAutoTeletext)
AutoTeletext = state;
else if (Act == actBells)
Bells = state;
else if (Act == actStripLF)
StripLF = state;
else if (Act == actIntervalBeep)
AlertBeep = state;
else if (Act == actConnectBeep)
ConnectBeep = state;
else if (Act == actnoConv)
convUTF8 = -1;
else if (Act == actAuto)
convUTF8 = 0;
else if (Act == actCP1251)
convUTF8 = 1251;
else if (Act == actCP1252)
convUTF8 = 1252;
else if (Act == actCP437)
convUTF8 = 437;
else
{
// look for port entry
for (int i = 0; i < MAXPORTS + 1; i++)
{
if (Act == MonPort[i])
{
uint64_t mmask;
if (i == 0) // BBS Mon - use bit 63 (Port 64)
mmask = (uint64_t)1 << 63;
else
mmask = (uint64_t)1 << (i - 1);
if (state)
ActiveSession->portmask |= mmask;
else
ActiveSession->portmask &= ~mmask;
break;
}
}
}
if (ActiveSession->clientSocket && ActiveSession->SessionType & Mon)
SendTraceOptions(ActiveSession);
else if (AGWUsers && ActiveSession == AGWUsers->MonSess)
{
AGWMonEnable = ActiveSession->EnableMonitor;
AGWLocalTime = ActiveSession->mlocaltime;
AGWMonNodes = ActiveSession->MonitorNODES;
Send_AGW_m_Frame((QTcpSocket*)ActiveSession->AGWSession);
}
else if (ActiveSession == KISSMonSess)
{
KISSLocalTime = ActiveSession->mlocaltime;
KISSMonEnable = ActiveSession->EnableMonitor;
KISSMonNodes = ActiveSession->MonitorNODES;
}
return;
}
void QtTermTCP::LDisconnect(Ui_ListenSession * LUI)
{
if (LUI->clientSocket)
LUI->clientSocket->disconnectFromHost();
}
extern QApplication * a;
void QtTermTCP::LreturnPressed(Ui_ListenSession * Sess)
{
QByteArray stringData = Sess->inputWindow->text().toUtf8();
// if multiline input (eg from copy/paste) replace LF with CR
char * ptr;
char * Msgptr;
char * Msg;
QScrollBar *scrollbar = Sess->termWindow->verticalScrollBar();
bool scrollbarAtBottom = (scrollbar->value() >= (scrollbar->maximum() - 4));
if (scrollbarAtBottom)
Sess->termWindow->moveCursor(QTextCursor::End); // So we don't get blank lines
// Stack it
Sess->StackIndex = 0;
if (Sess->KbdStack[49])
free(Sess->KbdStack[49]);
for (int i = 48; i >= 0; i--)
{
Sess->KbdStack[i + 1] = Sess->KbdStack[i];
}
Sess->KbdStack[0] = qstrdup(stringData.data());
stringData.append('\n');
Msgptr = stringData.data();
if (Sess->TTActive) // Teletext uses 0x5F for #
while ((ptr = strchr(Msgptr, 0x23))) // Replace VT with LF
*ptr++ = 0x5f;
while ((ptr = strchr(Msgptr, 11))) // Replace VT with LF
*ptr++ = 10;
LastWrite = time(NULL); // Stop initial beep
Sess->SlowTimer = 0;
Sess->pageBuffer[0] = 0; // Reset Teletext mode screen buffer
if (Sess->KISSMode == 1)
{
// UI session. Send as UI Message
while ((ptr = strchr(Msgptr, '\n')))
{
*ptr++ = 0;
Msg = (char *)malloc(strlen(Msgptr) + 10);
strcpy(Msg, Msgptr);
strcat(Msg, "\r");
Send_UI(Sess->UIPORT, 0xF0, KISSMYCALL, Sess->UIDEST, Sess->UIPATH, (unsigned char *)Msg, (int)strlen(Msg));
WritetoOutputWindowEx(Sess, (unsigned char *)Msg, (int)strlen(Msg),
Sess->termWindow, &Sess->OutputSaveLen, Sess->OutputSave, EchoText); // Black
free(Msg);
Msgptr = ptr;
}
}
else if (Sess->KISSSession)
{
// Send to ax.25 code
while ((ptr = strchr(Msgptr, '\n')))
{
*ptr++ = 0;
Msg = (char *)malloc(strlen(Msgptr) + 10);
strcpy(Msg, Msgptr);
strcat(Msg, "\r");
SendtoAX25(Sess->KISSSession, (unsigned char *)Msg, (int)strlen(Msg));
WritetoOutputWindowEx(Sess, (unsigned char *)Msg, (int)strlen(Msg),
Sess->termWindow, &Sess->OutputSaveLen, Sess->OutputSave, EchoText); // Black
free(Msg);
Msgptr = ptr;
}
}
else if (Sess->AGWSession)
{
// Terminal is in AGWPE mode - send as AGW frame
while ((ptr = strchr(Msgptr, '\n')))
{
*ptr++ = 0;
Msg = (char *)malloc(strlen(Msgptr) + 10);
strcpy(Msg, Msgptr);
strcat(Msg, "\r");
AGW_AX25_data_in(Sess->AGWSession, (unsigned char *)Msg, (int)strlen(Msg));
WritetoOutputWindowEx(Sess, (unsigned char *)Msg, (int)strlen(Msg),
Sess->termWindow, &Sess->OutputSaveLen, Sess->OutputSave, EchoText); // Black
// Sess->termWindow->insertPlainText(Msg);
free(Msg);
Msgptr = ptr;
}
}
else if (VARASock && VARASock->Sess == Sess)
{
while ((ptr = strchr(Msgptr, '\n')))
{
*ptr++ = 0;
Msg = (char *)malloc(strlen(Msgptr) + 10);
strcpy(Msg, Msgptr);
strcat(Msg, "\r");
VARADataSock->write(Msg);
WritetoOutputWindowEx(Sess, (unsigned char *)Msg, (int)strlen(Msg),
Sess->termWindow, &Sess->OutputSaveLen, Sess->OutputSave, EchoText); // Black
// Sess->termWindow->insertPlainText(Msg);
free(Msg);
Msgptr = ptr;
}
}
else if (Sess->clientSocket && Sess->clientSocket->state() == QAbstractSocket::ConnectedState)
{
while ((ptr = strchr(Msgptr, '\n')))
{
*ptr++ = 0;
Msg = (char *)malloc(strlen(Msgptr) + 10);
strcpy(Msg, Msgptr);
strcat(Msg, "\r");
Sess->clientSocket->write(Msg);
WritetoOutputWindowEx(Sess, (unsigned char *)Msg, (int)strlen(Msg),
Sess->termWindow, &Sess->OutputSaveLen, Sess->OutputSave, EchoText); // Black
// Sess->termWindow->insertPlainText(Msg);
free(Msg);
Msgptr = ptr;
}
}
else
{
// Not Connected
if (Sess->SessionType != Listen)
{
if (Sess->CurrentHost < MAXHOSTS)
{
// Last connect was TCP
char Msg[] = "Connecting....\r";
WritetoOutputWindowEx(Sess, (unsigned char *)Msg, (int)strlen(Msg),
Sess->termWindow, &Sess->OutputSaveLen, Sess->OutputSave, WarningText); // Red
delete(Sess->clientSocket);
Sess->clientSocket = new myTcpSocket();
Sess->clientSocket->Sess = Sess;
connect(Sess->clientSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(displayError(QAbstractSocket::SocketError)));
connect(Sess->clientSocket, SIGNAL(readyRead()), this, SLOT(readyRead()));
connect(Sess->clientSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onSocketStateChanged(QAbstractSocket::SocketState)));
Sess->clientSocket->connectToHost(&Host[Sess->CurrentHost][0], Port[Sess->CurrentHost]);
}
else if (Sess->CurrentHost == 16) // AGW
{
// Invoke AGW connect dialog
AGWConnect dialog(0);
dialog.exec();
WritetoOutputWindow(Sess, (unsigned char *)"Connecting...\r", 14);
return;
}
else if (Sess->CurrentHost == 17) // VARA
{
// Invoke VARA connect dialog
VARAConnect dialog(0);
dialog.exec();
WritetoOutputWindow(Sess, (unsigned char *)"Connecting...\r", 14);
return;
}
else if (Sess->CurrentHost == 18) // KISS
{
// Do we send as UI or invoke kiss connect dialog ??
// Try Connect Dialog for now - may make a menu option
KISSConnect dialog(0);
dialog.exec();
WritetoOutputWindow(Sess, (unsigned char *)"Connecting...\r", 14);
return;
}
}
else
{
char Msg[] = "Incoming Session - Can't Connect\r";
WritetoOutputWindowEx(Sess, (unsigned char *)Msg, (int)strlen(Msg),
Sess->termWindow, &Sess->OutputSaveLen, Sess->OutputSave, 81); // Red
}
}
if (scrollbarAtBottom)
Sess->termWindow->moveCursor(QTextCursor::End);
Sess->inputWindow->setText("");
a->inputMethod()->hide();
}
void QtTermTCP::displayError(QAbstractSocket::SocketError socketError)
{
myTcpSocket* sender = static_cast<myTcpSocket*>(QObject::sender());
switch (socketError)
{
case QAbstractSocket::RemoteHostClosedError:
break;
case QAbstractSocket::HostNotFoundError:
QMessageBox::information(this, tr("QtTermTCP"),
tr("The host was not found. Please check the "
"host name and portsettings->"));
break;
case QAbstractSocket::ConnectionRefusedError:
QMessageBox::information(this, tr("QtTermTCP"),
tr("The connection was refused by the peer."));
break;
default:
QMessageBox::information(this, tr("QtTermTCP"),
tr("The following error occurred: %1.")
.arg(sender->errorString()));
}
connectMenu->setEnabled(true);
}
void QtTermTCP::readyRead()
{
int Read;
unsigned char Buffer[8192];
myTcpSocket* Socket = static_cast<myTcpSocket*>(QObject::sender());
Ui_ListenSession * Sess = (Ui_ListenSession *)Socket->Sess;
// read the data from the socket
Read = Socket->read((char *)Buffer, 2047);
while (Read > 0)
{
// if (InputMode == 'Y') // Yapp
// {
// QString myString = QString::fromUtf8((char*)Buffer, Read);
// QByteArray ptr = myString.toLocal8Bit();
// memcpy(Buffer, ptr.data(), ptr.length());
// Read = ptr.length();
// }
Buffer[Read] = 0;
ProcessReceivedData(Sess, Buffer, Read);
QString myString = QString::fromUtf8((char*)Buffer);
// qDebug() << myString;
Read = Socket->read((char *)Buffer, 2047);
}
}
extern "C" int SocketSend(Ui_ListenSession * Sess, char * Buffer, int len)
{
myTcpSocket *Socket = Sess->clientSocket;
if (Socket && Socket->state() == QAbstractSocket::ConnectedState)
return Socket->write(Buffer, len);
return 0;
}
extern "C" int SocketFlush(Ui_ListenSession * Sess)
{
if (Sess->AGWSession || Sess->KISSSession || (VARASock && VARASock->Sess == Sess))
return 0;
myTcpSocket* Socket = Sess->clientSocket;
if (Socket && Socket->state() == QAbstractSocket::ConnectedState)
return Socket->flush();
return 0;
}
extern "C" void mySleep(int ms)
{
QThread::msleep(ms);
}
extern "C" void SetPortMonLine(int i, char * Text, int visible, int enabled)
{
MonPort[i]->setText(Text);
MonPort[i]->setVisible(visible);
MonPort[i]->setChecked(enabled);
}
bool scrollbarAtBottom = 0;
extern "C" void WritetoOutputWindowEx(Ui_ListenSession * Sess, unsigned char * Buffer, int len, QTextEdit * termWindow, int * OutputSaveLen, char * OutputSave, QColor Colour);
extern "C" void WritetoOutputWindow(Ui_ListenSession * Sess, unsigned char * Buffer, int len)
{
WritetoOutputWindowEx(Sess, Buffer, len, Sess->termWindow, &Sess->OutputSaveLen, Sess->OutputSave, outputText);
}
extern "C" void WritetoOutputWindowEx(Ui_ListenSession * Sess, unsigned char * Buffer, int len, QTextEdit * termWindow, int * OutputSaveLen, char * OutputSave, QColor Colour)
{
unsigned char Copy[8192];
unsigned char * ptr1, *ptr2;
unsigned char Line[8192];
unsigned char out[8192];
int outlen;
int num;
if (termWindow == NULL)
return;
time_t NOW = time(NULL);
// Beep if no output for a while
if (AlertInterval && (NOW - LastWrite) > AlertInterval)
{
if (AlertBeep)
myBeep(&IntervalWAV);
}
// Alert if QtTerm not active window (unless Mon window)
if((Sess->SessionType & Mon) == 0)
FlashifNotActive();
// if tabbed and not active tab set tab label red
if (TermMode == Tabbed)
{
if (Sess->monWindow != termWindow) // Not if Monitor
{
if (ActiveSession != Sess)
{
tabWidget->tabBar()->setTabTextColor(Sess->Tab, newTabText);
}
}
}
LastWrite = NOW;
// Mustn't mess with original buffer
memcpy(Copy, Buffer, len);
Copy[len] = 0;
ptr1 = Copy;
const QTextCursor old_cursor = termWindow->textCursor();
const int old_scrollbar_value = termWindow->verticalScrollBar()->value();
const bool is_scrolled_down = old_scrollbar_value == termWindow->verticalScrollBar()->maximum();
if (*OutputSaveLen)
{
// Have part line - append to it
memcpy(&OutputSave[*OutputSaveLen], Copy, len);
*OutputSaveLen += len;
ptr1 = (unsigned char *)OutputSave;
len = *OutputSaveLen;
*OutputSaveLen = 0;
// part line was written to screen so remove it
// termWindow->setFocus();
termWindow->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor);
termWindow->moveCursor(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
termWindow->moveCursor(QTextCursor::End, QTextCursor::KeepAnchor);
termWindow->textCursor().removeSelectedText();
// termWindow->textCursor().deletePreviousChar();
}
// Move the cursor to the end of the document.
termWindow->moveCursor(QTextCursor::End);
// Insert the text at the position of the cursor (which is the end of the document).
lineloop:
if (len <= 0)
{
if (old_cursor.hasSelection() || !is_scrolled_down)
{
// The user has selected text or scrolled away from the bottom: maintain position.
termWindow->setTextCursor(old_cursor);
termWindow->verticalScrollBar()->setValue(old_scrollbar_value);
}
else
{
// The user hasn't selected any text and the scrollbar is at the bottom: scroll to the bottom.
termWindow->moveCursor(QTextCursor::End);
termWindow->verticalScrollBar()->setValue(termWindow->verticalScrollBar()->maximum());
}
return;
}
// copy text to control a line at a time
ptr2 = (unsigned char *)memchr(ptr1, 13, len);
if (ptr2 == 0) // No CR
{
if (len > 8000)
len = 8000; // Should never get lines this long
memmove(OutputSave, ptr1, len);
*OutputSaveLen = len;
// Write part line to screen
memcpy(Line, ptr1, len);
Line[len] = 0;
// I don't think I need to worry if UTF8 as will be rewritten when rest arrives
if (Line[0] == 0x1b) // Colour Escape
{
if (Sess->MonitorColour)
termWindow->setTextColor(Colours[Line[1] - 10]);
termWindow->textCursor().insertText(QString::fromUtf8((char*)&Line[2]));
}
else
{
termWindow->setTextColor(Colour);
termWindow->textCursor().insertText(QString::fromUtf8((char*)Line));
}
// *OutputSaveLen = 0; // Test if we need to delete part line
// if (scrollbarAtBottom)
// termWindow->moveCursor(QTextCursor::End);
if (old_cursor.hasSelection() || !is_scrolled_down)
{
// The user has selected text or scrolled away from the bottom: maintain position.
termWindow->setTextCursor(old_cursor);
termWindow->verticalScrollBar()->setValue(old_scrollbar_value);
}
else
{
// The user hasn't selected any text and the scrollbar is at the bottom: scroll to the bottom.
termWindow->moveCursor(QTextCursor::End);
termWindow->verticalScrollBar()->setValue(termWindow->verticalScrollBar()->maximum());
}
return;
}
*(ptr2++) = 0;
if (Bells)
{
char * ptr;
do {
ptr = (char *)memchr(ptr1, 7, len);
if (ptr)
{
*(ptr) = 32;
myBeep(&BellWAV);
}
} while (ptr);
}
num = ptr2 - ptr1 - 1;
// if (LogMonitor) WriteMonitorLine(ptr1, ptr2 - ptr1);
memcpy(Line, ptr1, num);
Line[num++] = 13;
Line[num] = 0;
if (Line[0] == 0x1b) // Colour Escape
{
if (Sess->MonitorColour)
termWindow->setTextColor(Colours[Line[1] - 10]);
outlen = checkUTF8(&Line[2], num - 2, out);
out[outlen] = 0;
termWindow->textCursor().insertText(QString::fromUtf8((char*)out));
// termWindow->textCursor().insertText(QString::fromUtf8((char*)&Line[2]));
}
else
{
termWindow->setTextColor(Colour);
outlen = checkUTF8(Line, num, out);
out[outlen] = 0;
termWindow->textCursor().insertText(QString::fromUtf8((char*)out));
// termWindow->textCursor().insertText(QString::fromUtf8((char*)Line));
}
len -= (ptr2 - ptr1);
ptr1 = ptr2;
if ((len > 0) && StripLF)
{
if (*ptr1 == 0x0a) // Line Feed
{
ptr1++;
len--;
}
}
goto lineloop;
}
void WriteMonitorLog(Ui_ListenSession * Sess, char * Msg);
extern "C" void WritetoMonWindow(Ui_ListenSession * Sess, unsigned char * Msg, int len)
{
char * ptr1 = (char *)Msg, *ptr2;
char Line[512];
int num;
// QScrollBar *scrollbar = Sess->monWindow->verticalScrollBar();
// bool scrollbarAtBottom = (scrollbar->value() >= (scrollbar->maximum() - 4));
if (Sess->monWindow == nullptr)
return;
Sess->monWindow->setStyleSheet(monStyleSheet);
const QTextCursor old_cursor = Sess->monWindow->textCursor();
const int old_scrollbar_value = Sess->monWindow->verticalScrollBar()->value();
const bool is_scrolled_down = old_scrollbar_value == Sess->monWindow->verticalScrollBar()->maximum();
// Move the cursor to the end of the document.
Sess->monWindow->moveCursor(QTextCursor::End);
// Insert the text at the position of the cursor (which is the end of the document).
// Write a line at a time so we can action colour chars
// Buffer[Len] = 0;
// if (scrollbarAtBottom)
// Sess->monWindow->moveCursor(QTextCursor::End);
if (Sess->MonSaveLen)
{
// Have part line - append to it
memcpy(&Sess->MonSave[Sess->MonSaveLen], Msg, len);
Sess->MonSaveLen += len;
ptr1 = Sess->MonSave;
len = Sess->MonSaveLen;
Sess->MonSaveLen = 0;
}
lineloop:
if (len <= 0)
{
// if (scrollbarAtBottom)
// Sess->monWindow->moveCursor(QTextCursor::End);
if (old_cursor.hasSelection() || !is_scrolled_down)
{
// The user has selected text or scrolled away from the bottom: maintain position.
Sess->monWindow->setTextCursor(old_cursor);
Sess->monWindow->verticalScrollBar()->setValue(old_scrollbar_value);
}
else
{
// The user hasn't selected any text and the scrollbar is at the bottom: scroll to the bottom.
Sess->monWindow->moveCursor(QTextCursor::End);
Sess->monWindow->verticalScrollBar()->setValue(Sess->monWindow->verticalScrollBar()->maximum());
}
return;
}
// copy text to control a line at a time
ptr2 = (char *)memchr(ptr1, 13, len);
if (ptr2 == 0) // No CR
{
memmove(Sess->MonSave, ptr1, len);
Sess->MonSaveLen = len;
return;
}
*(ptr2++) = 0;
if (Sess->LogMonitor)
WriteMonitorLog(Sess, ptr1);
num = ptr2 - ptr1 - 1;
memcpy(Line, ptr1, num);
Line[num++] = 13;
Line[num] = 0;
if (Line[0] == 0x1b) // Colour Escape
{
if (Sess->MonitorColour)
{
if (Line[1] == 17)
Sess->monWindow->setTextColor(monRxText);
else
Sess->monWindow->setTextColor(monTxText);
}
else
Sess->monWindow->setTextColor(monOtherText);
Sess->monWindow->textCursor().insertText(QString::fromUtf8((char*)&Line[2]));
}
else
{
Sess->monWindow->textCursor().insertText(QString::fromUtf8((char*)Line));
}
len -= (ptr2 - ptr1);
ptr1 = ptr2;
goto lineloop;
}
int ProcessYAPPMessage(Ui_ListenSession * Sess, UCHAR * Msg, int Len);
void QueueMsg(Ui_ListenSession * Sess, char * Msg, int Len);
// YAPP stuff
#define SOH 1
#define STX 2
#define ETX 3
#define EOT 4
#define ENQ 5
#define ACK 6
#define DLE 0x10
#define NAK 0x15
#define CAN 0x18
extern "C" void SendtoTerm(Ui_ListenSession * Sess, char * Msg, int Len)
{
if (Sess == 0)
return;
Sess->SlowTimer = 0;
Msg[Len] = 0;
if (Sess->InputMode == 'Y') // Yapp
{
ProcessYAPPMessage(Sess, (unsigned char *)Msg, Len);
return;
}
// Could be a YAPP Header
if (Len == 2 && Msg[0] == ENQ && Msg[1] == 1) // YAPP Send_Init
{
char YAPPRR[2];
// Turn off monitoring
Sess->InputMode = 'Y';
YAPPRR[0] = ACK;
YAPPRR[1] = 1;
QueueMsg(Sess, YAPPRR, 2);
return;
}
if (AutoTeletext && (Msg[0] == 0x1e || Msg[0] == 0x0c))
{
if (Sess->TTActive == 0)
{
Sess->TTActive = 1;
DoTermResize(Sess);
}
}
if (Sess->TTActive)
{
// Feed to Teletext code
// We need to decode a whole page. There is no obvious delimiter so process till data stops.
// Buffer is cleared when next input is sent
if (strlen(&Sess->pageBuffer[0] + Len) > 4090)
Sess->pageBuffer[0] = 0; // Protect buffer
strcat(Sess->pageBuffer, (char *)Msg);
DecodeTeleText(Sess, (char *)Sess->pageBuffer); // Re-decode same data until we get the end
return;
}
WritetoOutputWindow(Sess, (unsigned char *)Msg, Len);
}
QSettings* settings;
// This makes geting settings for more channels easier
char Prefix[16] = "AX25_A";
void GetPortSettings(int Chan);
QVariant getAX25Param(const char * key, QVariant Default)
{
char fullKey[64];
sprintf(fullKey, "%s/%s", Prefix, key);
return settings->value(fullKey, Default);
}
void getAX25Params(int chan)
{
Prefix[5] = chan + 'A';
GetPortSettings(chan);
}
void GetPortSettings(int Chan)
{
txdelay[Chan] = getAX25Param("TXDelay", 250).toInt();
sendTXDelay[Chan] = getAX25Param("sendTXDelay", 0).toInt();
maxframe[Chan] = getAX25Param("Maxframe", 4).toInt();
fracks[Chan] = getAX25Param("Retries", 10).toInt();
frack_time[Chan] = getAX25Param("FrackTime", 8).toInt();
persist[Chan] = getAX25Param("Persist", 128).toInt();
kisspaclen[Chan] = getAX25Param("Paclen", 128).toInt();
idletime[Chan] = getAX25Param("IdleTime", 180).toInt();
slottime[Chan] = getAX25Param("SlotTime", 100).toInt();
resptime[Chan] = getAX25Param("RespTime", 1500).toInt();
TXFrmMode[Chan] = getAX25Param("TXFrmMode", 1).toInt();
max_frame_collector[Chan] = getAX25Param("FrameCollector", 6).toInt();
//exclude_callsigns[Chan]= getAX25Param("ExcludeCallsigns/");
//exclude_APRS_frm[Chan]= getAX25Param("ExcludeAPRSFrmType/");
KISS_opt[Chan] = getAX25Param("KISSOptimization", false).toInt();;
dyn_frack[Chan] = getAX25Param("DynamicFrack", false).toInt();;
IPOLL[Chan] = getAX25Param("IPOLL", 80).toInt();
}
void GetSettings()
{
QByteArray qb;
int i;
char Key[16];
char Param[256];
settings = new QSettings(GetConfPath(), QSettings::IniFormat);
// Get saved session definitions
sessionList = strdup(settings->value("Sessions", "1|3, 0, 5, 5, 600, 800|").toString().toUtf8());
for (i = 0; i < MAXHOSTS; i++)
{
sprintf(Key, "HostParams%d", i);
qb = settings->value(Key).toByteArray();
DecodeSettingsLine(i, qb.data());
}
Split = settings->value("Split", 50).toInt();
ChatMode = settings->value("ChatMode", 1).toInt();
AutoTeletext = settings->value("AutoTeletext", 0).toInt();
Bells = settings->value("Bells", 1).toInt();
StripLF = settings->value("StripLF", 1).toInt();
useBeep = settings->value("useBeep", true).toBool();
AlertBeep = settings->value("AlertBeep", 1).toInt();
AlertInterval = settings->value("AlertInterval", 300).toInt();
ConnectBeep = settings->value("ConnectBeep", 1).toInt();
ConnectWAV = settings->value("ConnectWAV", ":/PCBeep").toString().toUtf8();
AlertWAV = settings->value("AlertWAV", ":/PCBeep").toString().toUtf8();
BellWAV = settings->value("BellWAV", ":/PCBeep").toString().toUtf8();
IntervalWAV = settings->value("IntervalWAV", ":/PCBeep").toString().toUtf8();
UseKeywords = settings->value("UseKeywords", 0).toInt();
KeyWordsFile = settings->value("KeyWordsFile", "keywords.sys").toString().toUtf8();
SavedHost = settings->value("CurrentHost", 0).toInt();
strcpy(YAPPPath, settings->value("YAPPPath", "").toString().toUtf8());
MaxRXSize = settings->value("MaxRXSize", 100000).toInt();
listenPort = settings->value("listenPort", 8015).toInt();
listenEnable = settings->value("listenEnable", false).toBool();
strcpy(listenCText, settings->value("listenCText", "").toString().toUtf8());
TermMode = settings->value("TermMode", 0).toInt();
singlemodeFormat = settings->value("singlemodeFormat", Term + Mon).toInt();
AGWEnable = settings->value("AGWEnable", 0).toInt();
AGWMonEnable = settings->value("AGWMonEnable", 0).toInt();
AGWLocalTime = settings->value("AGWLocalTime", 0).toInt();
AGWMonNodes = settings->value("AGWMonNodes", 0).toInt();
strcpy(AGWTermCall, settings->value("AGWTermCall", "").toString().toUtf8());
strcpy(AGWBeaconDest, settings->value("AGWBeaconDest", "").toString().toUtf8());
strcpy(AGWBeaconPath, settings->value("AGWBeaconPath", "").toString().toUtf8());
AGWBeaconInterval = settings->value("AGWBeaconInterval", 0).toInt();
strcpy(AGWBeaconPorts, settings->value("AGWBeaconPorts", "").toString().toUtf8());
strcpy(AGWBeaconMsg, settings->value("AGWBeaconText", "").toString().toUtf8());
strcpy(AGWHost, settings->value("AGWHost", "127.0.0.1").toString().toUtf8());
AGWPortNum = settings->value("AGWPort", 8000).toInt();
AGWPaclen = settings->value("AGWPaclen", 80).toInt();
AGWToCalls = settings->value("AGWToCalls", "").toStringList();
convUTF8 = settings->value("convUTF8", 0).toInt();
KISSEnable = settings->value("KISSEnable", 0).toInt();
KISSMonEnable = settings->value("KISSMonEnable", 1).toInt();
KISSLocalTime = settings->value("KISSLocalTime", 0).toInt();
KISSMonNodes = settings->value("KISSMonNodes", 0).toInt();
KISSListen = settings->value("KISSListen", 1).toInt();
KISSChecksum = settings->value("KISSChecksum", 0).toInt();
KISSAckMode = settings->value("KISSAckMode", 0).toInt();
strcpy(KISSMYCALL, settings->value("MYCALL", "").toString().toUtf8());
strcpy(KISSHost, settings->value("KISSHost", "127.0.0.1").toString().toUtf8());
KISSPortNum = settings->value("KISSPort", 8100).toInt();
KISSMode = settings->value("KISSMode", 0).toInt();
strcpy(KISSVia, settings->value("KISSVia", "").toString().toUtf8());
strcpy(SerialPort, settings->value("KISSSerialPort", "None").toString().toUtf8());
KISSBAUD = settings->value("KISSBAUD", 19200).toInt();
getAX25Params(0);
VARAEnable = settings->value("VARAEnable", 0).toInt();
strcpy(VARAHost, settings->value("VARAHost", "127.0.0.1").toString().toUtf8());
strcpy(VARAHostFM, settings->value("VARAHostFM", "127.0.0.1").toString().toUtf8());
strcpy(VARAHostHF, settings->value("VARAHostHF", "127.0.0.1").toString().toUtf8());
strcpy(VARAHostSAT, settings->value("VARAHostSAT", "127.0.0.1").toString().toUtf8());
VARAPortNum = settings->value("VARAPort", 8300).toInt();
VARAPortHF = settings->value("VARAPortHF", 8300).toInt();
VARAPortFM = settings->value("VARAPortFM", 8300).toInt();
VARAPortSAT = settings->value("VARAPortSAT", 8300).toInt();
strcpy(VARAPath, settings->value("VARAPath", "C:\\VARA\\VARA.exe").toString().toUtf8());
strcpy(VARAPathHF, settings->value("VARAPathHF", "C:\\VARA\\VARA.exe").toString().toUtf8());
strcpy(VARAPathFM, settings->value("VARAPathFM", "C:\\VARA\\VARAFM.exe").toString().toUtf8());
strcpy(VARAPathSAT, settings->value("VARAPathSAT", "C:\\VARA\\VARASAT.exe").toString().toUtf8());
strcpy(VARATermCall, settings->value("VARATermCall", "").toString().toUtf8());
VARA500 = settings->value("VARA500", 0).toInt();
VARA2300 = settings->value("VARA2300", 1).toInt();
VARA2750 = settings->value("VARA2750", 0).toInt();
VARAHF = settings->value("VARAHF", 1).toInt();
VARAFM = settings->value("VARAFM", 0).toInt();
VARASAT = settings->value("VARASAT", 0).toInt();
strcpy(VARAInit, settings->value("VARAInit", "").toString().toUtf8());
strcpy(PTTPort, settings->value("PTT", "None").toString().toUtf8());
PTTMode = settings->value("PTTMode", 19200).toInt();
PTTBAUD = settings->value("PTTBAUD", 19200).toInt();
CATHex = settings->value("CATHex", 1).toInt();
strcpy(PTTOnString, settings->value("PTTOnString", "").toString().toUtf8());
strcpy(PTTOffString, settings->value("PTTOffString", "").toString().toUtf8());
pttGPIOPin = settings->value("pttGPIOPin", 17).toInt();
pttGPIOPinR = settings->value("pttGPIOPinR", 17).toInt();
#ifdef WIN32
strcpy(CM108Addr, settings->value("CM108Addr", "0xD8C:0x08").toString().toUtf8());
#else
strcpy(CM108Addr, settings->value("CM108Addr", "/dev/hidraw0").toString().toUtf8());
#endif
HamLibPort = settings->value("HamLibPort", 4532).toInt();
strcpy(HamLibHost, settings->value("HamLibHost", "127.0.0.1").toString().toUtf8());
FLRigPort = settings->value("FLRigPort", 12345).toInt();
strcpy(FLRigHost, settings->value("FLRigHost", "127.0.0.1").toString().toUtf8());
strcpy(Param, settings->value("TabType", "1 1 1 1 1 1 1 2 2").toString().toUtf8());
sscanf(Param, "%d %d %d %d %d %d %d %d %d %d",
&TabType[0], &TabType[1], &TabType[2], &TabType[3], &TabType[4],
&TabType[5], &TabType[6], &TabType[7], &TabType[8], &TabType[9]);
strcpy(Param, settings->value("AutoConnect", "0, 0 ,0, 0, 0, 0, 0, 0, 0, 0").toString().toUtf8());
sscanf(Param, "%d %d %d %d %d %d %d %d %d %d",
&AutoConnect[0], &AutoConnect[1], &AutoConnect[2], &AutoConnect[3], &AutoConnect[4],
&AutoConnect[5], &AutoConnect[6], &AutoConnect[7], &AutoConnect[8], &AutoConnect[9]);
strcpy(Param, settings->value("currentHost", "0, 0 ,0, 0, 0, 0, 0, 0, 0, 0").toString().toUtf8());
sscanf(Param, "%d %d %d %d %d %d %d %d %d %d",
&currentHost[0], &currentHost[1], &currentHost[2], &currentHost[3], &currentHost[4],
&currentHost[5], &currentHost[6], &currentHost[7], &currentHost[8], &currentHost[9]);
monBackground = settings->value("monBackground", QColor(255, 255, 255)).value<QColor>();
monRxText = settings->value("monRxText", QColor(0, 0, 255)).value<QColor>();
monTxText = settings->value("monTxText", QColor(255, 0, 0)).value<QColor>();
monOtherText = settings->value("monOtherText", QColor(0, 0, 0)).value<QColor>();
termBackground = settings->value("termBackground", QColor(255, 255, 255)).value<QColor>();
outputText = settings->value("outputText", QColor(0, 0, 255)).value<QColor>();
EchoText = settings->value("EchoText", QColor(0, 0, 0)).value<QColor>();
WarningText = settings->value("WarningText", QColor(255, 0, 0)).value<QColor>();
inputBackground = settings->value("inputBackground", QColor(255, 255, 255)).value<QColor>();
inputText = settings->value("inputText", QColor(0, 0, 0)).value<QColor>();
delete(settings);
}
void SavePortSettings(int Chan);
void saveAX25Param(const char * key, QVariant Value)
{
char fullKey[64];
sprintf(fullKey, "%s/%s", Prefix, key);
settings->setValue(fullKey, Value);
}
void saveAX25Params(int chan)
{
Prefix[5] = chan + 'A';
SavePortSettings(chan);
}
void SavePortSettings(int Chan)
{
saveAX25Param("TXDelay", txdelay[Chan]);
saveAX25Param("sendTXDelay", sendTXDelay[Chan]);
saveAX25Param("Maxframe", maxframe[Chan]);
saveAX25Param("Paclen", kisspaclen[Chan]);
saveAX25Param("FrackTime", frack_time[Chan]);
saveAX25Param("IdleTime", idletime[Chan]);
saveAX25Param("SlotTime", slottime[Chan]);
saveAX25Param("Persist", persist[Chan]);
saveAX25Param("RespTime", resptime[Chan]);
saveAX25Param("TXFrmMode", TXFrmMode[Chan]);
saveAX25Param("FrameCollector", max_frame_collector[Chan]);
saveAX25Param("ExcludeCallsigns", exclude_callsigns[Chan]);
saveAX25Param("ExcludeAPRSFrmType", exclude_APRS_frm[Chan]);
saveAX25Param("KISSOptimization", KISS_opt[Chan]);
saveAX25Param("DynamicFrack", dyn_frack[Chan]);
saveAX25Param("BitRecovery", recovery[Chan]);
saveAX25Param("IPOLL", IPOLL[Chan]);
saveAX25Param("MyDigiCall", MyDigiCall[Chan]);
}
extern "C" void SaveSettings()
{
int i;
char Param[512];
char Key[16];
settings = new QSettings(GetConfPath(), QSettings::IniFormat);
for (i = 0; i < MAXHOSTS; i++)
{
sprintf(Key, "HostParams%d", i);
EncodeSettingsLine(i, Param);
settings->setValue(Key, Param);
}
settings->setValue("Split", Split);
settings->setValue("ChatMode", ChatMode);
settings->setValue("AutoTeletext", AutoTeletext);
settings->setValue("Bells", Bells);
settings->setValue("StripLF", StripLF);
settings->setValue("AlertBeep", AlertBeep);
settings->setValue("useBeep", useBeep);
settings->setValue("ConnectBeep", ConnectBeep);
settings->setValue("BellWAV", BellWAV);
settings->setValue("AlertWAV", AlertWAV);
settings->setValue("IntervalWAV", IntervalWAV);
settings->setValue("ConnectWAV", ConnectWAV);
settings->setValue("UseKeywords", UseKeywords);
settings->setValue("KeyWordsFile", KeyWordsFile);
settings->setValue("AlertInterval", AlertInterval);
settings->setValue("CurrentHost", SavedHost);
settings->setValue("YAPPPath", YAPPPath);
settings->setValue("MaxRXSize", MaxRXSize);
settings->setValue("listenPort", listenPort);
settings->setValue("listenEnable", listenEnable);
settings->setValue("listenCText", listenCText);
settings->setValue("convUTF8", convUTF8);
settings->setValue("PTT", PTTPort);
settings->setValue("PTTBAUD", PTTBAUD);
settings->setValue("PTTMode", PTTMode);
settings->setValue("CATHex", CATHex);
settings->setValue("PTTOffString", PTTOffString);
settings->setValue("PTTOnString", PTTOnString);
settings->setValue("pttGPIOPin", pttGPIOPin);
settings->setValue("pttGPIOPinR", pttGPIOPinR);
settings->setValue("CM108Addr", CM108Addr);
settings->setValue("HamLibPort", HamLibPort);
settings->setValue("HamLibHost", HamLibHost);
settings->setValue("FLRigPort", FLRigPort);
settings->setValue("FLRigHost", FLRigHost);
// Save Sessions
char SessionString[1024];
int SessStringLen;
SessStringLen = sprintf(SessionString, "%d|", _sessions.size());
if (TermMode == MDI)
{
for (int i = 0; i < _sessions.size(); ++i)
{
Ui_ListenSession * Sess = _sessions.at(i);
QRect r = Sess->sw->geometry();
SessStringLen += sprintf(&SessionString[SessStringLen],
"%d, %d, %d, %d, %d, %d|", Sess->SessionType, Sess->CurrentHost, r.top(), r.left(), r.bottom(), r.right());
}
settings->setValue("Sessions", SessionString);
}
settings->setValue("AGWEnable", AGWEnable);
settings->setValue("AGWMonEnable", AGWMonEnable);
settings->setValue("AGWLocalTime", AGWLocalTime);
settings->setValue("AGWMonNodes", AGWMonNodes);
settings->setValue("AGWTermCall", AGWTermCall);
settings->setValue("AGWBeaconDest", AGWBeaconDest);
settings->setValue("AGWBeaconPath", AGWBeaconPath);
settings->setValue("AGWBeaconInterval", AGWBeaconInterval);
settings->setValue("AGWBeaconPorts", AGWBeaconPorts);
settings->setValue("AGWBeaconText", AGWBeaconMsg);
settings->setValue("AGWHost", AGWHost);
settings->setValue("AGWPort", AGWPortNum);
settings->setValue("AGWPaclen", AGWPaclen);
settings->setValue("AGWToCalls", AGWToCalls);
settings->setValue("KISSEnable", KISSEnable);
settings->setValue("KISSMonEnable", KISSMonEnable);
settings->setValue("KISSLocalTime", KISSLocalTime);
settings->setValue("KISSMonNodes", KISSMonNodes);
settings->setValue("KISSListen", KISSListen);
settings->setValue("KISSChecksum", KISSChecksum);
settings->setValue("KISSAckMode", KISSAckMode);
settings->setValue("MYCALL", KISSMYCALL);
settings->setValue("KISSHost", KISSHost);
settings->setValue("KISSMode", KISSMode);
settings->setValue("KISSPort", KISSPortNum);
settings->setValue("KISSSerialPort", SerialPort);
settings->setValue("KISSBAUD", KISSBAUD);
settings->setValue("KISSVia", KISSVia);
saveAX25Params(0);
settings->setValue("VARAEnable", VARAEnable);
settings->setValue("VARATermCall", VARATermCall);
settings->setValue("VARAHost", VARAHost);
settings->setValue("VARAPort", VARAPortNum);
settings->setValue("VARAInit", VARAInit);
settings->setValue("VARAPath", VARAPath);
settings->setValue("VARAHostHF", VARAHostHF);
settings->setValue("VARAPortHF", VARAPortHF);
settings->setValue("VARAPathHF", VARAPathHF);
settings->setValue("VARAHostFM", VARAHostFM);
settings->setValue("VARAPortFM", VARAPortFM);
settings->setValue("VARAPathFM", VARAPathFM);
settings->setValue("VARAHostSAT", VARAHostSAT);
settings->setValue("VARAPortSAT", VARAPortSAT);
settings->setValue("VARAPathSAT", VARAPathSAT);
settings->setValue("VARA500", VARA500);
settings->setValue("VARA2300", VARA2300);
settings->setValue("VARA2750", VARA2750);
settings->setValue("VARAHF", VARAHF);
settings->setValue("VARAFM", VARAFM);
settings->setValue("VARASAT", VARASAT);
sprintf(Param, "%d %d %d %d %d %d %d %d %d %d",
TabType[0], TabType[1], TabType[2], TabType[3], TabType[4], TabType[5], TabType[6], TabType[7], TabType[8], TabType[9]);
settings->setValue("TabType", Param);
sprintf(Param, "%d %d %d %d %d %d %d %d %d %d",
AutoConnect[0], AutoConnect[1], AutoConnect[2], AutoConnect[3], AutoConnect[4], AutoConnect[5], AutoConnect[6], AutoConnect[7], AutoConnect[8], AutoConnect[9]);
settings->setValue("AutoConnect", Param);
sprintf(Param, "%d %d %d %d %d %d %d %d %d %d",
currentHost[0], currentHost[1], currentHost[2], currentHost[3], currentHost[4], currentHost[5], currentHost[6], currentHost[7], currentHost[8], currentHost[9]);
settings->setValue("currentHost", Param);
settings->setValue("monBackground", monBackground);
settings->setValue("monRxText", monRxText);
settings->setValue("monTxText", monTxText);
settings->setValue("monOtherText", monOtherText);
settings->setValue("termBackground", termBackground);
settings->setValue("outputText", outputText);
settings->setValue("EchoText", EchoText);
settings->setValue("WarningText", WarningText);
settings->setValue("inputBackground", inputBackground);
settings->setValue("inputText", inputText);
settings->sync();
delete(settings);
}
#include <QCloseEvent>
void QtTermTCP::closeEvent(QCloseEvent *event)
{
QMessageBox::StandardButton resBtn = QMessageBox::question(this, "QtTermTCP",
tr("Are you sure?\n"),
QMessageBox::Cancel | QMessageBox::No | QMessageBox::Yes,
QMessageBox::Yes);
if (resBtn != QMessageBox::Yes) {
event->ignore();
}
else
{
event->accept();
#ifdef USESERIAL
if (hPTTDevice)
hPTTDevice->close();
#endif
if (process)
process->close();
}
}
QtTermTCP::~QtTermTCP()
{
Ui_ListenSession * Sess;
for (int i = 0; i < _sessions.size(); ++i)
{
Sess = _sessions.at(i);
if (Sess->monLogfile)
Sess->monLogfile->close();
if (Sess->clientSocket)
{
int loops = 100;
Sess->clientSocket->disconnectFromHost();
while (Sess->clientSocket && loops-- && Sess->clientSocket->state() != QAbstractSocket::UnconnectedState)
QThread::msleep(10);
}
}
if (AGWSock && AGWSock->ConnectedState == QAbstractSocket::ConnectedState)
{
int loops = 100;
AGWSock->disconnectFromHost();
QThread::msleep(10);
while (AGWSock && loops-- && AGWSock->state() != QAbstractSocket::UnconnectedState)
QThread::msleep(10);
}
if (_server->isListening())
_server->close();
delete(_server);
QSettings mysettings(GetConfPath(), QSettings::IniFormat);
mysettings.setValue("geometry", saveGeometry());
mysettings.setValue("windowState", saveState());
SaveSettings();
}
extern "C" void timer_event();
void QtTermTCP::KISSTimerSlot()
{
// Runs every 100 mS
timer_event();
}
void QtTermTCP::MyTimerSlot()
{
// Runs every 10 seconds
Ui_ListenSession * Sess;
for (int i = 0; i < _sessions.size(); ++i)
{
Sess = _sessions.at(i);
// for (Ui_ListenSession * Sess : _sessions)
// {
if (Sess == NULL)
continue;
if (!ChatMode)
continue;
if (Sess->KISSSession ||
(Sess->clientSocket && Sess->clientSocket->state() == QAbstractSocket::ConnectedState))
{
Sess->SlowTimer++;
if (Sess->SlowTimer > 54) // About 9 mins
{
unsigned char Msg[2] = "";
Sess->SlowTimer = 0;
if (Sess->KISSSession)
{
TAX25Port * ax25 = (TAX25Port *)Sess->KISSSession;
if (ax25->status == STAT_LINK)
SendtoAX25(Sess->KISSSession, Msg, 1);
}
else
Sess->clientSocket->write("\0", 1);
}
}
}
if (AGWEnable)
AGWTimer();
if (VARAEnable)
VARATimer();
if (KISSEnable)
KISSTimer();
}
void QtTermTCP::SlowTimerSlot()
{
// Runs every 60 S
Ui_ListenSession * Sess;
for (int i = 0; i < _sessions.size(); ++i)
{
Sess = _sessions.at(i);
if (Sess->monLogfile)
{
Sess->monLogfile->close();
Sess->monLogfile = nullptr;
}
}
}
extern "C" void myBeep(QString * WAV)
{
if (useBeep)
{
QApplication::beep();
return;
}
// Using .wav files
// QSoundEffect effect;
// effect.setSource(QUrl::fromLocalFile(*WAV));
// effect.setLoopCount(1);
// effect.setVolume(1.0f);
// effect.play();
QSound::play(*WAV);
}
void QtTermTCP::ListenSlot()
{
// This runs the Listen Configuration dialog
ListenDialog * xx = new ListenDialog();
xx->exec();
}
void QtTermTCP::AGWSlot()
{
// This runs the AGW Configuration dialog
AGWDialog dialog(0);
dialog.exec();
}
Ui_Dialog * Dev;
Ui_AlertDialog * Alert;
static Ui_KISSDialog * KISS;
static Ui_ColourDialog * COLOURS;
char NewPTTPort[80];
int newSoundMode = 0;
int oldSoundMode = 0;
#ifdef USESERIAL
QList<QSerialPortInfo> Ports = QSerialPortInfo::availablePorts();
#endif
void QtTermTCP::KISSSlot()
{
// This runs the KISS Configuration dialog
KISS = new(Ui_KISSDialog);
QDialog UI;
KISS->setupUi(&UI);
UI.setFont(*menufont);
deviceUI = &UI;
KISS->KISSEnable->setChecked(KISSEnable);
KISS->KISSListen->setChecked(KISSListen);
KISS->KISSChecksum->setChecked(KISSChecksum);
KISS->KISSACKMODE->setChecked(KISSAckMode);
KISS->MYCALL->setText(KISSMYCALL);
KISS->TXDELAY->setText(QString::number(txdelay[0]));
KISS->SetTXDelay->setChecked(sendTXDelay[0]);
// connect(KISS->SerialPort, SIGNAL(currentIndexChanged(int)), this, SLOT(PTTPortChanged(int)));
QStringList items;
#ifdef USESERIAL
for (const QSerialPortInfo &info : Ports)
{
items.append(info.portName());
}
items.sort();
items.insert(0, "TCP");
#endif
for (const QString &info : items)
{
KISS->SerialPort->addItem(info);
}
KISS->SerialPort->setCurrentIndex(KISS->SerialPort->findText(SerialPort, Qt::MatchFixedString));
KISS->Speed->setText(QString::number(KISSBAUD));
KISS->Host->setText(KISSHost);
KISS->Port->setText(QString::number(KISSPortNum));
KISS->Paclen->setText(QString::number(kisspaclen[0]));
KISS->Maxframe->setText(QString::number(maxframe[0]));
KISS->Frack->setText(QString::number(frack_time[0]));
KISS->Retries->setText(QString::number(fracks[0]));
QObject::connect(KISS->okButton, SIGNAL(clicked()), this, SLOT(KISSaccept()));
QObject::connect(KISS->cancelButton, SIGNAL(clicked()), this, SLOT(KISSreject()));
UI.exec();
}
void QtTermTCP::KISSaccept()
{
QVariant Q;
int OldEnable = KISSEnable;
char oldSerialPort[64];
int OldPort = KISSPortNum;
char oldHost[128];
strcpy(oldSerialPort, SerialPort);
strcpy(oldHost, KISSHost);
KISSEnable = KISS->KISSEnable->isChecked();
KISSListen = KISS->KISSListen->isChecked();
KISSChecksum = KISS->KISSChecksum->isChecked();
KISSAckMode = KISS->KISSACKMODE->isChecked();
actHost[18]->setVisible(KISSEnable); // Show KISS Connect Line
strcpy(KISSMYCALL, KISS->MYCALL->text().toUtf8().toUpper());
memset(axMYCALL, 0, 7);
ConvToAX25(KISSMYCALL, axMYCALL);
Q = KISS->Port->text();
KISSPortNum = Q.toInt();
Q = KISS->Speed->text();
KISSBAUD = Q.toInt();
strcpy(KISSHost, KISS->Host->text().toUtf8().toUpper());
Q = KISS->SerialPort->currentText();
strcpy(SerialPort, Q.toString().toUtf8());
Q = KISS->Paclen->text();
kisspaclen[0] = Q.toInt();
Q = KISS->Maxframe->text();
maxframe[0] = Q.toInt();
Q = KISS->Frack->text();
frack_time[0] = Q.toInt();
Q = KISS->Retries->text();
fracks[0] = Q.toInt();
Q = KISS->TXDELAY->text();
txdelay[0] = Q.toInt();
sendTXDelay[0] = KISS->SetTXDelay->isChecked();
myStatusBar->setVisible(AGWEnable | VARAEnable | KISSEnable);
if (KISSEnable != OldEnable || KISSPortNum != OldPort ||
strcmp(oldHost, KISSHost) != 0 ||
strcmp(oldSerialPort, SerialPort) != 0)
{
// (re)start connection
if (OldEnable)
{
if (KISSEnable)
Status3->setText("KISS Closed");
else
Status3->setText("KISS Disabled");
KISSConnected = 0;
if (KISSSock && KISSSock->ConnectedState == QAbstractSocket::ConnectedState)
KISSSock->disconnectFromHost();
else if (m_serial)
closeSerialPort();
}
}
delete(KISS);
SaveSettings();
deviceUI->accept();
// QSize newSize(this->size());
// QSize oldSize(this->size());
// QResizeEvent *myResizeEvent = new QResizeEvent(newSize, oldSize);
// QCoreApplication::postEvent(this, myResizeEvent);
}
void QtTermTCP::KISSreject()
{
delete(KISS);
deviceUI->reject();
}
void QtTermTCP::AlertSlot()
{
// This runs the VARA Configuration dialog
Alert = new(Ui_AlertDialog);
QDialog UI;
Alert->setupUi(&UI);
UI.setFont(*menufont);
deviceUI = &UI;
Alert->Bells->setChecked(Bells);
Alert->InboundBeep->setChecked(ConnectBeep);
Alert->InactivityBeep->setChecked(AlertBeep);
Alert->Interval->setText(QString::number(AlertInterval));
Alert->KeywordBeep->setChecked(UseKeywords);
Alert->keywordFile->setText(KeyWordsFile);
Alert->useBeep->setChecked(useBeep);
Alert->useFiles->setChecked(!useBeep);
Alert->connectFile->setCurrentText(ConnectWAV);
Alert->bellsFile->setCurrentText(BellWAV);
Alert->intervalFile->setCurrentText(IntervalWAV);
Alert->keywordWAV->setCurrentText(AlertWAV);
QObject::connect(Alert->chooseInbound, SIGNAL(clicked()), this, SLOT(chooseInboundWAV()));
QObject::connect(Alert->chooseBells, SIGNAL(clicked()), this, SLOT(chooseBellsWAV()));
QObject::connect(Alert->chooseInterval, SIGNAL(clicked()), this, SLOT(chooseIntervalWAV()));
QObject::connect(Alert->chooseKeyAlert, SIGNAL(clicked()), this, SLOT(chooseAlertWAV()));
QObject::connect(Alert->testBells, SIGNAL(clicked()), this, SLOT(testBellsWAV()));
QObject::connect(Alert->TestInbound, SIGNAL(clicked()), this, SLOT(testInboundWAV()));
QObject::connect(Alert->testInterval, SIGNAL(clicked()), this, SLOT(testIntervalWAV()));
QObject::connect(Alert->TestKeyAlert, SIGNAL(clicked()), this, SLOT(testAlertWAV()));
QObject::connect(Alert->okButton, SIGNAL(clicked()), this, SLOT(alertAccept()));
QObject::connect(Alert->cancelButton, SIGNAL(clicked()), this, SLOT(alertReject()));
UI.exec();
}
void QtTermTCP::chooseInboundWAV()
{
ConnectWAV = QFileDialog::getOpenFileName(this,
tr("Select Wav"), "", tr("Sound Files (*.wav)"));
Alert->connectFile->setCurrentText(ConnectWAV);
}
void QtTermTCP::chooseBellsWAV()
{
BellWAV = QFileDialog::getOpenFileName(this,
tr("Select Wav"), "", tr("Sound Files (*.wav)"));
Alert->bellsFile->setCurrentText(BellWAV);
}
void QtTermTCP::chooseIntervalWAV()
{
IntervalWAV = QFileDialog::getOpenFileName(this,
tr("Select Wav"), "", tr("Sound Files (*.wav)"));
Alert->intervalFile->setCurrentText(IntervalWAV);
}
void QtTermTCP::chooseAlertWAV()
{
AlertWAV = QFileDialog::getOpenFileName(this,
tr("Select Wav"), "", tr("Sound Files (*.wav)"));
Alert->keywordWAV->setCurrentText(AlertWAV);
}
void QtTermTCP::testInboundWAV()
{
QSound::play(Alert->connectFile->currentText());
}
void QtTermTCP::testBellsWAV()
{
QSound::play(Alert->bellsFile->currentText());
}
void QtTermTCP::testIntervalWAV()
{
QSound::play(Alert->intervalFile->currentText());
}
void QtTermTCP::testAlertWAV()
{
QSound::play(Alert->keywordWAV->currentText());
}
void QtTermTCP::alertAccept()
{
QVariant Q;
useBeep = Alert->useBeep->isChecked();
Bells = Alert->Bells->isChecked();
ConnectBeep = Alert->InboundBeep->isChecked();
AlertBeep = Alert->InactivityBeep->isChecked();
AlertInterval = Alert->Interval->text().toInt();
UseKeywords = Alert->KeywordBeep->isChecked();
KeyWordsFile = Alert->keywordFile->text();
ConnectWAV = Alert->connectFile->currentText();
BellWAV = Alert->bellsFile->currentText();
IntervalWAV = Alert->intervalFile->currentText();
AlertWAV = Alert->keywordWAV->currentText();
delete(Alert);
SaveSettings();
deviceUI->accept();
}
void QtTermTCP::alertReject()
{
delete(Alert);
deviceUI->reject();
}
void QtTermTCP::VARASlot()
{
// This runs the VARA Configuration dialog
char valChar[80];
Dev = new(Ui_Dialog);
QDialog UI;
Dev->setupUi(&UI);
UI.setFont(*menufont);
deviceUI = &UI;
Dev->VARAEnable->setChecked(VARAEnable);
Dev->TermCall->setText(VARATermCall);
SetVARAParams();
connect(Dev->VARAHF, SIGNAL(toggled(bool)), this, SLOT(VARAHFChanged(bool)));
connect(Dev->VARAFM, SIGNAL(toggled(bool)), this, SLOT(VARAFMChanged(bool)));
connect(Dev->VARASAT, SIGNAL(toggled(bool)), this, SLOT(VARASATChanged(bool)));
if (VARA500)
Dev->VARA500->setChecked(true);
else if (VARA2750)
Dev->VARA2750->setChecked(true);
else
Dev->VARA2300->setChecked(true);
if (VARAHF)
Dev->VARAHF->setChecked(true);
else if (VARAFM)
Dev->VARAFM->setChecked(true);
else if (VARASAT)
Dev->VARASAT->setChecked(true);
if (VARAHF == 0)
Dev->HFMode->setVisible(false);
connect(Dev->CAT, SIGNAL(toggled(bool)), this, SLOT(CATChanged(bool)));
connect(Dev->PTTPort, SIGNAL(currentIndexChanged(int)), this, SLOT(PTTPortChanged(int)));
if (PTTMode == PTTCAT)
Dev->CAT->setChecked(true);
else
Dev->RTSDTR->setChecked(true);
if (CATHex)
Dev->CATHex->setChecked(true);
else
Dev->CATText->setChecked(true);
sprintf(valChar, "%d", pttGPIOPin);
Dev->GPIOLeft->setText(valChar);
sprintf(valChar, "%d", pttGPIOPinR);
Dev->GPIORight->setText(valChar);
Dev->VIDPID->setText(CM108Addr);
QStringList items;
#ifdef USESERIAL
for (const QSerialPortInfo &info : Ports)
{
items.append(info.portName());
}
items.sort();
#endif
Dev->PTTPort->addItem("None");
Dev->PTTPort->addItem("CM108");
#ifdef __ARM_ARCH
// Dev->PTTPort->addItem("GPIO");
#endif
Dev->PTTPort->addItem("FLRIG");
Dev->PTTPort->addItem("HAMLIB");
for (const QString &info : items)
{
Dev->PTTPort->addItem(info);
}
Dev->PTTPort->setCurrentIndex(Dev->PTTPort->findText(PTTPort, Qt::MatchFixedString));
PTTPortChanged(0); // Force reevaluation
QObject::connect(Dev->okButton, SIGNAL(clicked()), this, SLOT(deviceaccept()));
QObject::connect(Dev->cancelButton, SIGNAL(clicked()), this, SLOT(devicereject()));
UI.exec();
}
extern QProcess *process;
void ClosePTTPort();
void QtTermTCP::deviceaccept()
{
QVariant Q;
int OldEnable = VARAEnable;
int OldPort = VARAPortNum;
char oldHost[128];
char oldPath[256];
strcpy(oldHost, VARAHost);
strcpy(oldPath, VARAPath);
VARAEnable = Dev->VARAEnable->isChecked();
strcpy(VARATermCall, Dev->TermCall->text().toUtf8().toUpper());
Q = Dev->Port->text();
VARAPortNum = Q.toInt();
strcpy(VARAHost, Dev->Host->text().toUtf8().toUpper());
strcpy(VARAPath, Dev->Path->text().toUtf8());
strcpy(VARAInit, Dev->InitCommands->text().toUtf8());
VARA500 = Dev->VARA500->isChecked();
VARA2300 = Dev->VARA2300->isChecked();
VARA2750 = Dev->VARA2750->isChecked();
VARAHF = Dev->VARAHF->isChecked();
VARAFM = Dev->VARAFM->isChecked();
VARASAT = Dev->VARASAT->isChecked();
if (VARAHF)
{
strcpy(VARAHostHF, VARAHost);
strcpy(VARAPathHF, VARAPath);
VARAPortHF = VARAPortNum;
}
else if (VARAFM)
{
strcpy(VARAHostFM, VARAHost);
strcpy(VARAPathFM, VARAPath);
VARAPortFM = VARAPortNum;
}
else if (VARASAT)
{
strcpy(VARAHostSAT, VARAHost);
strcpy(VARAPathSAT, VARAPath);
VARAPortSAT = VARAPortNum;
}
Q = Dev->PTTPort->currentText();
strcpy(PTTPort, Q.toString().toUtf8());
if (Dev->CAT->isChecked())
PTTMode = PTTCAT;
else
PTTMode = PTTRTS;
if (Dev->CATHex->isChecked())
CATHex = 1;
else
CATHex = 0;
Q = Dev->PTTOn->text();
strcpy(PTTOnString, Q.toString().toUtf8());
Q = Dev->PTTOff->text();
strcpy(PTTOffString, Q.toString().toUtf8());
Q = Dev->CATSpeed->text();
PTTBAUD = Q.toInt();
Q = Dev->GPIOLeft->text();
pttGPIOPin = Q.toInt();
Q = Dev->GPIORight->text();
pttGPIOPinR = Q.toInt();
Q = Dev->VIDPID->text();
if (strcmp(PTTPort, "CM108") == 0)
strcpy(CM108Addr, Q.toString().toUtf8());
else if (strcmp(PTTPort, "HAMLIB") == 0)
{
HamLibPort = Q.toInt();
Q = Dev->PTTOn->text();
strcpy(HamLibHost, Q.toString().toUtf8());
}
if (VARAEnable != OldEnable || VARAPortNum != OldPort || strcmp(oldHost, VARAHost) != 0)
{
// (re)start connection
if (OldEnable && VARASock && VARASock->ConnectedState == QAbstractSocket::ConnectedState)
{
VARASock->disconnectFromHost();
if (VARADataSock)
VARADataSock->disconnectFromHost();
Status2->setText("VARA Disconnected");
}
}
if (process && process->state() == QProcess::Running)
if ((VARAEnable == 0 || strcmp(oldPath, VARAPath) != 0))
process->close();
myStatusBar->setVisible(AGWEnable | VARAEnable | KISSEnable);
ClosePTTPort();
OpenPTTPort();
delete(Dev);
SaveSettings();
deviceUI->accept();
QSize newSize(this->size());
QSize oldSize(this->size());
QResizeEvent *myResizeEvent = new QResizeEvent(newSize, oldSize);
QCoreApplication::postEvent(this, myResizeEvent);
}
void QtTermTCP::devicereject()
{
delete(Dev);
deviceUI->reject();
}
// This handles incoming connections
void QtTermTCP::onNewConnection()
{
myTcpSocket *clientSocket = (myTcpSocket *)_server->nextPendingConnection();
clientSocket->Sess = NULL;
Ui_ListenSession * S;
Ui_ListenSession * Sess = NULL;
char Title[512];
int i = 0;
QByteArray Host = clientSocket->peerAddress().toString().toUtf8();
if (TermMode == MDI)
{
// See if an old session can be reused
for (int i = 0; i < _sessions.size(); ++i)
{
S = _sessions.at(i);
// for (Ui_ListenSession * S: _sessions)
// {
if ((S->SessionType & Listen) && S->clientSocket == NULL)
{
Sess = S;
break;
}
}
// Create a window if none found, else reuse old
if (Sess == NULL)
{
Sess = newWindow(this, Listen);
}
QByteArray Host = clientSocket->peerAddress().toString().toUtf8();
}
else
{
// Single or Tabbed - look for free session
for (i = 0; i < _sessions.size(); ++i)
{
S = _sessions.at(i);
if (S->clientSocket == NULL && S->AGWSession == NULL && S->AGWMonSession == NULL && S->KISSSession == NULL)
{
Sess = S;
break;
}
}
if (Sess == NULL)
{
// Clear connection
clientSocket->disconnectFromHost();
return;
}
}
_sockets.push_back(clientSocket);
clientSocket->Sess = Sess;
// See if any data from host - first msg should be callsign
clientSocket->waitForReadyRead(1000);
QByteArray datas = clientSocket->readAll();
datas.chop(2);
datas.truncate(10); // Just in case!
datas.append('\0');
sprintf(Title, "Inward Connect from %s:%d Call " + datas,
Host.data(), clientSocket->peerPort());
if (TermMode == MDI)
{
Sess->setWindowTitle(Title);
}
else if (TermMode == Tabbed)
{
tabWidget->setTabText(i, datas.data());
tabWidget->tabBar()->setTabTextColor(i, newTabText);
}
else if (TermMode == Single)
this->setWindowTitle(Title);
connect(clientSocket, SIGNAL(readyRead()), this, SLOT(readyRead()));
connect(clientSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onSocketStateChanged(QAbstractSocket::SocketState)));
Sess->clientSocket = clientSocket;
// We need to set Connect and Disconnect if the window is active
if (TermMode == MDI && Sess->sw == ActiveSubWindow)
setMenus(true);
if (TermMode == Tabbed && Sess->Tab == tabWidget->currentIndex())
setMenus(true);
if (TermMode == Single)
setMenus(true); // Single
// Send CText if defined
if (listenCText[0])
Sess->clientSocket->write(listenCText);
// Send Message to Terminal
char Msg[80];
sprintf(Msg, "Listen Connect from %s\r\r", datas.data());
WritetoOutputWindow(Sess, (unsigned char *)Msg, (int)strlen(Msg));
if (ConnectBeep)
myBeep(&ConnectWAV);
QApplication::alert(mythis, 0);
}
void QtTermTCP::onSocketStateChanged(QAbstractSocket::SocketState socketState)
{
myTcpSocket* sender = static_cast<myTcpSocket*>(QObject::sender());
Ui_ListenSession * Sess = (Ui_ListenSession *)sender->Sess;
if (socketState == QAbstractSocket::UnconnectedState)
{
char Msg[] = "Disconnected\r";
WritetoOutputWindowEx(Sess, (unsigned char *)Msg, (int)strlen(Msg),
Sess->termWindow, &Sess->OutputSaveLen, Sess->OutputSave, WarningText); // Red
if (TermMode == MDI)
{
if (Sess->SessionType == Mon) // Mon Only
Sess->setWindowTitle("Monitor Session Disconnected");
else
Sess->setWindowTitle("Disconnected");
}
else if (TermMode == Tabbed)
{
if (Sess->SessionType == Mon) // Mon Only
tabWidget->setTabText(Sess->Tab, "Monitor");
else
{
char Label[16];
sprintf(Label, "Sess %d", Sess->Tab + 1);
tabWidget->setTabText(Sess->Tab, Label);
}
}
else if (TermMode == Single)
{
if (Sess->AGWMonSession)
mythis->setWindowTitle("AGW Monitor Window");
else
{
if (Sess->SessionType == Mon) // Mon Only
this->setWindowTitle("Monitor Session Disconnected");
else
this->setWindowTitle("Disconnected");
}
}
if (Sess->TTActive)
{
Sess->TTActive = 0;
DoTermResize(Sess);
}
Sess->PortMonString[0] = 0;
// delete(Sess->clientSocket);
Sess->clientSocket = NULL;
discAction->setEnabled(false);
if ((Sess->SessionType & Listen))
_sockets.removeOne(sender);
else
{
connectMenu->setEnabled(true);
YAPPSend->setEnabled(false);
}
}
else if (socketState == QAbstractSocket::ConnectedState)
{
char Signon[256];
char Title[128];
// only seems to be triggered for outward connect
sprintf(Signon, "%s\r%s\rBPQTERMTCP\r", UserName[Sess->CurrentHost], Password[Sess->CurrentHost]);
Sess->clientSocket->write(Signon);
discAction->setEnabled(true);
YAPPSend->setEnabled(true);
connectMenu->setEnabled(false);
SendTraceOptions(Sess);
Sess->InputMode = 0;
Sess->SlowTimer = 0;
Sess->MonData = 0;
Sess->OutputSaveLen = 0; // Clear any part line
Sess->MonSaveLen = 0; // Clear any part line
if (Sess->SessionType == Mon) // Mon Only
sprintf(Title, "Monitor Session Connected to %s", Host[Sess->CurrentHost]);
else
{
char Label[256];
if (SessName[Sess->CurrentHost][0])
sprintf(Label, "%s(%s)", Host[Sess->CurrentHost], SessName[Sess->CurrentHost]);
else
strcpy(Label, Host[Sess->CurrentHost]);
sprintf(Title, "Connected to %s", Label);
}
if (TermMode == MDI)
Sess->setWindowTitle(Title);
else if (TermMode == Tabbed)
{
if (SessName[Sess->CurrentHost][0])
tabWidget->setTabText(Sess->Tab, SessName[Sess->CurrentHost]);
else
tabWidget->setTabText(Sess->Tab, Host[Sess->CurrentHost]);
}
else if (TermMode == Single)
this->setWindowTitle(Title);
}
}
void QtTermTCP::updateWindowMenu()
{
if (TermMode == MDI)
{
windowMenu->clear();
windowMenu->addAction(newTermAct);
windowMenu->addAction(newMonAct);
windowMenu->addAction(newCombinedAct);
windowMenu->addSeparator();
windowMenu->addAction(closeAct);
windowMenu->addAction(closeAllAct);
windowMenu->addSeparator();
windowMenu->addAction(tileAct);
windowMenu->addAction(cascadeAct);
windowMenu->addSeparator();
windowMenu->addAction(nextAct);
windowMenu->addAction(previousAct);
windowMenu->addAction(quitAction);
windowMenu->addAction(windowMenuSeparatorAct);
QList<QMdiSubWindow *> windows = mdiArea->subWindowList();
windowMenuSeparatorAct->setVisible(!windows.isEmpty());
Ui_ListenSession * Sess;
for (int i = 0; i < _sessions.size(); ++i)
{
Sess = _sessions.at(i);
Sess->actActivate = windowMenu->addAction(Sess->sw->windowTitle());
QAction::connect(Sess->actActivate, SIGNAL(triggered()), this, SLOT(actActivate()));
Sess->actActivate->setCheckable(true);
Sess->actActivate->setChecked(ActiveSubWindow == Sess->sw);
}
}
else if (TermMode == Tabbed)
{
windowMenu->clear();
Ui_ListenSession * Sess = (Ui_ListenSession *)tabWidget->currentWidget();
QActionGroup * termGroup = new QActionGroup(this);
delete(TabSingle);
delete(TabBoth);
delete(TabMon);
TabSingle = setupMenuLine(nullptr, (char *)"Terminal Only", this, (Sess->SessionType == Term));
TabBoth = setupMenuLine(nullptr, (char *)"Terminal + Monitor", this, (Sess->SessionType == Term + Mon));
TabMon = setupMenuLine(nullptr, (char *)"Monitor Only", this, (Sess->SessionType == Mon));
termGroup->addAction(TabSingle);
termGroup->addAction(TabBoth);
termGroup->addAction(TabMon);
windowMenu->addAction(TabSingle);
windowMenu->addAction(TabBoth);
windowMenu->addAction(TabMon);
}
}
Ui_ListenSession::~Ui_ListenSession()
{
if (this->clientSocket)
{
int loops = 100;
this->clientSocket->disconnectFromHost();
while (loops-- && this->clientSocket->state() != QAbstractSocket::UnconnectedState)
QThread::msleep(10);
}
}
extern "C" void setTraceOff(Ui_ListenSession * Sess)
{
if ((Sess->SessionType & Mon) == 0)
return; // Not Monitor
if (Sess->AGWMonSession)
return;
char Buffer[80];
int Len = sprintf(Buffer, "\\\\\\\\0 0 0 0 0 0 0 0\r");
SocketFlush(Sess);
SocketSend(Sess, Buffer, Len);
SocketFlush(Sess);
}
extern "C" void SendTraceOptions(Ui_ListenSession * Sess)
{
if ((Sess->SessionType & Mon) == 0)
return; // Not Monitor
if (Sess->AGWMonSession)
return;
char Buffer[80];
int Len = sprintf(Buffer, "\\\\\\\\%llx %x %x %x %x %x %x %x\r", Sess->portmask, Sess->mtxparam | (Sess->mlocaltime << 7),
Sess->mcomparam, Sess->MonitorNODES, Sess->MonitorColour, Sess->monUI, 0, 1);
strcpy(&MonParams[Sess->CurrentHost][0], &Buffer[4]);
SaveSettings();
SocketFlush(Sess);
SocketSend(Sess, Buffer, Len);
SocketFlush(Sess);
}
void QtTermTCP::doNewTerm()
{
newWindow(this, Term);
}
void QtTermTCP::doNewMon()
{
newWindow(this, Mon);
}
void QtTermTCP::doNewCombined()
{
newWindow(this, Term + Mon);
}
void QtTermTCP::doCascade()
{
// Qt Cascade Minimizes windows so do it ourselves
int x = 0, y = 0;
Ui_ListenSession * Sess;
for (int i = 0; i < _sessions.size(); ++i)
{
Sess = _sessions.at(i);
Sess->sw->move(x, y);
x += 14;
y += 30;
}
}
void QtTermTCP::actActivate()
{
QAction * sender = static_cast<QAction*>(QObject::sender());
Ui_ListenSession * Sess;
for (int i = 0; i < _sessions.size(); ++i)
{
Sess = _sessions.at(i);
if (Sess->actActivate == sender)
{
mdiArea->setActiveSubWindow(Sess->sw);
return;
}
}
}
void QtTermTCP::xon_mdiArea_changed()
{
// This is triggered when the Active MDI window changes
// and is used to enable/disable Connect, Disconnect and YAPP Send
QMdiSubWindow *SW = mdiArea->activeSubWindow();
// Dont waste time if not changed
if (ActiveSubWindow == SW)
return;
ActiveSubWindow = SW;
Ui_ListenSession * Sess;
for (int i = 0; i < _sessions.size(); ++i)
{
Sess = _sessions.at(i);
// for (Ui_ListenSession * Sess : _sessions)
// {
if (Sess->sw == SW)
{
ActiveSession = Sess;
if (Sess->clientSocket && Sess->clientSocket->state() == QAbstractSocket::ConnectedState)
{
discAction->setEnabled(true);
YAPPSend->setEnabled(true);
connectMenu->setEnabled(false);
}
else if (Sess->AGWMonSession)
{
// Connected AGW Monitor Session
discAction->setEnabled(false);
YAPPSend->setEnabled(false);
connectMenu->setEnabled(false);
}
else if (Sess->AGWSession || Sess->KISSSession)
{
// Connected AGW or KISS Terminal Session
discAction->setEnabled(true);
YAPPSend->setEnabled(true);
connectMenu->setEnabled(false);
}
else
{
// Not connected
discAction->setEnabled(false);
YAPPSend->setEnabled(false);
if ((Sess->SessionType & Listen)) // Listen Sessions can't connect
connectMenu->setEnabled(false);
else
connectMenu->setEnabled(true);
}
// If a monitor Window, change Monitor config settings
EnableMonLog->setChecked(Sess->LogMonitor);
if (AGWUsers && Sess == AGWUsers->MonSess) // AGW Monitor
{
for (int i = 0; i < 64; i++)
SetPortMonLine(i, (char *)"", 0, 0); // Set all hidden
connectMenu->setEnabled(false);
MonTX->setVisible(0);
MonSup->setVisible(0);
MonUI->setVisible(0);
MonColour->setVisible(0);
EnableMonitor->setVisible(1);
EnableMonitor->setChecked(AGWMonEnable);
MonLocalTime->setChecked(Sess->mlocaltime);
MonNodes->setChecked(Sess->MonitorNODES);
}
else if (Sess == KISSMonSess) // KISS Monitor
{
for (int i = 0; i < 64; i++)
SetPortMonLine(i, (char *)"", 0, 0); // Set all hidden
connectMenu->setEnabled(false);
MonTX->setVisible(0);
MonSup->setVisible(0);
MonUI->setVisible(0);
MonColour->setVisible(0);
EnableMonitor->setVisible(1);
EnableMonitor->setChecked(KISSMonEnable);
MonLocalTime->setChecked(Sess->mlocaltime);
MonNodes->setChecked(Sess->MonitorNODES);
}
else
{
EnableMonitor->setVisible(0);
MonTX->setVisible(1);
MonSup->setVisible(1);
MonUI->setVisible(1);
MonColour->setVisible(1);
}
if (Sess->PortMonString[0])
{
char * ptr = (char *)malloc(2048);
memcpy(ptr, Sess->PortMonString, 2048);
int NumberofPorts = atoi((char *)&ptr[2]);
char *p, *Context;
char msg[80];
int portnum;
char delim[] = "|";
// Remove old Monitor menu
for (int i = 0; i < 64; i++)
{
SetPortMonLine(i, (char *)"", 0, 0); // Set all hidden
}
p = strtok_s((char *)&ptr[2], delim, &Context);
while (NumberofPorts--)
{
p = strtok_s(NULL, delim, &Context);
if (p == NULL)
break;
portnum = atoi(p);
sprintf(msg, "Port %s", p);
if (portnum == 0)
portnum = 64;
if (Sess->portmask & (1ll << (portnum - 1)))
SetPortMonLine(portnum, msg, 1, 1);
else
SetPortMonLine(portnum, msg, 1, 0);
}
free(ptr);
MonLocalTime->setChecked(Sess->mlocaltime);
MonTX->setChecked(Sess->mtxparam);
MonSup->setChecked(Sess->mcomparam);
MonUI->setChecked(Sess->monUI);
MonNodes->setChecked(Sess->MonitorNODES);
MonColour->setChecked(Sess->MonitorColour);
}
return;
}
}
}
void QtTermTCP::doFonts()
{
fontDialog * xx = new fontDialog(0);
xx->exec();
}
void QtTermTCP::doMFonts()
{
fontDialog * xx = new fontDialog(1);
xx->exec();
}
QColor TempmonBackground = monBackground;
QColor TemptermBackground = termBackground;
QColor TempinputBackground = inputBackground;
QColor TempmonRxText = monRxText;
QColor TempmonTxText = monTxText;
QColor TempmonOtherText = monOtherText;
QColor TempoutputText = outputText;
QColor TempEchoText = EchoText;
QColor TempWarningText = WarningText;
QColor TempinputText = inputText;
void setDialogColours()
{
char monStyle[128];
sprintf(monStyle, "color: rgb(%d, %d, %d); background-color: rgb(%d, %d, %d);",
TempmonOtherText.red(), TempmonOtherText.green(), TempmonOtherText.blue(),
TempmonBackground.red(), TempmonBackground.green(), TempmonBackground.blue());
COLOURS->MonitorBG->setStyleSheet(monStyle);
COLOURS->MonOther->setStyleSheet(monStyle);
sprintf(monStyle, "color: rgb(%d, %d, %d); background-color: rgb(%d, %d, %d);",
TempmonTxText.red(), TempmonTxText.green(), TempmonTxText.blue(),
TempmonBackground.red(), TempmonBackground.green(), TempmonBackground.blue());
COLOURS->MonTX->setStyleSheet(monStyle);
sprintf(monStyle, "color: rgb(%d, %d, %d); background-color: rgb(%d, %d, %d);",
TempmonRxText.red(), TempmonRxText.green(), TempmonRxText.blue(),
TempmonBackground.red(), TempmonBackground.green(), TempmonBackground.blue());
COLOURS->MonRX->setStyleSheet(monStyle);
sprintf(monStyle, "color: rgb(%d, %d, %d); background-color: rgb(%d, %d, %d);",
TempoutputText.red(), TempoutputText.green(), TempoutputText.blue(),
TemptermBackground.red(), TemptermBackground.green(), TemptermBackground.blue());
COLOURS->TermBG->setStyleSheet(monStyle);
COLOURS->TermNormal->setStyleSheet(monStyle);
sprintf(monStyle, "color: rgb(%d, %d, %d); background-color: rgb(%d, %d, %d);",
TempEchoText.red(), TempEchoText.green(), TempEchoText.blue(),
TemptermBackground.red(), TemptermBackground.green(), TemptermBackground.blue());
COLOURS->Echoed->setStyleSheet(monStyle);
sprintf(monStyle, "color: rgb(%d, %d, %d); background-color: rgb(%d, %d, %d);",
TempWarningText.red(), TempWarningText.green(), TempWarningText.blue(),
TemptermBackground.red(), TemptermBackground.green(), TemptermBackground.blue());
COLOURS->Warning->setStyleSheet(monStyle);
sprintf(monStyle, "color: rgb(%d, %d, %d); background-color: rgb(%d, %d, %d);",
TempinputText.red(), TempinputText.green(), TempinputText.blue(),
TempinputBackground.red(), TempinputBackground.green(), TempinputBackground.blue());
COLOURS->InputBG->setStyleSheet(monStyle);
COLOURS->InputColour->setStyleSheet(monStyle);
}
void QtTermTCP::doColours()
{
COLOURS = new(Ui_ColourDialog);
QDialog UI;
COLOURS->setupUi(&UI);
UI.setFont(*menufont);
deviceUI = &UI;
TempmonBackground = monBackground;
TempmonRxText = monRxText;
TempmonTxText = monTxText;
TempmonOtherText = monOtherText;
TemptermBackground = termBackground;
TempoutputText = outputText;
TempEchoText = EchoText;
TempWarningText = WarningText;
TempinputBackground = inputBackground;
TempinputText = inputText;
setDialogColours();
QObject::connect(COLOURS->MonitorBG, SIGNAL(clicked()), this, SLOT(ColourPressed()));
QObject::connect(COLOURS->TermBG, SIGNAL(clicked()), this, SLOT(ColourPressed()));
QObject::connect(COLOURS->InputBG, SIGNAL(clicked()), this, SLOT(ColourPressed()));
QObject::connect(COLOURS->MonRX, SIGNAL(clicked()), this, SLOT(ColourPressed()));
QObject::connect(COLOURS->MonTX, SIGNAL(clicked()), this, SLOT(ColourPressed()));
QObject::connect(COLOURS->MonOther, SIGNAL(clicked()), this, SLOT(ColourPressed()));
QObject::connect(COLOURS->TermNormal, SIGNAL(clicked()), this, SLOT(ColourPressed()));
QObject::connect(COLOURS->Echoed, SIGNAL(clicked()), this, SLOT(ColourPressed()));
QObject::connect(COLOURS->Warning, SIGNAL(clicked()), this, SLOT(ColourPressed()));
QObject::connect(COLOURS->InputColour, SIGNAL(clicked()), this, SLOT(ColourPressed()));
QObject::connect(COLOURS->okButton, SIGNAL(clicked()), this, SLOT(Colouraccept()));
QObject::connect(COLOURS->cancelButton, SIGNAL(clicked()), this, SLOT(Colourreject()));
UI.exec();
}
void QtTermTCP::ColourPressed()
{
char Name[32];
strcpy(Name, sender()->objectName().toUtf8());
if (strcmp(Name, "MonitorBG") == 0)
TempmonBackground = setColor(TempmonBackground);
else if (strcmp(Name, "MonTX") == 0)
TempmonTxText = setColor(TempmonTxText);
else if (strcmp(Name, "MonRX") == 0)
TempmonRxText = setColor(TempmonRxText);
else if (strcmp(Name, "MonOther") == 0)
TempmonOtherText = setColor(TempmonOtherText);
else if (strcmp(Name, "TermBG") == 0)
TemptermBackground = setColor(TemptermBackground);
else if (strcmp(Name, "InputBG") == 0)
TempinputBackground = setColor(TempinputBackground);
else if (strcmp(Name, "InputColour") == 0)
TempinputText = setColor(TempinputText);
else if (strcmp(Name, "TermNormal") == 0)
TempoutputText = setColor(TempoutputText);
else if (strcmp(Name, "Echoed") == 0)
TempEchoText = setColor(TempEchoText);
else if (strcmp(Name, "Warning") == 0)
TempWarningText = setColor(TempWarningText);
setDialogColours();
}
void QtTermTCP::Colouraccept()
{
monBackground = TempmonBackground;
monRxText = TempmonRxText;
monTxText = TempmonTxText;
monOtherText = TempmonOtherText;
termBackground = TemptermBackground;
EchoText = TempEchoText;
WarningText = TempWarningText;
outputText = TempoutputText;
inputBackground = TempinputBackground;
inputText = TempinputText;
// Set background colour for new windows
sprintf(monStyleSheet, "background-color: rgb(%d, %d, %d);",
monBackground.red(), monBackground.green(), monBackground.blue());
sprintf(termStyleSheet, "background-color: rgb(%d, %d, %d);",
termBackground.red(), termBackground.green(), termBackground.blue());
sprintf(inputStyleSheet, "color: rgb(%d, %d, %d); background-color: rgb(%d, %d, %d);",
inputText.red(), inputText.green(), inputText.blue(),
inputBackground.red(), inputBackground.green(), inputBackground.blue());
// Update existing windows
for (int i = 0; i < _sessions.size(); ++i)
{
Ui_ListenSession * S = _sessions.at(i);
if (S->monWindow)
S->monWindow->setStyleSheet(monStyleSheet);
if (S->termWindow)
S->termWindow->setStyleSheet(termStyleSheet);
if (S->inputWindow)
S->inputWindow->setStyleSheet(inputStyleSheet);
}
delete(COLOURS);
SaveSettings();
deviceUI->accept();
}
void QtTermTCP::Colourreject()
{
delete(COLOURS);
deviceUI->reject();
}
void QtTermTCP::ConnecttoVARA()
{
delete(VARASock);
VARASock = new myTcpSocket();
connect(VARASock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(VARAdisplayError(QAbstractSocket::SocketError)));
connect(VARASock, SIGNAL(readyRead()), this, SLOT(VARAreadyRead()));
connect(VARASock, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onVARASocketStateChanged(QAbstractSocket::SocketState)));
VARASock->connectToHost(VARAHost, VARAPortNum);
Status2->setText("VARA Control Connecting");
return;
}
void QtTermTCP::VARATimer()
{
// Runs every 10 Seconds
if (VARAConnected == 0 && VARAConnecting == 0)
{
if (process == nullptr || process->state() == QProcess::NotRunning)
{
if (VARAPath[0])
{
process = new QProcess(this);
QString file = VARAPath;
process->start(file);
}
}
QThread::msleep(1000);
VARAConnecting = true;
ConnecttoVARA();
}
}
void QtTermTCP::VARAdisplayError(QAbstractSocket::SocketError socketError)
{
switch (socketError)
{
case QAbstractSocket::RemoteHostClosedError:
break;
case QAbstractSocket::HostNotFoundError:
QMessageBox::information(this, tr("QtTermTCP"),
tr("VARA host was not found. Please check the "
"host name and portsettings->"));
Status2->setText("VARA Connection Failed");
break;
case QAbstractSocket::ConnectionRefusedError:
Status2->setText("VARA Connection Refused");
break;
default:
Status2->setText("VARA Connection Failed");
}
VARAConnecting = 0;
VARAConnected = 0;
}
void QtTermTCP::VARAreadyRead()
{
int Read;
char Buffer[4096];
char * ptr;
char * Msg;
myTcpSocket* Socket = static_cast<myTcpSocket*>(QObject::sender());
// read the data from the socket
Read = Socket->read((char *)Buffer, 4095);
Buffer[Read] = 0;
Msg = Buffer;
ptr = strchr(Msg, 0x0d);
while (ptr)
{
*ptr++ = 0;
if (strcmp(Msg, "IAMALIVE") == 0)
{
}
else if (strcmp(Msg, "PTT ON") == 0)
{
RadioPTT(1);
}
else if (strcmp(Msg, "PTT OFF") == 0)
{
RadioPTT(0);
}
else if (strcmp(Msg, "PENDING") == 0)
{
}
else if (strcmp(Msg, "CANCELPENDING") == 0)
{
}
else if (strcmp(Msg, "OK") == 0)
{
}
else if (memcmp(Msg, "CONNECTED ", 10) == 0)
{
Ui_ListenSession * Sess = (Ui_ListenSession *)VARASock->Sess;
char Title[128] = "";
char CallFrom[64] = "";
char CallTo[64] = "";
char Mode[64] = "";
char Message[128];
int n;
sscanf(&Msg[10], "%s %s %s", CallFrom, CallTo, Mode);
if (Sess)
{
if (Mode[0])
sprintf(Title, "Connected to %s %s Mode", CallTo, Mode);
else
sprintf(Title, "Connected to %s", CallTo);
n = sprintf(Message, "%s\r\n", Title);
WritetoOutputWindow(Sess, (unsigned char *)Message, n);
if (TermMode == MDI)
Sess->setWindowTitle(Title);
else if (TermMode == Tabbed)
tabWidget->setTabText(Sess->Tab, CallTo);
else if (TermMode == Single)
mythis->setWindowTitle(Title);
setMenus(true);
}
else
{
// Incoming Call
Ui_ListenSession * S;
int i = 0;
if (TermMode == MDI)
{
// See if an old session can be reused
for (int i = 0; i < _sessions.size(); ++i)
{
S = _sessions.at(i);
// for (Ui_ListenSession * S: _sessions)
// {
if ((S->SessionType & Listen) && S->clientSocket == NULL)
{
Sess = S;
break;
}
}
// Create a window if none found, else reuse old
if (Sess == NULL)
{
Sess = newWindow(this, Listen);
}
}
else
{
// Single or Tabbed - look for free session
for (i = 0; i < _sessions.size(); ++i)
{
S = _sessions.at(i);
if (S->clientSocket == NULL && S->AGWSession == NULL && S->AGWMonSession == NULL && S->KISSSession == NULL)
{
Sess = S;
break;
}
}
}
if (Sess == NULL)
{
// Clear connection
VARASock->write("DISCONNECT\r");
}
else
{
if (Mode[0])
{
sprintf(Title, "Connected to %s Mode %s", CallFrom, Mode);
n = sprintf(Message, "Incoming VARA Connect from %s %s Mode\r\n", CallFrom, Mode);
}
else
{
sprintf(Title, "Connected to %s", CallFrom);
n = sprintf(Message, "Incoming VARA Connect from %s\r\n", CallFrom);
}
WritetoOutputWindow(Sess, (unsigned char *)Message, n);
VARASock->Sess = Sess;
VARADataSock->Sess = Sess;
if (TermMode == MDI)
Sess->setWindowTitle(Title);
else if (TermMode == Tabbed)
{
tabWidget->setTabText(Sess->Tab, CallFrom);
tabWidget->tabBar()->setTabTextColor(Sess->Tab, newTabText);
}
else if (TermMode == Single)
mythis->setWindowTitle(Title);
setMenus(true);
if (ConnectBeep)
myBeep(&ConnectWAV);
if (listenCText[0])
VARADataSock->write(listenCText);
QApplication::alert(mythis, 0);
}
}
}
else if (strcmp(Msg, "DISCONNECTED") == 0)
{
Ui_ListenSession * Sess = (Ui_ListenSession *)VARASock->Sess;
if (Sess)
{
WritetoOutputWindow(Sess, (unsigned char *)"Disconnected\r\n", 14);
VARASock->Sess = 0;
VARADataSock->Sess = 0;
if (TermMode == MDI)
{
if (Sess->SessionType == Mon) // Mon Only
Sess->setWindowTitle("Monitor Session Disconnected");
else
Sess->setWindowTitle("Disconnected");
}
else if (TermMode == Tabbed)
{
if (Sess->SessionType == Mon) // Mon Only
tabWidget->setTabText(Sess->Tab, "Monitor");
else
{
char Label[16];
sprintf(Label, "Sess %d", Sess->Tab + 1);
tabWidget->setTabText(Sess->Tab, Label);
}
}
else if (TermMode == Single)
{
if (Sess->AGWMonSession)
mythis->setWindowTitle("AGW Monitor Window");
else
{
if (Sess->SessionType == Mon) // Mon Only
this->setWindowTitle("Monitor Session Disconnected");
else
this->setWindowTitle("Disconnected");
}
}
setMenus(false);
}
}
Msg = ptr;
ptr = strchr(Msg, 0x0d);
}
}
void QtTermTCP::onVARASocketStateChanged(QAbstractSocket::SocketState socketState)
{
// myTcpSocket* sender = static_cast<myTcpSocket*>(QObject::sender());
if (socketState == QAbstractSocket::UnconnectedState)
{
// Close any connections
Status2->setText("VARA Disconnected");
actHost[17]->setVisible(0);
VARAConnecting = VARAConnected = 0;
}
else if (socketState == QAbstractSocket::ConnectedState)
{
// Connect Data Session. Leave Connecting till that completes
VARADataSock = new myTcpSocket();
connect(VARADataSock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(VARADatadisplayError(QAbstractSocket::SocketError)));
connect(VARADataSock, SIGNAL(readyRead()), this, SLOT(VARADatareadyRead()));
connect(VARADataSock, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onVARADataSocketStateChanged(QAbstractSocket::SocketState)));
VARADataSock->connectToHost(VARAHost, VARAPortNum + 1);
Status2->setText("VARA Data Connecting");
}
}
void QtTermTCP::VARADatadisplayError(QAbstractSocket::SocketError socketError)
{
switch (socketError)
{
case QAbstractSocket::RemoteHostClosedError:
break;
case QAbstractSocket::HostNotFoundError:
QMessageBox::information(this, tr("QtTermTCP"),
tr("VARA host was not found. Please check the "
"host name and portsettings->"));
Status2->setText("VARA Connection Failed");
break;
case QAbstractSocket::ConnectionRefusedError:
Status2->setText("VARA Connection Refused");
break;
default:
Status2->setText("VARA Connection Failed");
}
VARAConnecting = 0;
VARAConnected = 0;
}
void QtTermTCP::VARADatareadyRead()
{
int Read;
unsigned char Buffer[4096];
myTcpSocket* Socket = static_cast<myTcpSocket*>(QObject::sender());
Ui_ListenSession * Sess = (Ui_ListenSession *)Socket->Sess;
// read the data from the socket
Read = Socket->read((char *)Buffer, 2047);
while (Read > 0)
{
// if (InputMode == 'Y') // Yapp
// {
// QString myString = QString::fromUtf8((char*)Buffer, Read);
// QByteArray ptr = myString.toLocal8Bit();
// memcpy(Buffer, ptr.data(), ptr.length());
// Read = ptr.length();
// }
ProcessReceivedData(Sess, Buffer, Read);
QString myString = QString::fromUtf8((char*)Buffer);
// qDebug() << myString;
Read = Socket->read((char *)Buffer, 2047);
}
}
void QtTermTCP::onVARADataSocketStateChanged(QAbstractSocket::SocketState socketState)
{
// myTcpSocket* sender = static_cast<myTcpSocket*>(QObject::sender());
if (socketState == QAbstractSocket::UnconnectedState)
{
// Close any connections
Status2->setText("VARA Disconnected");
actHost[17]->setVisible(0);
VARAConnecting = VARAConnected = 0;
}
else if (socketState == QAbstractSocket::ConnectedState)
{
char VARACommand[256];
VARAConnected = 1;
VARAConnecting = 0;
Status2->setText("VARA Connected");
actHost[17]->setVisible(1); // Enable VARA Connect Line
sprintf(VARACommand, "MYCALL %s\r", VARATermCall);
VARASock->write(VARACommand);
if (VARA500)
VARASock->write("BW500\r");
else if (VARA2300)
VARASock->write("BW2300\r");
else if (VARA2750)
VARASock->write("BW2750\r");
VARASock->write("COMPRESSION FILES\r");
if (VARAInit[0])
{
char Copy[512];
char * param, *context;
strcpy(Copy, VARAInit);
param = strtok_s(Copy, ",", &context);
while (param && param[0])
{
sprintf(VARACommand, "%s\r", param);
VARASock->write(VARACommand);
param = strtok_s(nullptr, ",", &context);
}
}
if (listenEnable)
VARASock->write("LISTEN ON\r");
}
}
// PTT Stuff
#include "hidapi.h"
// Serial Port Stuff
QTcpSocket * HAMLIBsock;
int HAMLIBConnected = 0;
int HAMLIBConnecting = 0;
void QtTermTCP::HAMLIBdisplayError(QAbstractSocket::SocketError socketError)
{
switch (socketError)
{
case QAbstractSocket::RemoteHostClosedError:
break;
case QAbstractSocket::HostNotFoundError:
QMessageBox::information(nullptr, tr("QtSM"),
"HAMLIB host was not found. Please check the "
"host name and portsettings->");
break;
case QAbstractSocket::ConnectionRefusedError:
qDebug() << "HAMLIB Connection Refused";
break;
default:
qDebug() << "HAMLIB Connection Failed";
break;
}
HAMLIBConnecting = 0;
HAMLIBConnected = 0;
}
void QtTermTCP::HAMLIBreadyRead()
{
unsigned char Buffer[4096];
QTcpSocket* Socket = static_cast<QTcpSocket*>(QObject::sender());
// read the data from the socket. Don't do anyhing with it at the moment
Socket->read((char *)Buffer, 4095);
}
void QtTermTCP::onHAMLIBSocketStateChanged(QAbstractSocket::SocketState socketState)
{
if (socketState == QAbstractSocket::UnconnectedState)
{
// Close any connections
HAMLIBConnected = 0;
HAMLIBConnecting = 0;
// delete (HAMLIBsock);
// HAMLIBsock = 0;
qDebug() << "HAMLIB Connection Closed";
}
else if (socketState == QAbstractSocket::ConnectedState)
{
HAMLIBConnected = 1;
HAMLIBConnecting = 0;
qDebug() << "HAMLIB Connected";
}
}
void QtTermTCP::ConnecttoHAMLIB()
{
delete(HAMLIBsock);
HAMLIBConnected = 0;
HAMLIBConnecting = 1;
HAMLIBsock = new QTcpSocket();
connect(HAMLIBsock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(HAMLIBdisplayError(QAbstractSocket::SocketError)));
connect(HAMLIBsock, SIGNAL(readyRead()), this, SLOT(HAMLIBreadyRead()));
connect(HAMLIBsock, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onHAMLIBSocketStateChanged(QAbstractSocket::SocketState)));
HAMLIBsock->connectToHost(HamLibHost, HamLibPort);
return;
}
void QtTermTCP::HAMLIBSetPTT(int PTTState)
{
char Msg[16];
if (HAMLIBsock == nullptr || HAMLIBsock->state() != QAbstractSocket::ConnectedState)
ConnecttoHAMLIB();
if (HAMLIBsock == nullptr || HAMLIBsock->state() != QAbstractSocket::ConnectedState)
return;
sprintf(Msg, "T %d\r\n", PTTState);
HAMLIBsock->write(Msg);
HAMLIBsock->waitForBytesWritten(3000);
QByteArray datas = HAMLIBsock->readAll();
qDebug(datas.data());
}
QTcpSocket * FLRigsock;
int FLRigConnected = 0;
int FLRigConnecting = 0;
void QtTermTCP::FLRigdisplayError(QAbstractSocket::SocketError socketError)
{
switch (socketError)
{
case QAbstractSocket::RemoteHostClosedError:
break;
case QAbstractSocket::HostNotFoundError:
QMessageBox::information(nullptr, tr("QtSM"),
"FLRig host was not found. Please check the "
"host name and portsettings->");
break;
case QAbstractSocket::ConnectionRefusedError:
qDebug() << "FLRig Connection Refused";
break;
default:
qDebug() << "FLRig Connection Failed";
break;
}
FLRigConnecting = 0;
FLRigConnected = 0;
}
void QtTermTCP::FLRigreadyRead()
{
unsigned char Buffer[4096];
QTcpSocket* Socket = static_cast<QTcpSocket*>(QObject::sender());
// read the data from the socket. Don't do anyhing with it at the moment
Socket->read((char *)Buffer, 4095);
}
void QtTermTCP::onFLRigSocketStateChanged(QAbstractSocket::SocketState socketState)
{
if (socketState == QAbstractSocket::UnconnectedState)
{
// Close any connections
FLRigConnected = 0;
FLRigConnecting = 0;
// delete (FLRigsock);
// FLRigsock = 0;
qDebug() << "FLRig Connection Closed";
}
else if (socketState == QAbstractSocket::ConnectedState)
{
FLRigConnected = 1;
FLRigConnecting = 0;
qDebug() << "FLRig Connected";
}
}
void QtTermTCP::ConnecttoFLRig()
{
delete(FLRigsock);
FLRigConnected = 0;
FLRigConnecting = 1;
FLRigsock = new QTcpSocket();
connect(FLRigsock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(FLRigdisplayError(QAbstractSocket::SocketError)));
connect(FLRigsock, SIGNAL(readyRead()), this, SLOT(FLRigreadyRead()));
connect(FLRigsock, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onFLRigSocketStateChanged(QAbstractSocket::SocketState)));
FLRigsock->connectToHost(FLRigHost, FLRigPort);
return;
}
static char MsgHddr[] = "POST /RPC2 HTTP/1.1\r\n"
"User-Agent: XMLRPC++ 0.8\r\n"
"Host: 127.0.0.1:7362\r\n"
"Content-Type: text/xml\r\n"
"Content-length: %d\r\n"
"\r\n%s";
static char Req[] = "<?xml version=\"1.0\"?>\r\n"
"<methodCall><methodName>%s</methodName>\r\n"
"%s"
"</methodCall>\r\n";
void QtTermTCP::FLRigSetPTT(int PTTState)
{
int Len;
char ReqBuf[512];
char SendBuff[512];
char ValueString[256] = "";
sprintf(ValueString, "<params><param><value><i4>%d</i4></value></param></params\r\n>", PTTState);
Len = sprintf(ReqBuf, Req, "rig.set_ptt", ValueString);
Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf);
if (FLRigsock == nullptr || FLRigsock->state() != QAbstractSocket::ConnectedState)
ConnecttoFLRig();
if (FLRigsock == nullptr || FLRigsock->state() != QAbstractSocket::ConnectedState)
return;
FLRigsock->write(SendBuff);
FLRigsock->waitForBytesWritten(3000);
QByteArray datas = FLRigsock->readAll();
qDebug(datas.data());
}
void QtTermTCP::CATChanged(bool State)
{
UNUSED(State);
PTTPortChanged(0);
}
void QtTermTCP::VARAHFChanged(bool State)
{
Dev->HFMode->setVisible(State);
if (State)
{
Dev->TNCInfo->setTitle("VARA HF Paramters");
Dev->Host->setText(VARAHostHF);
Dev->Port->setText(QString::number(VARAPortHF));
Dev->Path->setText(VARAPathHF);
}
}
void QtTermTCP::VARAFMChanged(bool State)
{
if (State)
{
Dev->TNCInfo->setTitle("VARA FM Paramters");
Dev->Host->setText(VARAHostFM);
Dev->Port->setText(QString::number(VARAPortFM));
Dev->Path->setText(VARAPathFM);
}
}
void QtTermTCP::VARASATChanged(bool State)
{
if (State)
{
Dev->TNCInfo->setTitle("VARA SAT Paramters");
Dev->Host->setText(VARAHostSAT);
Dev->Port->setText(QString::number(VARAPortSAT));
Dev->Path->setText(VARAPathSAT);
}
}
void QtTermTCP::SetVARAParams()
{
Dev->Host->setText(VARAHost);
Dev->Port->setText(QString::number(VARAPortNum));
Dev->Path->setText(VARAPath);
Dev->InitCommands->setText(VARAInit);
}
void QtTermTCP::PTTPortChanged(int Selected)
{
UNUSED(Selected);
QVariant Q = Dev->PTTPort->currentText();
strcpy(NewPTTPort, Q.toString().toUtf8());
Dev->RTSDTR->setVisible(false);
Dev->CAT->setVisible(false);
Dev->PTTOnLab->setVisible(false);
Dev->PTTOn->setVisible(false);
Dev->PTTOff->setVisible(false);
Dev->PTTOffLab->setVisible(false);
Dev->CATLabel->setVisible(false);
Dev->CATSpeed->setVisible(false);
Dev->CATHex->setVisible(false);
Dev->CATText->setVisible(false);
Dev->GPIOLab->setVisible(false);
Dev->GPIOLeft->setVisible(false);
Dev->GPIORight->setVisible(false);
Dev->GPIOLab2->setVisible(false);
Dev->CM108Label->setVisible(false);
Dev->VIDPID->setVisible(false);
if (strcmp(NewPTTPort, "None") == 0)
{
}
else if (strcmp(NewPTTPort, "GPIO") == 0)
{
Dev->GPIOLab->setVisible(true);
Dev->GPIOLeft->setVisible(true);
}
else if (strcmp(NewPTTPort, "CM108") == 0)
{
Dev->CM108Label->setVisible(true);
#ifdef WIN32
Dev->CM108Label->setText("CM108 VID/PID");
#else
Dev->CM108Label->setText("CM108 Device");
#endif
Dev->VIDPID->setText(CM108Addr);
Dev->VIDPID->setVisible(true);
}
else if (strcmp(NewPTTPort, "HAMLIB") == 0)
{
Dev->CM108Label->setVisible(true);
Dev->CM108Label->setText("rigctrld Port");
Dev->VIDPID->setText(QString::number(HamLibPort));
Dev->VIDPID->setVisible(true);
Dev->PTTOnLab->setText("rigctrld Host");
Dev->PTTOnLab->setVisible(true);
Dev->PTTOn->setText(HamLibHost);
Dev->PTTOn->setVisible(true);
}
else if (strcmp(NewPTTPort, "FLRIG") == 0)
{
Dev->CM108Label->setVisible(true);
Dev->CM108Label->setText("FLRig Port");
Dev->VIDPID->setText(QString::number(FLRigPort));
Dev->VIDPID->setVisible(true);
Dev->PTTOnLab->setText("FLRig Host");
Dev->PTTOnLab->setVisible(true);
Dev->PTTOn->setText(FLRigHost);
Dev->PTTOn->setVisible(true);
}
else
{
Dev->RTSDTR->setVisible(true);
Dev->CAT->setVisible(true);
if (Dev->CAT->isChecked())
{
Dev->CATHex->setVisible(true);
Dev->CATText->setVisible(true);
Dev->PTTOnLab->setVisible(true);
Dev->PTTOnLab->setText("PTT On String");
Dev->PTTOn->setText(PTTOnString);
Dev->PTTOn->setVisible(true);
Dev->PTTOff->setVisible(true);
Dev->PTTOffLab->setVisible(true);
Dev->PTTOff->setVisible(true);
Dev->PTTOff->setText(PTTOffString);
Dev->CATLabel->setVisible(true);
Dev->CATSpeed->setVisible(true);
Dev->CATSpeed->setText(QString::number(PTTBAUD));
}
}
}
void DecodeCM108(char * ptr)
{
// Called if Device Name or PTT = Param is CM108
#ifdef WIN32
// Next Param is VID and PID - 0xd8c:0x8 or Full device name
// On Windows device name is very long and difficult to find, so
// easier to use VID/PID, but allow device in case more than one needed
char * next;
long VID = 0, PID = 0;
char product[256] = "Unknown";
struct hid_device_info *devs, *cur_dev;
const char *path_to_open = NULL;
hid_device *handle = NULL;
if (strlen(ptr) > 16)
CM108Device = _strdup(ptr);
else
{
VID = strtol(ptr, &next, 0);
if (next)
PID = strtol(++next, &next, 0);
// Look for Device
devs = hid_enumerate((unsigned short)VID, (unsigned short)PID);
cur_dev = devs;
while (cur_dev)
{
if (cur_dev->product_string)
wcstombs(product, cur_dev->product_string, 255);
printf("HID Device %s VID %X PID %X", product, cur_dev->vendor_id, cur_dev->product_id);
if (cur_dev->vendor_id == VID && cur_dev->product_id == PID)
{
path_to_open = cur_dev->path;
break;
}
cur_dev = cur_dev->next;
}
if (path_to_open)
{
handle = hid_open_path(path_to_open);
if (handle)
{
hid_close(handle);
CM108Device = _strdup(path_to_open);
}
else
{
printf("Unable to open CM108 device %x %x", VID, PID);
}
}
else
printf("Couldn't find CM108 device %x %x", VID, PID);
hid_free_enumeration(devs);
}
#else
// Linux - Next Param HID Device, eg /dev/hidraw0
CM108Device = strdup(ptr);
#endif
}
void QtTermTCP::OpenPTTPort()
{
PTTMode &= ~PTTCM108;
PTTMode &= ~PTTHAMLIB;
PTTMode &= ~PTTFLRIG;
if (PTTPort[0] && strcmp(PTTPort, "None") != 0)
{
if (PTTMode == PTTCAT)
{
// convert config strings from Hex
if (CATHex == 0) // Ascii Strings
{
strcpy((char *)PTTOffCmd, PTTOffString);
PTTOffCmdLen = strlen(PTTOffString);
strcpy((char *)PTTOnCmd, PTTOnString);
PTTOnCmdLen = strlen(PTTOnString);
}
else
{
char * ptr1 = PTTOffString;
unsigned char * ptr2 = PTTOffCmd;
char c;
int val;
while ((c = *(ptr1++)))
{
val = c - 0x30;
if (val > 15) val -= 7;
val <<= 4;
c = *(ptr1++) - 0x30;
if (c > 15) c -= 7;
val |= c;
*(ptr2++) = val;
}
PTTOffCmdLen = ptr2 - PTTOffCmd;
ptr1 = PTTOnString;
ptr2 = PTTOnCmd;
while ((c = *(ptr1++)))
{
val = c - 0x30;
if (val > 15) val -= 7;
val <<= 4;
c = *(ptr1++) - 0x30;
if (c > 15) c -= 7;
val |= c;
*(ptr2++) = val;
}
PTTOnCmdLen = ptr2 - PTTOnCmd;
}
}
if (strcmp(PTTPort, "GPIO") == 0)
{
// Initialise GPIO for PTT if available
#ifdef __ARM_ARCH
// if (gpioInitialise() == 0)
// {
// printf("GPIO interface for PTT available\n");
// gotGPIO = TRUE;
// SetupGPIOPTT();
// }
// else
// printf("Couldn't initialise GPIO interface for PTT\n");
//
#else
printf("GPIO interface for PTT not available on this platform\n");
#endif
}
else if (strcmp(PTTPort, "CM108") == 0)
{
DecodeCM108(CM108Addr);
PTTMode |= PTTCM108;
}
else if (strcmp(PTTPort, "HAMLIB") == 0)
{
PTTMode |= PTTHAMLIB;
HAMLIBSetPTT(0); // to open port
return;
}
else if (strcmp(PTTPort, "FLRIG") == 0)
{
PTTMode |= PTTFLRIG;
FLRigSetPTT(0); // to open port
return;
}
else // Serial Port
{
#ifdef USESERIAL
hPTTDevice = new QSerialPort(this);
hPTTDevice->setPortName(PTTPort);
hPTTDevice->setBaudRate(PTTBAUD);
hPTTDevice->setDataBits(QSerialPort::Data8);
hPTTDevice->setParity(QSerialPort::NoParity);
hPTTDevice->setStopBits(QSerialPort::OneStop);
hPTTDevice->setFlowControl(QSerialPort::NoFlowControl);
if (hPTTDevice->open(QIODevice::ReadWrite))
{
qDebug() << "PTT Port Opened";
}
else
{
QMessageBox msgBox;
msgBox.setText("PTT COM Port Open Failed.");
msgBox.exec();
qDebug() << "PTT Port Open failed";
delete(hPTTDevice);
hPTTDevice = 0;
}
#endif
}
}
}
void ClosePTTPort()
{
#ifdef USESERIAL
if (hPTTDevice)
hPTTDevice->close();
hPTTDevice = 0;
#endif
}
#ifndef ANDRIOD
void CM108_set_ptt(int PTTState)
{
unsigned char io[5];
int n;
io[0] = 0;
io[1] = 0;
io[2] = 1 << (3 - 1);
io[3] = PTTState << (3 - 1);
io[4] = 0;
if (CM108Device == NULL)
return;
#ifdef WIN32
hid_device *handle;
handle = hid_open_path(CM108Device);
if (!handle) {
printf("unable to open device\n");
return;
}
n = hid_write(handle, io, 5);
if (n < 0)
{
printf("Unable to write()\n");
printf("Error: %ls\n", hid_error(handle));
}
hid_close(handle);
#else
int fd;
fd = open(CM108Device, O_WRONLY);
if (fd == -1)
{
printf("Could not open %s for write, errno=%d\n", CM108Device, errno);
return;
}
io[0] = 0;
io[1] = 0;
io[2] = 1 << (3 - 1);
io[3] = PTTState << (3 - 1);
io[4] = 0;
n = write(fd, io, 5);
if (n != 5)
{
printf("Write to %s failed, n=%d, errno=%d\n", CM108Device, n, errno);
}
close(fd);
#endif
return;
}
#endif
void QtTermTCP::RadioPTT(bool PTTState)
{
#ifdef __ARM_ARCH
if (useGPIO)
{
// gpioWrite(pttGPIOPin, (pttGPIOInvert ? (1 - PTTState) : (PTTState)));
// return;
}
#endif
if ((PTTMode & PTTCM108))
{
CM108_set_ptt(PTTState);
return;
}
if ((PTTMode & PTTHAMLIB))
{
HAMLIBSetPTT(PTTState);
return;
}
if ((PTTMode & PTTFLRIG))
{
FLRigSetPTT(PTTState);
return;
}
#ifdef USESERIAL
if (hPTTDevice == 0)
return;
if ((PTTMode & PTTCAT))
{
if (PTTState)
hPTTDevice->write((char *)PTTOnCmd, PTTOnCmdLen);
else
hPTTDevice->write((char *)PTTOffCmd, PTTOffCmdLen);
hPTTDevice->flush();
// hPTTDevice->error();
return;
}
if ((PTTMode & PTTRTS))
{
int n = hPTTDevice->setRequestToSend(PTTState);
n = n;
}
#endif
}
extern "C" void WriteDebugLog(char * Mess)
{
qDebug() << Mess;
}
void QtTermTCP::ConnecttoKISS()
{
if (strcmp(SerialPort, "TCP") == 0)
{
delete(KISSSock);
KISSSock = new myTcpSocket();
KISSSockCopy[0] = (void *)KISSSock;
connect(KISSSock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(KISSdisplayError(QAbstractSocket::SocketError)));
connect(KISSSock, SIGNAL(readyRead()), this, SLOT(KISSreadyRead()));
connect(KISSSock, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onKISSSocketStateChanged(QAbstractSocket::SocketState)));
KISSSock->connectToHost(KISSHost, KISSPortNum);
Status3->setText("KISS Connecting");
}
else
openSerialPort();
return;
}
void QtTermTCP::KISSTimer()
{
// Runs every 10 Seconds
if (KISSConnected == 0 && KISSConnecting == 0)
{
ConnecttoKISS();
}
else
{
// Verify Serial port is still ok
if (m_serial && KISSConnected)
{
m_serial->clearError();
boolean rc = m_serial->isDataTerminalReady();
if (m_serial->error())
{
Debugprintf("Serial Port Lost - isOpen %d Error %d", m_serial->isOpen(), m_serial->error());
closeSerialPort();
}
}
}
}
void QtTermTCP::KISSdisplayError(QAbstractSocket::SocketError socketError)
{
switch (socketError)
{
case QAbstractSocket::RemoteHostClosedError:
break;
case QAbstractSocket::HostNotFoundError:
QMessageBox::information(this, tr("QtTermTCP"),
tr("KISS host was not found. Please check the "
"host name and portsettings->"));
Status3->setText("KISS Connection Failed");
break;
case QAbstractSocket::ConnectionRefusedError:
Status3->setText("KISS Connection Refused");
break;
default:
Status3->setText("KISS Connection Failed");
}
KISSConnecting = 0;
KISSConnected = 0;
}
extern "C" void KISSSendtoServer(myTcpSocket* Socket, char * Data, int Length)
{
if (m_serial)
{
if (m_serial->isOpen())
{
m_serial->clearError();
int n = m_serial->write(Data, Length);
n = m_serial->flush();
if (m_serial->error())
{
Debugprintf("Serial Flush Error - Requested = %d Actual %d Error %d", Length, n, m_serial->error());
closeSerialPort();
}
}
}
else if (Socket)
Socket->write(Data, Length);
}
void QtTermTCP::KISSreadyRead()
{
int Read;
unsigned char Buffer[4096]; myTcpSocket* Socket = static_cast<myTcpSocket*>(QObject::sender());
// read the data from the socket
Read = Socket->read((char *)Buffer, 4095);
KISSDataReceived(Socket, Buffer, Read);
}
extern "C" void KISS_del_socket(void * socket);
extern "C" void KISS_add_stream(void * Socket);
void QtTermTCP::onKISSSocketStateChanged(QAbstractSocket::SocketState socketState)
{
// myTcpSocket* sender = static_cast<myTcpSocket*>(QObject::sender());
QTcpSocket* sender = static_cast<QTcpSocket*>(QObject::sender());
if (socketState == QAbstractSocket::UnconnectedState)
{
// Close any connections
Ui_ListenSession * Sess = NULL;
Status3->setText("KISS Disconnected");
actHost[18]->setEnabled(0);
KISSConnecting = KISSConnected = 0;
// Free the monitor Window
if (KISSMonSess)
{
Sess = KISSMonSess;
if (TermMode == MDI)
Sess->setWindowTitle("Monitor Session Disconnected");
else if (TermMode == Tabbed)
tabWidget->setTabText(Sess->Tab, "Monitor");
KISSMonSess = nullptr;
}
KISS_del_socket(sender);
KISSSock = NULL;
}
else if (socketState == QAbstractSocket::ConnectedState)
{
int i;
KISSConnected = 1;
KISSConnecting = 0;
Status3->setText("KISS Connected");
actHost[18]->setEnabled(1); // Enable KISS Connect Line
KISS_add_stream(sender);
// send TXDelay if enabled
if (sendTXDelay[0])
{
unsigned char Msg[5] = { FEND, 1, 25 , FEND };
Msg[2] = txdelay[0] / 10;
KISSSock->write((char *)Msg, 4);
}
// Attach a Monitor Window if available
Ui_ListenSession * Sess = NULL;
Ui_ListenSession * S;
if (TermMode == MDI)
{
// See if an old session can be reused
for (int i = 0; i < _sessions.size(); ++i)
{
S = _sessions.at(i);
// for (Ui_ListenSession * S: _sessions)
// {
if ((S->SessionType == Mon) && S->clientSocket == NULL && S->KISSSession == NULL && (AGWUsers == NULL || (S != AGWUsers->MonSess)) && S != KISSMonSess)
{
Sess = S;
break;
}
}
// Create a window if none found, else reuse old
if (Sess == NULL)
{
Sess = newWindow((QObject *)mythis, Mon, "");
}
}
else if (TermMode == Tabbed)
{
// Tabbed - look for free session
for (i = 8; i; i--)
{
S = _sessions.at(i);
if (S->clientSocket == NULL && S->KISSSession == NULL && S->AGWSession == NULL && (AGWUsers == NULL || (S != AGWUsers->MonSess)) && S != KISSMonSess)
{
Sess = S;
break;
}
}
}
else if (TermMode == Single && (singlemodeFormat & Mon))
{
S = _sessions.at(0);
if (S->clientSocket == NULL && S->AGWSession == NULL && S->KISSSession == NULL && (AGWUsers == NULL || (S != AGWUsers->MonSess)) && S != KISSMonSess)
Sess = S;
}
if (Sess)
{
KISSMonSess = Sess; // Flag as in use
if (TermMode == MDI)
Sess->setWindowTitle("KISS Monitor Window");
else if (TermMode == Tabbed)
tabWidget->setTabText(Sess->Tab, "KISS Mon");
else if (TermMode == Single)
mythis->setWindowTitle("KISS Monitor Window");
Sess->mlocaltime = KISSLocalTime;
Sess->MonitorNODES = KISSMonNodes;
// if (TermMode == Single)
// {
// discAction->setEnabled(false);
// YAPPSend->setEnabled(false);
// connectMenu->setEnabled(false);
// }
}
}
}
extern "C" char * frame_monitor(string * frame, char * code, bool tx_stat);
extern "C" char * ShortDateTime();
extern "C" void monitor_frame(int snd_ch, string * frame, char * code, int tx, int excluded)
{
UNUSED(excluded);
UNUSED(snd_ch);
int Len;
char Msg[1024];
if (tx)
sprintf(Msg, "\x1b\x10%s", frame_monitor(frame, code, tx));
else
sprintf(Msg, "\x1b\x11%s", frame_monitor(frame, code, tx));
Len = strlen(Msg);
if (Len < 10) // Suppressed NODES
return;
if (Msg[Len - 1] != '\r')
{
Msg[Len++] = '\r';
Msg[Len] = 0;
}
if (KISSMonSess)
WritetoMonWindow(KISSMonSess, (unsigned char *)Msg, Len);
}
extern "C" Ui_ListenSession * ax25IncomingConnect(TAX25Port * AX25Sess)
{
// Look for/create Terminal Window for connection
Ui_ListenSession * Sess = NULL;
Ui_ListenSession * S;
char Title[80];
int i = 0;
if (TermMode == MDI)
{
// See if an old session can be reused
for (int i = 0; i < _sessions.size(); ++i)
{
S = _sessions.at(i);
if ((S->SessionType & Listen) && S->clientSocket == NULL && S->AGWSession == NULL && S->KISSSession == NULL)
{
Sess = S;
break;
}
}
// Create a window if none found, else reuse old
if (Sess == NULL)
{
Sess = newWindow((QObject *)mythis, Listen, "");
}
}
else
{
// Single or Tabbed - look for free session
for (i = 0; i < _sessions.size(); ++i)
{
S = _sessions.at(i);
if (S->clientSocket == NULL && S->AGWSession == NULL && S->KISSSession == NULL)
{
Sess = S;
break;
}
}
if (Sess == NULL)
{
// Clear connection
return NULL;
}
}
if (Sess)
{
sprintf(Title, "Connected to %s", AX25Sess->corrcall);
if (TermMode == MDI)
{
Sess->setWindowTitle(Title);
}
else if (TermMode == Tabbed)
{
tabWidget->setTabText(i, AX25Sess->corrcall);
tabWidget->tabBar()->setTabTextColor(i, newTabText);
}
else if (TermMode == Single)
mythis->setWindowTitle(Title);
AX25Sess->port = 0;
AX25Sess->Sess = Sess; // Crosslink KISS and Term Sessions
AX25Sess->PID = 240;;
Sess->KISSSession = AX25Sess;
setMenus(true);
if (ConnectBeep)
myBeep(&ConnectWAV);
QApplication::alert(mythis, 0);
// Send CText if defined
if (listenCText[0])
SendtoAX25(Sess->KISSSession, (unsigned char *)listenCText, (int)strlen(listenCText));
}
return Sess;
}
extern "C" void AX25_disc(TAX25Port * AX25Sess, Byte mode)
{
char Msg[128];
int Len = 0;
Ui_ListenSession * Sess = (Ui_ListenSession *)AX25Sess->Sess;
if (AX25Sess->status == STAT_TRY_LINK)
{
// Connect failed
Len = sprintf(Msg, "Connection to %s failed\r", AX25Sess->corrcall);
}
else
{
switch (mode)
{
case MODE_OTHER:
case MODE_OUR:
Len = sprintf(Msg, "Disconnected from %s\r", AX25Sess->corrcall);
break;
case MODE_RETRY:
Len = sprintf(Msg, "Disconnected from %s - Retry count exceeded\r", AX25Sess->corrcall);
break;
};
}
SendtoTerm(Sess, Msg, Len);
ClearSessLabel(Sess);
Sess->KISSSession = NULL;
AX25Sess->Sess = 0;
setMenus(0);
};
int QtTermTCP::openSerialPort()
{
if (m_serial && m_serial->isOpen())
{
m_serial->close();
}
m_serial = nullptr;
m_serial = new QSerialPort(this);
m_serial->setPortName(SerialPort);
boolean ok = m_serial->setBaudRate(KISSBAUD);
if (m_serial->open(QIODevice::ReadWrite))
{
int i;
ok = m_serial->setRequestToSend(true);
connect(m_serial, &QSerialPort::readyRead, this, &QtTermTCP::readSerialData);
// connect(m_serial, &QSerialPort::errorOccurred, this, &QtTermTCP::handleError);
KISSConnected = 1;
KISSConnecting = 0;
Status3->setText("KISS Connected");
actHost[18]->setEnabled(1); // Enable KISS Connect Line
KISS_add_stream(m_serial);
// send TXDelay if enabled
if (sendTXDelay[0])
{
unsigned char Msg[5] = { FEND, 1, 25 , FEND };
Msg[2] = txdelay[0] / 10;
m_serial->write((char *)Msg, 4);
}
// Attach a Monitor Window if available
Ui_ListenSession * Sess = NULL;
Ui_ListenSession * S;
if (TermMode == MDI)
{
// See if an old session can be reused
for (int i = 0; i < _sessions.size(); ++i)
{
S = _sessions.at(i);
// for (Ui_ListenSession * S: _sessions)
//
if ((S->SessionType == Mon) && S->clientSocket == NULL && S->KISSSession == NULL)
{
Sess = S;
break;
}
}
// Create a window if none found, else reuse old
if (Sess == NULL)
{
Sess = newWindow((QObject *)mythis, Mon, "");
}
}
else if (TermMode == Tabbed)
{
// Tabbed - look for free session
for (i = 8; i; i--)
{
S = _sessions.at(i);
if (S->clientSocket == NULL && S->KISSSession == NULL)
{
Sess = S;
break;
}
}
}
else if (TermMode == Single && (singlemodeFormat & Mon))
{
S = _sessions.at(0);
if (S->clientSocket == NULL && S->KISSSession == NULL)
Sess = S;
}
if (Sess)
{
KISSMonSess = Sess; // Flag as in use
if (TermMode == MDI)
Sess->setWindowTitle("KISS Monitor Window");
else if (TermMode == Tabbed)
tabWidget->setTabText(Sess->Tab, "KISS Mon");
else if (TermMode == Single)
mythis->setWindowTitle("KISS Monitor Window");
// if (TermMode == Single)
// {
// discAction->setEnabled(false);
// YAPPSend->setEnabled(false);
// connectMenu->setEnabled(false);
// }
}
return 1;
}
else
{
Status3->setText("KISS Open Failed");
KISSConnected = 0;
KISSConnecting = 0;
return 0;
}
}
void closeSerialPort()
{
if (m_serial && m_serial->isOpen())
{
m_serial->close();
m_serial = nullptr;
}
m_serial = nullptr;
KISSConnected = 0;
KISSConnecting = 0;
Status3->setText("KISS Closed");
actHost[18]->setEnabled(0); // Enable KISS Connect Line
}
void QtTermTCP::readSerialData()
{
int Read;
unsigned char Buffer[8192];
// read the data from the socket
m_serial->clearError();
Read = m_serial->read((char *)Buffer, 2047);
if (m_serial->error())
{
Debugprintf("Serial Read Error - RC %d Error %d", Read, m_serial->error());
closeSerialPort();
return;
}
while (Read > 0)
{
KISSDataReceived(m_serial, Buffer, Read);
Read = m_serial->read((char *)Buffer, 2047);
}
}
void QtTermTCP::handleError(QSerialPort::SerialPortError serialPortError)
{
Debugprintf("Serial port Error %d", serialPortError);
closeSerialPort();
}
extern "C" void CheckUIFrame(unsigned char * path, string * data)
{
// If we have KISS enabled and dest is UIDEST look for a KISS window in UI Mode
if (KISSSock == 0)
return;
char Dest[10];
char From[10];
Dest[ConvFromAX25(path, Dest)] = 0;
From[ConvFromAX25(&path[7], From)] = 0;
// ok, Find a Kiss Session with this Dest
Ui_ListenSession * Sess = NULL;
for (int i = 0; i < _sessions.size(); ++i)
{
Sess = _sessions.at(i);
if (Sess->KISSMode == 1 && strcmp(Dest, Sess->UIDEST) == 0)
{
char Msg[512];
int Len;
data->Data[data->Length] = 0;
Len = sprintf(Msg, "%s:%s", From, data->Data);
SendtoTerm(Sess, Msg, Len);
return;
}
}
}
QColor QtTermTCP::setColor(QColor Colour)
{
// const QColorDialog::ColorDialogOptions options = QFlag(colorDialogOptionsWidget->value());
QColor col = Colour;
QColorDialog dialog;
dialog.setCurrentColor(Colour);
dialog.setOption(QColorDialog::DontUseNativeDialog);
if (dialog.exec() == QColorDialog::Accepted)
col = QVariant(dialog.currentColor()).toString();
// const QColor color = QColorDialog::getColor(Qt::green, this, "Select Color", 0);
return col;
}
// Experimental Viewdata/Teletext/Prestel/CEEFAX Mode
// Uses ideas and some code from
/***************************************************************
* Name: wxTEDMain.cpp
* Purpose: Teletext editor Application Frame
* Author: Peter Kwan (peterk.vt80@gmail.com)
* Created: 2014-10-30
* Copyright: Peter Kwan
* License:
*
* Copyright (C) 2014-2022, Peter Kwan
*
* Permission to use, copy, modify, and distribute this software
* and its documentation for any purpose and without fee is hereby
* granted, provided that the above copyright notice appear in all
* copies and that both that the copyright notice and this
* permission notice and warranty disclaimer appear in supporting
* documentation, and that the name of the author not be used in
* advertising or publicity pertaining to distribution of the
* software without specific, written prior permission.
*
* The author disclaims all warranties with regard to this
* software, including all implied warranties of merchantability
* and fitness. In no event shall the author be liable for any
* special, indirect or consequential damages or any damages
* whatsoever resulting from loss of use, data or profits, whether
* in an action of contract, negligence or other tortious action,
* arising out of or in connection with the use or performance of
* this software.
*************************************************************************** **/
int FontWidth = 16;
int FontHeight = 16;
bool isMosaic(char ch)
{
ch &= 0x7f;
return (ch >= 0x20 && ch < 0x40) || ch >= 0x60;
}
void DecodeTeleText(Ui_ListenSession * Sess, char * page)
{
// redraw the whole teletext page
QColor fg = Qt::white;
QColor bg = Qt::black;
QColor holdColour;
QColor holdfg = fg; // Colour for held graphic
int usehold = 0;
int sep = 0;
QPainter p(Sess->TTBitmap);
QSettings settings(GetConfPath(), QSettings::IniFormat);
//p.setFont(QFont("Courier", 14, QFont::Bold));
p.setFont(QFont(settings.value("FontFamily", "Courier New").value<QString>(), 14));
p.setPen(QPen(fg));
bool graphicsMode = false;
bool separated = false;
bool doubleHeight = false;
int skipnextrow = 99;
bool flashing = false;
bool hold = false;
char holdChar = 0;
char holdMode = 0;
Sess->timer.stop();
bool concealed = false;
char c;
fg = Qt::white;
bg = Qt::black;
char * ptr = page;
int col = 0;
int line = 0;
// XXXXXXXXTEEFAX %%# %%a %d %%b C %H:%M.%S
//p.drawText(0, 19, "P199 HAMFAX");
// interpret data, for now, line by line
while ((c = *(ptr++)))
{
char ch = c;
if (c == 0x11) // Curson On ??
continue;
if (c == 0x14) // Curson Off ??
continue;
if (c == 9) // Curson On ??
{
col++;
continue;
}
if (c == 0x0a)
{
line++;
// if (doubleHeight)
// line++;
if (line > 24)
line = 0;
continue;
}
if (c == 0x1e) // Cursor Home
{
line = col = 0;
c = 13; // So we reset page flags below
}
if (c == 12)
{
// Clear the page
Sess->TTBitmap->fill(Qt::black);
Sess->TTLabel->setPixmap(QPixmap::fromImage(*Sess->TTBitmap));
line = col = 0;
c = 13; // So we reset page flags below
}
if (c == 13)
{
col = 0;
graphicsMode = false;
separated = false;
doubleHeight = false;
flashing = false;
hold = false;
holdChar = 0;
concealed = false;
fg = Qt::white;
bg = Qt::black;
continue; // Next line
}
if (c == 0x1b) // Esc
{
// I think the control char is displayed as it was before being actioned, sa save current state
holdfg = holdColour; // Colour if using held - may be changed before displaying
usehold = hold; // May be changed
sep = holdMode;
char echar = *(ptr++);
if (echar == 0)
{
// Esc pair spilt - wait for rest
Sess->TTLabel->setPixmap(QPixmap::fromImage(*Sess->TTBitmap));
return;
}
switch (echar)
{
case '@':
fg = Qt::black;
concealed = false; // Side effect of colour. It cancels a conceal.
graphicsMode = false;
// hold = false;
break;
case 'A':
fg = Qt::red;
concealed = false;
graphicsMode = false;
hold = false;
break;
case 'B':
fg = Qt::green;
concealed = false;
graphicsMode = false;
hold = false;
break;
case 'C':
fg = Qt::yellow;
concealed = false;
graphicsMode = false;
hold = false;
break;
case 'D':
fg = Qt::blue;
concealed = false;
graphicsMode = false;
hold = false;
break;
case 'E':
fg = Qt::magenta;
concealed = false;
graphicsMode = false;
hold = false;
break;
case 'F':
fg = Qt::cyan;
concealed = false;
graphicsMode = false;
hold = false;
break;
case 'G':
fg = Qt::white;
concealed = false;
graphicsMode = false;
hold = false;
break;
case 'H': // Flash
flashing = true;
Sess->timer.start(1000, Sess);
concealed = false;
graphicsMode = false;
hold = false;
break;
case 'I': // Steady
flashing = false;
concealed = false;
graphicsMode = false;
hold = false;
break;
//
case 'J': //ttxCodeEndBox:
case 'K': //ttxCodeStartBox:
concealed = false;
graphicsMode = false;
hold = false;
break;
case 'L': // Normal height
doubleHeight = false;
hold = false;
break;
case 'M': // Double height
doubleHeight = true;
skipnextrow = line + 1; // ETSI: row to ignore
hold = false;
holdChar = 0;
break;
case 'P': // Graphics black
// concealed = false;
graphicsMode = true;
holdColour = fg = Qt::black;
break;
case 'Q': // Graphics red
// concealed = false;
graphicsMode = true;
holdColour = fg = Qt::red;
break;
case 'R': // Graphics green
// concealed = false;
graphicsMode = true;
holdColour = fg = Qt::green;
break;
case 'S': // Graphics yellow
// concealed = false;
graphicsMode = true;
holdColour = fg = Qt::yellow;
break;
case 'T': // Graphics blue
// concealed = false;
graphicsMode = true;
holdColour = fg = Qt::blue;
break;
case 'U': // Graphics magenta
// concealed = false;
graphicsMode = true;
holdColour = fg = Qt::magenta;
break;
case 'V': // Graphics cyan
// concealed = false;
graphicsMode = true;
holdColour = fg = Qt::cyan;
break;
case 'W': // Graphics white
// concealed = false;
graphicsMode = true;
holdColour = fg = Qt::white;
break;
case 'X': // Conceal display
concealed = 1;
break;
case 'Y': // Contiguous graphics
separated = false;
break;
case 'Z': // Separated gfx
separated = true;
break;
case 0x5c: // Background black
bg = Qt::black;
break;
case 0x5d: // New background
bg = fg;
break;
case 0x5e: // Hold gfx
if (hold == 0)
{
hold = true;
holdColour = fg;
holdMode = separated;
}
break;
case 0x5f: // Non-hold gfx
hold = false;
break;
default:
echar++; // For testign
break;
} //end of esc case processing
if (usehold)
{
// We set sep and color for held char earlier
ch = holdChar; // Repeat held char
}
else
ch = 0x0; // Default is space
if (line > skipnextrow)
skipnextrow = 99;
if (line == skipnextrow)
{
line = line;
}
else
{
if (concealed == 0 && (flashing == 0 || Sess->TTFlashToggle == 0))
{
p.fillRect(col * 15, line * 19, 15, 19, bg);
// if double height also draw background for next row
if (doubleHeight)
p.fillRect(col * 15, line * 19 + 19, 15, 19, bg);
if (sep)
{
if (ch & 1)
p.fillRect(col * 15 + 2, line * 19 + 2, 5, 4, holdfg);
if (ch & 2)
p.fillRect(col * 15 + 9, line * 19 + 2, 6, 4, holdfg);
if (ch & 4)
p.fillRect(col * 15 + 2, line * 19 + 8, 5, 4, holdfg);
if (ch & 8)
p.fillRect(col * 15 + 9, line * 19 + 8, 6, 4, holdfg);
if (ch & 16)
p.fillRect(col * 15 + 2, line * 19 + 14, 5, 5, holdfg);
if (ch & 32)
p.fillRect(col * 15 + 9, line * 19 + 14, 6, 5, holdfg);
}
else
{
if (ch & 1)
p.fillRect(col * 15, line * 19, 7, 6, holdfg);
if (ch & 2)
p.fillRect(col * 15 + 7, line * 19, 8, 6, holdfg);
if (ch & 4)
p.fillRect(col * 15, line * 19 + 6, 7, 6, holdfg);
if (ch & 8)
p.fillRect(col * 15 + 7, line * 19 + 6, 8, 6, holdfg);
if (ch & 16)
p.fillRect(col * 15, line * 19 + 12, 7, 7, holdfg);
if (ch & 32)
p.fillRect(col * 15 + 7, line * 19 + 12, 8, 7, holdfg);
}
}
}
col++;
}
else
{
// Not esc - so normal or graphics
if (ch < 0x20)
continue;
if (line > skipnextrow)
skipnextrow = 99;
if (line == skipnextrow)
{
line = line;
}
else
{
if (concealed == 0 && (flashing == 0 || Sess->TTFlashToggle == 0))
{
p.fillRect(col * 15, line * 19, 15, 19, bg);
if (doubleHeight)
p.fillRect(col * 15, line * 19 + 19, 15, 19, bg);
p.setPen(QPen(fg));;
if (graphicsMode)
{
if (ch < 0x40)
ch -= 0x20;
else
if (ch >= 0x60)
ch -= 0x40;
else
goto isText;
holdChar = ch;
// Now have 00 - 3f
// C is bit mask bit posns are
// 01
// 23
// 45
// Char cell is 15 * 19 which is a bit asymetrical
// Chars are 20 - 3f and 60 to 7f but I cant see a logic to the mapping
if (separated)
{
if (ch & 1)
p.fillRect(col * 15 + 2, line * 19 + 2, 5, 4, fg);
if (ch & 2)
p.fillRect(col * 15 + 9, line * 19 + 2, 6, 4, fg);
if (ch & 4)
p.fillRect(col * 15 + 2, line * 19 + 8, 5, 4, fg);
if (ch & 8)
p.fillRect(col * 15 + 9, line * 19 + 8, 6, 4, fg);
if (ch & 16)
p.fillRect(col * 15 + 2, line * 19 + 14, 5, 5, fg);
if (ch & 32)
p.fillRect(col * 15 + 9, line * 19 + 14, 6, 5, fg);
}
else
{
if (ch & 1)
p.fillRect(col * 15, line * 19, 7, 6, fg);
if (ch & 2)
p.fillRect(col * 15 + 7, line * 19, 8, 6, fg);
if (ch & 4)
p.fillRect(col * 15, line * 19 + 6, 7, 6, fg);
if (ch & 8)
p.fillRect(col * 15 + 7, line * 19 + 6, 8, 6, fg);
if (ch & 16)
p.fillRect(col * 15, line * 19 + 12, 7, 7, fg);
if (ch & 32)
p.fillRect(col * 15 + 7, line * 19 + 12, 8, 7, fg);
}
}
else
{
// Just write char at current col and line
isText:
char s[5];
unsigned char su[5] = "";
// Some chars are in wrong char set
su[0] = ch;
if (ch == '_')
su[0] = '#';
else if (ch == 0x7e) // division
{
su[0] = 0xC3;
su[1] = 0xB7;
}
else if (ch == 0x5e) // up arrow
{
su[0] = 0xF0;
su[1] = 0x9F;
su[2] = 0xA0;
su[3] = 0x95;
}
else if (ch == 0x7f) // up arrow
{
su[0] = 0xE2;
su[1] = 0x96;
su[2] = 0x88;
}
memcpy(s, su, 5);
// if (doubleHeight)
// p.drawText(col * 15, line * 19 + 25, s);
// else
// p.drawText(col * 15, line * 19 + 15, s);
// if double height draw normally then copy pixels each row of pixels to two scanlines (starting at the bottom)
if (doubleHeight)
{
int inscanline = line * 19 + 18;
int outscanline = line * 19 + 35;
unsigned char * inptr = Sess->TTBitmap->scanLine(inscanline);
unsigned char * outptr = Sess->TTBitmap->scanLine(outscanline);
int linelen = Sess->TTBitmap->bytesPerLine();
int charlen = linelen / 40; // bytes per char position
p.drawText(col * 15, line * 19 + 16, s);
inptr += col * charlen;
outptr += col * charlen;
for (int i = 0; i < 18; i++)
{
memcpy(outptr, inptr, charlen);
outptr -= linelen;
memcpy(outptr, inptr, charlen);
inptr -= linelen;
outptr -= linelen;
}
}
else
p.drawText(col * 15, line * 19 + 15, s);
}
}
}
col++;
}
if (col > 39)
{
col = 0;
line++;
if (line > 24)
line = 0;
graphicsMode = false;
separated = false;
doubleHeight = false;
flashing = false;
hold = false;
holdChar = 0;
concealed = false;
fg = Qt::white;
bg = Qt::black;
}
}
Sess->TTLabel->setPixmap(QPixmap::fromImage(*Sess->TTBitmap));
return;
QFile file("D:/savepage.txt");
file.open(QIODevice::WriteOnly);
file.write(Sess->pageBuffer);
file.close();
}
void Ui_ListenSession::timerEvent(QTimerEvent *event)
{
Ui_ListenSession * Sess = NULL;
// Used for flashing - resfresh window
for (int i = 0; i < _sessions.size(); ++i)
{
Sess = _sessions.at(i);
if (Sess->timer.timerId() == event->timerId())
{
if (Sess->TTActive)
{
Sess->TTFlashToggle ^= 1;
// if in Tabbed mode only refresh active tab
if (TermMode == Tabbed && Sess != ActiveSession)
return;
DecodeTeleText(Sess, (char *)Sess->pageBuffer); // Re-decode same data until we get the end
}
else
Sess->timer.stop();
return;
}
}
QWidget::timerEvent(event);
}
// Monitor Log FIle routines
char * doXMLTransparency(char * string)
{
// Make sure string doesn't contain forbidden XML chars (<>"'&)
char * newstring = (char *)malloc(5 * strlen(string) + 1); // If len is zero still need null terminator
char * in = string;
char * out = newstring;
char c;
c = *(in++);
while (c)
{
switch (c)
{
case '<':
strcpy(out, "&lt;");
out += 4;
break;
case '>':
strcpy(out, "&gt;");
out += 4;
break;
case '"':
strcpy(out, "&quot;");
out += 6;
break;
case '\'':
strcpy(out, "&apos;");
out += 6;
break;
case '&':
strcpy(out, "&amp;");
out += 5;
break;
default:
*(out++) = c;
}
c = *(in++);
}
*(out++) = 0;
return newstring;
}
void WriteMonitorLog(Ui_ListenSession * Sess, char * Msg)
{
// Write as HTML to preserve formatting
char Line[512];
char * HTMLText;
if (Sess->monLogfile == nullptr)
{
QString FN = "QTTermMonLog" + timeLoaded + "_" + QString::number(Sess->sessNo) + ".html";
Sess->monLogfile = new QFile(FN);
if (Sess->monLogfile)
Sess->monLogfile->open(QIODevice::Append);
else
return;
}
if (Msg[0] == 0x1b)
{
// Colour Escape
if (Msg[1] == 17)
Sess->monSpan = (char *)"<span style=\"color:blue; white-space:pre;font-family: monospace\">";// , monRxColour.data());
else
Sess->monSpan = (char *)"<span style=\"color:red; white-space:pre;font-family: monospace\">";// , monTxColour.data());
HTMLText = doXMLTransparency(&Msg[2]);
}
else
{
// Leave colour at last set value
HTMLText = doXMLTransparency(Msg);
}
sprintf(Line, "%s%s</span><br>\r\n", Sess->monSpan, HTMLText);
Sess->monLogfile->write(Line);
free(HTMLText);
}

Powered by TurnKey Linux.