g4dialog
Loading...
Searching...
No Matches
gui_session.cc
Go to the documentation of this file.
1#include "gui_session.h"
2#include "g4dialog_options.h" // Provides G4DIALOG_LOGGER constant and option definitions
3
4
5GUI_Session::GUI_Session(const std::shared_ptr<GOptions>& gopt, GBoard* b) :
6 GBase(gopt, G4DIALOG_LOGGER),
7 board(b) {
8 log->info(1, SFUNCTION_NAME," g4 dialog : GUI_Session created");
9}
10
11G4int GUI_Session::ReceiveG4cout(const G4String& coutString) {
12 if (board) {
13 QString fullQString = QString::fromStdString(coutString);
14
15 // --- Define and Validate Regex ---
16 // Split the QString into lines based on newline characters
17 // Use Qt::KeepEmptyParts if you want empty lines preserved,
18 // Use Qt::SkipEmptyParts to ignore lines that are just whitespace after splitting.
19 // Using KeepEmptyParts is generally safer for logs.
20 // Note: '\n' might need to be adjusted if G4 uses different line endings (\r\n)
21 // but '\n' usually works on most platforms. QRegularExpression can handle \r?\n
22 QRegularExpression lineBreakRegex("\r?\n|\u2028");
23 QStringList lines = fullQString.split(lineBreakRegex, Qt::KeepEmptyParts);
24
25 for (const QString& line : lines) {
26 QString htmlLine = ansiToHtml(line);
27 board->appendLog(htmlLine);
28 }
29 }
30 return 0;
31}
32
33
34G4int GUI_Session::ReceiveG4cerr(const G4String& cerrString) {
35 if (board) {
36 QString fullQString = QString::fromStdString(cerrString);
37
38 // Use the same robust regex for splitting error output
39 QRegularExpression lineBreakRegex("\\r?\\n|\\u2028");
40 QStringList lines = fullQString.split(lineBreakRegex, Qt::KeepEmptyParts);
41
42 for (const QString& line : lines) {
43 QString htmlLine = ansiToHtml(line);
44 board->appendLog(htmlLine);
45 }
46 }
47 return 0;
48}
49
50
51// Helper function to convert ANSI escape codes to HTML
52// This version handles colors (30-37), bold (1), underline (4), and reset (0).
53// It also processes combined codes like [1;31m.
54QString GUI_Session::ansiToHtml(const QString& ansiText) {
55 QString htmlText;
56 htmlText.reserve(ansiText.length() * 1.2); // Pre-allocate buffer slightly larger
57
58 // State variables
59 bool isBold = false;
60 bool isUnderlined = false;
61 QString currentColor = ""; // Store the HTML color name/code
62
63 // ANSI color code to HTML color map
64 QMap<int, QString> colorMap;
65 colorMap[30] = "black";
66 colorMap[31] = "red";
67 colorMap[32] = "green";
68 colorMap[33] = "darkorange"; // Or "yellow" - adjust as needed for visibility
69 colorMap[34] = "blue";
70 colorMap[35] = "magenta";
71 colorMap[36] = "cyan";
72 colorMap[37] = "grey"; // Or "white" - adjust as needed
73
74 // Regex to find ANSI escape sequences: \x1B[ followed by numbers/semicolons, ending in m
75 QRegularExpression ansiRegex("\x1B\\[([0-9;]*)m");
76
77 int lastPos = 0;
78 QRegularExpressionMatchIterator i = ansiRegex.globalMatch(ansiText);
79
80 while (i.hasNext()) {
81 QRegularExpressionMatch match = i.next();
82 int currentPos = match.capturedStart();
83
84 // 1. Append text segment before the matched ANSI code, escaping it
85 if (currentPos > lastPos) {
86 QString textSegment = ansiText.mid(lastPos, currentPos - lastPos);
87 // Use toHtmlEscaped for robust escaping of <, >, &
88 htmlText += textSegment.toHtmlEscaped();
89 }
90
91 // 2. Process the ANSI code(s)
92 QString codesStr = match.captured(1); // The part between [ and m
93 QStringList codes = codesStr.split(';', Qt::SkipEmptyParts);
94
95 if (codes.isEmpty()) {
96 // Handle CSI m (equivalent to CSI 0 m - reset)
97 codes.append("0");
98 }
99
100 // Temporary state for processing multiple codes in one sequence
101 bool nextBold = isBold;
102 bool nextUnderlined = isUnderlined;
103 QString nextColor = currentColor;
104 bool resetDetected = false;
105
106 for (const QString& codeStr : codes) {
107 bool ok;
108 int code = codeStr.toInt(&ok);
109 if (!ok) continue; // Skip invalid codes
110
111 if (code == 0) { // Reset all attributes
112 resetDetected = true;
113 nextBold = false;
114 nextUnderlined = false;
115 nextColor = "";
116 // Important: Reset applies to *all* subsequent codes in the sequence too,
117 // so we break the inner loop after applying the effects of the reset below.
118 break;
119 }
120 else if (code == 1) { // Bold
121 nextBold = true;
122 }
123 else if (code == 4) { // Underline
124 nextUnderlined = true;
125 }
126 else if (code == 22) { // Normal intensity (neither bold nor faint)
127 nextBold = false; // Turn off bold
128 }
129 else if (code == 24) { // Not underlined
130 nextUnderlined = false; // Turn off underline
131 }
132 else if (code >= 30 && code <= 37) { // Foreground color
133 nextColor = colorMap.value(code, ""); // Get HTML color, empty if unknown
134 }
135 else if (code == 39) { // Default foreground color
136 nextColor = "";
137 }
138 // Ignore other codes (background colors 40-47, faint 2, italic 3, etc.)
139 }
140
141 // 3. Apply state changes by closing/opening tags *only* if state changed
142
143 // Close tags in reverse order if they are being turned off or changed
144 if (isUnderlined && !nextUnderlined) { htmlText += "</u>"; }
145 if (isBold && !nextBold) { htmlText += "</b>"; }
146 if (!currentColor.isEmpty() && currentColor != nextColor) { htmlText += "</font>"; }
147 // If reset was detected, ensure all current tags are closed
148 if (resetDetected) {
149 if (isUnderlined) htmlText += "</u>";
150 if (isBold) htmlText += "</b>";
151 if (!currentColor.isEmpty()) htmlText += "</font>";
152 }
153
154
155 // Open tags in normal order if they are being turned on or changed
156 if (!currentColor.isEmpty() && currentColor != nextColor) {
157 // Already closed above, now open the new one if needed
158 if (!nextColor.isEmpty()) { htmlText += QString("<font color=\"%1\">").arg(nextColor); }
159 }
160 else if (currentColor.isEmpty() && !nextColor.isEmpty()) {
161 // Was default, now has color
162 htmlText += QString("<font color=\"%1\">").arg(nextColor);
163 }
164
165 if (!isBold && nextBold) { htmlText += "<b>"; }
166 if (!isUnderlined && nextUnderlined) { htmlText += "<u>"; }
167
168 // Update current state
169 isBold = nextBold;
170 isUnderlined = nextUnderlined;
171 currentColor = nextColor;
172
173 // Update position for next segment
174 lastPos = match.capturedEnd();
175 }
176
177 // Append any remaining text after the last ANSI code
178 if (lastPos < ansiText.length()) {
179 QString textSegment = ansiText.mid(lastPos);
180 htmlText += textSegment.toHtmlEscaped();
181 }
182
183 // Close any remaining open tags at the very end
184 if (isUnderlined) { htmlText += "</u>"; }
185 if (isBold) { htmlText += "</b>"; }
186 if (!currentColor.isEmpty()) { htmlText += "</font>"; }
187
188 return htmlText;
189}
A widget that displays read-only log text along with a top bar for search, clear, and save actions.
Definition gboard.h:17
void appendLog(const QString &text)
Appends a log message to the log tab.
Definition gboard.cc:64
GUI_Session(const std::shared_ptr< GOptions > &gopt, GBoard *board)
Constructs a new GUI_Session object.
Definition gui_session.cc:5
G4int ReceiveG4cerr(const G4String &cerrString) override
Receives error output from GEANT4.
G4int ReceiveG4cout(const G4String &coutString) override
Receives standard output from GEANT4.
constexpr const char * G4DIALOG_LOGGER