Skip to content
API Documentation
BastionGuard™
LanguageDialog.cpp
Go to the documentation of this file.
1/*
2 * BastionGuard™ Secure Connection
3 * Copyright (C) 2025–2026 Calogero Scarnà
4 * GPL-3.0 — BastionGuard™ is a trademark of Calogero Scarnà.
5 */
6
7#include "LanguageDialog.hpp"
8#include <gdkmm/pixbuf.h>
9#include "../Resource.hpp"
10
11#include <glibmm/i18n.h>
12#include <fstream>
13#include <iostream>
14#include <sstream>
15#include <unistd.h>
16#include <linux/limits.h>
17
18// ---------------------------------------------------------------------------
19// Lingue disponibili — aggiungerne di nuove qui quando si aggiungono i .po
20// ---------------------------------------------------------------------------
21const std::vector<LanguageDialog::LangEntry>& LanguageDialog::availableLanguages()
22{
23 static const std::vector<LangEntry> langs = {
24 { "", "System default" },
25 { "en_US", "English (US)" },
26 { "it_IT", "Italiano" },
27 { "de_DE", "Deutsch" },
28 { "fr_FR", "Français" },
29 { "es_ES", "Español" },
30 { "pt_BR", "Português (BR)" },
31 { "zh_CN", "中文 (简体)" },
32 { "ja_JP", "日本語" },
33 };
34 return langs;
35}
36
37// ---------------------------------------------------------------------------
38// Costruttore
39// ---------------------------------------------------------------------------
40LanguageDialog::LanguageDialog(const std::string& configFile,
41 const std::string& currentLang)
42 : Gtk::Dialog()
43 , m_configFile(configFile)
44 , m_selected(currentLang)
45{
46 set_default_size(400, -1);
47 set_resizable(false);
49 buildUI(currentLang);
50}
51
52
53// ---------------------------------------------------------------------------
54// HeaderBar identica a MainWindow
55// ---------------------------------------------------------------------------
56
58{
59 auto header = Gtk::make_managed<Gtk::HeaderBar>();
60 header->set_show_title_buttons(false);
61 header->set_decoration_layout("");
62 header->get_style_context()->add_class("custom-headerbar");
63
64 // Titolo centrato
65 auto titleLabel = Gtk::make_managed<Gtk::Label>();
66 titleLabel->set_markup("<b>" + Glib::ustring(_("Seleziona lingua")) + "</b>");
67 titleLabel->get_style_context()->add_class("headerbar-title");
68 header->set_title_widget(*titleLabel);
69
70 // Pulsanti destra: minimizza + chiudi
71 auto headerRight = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 4);
72 headerRight->get_style_context()->add_class("header-right");
73
74 auto closeBtn = Gtk::make_managed<Gtk::Button>();
75 closeBtn->get_style_context()->add_class("header-button");
76 try {
77 auto pix = Gdk::Pixbuf::create_from_file(resource("icon-close.png"), 32, 32, true);
78 closeBtn->set_child(*Gtk::make_managed<Gtk::Image>(pix));
79 } catch (...) {
80 closeBtn->set_label("✕");
81 }
82 closeBtn->signal_clicked().connect([this]() { hide(); });
83 headerRight->append(*closeBtn);
84
85 header->pack_end(*headerRight);
86 set_titlebar(*header);
87}
88
89void LanguageDialog::buildUI(const std::string& currentLang)
90{
91 auto* area = get_content_area();
92 area->set_spacing(12);
93 area->set_margin_top(16);
94 area->set_margin_bottom(4);
95 area->set_margin_start(16);
96 area->set_margin_end(16);
97
98 // Titolo interno
99 auto title = Gtk::make_managed<Gtk::Label>();
100 title->set_markup(
101 Glib::ustring("<b>") + _("Lingua dell'interfaccia") + "</b>");
102 title->set_halign(Gtk::Align::START);
103 area->append(*title);
104
105 // ComboBox con le lingue
106 for (const auto& lang : availableLanguages())
107 m_combo.append(lang.code, lang.label);
108
109 // Seleziona quella corrente
110 if (currentLang.empty())
111 m_combo.set_active_id("");
112 else
113 m_combo.set_active_id(currentLang);
114
115 if (m_combo.get_active_row_number() < 0)
116 m_combo.set_active(0); // fallback: System default
117
118 area->append(m_combo);
119
120 // Nota riavvio
121 auto note = Gtk::make_managed<Gtk::Label>(
122 _("⚠ L'applicazione verrà riavviata automaticamente per applicare la lingua."));
123 note->set_wrap(true);
124 note->set_max_width_chars(48);
125 note->set_halign(Gtk::Align::START);
126 note->get_style_context()->add_class("dim-label");
127 area->append(*note);
128
129 // Pulsanti
130 add_button(_("Annulla"), Gtk::ResponseType::CANCEL);
131 add_button(_("Applica"), Gtk::ResponseType::APPLY);
132 set_default_response(Gtk::ResponseType::APPLY);
133
134 signal_response().connect([this](int response) {
135 // Ignora risposte che non siano APPLY o CANCEL (es. DELETE_EVENT)
136 if (response == static_cast<int>(Gtk::ResponseType::APPLY)) {
137 onApply();
138 // onApply() chiama execvp — se arriviamo qui execvp è fallito
139 hide();
140 } else if (response == static_cast<int>(Gtk::ResponseType::CANCEL)) {
141 hide();
142 }
143 // Nessun hide() per altri response (evita doppio trigger)
144 });
145}
146
147// ---------------------------------------------------------------------------
148// Salvataggio scelta
149// ---------------------------------------------------------------------------
151{
152 m_selected = m_combo.get_active_id();
154 std::cerr << "[LanguageDialog] Impossibile salvare la lingua nel config\n";
155 return;
156 }
157
158 // Riavvia il processo per applicare subito la lingua.
159 // IMPORTANTE: prima di execvp puliamo LANGUAGE e LANG dall'ambiente
160 // del processo corrente — altrimenti il processo figlio erediterebbe
161 // le variabili vecchie e la lingua non cambierebbe (es. en_US → it_IT
162 // funziona, ma it_IT → en_US no perché LANGUAGE=it_IT resterebbe).
163 ::unsetenv("LANGUAGE");
164 ::unsetenv("LANG");
165 ::unsetenv("LC_ALL");
166 ::unsetenv("LC_MESSAGES");
167
168 // Imposta le nuove variabili solo se non è "System default"
169 if (!m_selected.empty()) {
170 const std::string lang_utf8 = m_selected + ".UTF-8";
171 ::setenv("LANGUAGE", m_selected.c_str(), 1);
172 ::setenv("LANG", lang_utf8.c_str(), 1);
173 ::setenv("LC_ALL", lang_utf8.c_str(), 1);
174 }
175
176 char exe[PATH_MAX];
177 ssize_t len = ::readlink("/proc/self/exe", exe, sizeof(exe) - 1);
178 if (len > 0) {
179 exe[len] = '\0';
180 char* argv[] = { exe, nullptr };
181 ::execvp(exe, argv);
182 std::perror("[LanguageDialog] execvp fallito");
183 }
184 std::exit(0);
185}
186
187bool LanguageDialog::saveLangToConfig(const std::string& lang)
188{
189 // Leggi il file corrente (se esiste)
190 std::string content;
191 {
192 std::ifstream in(m_configFile);
193 if (in.is_open()) {
194 std::ostringstream ss;
195 ss << in.rdbuf();
196 content = ss.str();
197 }
198 }
199
200 // Rimuovi eventuale riga language= precedente
201 {
202 std::istringstream lines(content);
203 std::ostringstream out;
204 std::string line;
205 while (std::getline(lines, line)) {
206 if (line.rfind("language=", 0) == 0)
207 continue; // salta la vecchia riga
208 out << line << "\n";
209 }
210 content = out.str();
211 }
212
213 // Assicura sezione [General]
214 if (content.find("[General]") == std::string::npos)
215 content = "[General]\n" + content;
216
217 // Aggiungi la nuova riga dopo [General]
218 const std::string marker = "[General]\n";
219 auto pos = content.find(marker);
220 if (pos != std::string::npos)
221 content.insert(pos + marker.size(),
222 "language=" + lang + "\n");
223
224 // Scrivi
225 std::ofstream out(m_configFile, std::ios::trunc);
226 if (!out.is_open())
227 return false;
228 out << content;
229 return true;
230}
#define _(s)
static const std::vector< LangEntry > & availableLanguages()
Gtk::ComboBoxText m_combo
bool saveLangToConfig(const std::string &lang)
std::string m_selected
std::string m_configFile
void buildUI(const std::string &currentLang)
LanguageDialog(const std::string &configFile, const std::string &currentLang)
std::string resource(const std::string &rel)
Definition Resource.hpp:31