Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2003-2025 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file GUIMessageWindow.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Michael Behrisch
17 : /// @author Jakob Erdmann
18 : /// @date Tue, 25 Nov 2003
19 : ///
20 : // A logging window for the gui
21 : /****************************************************************************/
22 : #include <config.h>
23 :
24 : #include <cassert>
25 : #include <utils/common/MsgHandler.h>
26 : #include <utils/gui/globjects/GUIGlObjectStorage.h>
27 : #include <utils/gui/windows/GUIGlChildWindow.h>
28 : #include <utils/gui/windows/GUIMainWindow.h>
29 : #include <utils/gui/div/GUIGlobalSelection.h>
30 : #include <fxkeys.h>
31 : #include "GUIMessageWindow.h"
32 :
33 :
34 : // ===========================================================================
35 : // static members
36 : // ===========================================================================
37 :
38 : bool GUIMessageWindow::myLocateLinks = true;
39 : SUMOTime GUIMessageWindow::myBreakPointOffset = TIME2STEPS(-5);
40 : FXHiliteStyle* GUIMessageWindow::myStyles = new FXHiliteStyle[8];
41 : std::string GUIMessageWindow::myTimeText;
42 : std::map<std::string, std::string> GUIMessageWindow::myTypeStrings;
43 :
44 : // ===========================================================================
45 : // FOX callback mapping
46 : // ===========================================================================
47 :
48 : FXDEFMAP(GUIMessageWindow) GUIMessageWindowMap[] = {
49 : FXMAPFUNC(SEL_KEYPRESS, 0, GUIMessageWindow::onKeyPress),
50 : };
51 :
52 4386402 : FXIMPLEMENT_ABSTRACT(GUIMessageWindow, FXText, GUIMessageWindowMap, ARRAYNUMBER(GUIMessageWindowMap))
53 :
54 : // ===========================================================================
55 : // method definitions
56 : // ===========================================================================
57 7883 : GUIMessageWindow::GUIMessageWindow(FXComposite* parent, GUIMainWindow* mainWindow) :
58 : FXText(parent, nullptr, 0, 0, 0, 0, 0, 50),
59 7883 : myMainWindow(mainWindow),
60 7883 : myErrorRetriever(nullptr),
61 7883 : myMessageRetriever(nullptr),
62 7883 : myWarningRetriever(nullptr) {
63 7883 : setStyled(true);
64 7883 : setEditable(false);
65 : // fill styles
66 7883 : fillStyles();
67 : // set styles
68 7883 : setHiliteStyles(myStyles);
69 7883 : myTimeText = TL(" time");
70 : // see GUIGlObject.cpp
71 7883 : myTypeStrings[StringUtils::to_lower_case(TL("edge"))] = "edge";
72 7883 : myTypeStrings[StringUtils::to_lower_case(TL("lane"))] = "lane";
73 7883 : myTypeStrings[StringUtils::to_lower_case(TL("junction"))] = "junction";
74 7883 : myTypeStrings[StringUtils::to_lower_case(TL("vehicle"))] = "vehicle";
75 7883 : myTypeStrings[StringUtils::to_lower_case(TL("person"))] = "person";
76 7883 : myTypeStrings[StringUtils::to_lower_case(TL("tlLogic"))] = "tlLogic";
77 7883 : myTypeStrings[StringUtils::to_lower_case(TL("busStop"))] = "busStop";
78 7883 : myTypeStrings[StringUtils::to_lower_case(TL("trainStop"))] = "busStop";
79 7883 : myTypeStrings[StringUtils::to_lower_case(TL("containerStop"))] = "containerStop";
80 7883 : myTypeStrings[StringUtils::to_lower_case(TL("chargingStation"))] = "chargingStation";
81 7883 : myTypeStrings[StringUtils::to_lower_case(TL("overheadWireSegment"))] = "overheadWireSegment";
82 7883 : myTypeStrings[StringUtils::to_lower_case(TL("parkingArea"))] = "parkingArea";
83 7883 : }
84 :
85 :
86 15720 : GUIMessageWindow::~GUIMessageWindow() {
87 7860 : delete[] myStyles;
88 7860 : delete myMessageRetriever;
89 7860 : delete myErrorRetriever;
90 7860 : delete myWarningRetriever;
91 15720 : }
92 :
93 :
94 : const GUIGlObject*
95 91548 : GUIMessageWindow::getActiveStringObject(const FXString& text, const FXint pos, const FXint lineS, const FXint lineE) const {
96 91548 : const FXint idS = MAX2(text.rfind(" '", pos), text.rfind("='", pos));
97 91548 : const FXint idE = text.find("'", pos);
98 91548 : if (idS >= 0 && idE >= 0 && idS >= lineS && idE <= lineE) {
99 67512 : FXint typeS = text.rfind(" ", idS - 1);
100 67512 : if (typeS >= 0) {
101 65890 : if (text.at(typeS + 1) == '(') {
102 : typeS++;
103 : }
104 65890 : std::string type(text.mid(typeS + 1, idS - typeS - 1).lower().text());
105 : const auto& tsIt = myTypeStrings.find(type);
106 65890 : if (tsIt != myTypeStrings.end()) {
107 39329 : type = tsIt->second;
108 : }
109 65890 : const std::string id(text.mid(idS + 2, idE - idS - 2).text());
110 65890 : const std::string typedID = type + ":" + id;
111 65890 : const GUIGlObject* o = GUIGlObjectStorage::gIDStorage.getObjectBlocking(typedID);
112 : //std::cout << " getActiveStringObject '" << typedID << "' o=" << (o == nullptr ? "NULL" : o->getMicrosimID()) << "\n";
113 : return o;
114 : }
115 : }
116 : return nullptr;
117 : }
118 :
119 : SUMOTime
120 23101 : GUIMessageWindow::getTimeString(const FXString& text, const FXint pos) const {
121 23101 : const FXint end = text.find_first_of(" ,", pos + 1);
122 : std::string time;
123 23101 : if (end >= 0) {
124 2844 : time = text.mid(pos, end - pos).text();
125 : } else {
126 43358 : time = text.mid(pos, text.length() - pos).text();
127 21679 : if (time.empty()) {
128 : return -1;
129 : }
130 21679 : if (time.back() == '\n') {
131 : time.pop_back();
132 : }
133 21679 : if (time.empty()) {
134 : return -1;
135 : }
136 21676 : if (time.back() == '.') {
137 : time.pop_back();
138 : }
139 : }
140 23098 : if (time.empty()) {
141 : return -1;
142 : }
143 21750 : if (time.front() == ' ') {
144 14876 : time = time.substr(1);
145 : }
146 : //std::cout << "text='" << text.text() << "' pos=" << pos << " time='" << time << "'\n";
147 : try {
148 : //std::cout << " SUMOTime=" << string2time(time) << "\n";
149 21750 : return string2time(time);
150 85 : } catch (...) {
151 : return -1;
152 85 : }
153 : }
154 :
155 :
156 : void
157 0 : GUIMessageWindow::setCursorPos(FXint pos, FXbool notify) {
158 0 : FXText::setCursorPos(pos, notify);
159 0 : if (myLocateLinks) {
160 0 : std::vector<std::string> viewIDs = myMainWindow->getViewIDs();
161 0 : if (viewIDs.empty()) {
162 : return;
163 : }
164 0 : GUIGlChildWindow* const child = myMainWindow->getViewByID(viewIDs[0]);
165 0 : const FXString text = getText();
166 0 : const GUIGlObject* const glObj = getActiveStringObject(text, pos, lineStart(pos), lineEnd(pos));
167 0 : if (glObj != nullptr) {
168 0 : child->setView(glObj->getGlID());
169 0 : GUIGlObjectStorage::gIDStorage.unblockObject(glObj->getGlID());
170 0 : if (getApp()->getKeyState(KEY_Control_L)) {
171 0 : gSelected.toggleSelection(glObj->getGlID());
172 : }
173 0 : } else if (gSimulation) {
174 : const int lookback = MIN2(pos, 20);
175 0 : const int start = MAX2(lineStart(pos), pos - lookback);
176 0 : const FXString candidate = text.mid(start, lineEnd(pos) - start);
177 0 : FXint timePos = candidate.find(myTimeText.c_str());
178 0 : if (timePos > -1) {
179 0 : timePos += (int)myTimeText.size() + 1;
180 0 : if (pos >= 0 && pos > start + timePos) {
181 0 : const SUMOTime t = getTimeString(candidate, timePos);
182 0 : if (t >= 0) {
183 0 : myMainWindow->addBreakpoint(t + myBreakPointOffset);
184 : }
185 : }
186 : }
187 0 : }
188 0 : }
189 : }
190 :
191 :
192 : void
193 69679 : GUIMessageWindow::appendMsg(GUIEventType eType, const std::string& msg) {
194 69679 : if (!isEnabled()) {
195 0 : show();
196 : }
197 : // build the styled message
198 : FXint style = 1;
199 69679 : switch (eType) {
200 : case GUIEventType::DEBUG_OCCURRED:
201 : // color: blue
202 : style = 0;
203 : break;
204 : case GUIEventType::GLDEBUG_OCCURRED:
205 : // color: fuchsia
206 : style = 7;
207 : break;
208 : case GUIEventType::ERROR_OCCURRED:
209 : // color: red
210 : style = 2;
211 : break;
212 : case GUIEventType::WARNING_OCCURRED:
213 : // color: yellow
214 : style = 3;
215 : break;
216 : case GUIEventType::MESSAGE_OCCURRED:
217 : // color: green
218 : style = 1;
219 : break;
220 69679 : default:
221 : assert(false);
222 : }
223 69679 : FXString text(msg.c_str());
224 69679 : if (myLocateLinks) {
225 69679 : FXint pos = text.find("'");
226 161227 : while (pos >= 0) {
227 91548 : const GUIGlObject* const glObj = getActiveStringObject(text, pos + 1, 0, text.length());
228 91548 : if (glObj != nullptr) {
229 25579 : GUIGlObjectStorage::gIDStorage.unblockObject(glObj->getGlID());
230 25579 : FXString insText = text.left(pos + 1);
231 25579 : FXText::appendStyledText(insText, style + 1);
232 25579 : text.erase(0, pos + 1);
233 25579 : pos = text.find("'");
234 25579 : insText = text.left(pos);
235 25579 : FXText::appendStyledText(insText, style + 4);
236 25579 : text.erase(0, pos);
237 25579 : }
238 91548 : pos = text.find("'", pos + 1);
239 : }
240 : // find time links
241 69679 : pos = text.find(myTimeText.c_str());
242 69679 : const int timeTerm = (int)myTimeText.size() + 1;
243 : SUMOTime t = -1;
244 69679 : if (pos >= 0) {
245 23101 : t = getTimeString(text, pos + timeTerm);
246 : }
247 23101 : if (t >= 0) {
248 21665 : FXString insText = text.left(pos + timeTerm);
249 21665 : FXText::appendStyledText(insText, style + 1);
250 21665 : text.erase(0, pos + timeTerm);
251 21665 : pos = text.find(" ");
252 21665 : if (pos < 0) {
253 12868 : pos = text.rfind(".");
254 : }
255 21665 : insText = text.left(pos);
256 21665 : FXText::appendStyledText(insText, style + 4);
257 21665 : text.erase(0, pos);
258 21665 : }
259 : }
260 : // insert rest of the message
261 69679 : FXText::appendStyledText(text, style + 1, true);
262 69679 : FXText::setCursorPos(getLength() - 1);
263 69679 : FXText::setBottomLine(getLength() - 1);
264 69679 : if (isEnabled()) {
265 69679 : layout();
266 69679 : update();
267 : }
268 69679 : }
269 :
270 :
271 : void
272 23090 : GUIMessageWindow::addSeparator() {
273 23090 : std::string msg = std::string(100, '-') + "\n";
274 23090 : FXText::appendStyledText(msg.c_str(), (FXint) msg.length(), 1, true);
275 23090 : FXText::setCursorPos(getLength() - 1);
276 23090 : FXText::setBottomLine(getLength() - 1);
277 23090 : if (isEnabled()) {
278 23090 : layout();
279 23090 : update();
280 : }
281 23090 : }
282 :
283 :
284 : void
285 0 : GUIMessageWindow::clear() {
286 0 : if (getLength() == 0) {
287 : return;
288 : }
289 0 : FXText::removeText(0, getLength() - 1, true);
290 0 : if (isEnabled()) {
291 0 : layout();
292 0 : update();
293 : }
294 : }
295 :
296 :
297 : void
298 0 : GUIMessageWindow::registerMsgHandlers() {
299 0 : if (myMessageRetriever == nullptr) {
300 : // initialize only if registration is requested
301 0 : myMessageRetriever = new MsgOutputDevice(this, GUIEventType::MESSAGE_OCCURRED);
302 0 : myErrorRetriever = new MsgOutputDevice(this, GUIEventType::ERROR_OCCURRED);
303 0 : myWarningRetriever = new MsgOutputDevice(this, GUIEventType::WARNING_OCCURRED);
304 : }
305 0 : MsgHandler::getMessageInstance()->addRetriever(myMessageRetriever);
306 0 : MsgHandler::getErrorInstance()->addRetriever(myErrorRetriever);
307 0 : MsgHandler::getWarningInstance()->addRetriever(myWarningRetriever);
308 0 : }
309 :
310 :
311 : void
312 0 : GUIMessageWindow::unregisterMsgHandlers() {
313 0 : MsgHandler::getMessageInstance()->removeRetriever(myMessageRetriever);
314 0 : MsgHandler::getErrorInstance()->removeRetriever(myErrorRetriever);
315 0 : MsgHandler::getWarningInstance()->removeRetriever(myWarningRetriever);
316 0 : }
317 :
318 :
319 : long
320 0 : GUIMessageWindow::onKeyPress(FXObject* o, FXSelector sel, void* ptr) {
321 : FXEvent* e = (FXEvent*) ptr;
322 : // permit ctrl+a, ctrl+c
323 0 : if (e->state & CONTROLMASK) {
324 0 : return FXText::onKeyPress(o, sel, ptr);
325 : }
326 : return 0;
327 : }
328 :
329 :
330 : FXHiliteStyle*
331 0 : GUIMessageWindow::getStyles() {
332 0 : return myStyles;
333 : }
334 :
335 :
336 : void
337 7883 : GUIMessageWindow::fillStyles() {
338 : const FXColor white = FXRGB(0xff, 0xff, 0xff);
339 : const FXColor blue = FXRGB(0x00, 0x00, 0x88);
340 : const FXColor green = FXRGB(0x00, 0x88, 0x00);
341 : const FXColor red = FXRGB(0x88, 0x00, 0x00);
342 : const FXColor yellow = FXRGB(0xe6, 0x98, 0x00);
343 : const FXColor fuchsia = FXRGB(0x88, 0x00, 0x88);
344 : // set separator style
345 7883 : myStyles[0].normalForeColor = blue;
346 7883 : myStyles[0].normalBackColor = white;
347 7883 : myStyles[0].selectForeColor = white;
348 7883 : myStyles[0].selectBackColor = blue;
349 7883 : myStyles[0].hiliteForeColor = blue;
350 7883 : myStyles[0].hiliteBackColor = white;
351 7883 : myStyles[0].activeBackColor = white;
352 7883 : myStyles[0].style = 0;
353 : // set message text style
354 7883 : myStyles[1] = myStyles[0];
355 7883 : myStyles[1].normalForeColor = green;
356 7883 : myStyles[1].selectBackColor = green;
357 7883 : myStyles[1].hiliteForeColor = green;
358 7883 : myStyles[4] = myStyles[1];
359 7883 : myStyles[4].style = STYLE_UNDERLINE;
360 : // set error text style
361 7883 : myStyles[2] = myStyles[0];
362 7883 : myStyles[2].normalForeColor = red;
363 7883 : myStyles[2].selectBackColor = red;
364 7883 : myStyles[2].hiliteForeColor = red;
365 7883 : myStyles[5] = myStyles[2];
366 7883 : myStyles[5].style = STYLE_UNDERLINE;
367 : // set warning text style
368 7883 : myStyles[3] = myStyles[0];
369 7883 : myStyles[3].normalForeColor = yellow;
370 7883 : myStyles[3].selectBackColor = yellow;
371 7883 : myStyles[3].hiliteForeColor = yellow;
372 7883 : myStyles[6] = myStyles[3];
373 7883 : myStyles[6].style = STYLE_UNDERLINE;
374 : // set GLDebug text style
375 7883 : myStyles[7] = myStyles[0];
376 7883 : myStyles[7].normalForeColor = fuchsia;
377 7883 : myStyles[7].selectBackColor = fuchsia;
378 7883 : myStyles[7].hiliteForeColor = fuchsia;
379 7883 : }
380 :
381 : /****************************************************************************/
|