Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2003-2024 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 2635185 : FXIMPLEMENT_ABSTRACT(GUIMessageWindow, FXText, GUIMessageWindowMap, ARRAYNUMBER(GUIMessageWindowMap))
53 :
54 : // ===========================================================================
55 : // method definitions
56 : // ===========================================================================
57 7533 : GUIMessageWindow::GUIMessageWindow(FXComposite* parent, GUIMainWindow* mainWindow) :
58 : FXText(parent, nullptr, 0, 0, 0, 0, 0, 50),
59 7533 : myMainWindow(mainWindow),
60 7533 : myErrorRetriever(nullptr),
61 7533 : myMessageRetriever(nullptr),
62 7533 : myWarningRetriever(nullptr) {
63 7533 : setStyled(true);
64 7533 : setEditable(false);
65 : // fill styles
66 7533 : fillStyles();
67 : // set styles
68 7533 : setHiliteStyles(myStyles);
69 7533 : myTimeText = TL(" time");
70 : // see GUIGlObject.cpp
71 7533 : myTypeStrings[StringUtils::to_lower_case(TL("edge"))] = "edge";
72 7533 : myTypeStrings[StringUtils::to_lower_case(TL("lane"))] = "lane";
73 7533 : myTypeStrings[StringUtils::to_lower_case(TL("junction"))] = "junction";
74 7533 : myTypeStrings[StringUtils::to_lower_case(TL("vehicle"))] = "vehicle";
75 7533 : myTypeStrings[StringUtils::to_lower_case(TL("person"))] = "person";
76 7533 : myTypeStrings[StringUtils::to_lower_case(TL("tlLogic"))] = "tlLogic";
77 7533 : myTypeStrings[StringUtils::to_lower_case(TL("busStop"))] = "busStop";
78 7533 : myTypeStrings[StringUtils::to_lower_case(TL("trainStop"))] = "busStop";
79 7533 : myTypeStrings[StringUtils::to_lower_case(TL("containerStop"))] = "containerStop";
80 7533 : myTypeStrings[StringUtils::to_lower_case(TL("chargingStation"))] = "chargingStation";
81 7533 : myTypeStrings[StringUtils::to_lower_case(TL("overheadWireSegment"))] = "overheadWireSegment";
82 7533 : myTypeStrings[StringUtils::to_lower_case(TL("parkingArea"))] = "parkingArea";
83 7533 : }
84 :
85 :
86 15046 : GUIMessageWindow::~GUIMessageWindow() {
87 7523 : delete[] myStyles;
88 7523 : delete myMessageRetriever;
89 7523 : delete myErrorRetriever;
90 7523 : delete myWarningRetriever;
91 15046 : }
92 :
93 :
94 : const GUIGlObject*
95 74148 : GUIMessageWindow::getActiveStringObject(const FXString& text, const FXint pos, const FXint lineS, const FXint lineE) const {
96 74148 : const FXint idS = MAX2(text.rfind(" '", pos), text.rfind("='", pos));
97 74148 : const FXint idE = text.find("'", pos);
98 74148 : if (idS >= 0 && idE >= 0 && idS >= lineS && idE <= lineE) {
99 56885 : FXint typeS = text.rfind(" ", idS - 1);
100 56885 : if (typeS >= 0) {
101 55370 : if (text.at(typeS + 1) == '(') {
102 : typeS++;
103 : }
104 55370 : std::string type(text.mid(typeS + 1, idS - typeS - 1).lower().text());
105 : const auto& tsIt = myTypeStrings.find(type);
106 55370 : if (tsIt != myTypeStrings.end()) {
107 36257 : type = tsIt->second;
108 : }
109 55370 : const std::string id(text.mid(idS + 2, idE - idS - 2).text());
110 55370 : const std::string typedID = type + ":" + id;
111 55370 : 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 22819 : GUIMessageWindow::getTimeString(const FXString& text, const FXint pos) const {
121 22819 : const FXint end = text.find_first_of(" ,", pos + 1);
122 : std::string time;
123 22819 : if (end >= 0) {
124 2816 : time = text.mid(pos, end - pos).text();
125 : } else {
126 42822 : time = text.mid(pos, text.length() - pos).text();
127 21411 : if (time.empty()) {
128 : return -1;
129 : }
130 21411 : if (time.back() == '\n') {
131 : time.pop_back();
132 : }
133 21411 : if (time.empty()) {
134 : return -1;
135 : }
136 21408 : if (time.back() == '.') {
137 : time.pop_back();
138 : }
139 : }
140 22816 : if (time.empty()) {
141 : return -1;
142 : }
143 21545 : if (time.front() == ' ') {
144 14172 : 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 21545 : return string2time(time);
150 82 : } catch (...) {
151 : return -1;
152 82 : }
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 61810 : GUIMessageWindow::appendMsg(GUIEventType eType, const std::string& msg) {
194 61810 : if (!isEnabled()) {
195 0 : show();
196 : }
197 : // build the styled message
198 : FXint style = 1;
199 61810 : 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 61810 : default:
221 : assert(false);
222 : }
223 61810 : FXString text(msg.c_str());
224 61810 : if (myLocateLinks) {
225 61810 : FXint pos = text.find("'");
226 135958 : while (pos >= 0) {
227 74148 : const GUIGlObject* const glObj = getActiveStringObject(text, pos + 1, 0, text.length());
228 74148 : if (glObj != nullptr) {
229 28331 : GUIGlObjectStorage::gIDStorage.unblockObject(glObj->getGlID());
230 28331 : FXString insText = text.left(pos + 1);
231 28331 : FXText::appendStyledText(insText, style + 1);
232 28331 : text.erase(0, pos + 1);
233 28331 : pos = text.find("'");
234 28331 : insText = text.left(pos);
235 28331 : FXText::appendStyledText(insText, style + 4);
236 28331 : text.erase(0, pos);
237 28331 : }
238 74148 : pos = text.find("'", pos + 1);
239 : }
240 : // find time links
241 61810 : pos = text.find(myTimeText.c_str());
242 61810 : const int timeTerm = (int)myTimeText.size() + 1;
243 : SUMOTime t = -1;
244 61810 : if (pos >= 0) {
245 22819 : t = getTimeString(text, pos + timeTerm);
246 : }
247 22819 : if (t >= 0) {
248 21463 : FXString insText = text.left(pos + timeTerm);
249 21463 : FXText::appendStyledText(insText, style + 1);
250 21463 : text.erase(0, pos + timeTerm);
251 21463 : pos = text.find(" ");
252 21463 : if (pos < 0) {
253 13026 : pos = text.rfind(".");
254 : }
255 21463 : insText = text.left(pos);
256 21463 : FXText::appendStyledText(insText, style + 4);
257 21463 : text.erase(0, pos);
258 21463 : }
259 : }
260 : // insert rest of the message
261 61810 : FXText::appendStyledText(text, style + 1, true);
262 61810 : FXText::setCursorPos(getLength() - 1);
263 61810 : FXText::setBottomLine(getLength() - 1);
264 61810 : if (isEnabled()) {
265 61810 : layout();
266 61810 : update();
267 : }
268 61810 : }
269 :
270 :
271 : void
272 22107 : GUIMessageWindow::addSeparator() {
273 22107 : std::string msg = std::string(100, '-') + "\n";
274 22107 : FXText::appendStyledText(msg.c_str(), (FXint) msg.length(), 1, true);
275 22107 : FXText::setCursorPos(getLength() - 1);
276 22107 : FXText::setBottomLine(getLength() - 1);
277 22107 : if (isEnabled()) {
278 22107 : layout();
279 22107 : update();
280 : }
281 22107 : }
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 : myDebugRetriever = new MsgOutputDevice(this, GUIEventType::DEBUG_OCCURRED);
304 0 : myGLDebugRetriever = new MsgOutputDevice(this, GUIEventType::GLDEBUG_OCCURRED);
305 0 : myWarningRetriever = new MsgOutputDevice(this, GUIEventType::WARNING_OCCURRED);
306 : }
307 0 : MsgHandler::getMessageInstance()->addRetriever(myMessageRetriever);
308 0 : MsgHandler::getDebugInstance()->addRetriever(myDebugRetriever);
309 0 : MsgHandler::getGLDebugInstance()->addRetriever(myGLDebugRetriever);
310 0 : MsgHandler::getErrorInstance()->addRetriever(myErrorRetriever);
311 0 : MsgHandler::getWarningInstance()->addRetriever(myWarningRetriever);
312 0 : }
313 :
314 :
315 : void
316 0 : GUIMessageWindow::unregisterMsgHandlers() {
317 0 : MsgHandler::getMessageInstance()->removeRetriever(myMessageRetriever);
318 0 : MsgHandler::getDebugInstance()->removeRetriever(myDebugRetriever);
319 0 : MsgHandler::getGLDebugInstance()->removeRetriever(myGLDebugRetriever);
320 0 : MsgHandler::getErrorInstance()->removeRetriever(myErrorRetriever);
321 0 : MsgHandler::getWarningInstance()->removeRetriever(myWarningRetriever);
322 0 : }
323 :
324 :
325 : long
326 0 : GUIMessageWindow::onKeyPress(FXObject* o, FXSelector sel, void* ptr) {
327 : FXEvent* e = (FXEvent*) ptr;
328 : // permit ctrl+a, ctrl+c
329 0 : if (e->state & CONTROLMASK) {
330 0 : return FXText::onKeyPress(o, sel, ptr);
331 : }
332 : return 0;
333 : }
334 :
335 :
336 : FXHiliteStyle*
337 0 : GUIMessageWindow::getStyles() {
338 0 : return myStyles;
339 : }
340 :
341 :
342 : void
343 7533 : GUIMessageWindow::fillStyles() {
344 : const FXColor white = FXRGB(0xff, 0xff, 0xff);
345 : const FXColor blue = FXRGB(0x00, 0x00, 0x88);
346 : const FXColor green = FXRGB(0x00, 0x88, 0x00);
347 : const FXColor red = FXRGB(0x88, 0x00, 0x00);
348 : const FXColor yellow = FXRGB(0xe6, 0x98, 0x00);
349 : const FXColor fuchsia = FXRGB(0x88, 0x00, 0x88);
350 : // set separator style
351 7533 : myStyles[0].normalForeColor = blue;
352 7533 : myStyles[0].normalBackColor = white;
353 7533 : myStyles[0].selectForeColor = white;
354 7533 : myStyles[0].selectBackColor = blue;
355 7533 : myStyles[0].hiliteForeColor = blue;
356 7533 : myStyles[0].hiliteBackColor = white;
357 7533 : myStyles[0].activeBackColor = white;
358 7533 : myStyles[0].style = 0;
359 : // set message text style
360 7533 : myStyles[1] = myStyles[0];
361 7533 : myStyles[1].normalForeColor = green;
362 7533 : myStyles[1].selectBackColor = green;
363 7533 : myStyles[1].hiliteForeColor = green;
364 7533 : myStyles[4] = myStyles[1];
365 7533 : myStyles[4].style = STYLE_UNDERLINE;
366 : // set error text style
367 7533 : myStyles[2] = myStyles[0];
368 7533 : myStyles[2].normalForeColor = red;
369 7533 : myStyles[2].selectBackColor = red;
370 7533 : myStyles[2].hiliteForeColor = red;
371 7533 : myStyles[5] = myStyles[2];
372 7533 : myStyles[5].style = STYLE_UNDERLINE;
373 : // set warning text style
374 7533 : myStyles[3] = myStyles[0];
375 7533 : myStyles[3].normalForeColor = yellow;
376 7533 : myStyles[3].selectBackColor = yellow;
377 7533 : myStyles[3].hiliteForeColor = yellow;
378 7533 : myStyles[6] = myStyles[3];
379 7533 : myStyles[6].style = STYLE_UNDERLINE;
380 : // set GLDebug text style
381 7533 : myStyles[7] = myStyles[0];
382 7533 : myStyles[7].normalForeColor = fuchsia;
383 7533 : myStyles[7].selectBackColor = fuchsia;
384 7533 : myStyles[7].hiliteForeColor = fuchsia;
385 7533 : }
386 :
387 : /****************************************************************************/
|