4343
4444#include < functional>
4545
46+
47+ #include < QTcpSocket>
48+ #include < QHostAddress>
49+ #include < QTimer>
50+
4651#include < QAction>
4752#include < QActionGroup>
4853#include < QApplication>
4954#include < QComboBox>
5055#include < QCursor>
5156#include < QDateTime>
57+ #include < QDebug>
5258#include < QDragEnterEvent>
5359#include < QInputDialog>
5460#include < QKeySequence>
5763#include < QMenuBar>
5864#include < QMessageBox>
5965#include < QMimeData>
66+ #include < QProcess>
6067#include < QProgressDialog>
6168#include < QScreen>
6269#include < QSettings>
@@ -244,6 +251,60 @@ BitcoinGUI::~BitcoinGUI()
244251 delete rpcConsole;
245252}
246253
254+ bool BitcoinGUI::isPortAvailable (const QHostAddress& address, quint16 port, int timeoutMs = 100 ) {
255+ QTcpSocket socket;
256+
257+ // Attempt to connect to the port with a short timeout
258+ socket.connectToHost (address, port);
259+
260+ // Wait for the connection attempt to complete
261+ if (socket.waitForConnected (timeoutMs)) {
262+ // If connected, a service is listening, so the port is NOT AVAILABLE for use by a new service
263+ socket.disconnectFromHost ();
264+ socket.close ();
265+ return false ; // Port is in use
266+ }
267+
268+ // Check for the specific error indicating refusal or timeout
269+ // QAbstractSocket::ConnectionRefusedError usually means nothing is listening.
270+ // QAbstractSocket::RemoteHostClosedError or QAbstractSocket::SocketTimeoutError
271+ // often means the port is available (or blocked by a firewall).
272+ if (socket.state () == QAbstractSocket::UnconnectedState ||
273+ socket.error () == QAbstractSocket::ConnectionRefusedError ||
274+ socket.error () == QAbstractSocket::SocketTimeoutError) {
275+
276+ return true ; // Port is available/free
277+ }
278+
279+ return false ; // Assume unavailable/error otherwise
280+ }
281+
282+ void BitcoinGUI::addChainProcess ()
283+ {
284+
285+ // QString program = QApplication::applicationFilePath();
286+
287+ // QStringList arguments = QApplication::arguments();
288+ // bool success = QProcess::startDetached(program, arguments); //add -listen?
289+
290+ bool success = QProcess::startDetached (QApplication::applicationFilePath ());
291+
292+ if (success)
293+ {
294+ // QApplication::quit();
295+ // Q_EMIT quitRequested();
296+ Q_EMIT detectShutdown ();
297+ }
298+ else
299+ {
300+ qDebug () << " Failed to restart Bitcoin-Gui. Could not launch new process." ;
301+ // Q_EMIT quitRequested();
302+ QApplication::quit ();
303+
304+ }
305+ }
306+
307+
247308void BitcoinGUI::createActions ()
248309{
249310 QActionGroup *tabGroup = new QActionGroup (this );
@@ -495,6 +556,15 @@ void BitcoinGUI::createActions()
495556 connect (new QShortcut (QKeySequence (Qt::CTRL | Qt::SHIFT | Qt::Key_D), this ), &QShortcut::activated, this , &BitcoinGUI::showDebugWindow);
496557}
497558
559+
560+ quint16 get_default_port (const QString& chain_id) {
561+ if (chain_id == " main" ) return 8333 ;
562+ if (chain_id == " testnet4" ) return 48334 ; // Actual port may vary
563+ if (chain_id == " regtest" ) return 18444 ;
564+ if (chain_id == " signet" ) return 38333 ;
565+ return 0 ; // Default or error
566+ }
567+
498568void BitcoinGUI::createMenuBar ()
499569{
500570 appMenuBar = menuBar ();
@@ -533,29 +603,92 @@ void BitcoinGUI::createMenuBar()
533603 settings->addAction (optionsAction);
534604
535605 // Add chain selection submenu
536- QAction* chain_selection_action = new QAction (tr (" &Switch chain " ), this );
537- chain_selection_action->setStatusTip (tr (" Restart application using a different (test) network" ));
606+ QAction* chain_selection_action = new QAction (tr (" &Add Chain Process " ), this );
607+ chain_selection_action->setStatusTip (tr (" Start process using a different network" ));
538608 QMenu* chain_selection_menu = new QMenu (this );
539609 chain_selection_action->setMenu (chain_selection_menu);
540610
611+
612+
613+
614+
615+
616+
541617 connect (chain_selection_menu, &QMenu::aboutToShow, [this , chain_selection_menu] {
542618 chain_selection_menu->clear ();
543619 const std::vector<std::pair<QString, QString>> chains = {{" main" , " &Bitcoin" }, {" testnet4" , " &Testnet4" }, {" regtest" , " &Regtest" }, {" signet" , " &Signet" }};
544620 const std::string current_chain = Params ().GetChainTypeString ();
545621 for (const auto & chain : chains) {
622+ // add port detection here?
546623 const bool is_current = current_chain == chain.first .toStdString ();
547- QAction* action = chain_selection_menu->addAction (chain.second );
624+
625+
626+ // 🔑 NEW LOGIC STARTS HERE 🔑
627+
628+ // 1. Get the port for the chain
629+ quint16 chain_port = get_default_port (chain.first );
630+
631+ // 2. Check if the port is already in use by another process
632+ // The check for 'isPortAvailable' returns TRUE if it's free/NOT in use.
633+ // We want to disable the action if the port is IN USE.
634+ // We check the local host address.
635+ bool port_in_use = !isPortAvailable (QHostAddress::LocalHost, chain_port);
636+
637+ // The action should be disabled if:
638+ // a) It's the current chain (line 612 handles this already)
639+ // b) Another program is using the port (port_in_use is true)
640+
641+ // 🔑 NEW LOGIC ENDS HERE 🔑
642+
643+
644+ QAction* action = chain_selection_menu->addAction (chain.second );
548645 action->setCheckable (true );
549646 action->setChecked (is_current);
550- action->setEnabled (!is_current);
647+
648+
649+ bool should_be_disabled = is_current || port_in_use;
650+ action->setEnabled (!should_be_disabled); // Enable if NOT disabled
651+ // action->setEnabled(!is_current);
652+
653+
654+ if (port_in_use && !is_current) {
655+ // Set a tooltip to explain *why* it's disabled if another program is using the port
656+ action->setToolTip (tr (" Cannot switch to this chain; its default port (%1) is in use by another application." ).arg (chain_port));
657+ } else if (is_current) {
658+ action->setToolTip (tr (" Currently running on this chain." ));
659+ } else {
660+ action->setToolTip (tr (" Switch to %1. Requires application restart." ).arg (QString (chain.second ).remove (' &' )));
661+ }
662+
551663 connect (action, &QAction::triggered, [this , chain] {
552- // : Switch to the mainnet, testnet, signet or regtest chain.
553- QMessageBox::StandardButton btn_ret_val = QMessageBox::question (this , tr (" Switch chain" ),
554- // : Switching between mainnet, testnet, signet or regtest chain requires a restart.
555- tr (" Client restart required to switch chain.\n\n Client will be shut down. Do you want to proceed?" ),
556- QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
557664
558- if (btn_ret_val == QMessageBox::Cancel) return ;
665+
666+ QMessageBox msgBox;
667+
668+ // 2. Set the text and title using the translated string
669+ msgBox.setWindowTitle (tr (" Add chain process?" ));
670+ msgBox.setText (tr (" Do you want to proceed?" ));
671+
672+ // 3. Set the icon explicitly on the QMessageBox object
673+ msgBox.setIcon (QMessageBox::Question);
674+
675+ // 4. Set the buttons (e.g., Yes and Cancel)
676+ msgBox.setStandardButtons (QMessageBox::Yes | QMessageBox::Cancel);
677+
678+
679+ // //: Switch to the mainnet, testnet, signet or regtest chain.
680+ // QMessageBox::StandardButton btn_ret_val = QMessageBox::question(this, tr("Switch chain"),
681+ // //: Switching between mainnet, testnet, signet or regtest chain requires a restart.
682+ // tr("Add chain process?\n\nDo you want to proceed?").setIcon(QMessageBox::Question),
683+ // QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
684+
685+
686+ int clickedButton = msgBox.exec ();
687+
688+ // 2. Check the stored variable instead of the non-existent member.
689+ if (clickedButton == QMessageBox::Cancel) {
690+ return ;
691+ }
559692
560693 // QSettings are stored seperately for each network. Switch application name
561694 // to mainnet before storing the selected chain.
@@ -565,7 +698,8 @@ void BitcoinGUI::createMenuBar()
565698
566699 QSettings settings;
567700 settings.setValue (" chain" , chain.first );
568- Q_EMIT quitRequested ();
701+ // Q_EMIT quitRequested();
702+ Q_EMIT addChainProcess ();
569703 });
570704 }
571705 });
0 commit comments