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 :
42 : // ===========================================================================
43 : // FOX callback mapping
44 : // ===========================================================================
45 :
46 : FXDEFMAP(GUIMessageWindow) GUIMessageWindowMap[] = {
47 : FXMAPFUNC(SEL_KEYPRESS, 0, GUIMessageWindow::onKeyPress),
48 : };
49 :
50 2688636 : FXIMPLEMENT_ABSTRACT(GUIMessageWindow, FXText, GUIMessageWindowMap, ARRAYNUMBER(GUIMessageWindowMap))
51 :
52 : // ===========================================================================
53 : // method definitions
54 : // ===========================================================================
55 7257 : GUIMessageWindow::GUIMessageWindow(FXComposite* parent, GUIMainWindow* mainWindow) :
56 : FXText(parent, nullptr, 0, 0, 0, 0, 0, 50),
57 7257 : myMainWindow(mainWindow),
58 7257 : myErrorRetriever(nullptr),
59 7257 : myMessageRetriever(nullptr),
60 7257 : myWarningRetriever(nullptr) {
61 7257 : setStyled(true);
62 7257 : setEditable(false);
63 : // fill styles
64 7257 : fillStyles();
65 : // set styles
66 7257 : setHiliteStyles(myStyles);
67 7257 : }
68 :
69 :
70 14496 : GUIMessageWindow::~GUIMessageWindow() {
71 7248 : delete[] myStyles;
72 7248 : delete myMessageRetriever;
73 7248 : delete myErrorRetriever;
74 7248 : delete myWarningRetriever;
75 14496 : }
76 :
77 :
78 : const GUIGlObject*
79 65176 : GUIMessageWindow::getActiveStringObject(const FXString& text, const FXint pos, const FXint lineS, const FXint lineE) const {
80 65176 : const FXint idS = MAX2(text.rfind(" '", pos), text.rfind("='", pos));
81 65176 : const FXint idE = text.find("'", pos);
82 65176 : if (idS >= 0 && idE >= 0 && idS >= lineS && idE <= lineE) {
83 48230 : FXint typeS = text.rfind(" ", idS - 1);
84 48230 : if (typeS >= 0) {
85 46726 : if (text.at(typeS + 1) == '(') {
86 : typeS++;
87 : }
88 93452 : std::string type(text.mid(typeS + 1, idS - typeS - 1).lower().text());
89 46726 : if (type == "tllogic") {
90 : type = "tlLogic"; // see GUIGlObject.cpp
91 93003 : } else if (type == "busstop" || type == "trainstop") {
92 : type = "busStop";
93 46461 : } else if (type == "containerstop") {
94 : type = "containerStop";
95 46449 : } else if (type == "chargingstation") {
96 : type = "chargingStation";
97 46449 : } else if (type == "overheadwiresegment") {
98 : type = "overheadWireSegment";
99 46449 : } else if (type == "parkingarea") {
100 : type = "parkingArea";
101 : }
102 46726 : const std::string id(text.mid(idS + 2, idE - idS - 2).text());
103 46726 : const std::string typedID = type + ":" + id;
104 46726 : const GUIGlObject* o = GUIGlObjectStorage::gIDStorage.getObjectBlocking(typedID);
105 : //std::cout << " getActiveStringObject '" << typedID << "' o=" << (o == nullptr ? "NULL" : o->getMicrosimID()) << "\n";
106 : return o;
107 : }
108 : }
109 : return nullptr;
110 : }
111 :
112 : SUMOTime
113 18538 : GUIMessageWindow::getTimeString(const FXString& text, const FXint pos, const FXint /*lineS*/, const FXint /*lineE*/) const {
114 18538 : const FXint end = text.find(" ", pos + 1);
115 : std::string time;
116 18538 : if (end >= 0) {
117 2522 : time = text.mid(pos, end - pos).text();
118 : } else {
119 34554 : time = text.mid(pos, text.length() - pos).text();
120 17277 : if (time.empty()) {
121 : return -1;
122 : }
123 17277 : if (time.back() == '\n') {
124 : time.pop_back();
125 : }
126 17277 : if (time.empty()) {
127 : return -1;
128 : }
129 17274 : if (time.back() == '.') {
130 : time.pop_back();
131 : }
132 : }
133 18535 : if (time.empty()) {
134 : return -1;
135 : }
136 17346 : if (time.front() == ' ') {
137 13656 : time = time.substr(1);
138 : }
139 : //std::cout << "text='" << text.text() << "' pos=" << pos << " time='" << time << "'\n";
140 : try {
141 : //std::cout << " SUMOTime=" << string2time(time) << "\n";
142 17346 : return string2time(time);
143 129 : } catch (...) {
144 : return -1;
145 129 : }
146 : }
147 :
148 :
149 : void
150 0 : GUIMessageWindow::setCursorPos(FXint pos, FXbool notify) {
151 0 : FXText::setCursorPos(pos, notify);
152 0 : if (myLocateLinks) {
153 0 : GUIMainWindow* const main = GUIMainWindow::getInstance();
154 0 : std::vector<std::string> viewIDs = main->getViewIDs();
155 0 : if (viewIDs.empty()) {
156 : return;
157 : }
158 0 : GUIGlChildWindow* const child = main->getViewByID(viewIDs[0]);
159 0 : const FXString text = getText();
160 0 : const GUIGlObject* const glObj = getActiveStringObject(text, pos, lineStart(pos), lineEnd(pos));
161 0 : if (glObj != nullptr) {
162 0 : child->setView(glObj->getGlID());
163 0 : GUIGlObjectStorage::gIDStorage.unblockObject(glObj->getGlID());
164 0 : if (getApp()->getKeyState(KEY_Control_L)) {
165 0 : gSelected.toggleSelection(glObj->getGlID());
166 : }
167 : } else {
168 : const int lookback = MIN2(pos, 20);
169 0 : const int start = MAX2(lineStart(pos), pos - lookback);
170 0 : const FXString candidate = text.mid(start, lineEnd(pos) - start);
171 0 : FXint timePos = candidate.find(TL(" time"));
172 0 : if (timePos > -1) {
173 0 : timePos += (int)std::string(TL(" time")).size() + 1;
174 0 : SUMOTime t = -1;
175 0 : if (pos >= 0 && pos > start + timePos) {
176 0 : t = getTimeString(candidate, timePos, 0, (int)candidate.length());
177 0 : if (t >= 0) {
178 0 : t += myBreakPointOffset;
179 0 : std::vector<SUMOTime> breakpoints = myMainWindow->retrieveBreakpoints();
180 0 : if (std::find(breakpoints.begin(), breakpoints.end(), t) == breakpoints.end()) {
181 0 : breakpoints.push_back(t);
182 0 : std::sort(breakpoints.begin(), breakpoints.end());
183 0 : myMainWindow->setBreakpoints(breakpoints);
184 0 : myMainWindow->setStatusBarText(TLF("Set breakpoint at %", time2string(t)));
185 : }
186 : }
187 : }
188 : }
189 0 : }
190 0 : }
191 : }
192 :
193 :
194 : void
195 56462 : GUIMessageWindow::appendMsg(GUIEventType eType, const std::string& msg) {
196 56462 : if (!isEnabled()) {
197 0 : show();
198 : }
199 : // build the styled message
200 : FXint style = 1;
201 56462 : switch (eType) {
202 : case GUIEventType::DEBUG_OCCURRED:
203 : // color: blue
204 : style = 0;
205 : break;
206 : case GUIEventType::GLDEBUG_OCCURRED:
207 : // color: fuchsia
208 : style = 7;
209 : break;
210 : case GUIEventType::ERROR_OCCURRED:
211 : // color: red
212 : style = 2;
213 : break;
214 : case GUIEventType::WARNING_OCCURRED:
215 : // color: yellow
216 : style = 3;
217 : break;
218 : case GUIEventType::MESSAGE_OCCURRED:
219 : // color: green
220 : style = 1;
221 : break;
222 56462 : default:
223 : assert(false);
224 : }
225 112924 : FXString text(msg.c_str());
226 56462 : if (myLocateLinks) {
227 56462 : FXint pos = text.find("'");
228 121638 : while (pos >= 0) {
229 65176 : const GUIGlObject* const glObj = getActiveStringObject(text, pos + 1, 0, text.length());
230 65176 : if (glObj != nullptr) {
231 20495 : GUIGlObjectStorage::gIDStorage.unblockObject(glObj->getGlID());
232 20495 : FXString insText = text.left(pos + 1);
233 20495 : FXText::appendStyledText(insText, style + 1);
234 20495 : text.erase(0, pos + 1);
235 20495 : pos = text.find("'");
236 20495 : insText = text.left(pos);
237 20495 : FXText::appendStyledText(insText, style + 4);
238 20495 : text.erase(0, pos);
239 20495 : }
240 65176 : pos = text.find("'", pos + 1);
241 : }
242 : // find time links
243 56462 : pos = text.find(TL(" time"));
244 56462 : const int timeTerm = (int)std::string(TL(" time")).size() + 1;
245 : SUMOTime t = -1;
246 56462 : if (pos >= 0) {
247 18538 : t = getTimeString(text, pos + timeTerm, 0, text.length());
248 : }
249 18538 : if (t >= 0) {
250 17217 : FXString insText = text.left(pos + timeTerm);
251 17217 : FXText::appendStyledText(insText, style + 1);
252 17217 : text.erase(0, pos + timeTerm);
253 17217 : pos = text.find(" ");
254 17217 : if (pos < 0) {
255 9233 : pos = text.rfind(".");
256 : }
257 17217 : insText = text.left(pos);
258 17217 : FXText::appendStyledText(insText, style + 4);
259 17217 : text.erase(0, pos);
260 17217 : }
261 : }
262 : // insert rest of the message
263 56462 : FXText::appendStyledText(text, style + 1, true);
264 56462 : FXText::setCursorPos(getLength() - 1);
265 56462 : FXText::setBottomLine(getLength() - 1);
266 56462 : if (isEnabled()) {
267 56462 : layout();
268 56462 : update();
269 : }
270 56462 : }
271 :
272 :
273 : void
274 21292 : GUIMessageWindow::addSeparator() {
275 21292 : std::string msg = "----------------------------------------------------------------------------------------\n";
276 21292 : FXText::appendStyledText(msg.c_str(), (FXint) msg.length(), 1, true);
277 21292 : FXText::setCursorPos(getLength() - 1);
278 21292 : FXText::setBottomLine(getLength() - 1);
279 21292 : if (isEnabled()) {
280 21292 : layout();
281 21292 : update();
282 : }
283 21292 : }
284 :
285 :
286 : void
287 0 : GUIMessageWindow::clear() {
288 0 : if (getLength() == 0) {
289 : return;
290 : }
291 0 : FXText::removeText(0, getLength() - 1, true);
292 0 : if (isEnabled()) {
293 0 : layout();
294 0 : update();
295 : }
296 : }
297 :
298 :
299 : void
300 0 : GUIMessageWindow::registerMsgHandlers() {
301 0 : if (myMessageRetriever == nullptr) {
302 : // initialize only if registration is requested
303 0 : myMessageRetriever = new MsgOutputDevice(this, GUIEventType::MESSAGE_OCCURRED);
304 0 : myErrorRetriever = new MsgOutputDevice(this, GUIEventType::ERROR_OCCURRED);
305 0 : myDebugRetriever = new MsgOutputDevice(this, GUIEventType::DEBUG_OCCURRED);
306 0 : myGLDebugRetriever = new MsgOutputDevice(this, GUIEventType::GLDEBUG_OCCURRED);
307 0 : myWarningRetriever = new MsgOutputDevice(this, GUIEventType::WARNING_OCCURRED);
308 : }
309 0 : MsgHandler::getMessageInstance()->addRetriever(myMessageRetriever);
310 0 : MsgHandler::getDebugInstance()->addRetriever(myDebugRetriever);
311 0 : MsgHandler::getGLDebugInstance()->addRetriever(myGLDebugRetriever);
312 0 : MsgHandler::getErrorInstance()->addRetriever(myErrorRetriever);
313 0 : MsgHandler::getWarningInstance()->addRetriever(myWarningRetriever);
314 0 : }
315 :
316 :
317 : void
318 0 : GUIMessageWindow::unregisterMsgHandlers() {
319 0 : MsgHandler::getMessageInstance()->removeRetriever(myMessageRetriever);
320 0 : MsgHandler::getDebugInstance()->removeRetriever(myDebugRetriever);
321 0 : MsgHandler::getGLDebugInstance()->removeRetriever(myGLDebugRetriever);
322 0 : MsgHandler::getErrorInstance()->removeRetriever(myErrorRetriever);
323 0 : MsgHandler::getWarningInstance()->removeRetriever(myWarningRetriever);
324 0 : }
325 :
326 :
327 : long
328 0 : GUIMessageWindow::onKeyPress(FXObject* o, FXSelector sel, void* ptr) {
329 : FXEvent* e = (FXEvent*) ptr;
330 : // permit ctrl+a, ctrl+c
331 0 : if (e->state & CONTROLMASK) {
332 0 : return FXText::onKeyPress(o, sel, ptr);
333 : }
334 : return 0;
335 : }
336 :
337 :
338 : FXHiliteStyle*
339 0 : GUIMessageWindow::getStyles() {
340 0 : return myStyles;
341 : }
342 :
343 :
344 : void
345 7257 : GUIMessageWindow::fillStyles() {
346 : const FXColor white = FXRGB(0xff, 0xff, 0xff);
347 : const FXColor blue = FXRGB(0x00, 0x00, 0x88);
348 : const FXColor green = FXRGB(0x00, 0x88, 0x00);
349 : const FXColor red = FXRGB(0x88, 0x00, 0x00);
350 : const FXColor yellow = FXRGB(0xe6, 0x98, 0x00);
351 : const FXColor fuchsia = FXRGB(0x88, 0x00, 0x88);
352 : // set separator style
353 7257 : myStyles[0].normalForeColor = blue;
354 7257 : myStyles[0].normalBackColor = white;
355 7257 : myStyles[0].selectForeColor = white;
356 7257 : myStyles[0].selectBackColor = blue;
357 7257 : myStyles[0].hiliteForeColor = blue;
358 7257 : myStyles[0].hiliteBackColor = white;
359 7257 : myStyles[0].activeBackColor = white;
360 7257 : myStyles[0].style = 0;
361 : // set message text style
362 7257 : myStyles[1] = myStyles[0];
363 7257 : myStyles[1].normalForeColor = green;
364 7257 : myStyles[1].selectBackColor = green;
365 7257 : myStyles[1].hiliteForeColor = green;
366 7257 : myStyles[4] = myStyles[1];
367 7257 : myStyles[4].style = STYLE_UNDERLINE;
368 : // set error text style
369 7257 : myStyles[2] = myStyles[0];
370 7257 : myStyles[2].normalForeColor = red;
371 7257 : myStyles[2].selectBackColor = red;
372 7257 : myStyles[2].hiliteForeColor = red;
373 7257 : myStyles[5] = myStyles[2];
374 7257 : myStyles[5].style = STYLE_UNDERLINE;
375 : // set warning text style
376 7257 : myStyles[3] = myStyles[0];
377 7257 : myStyles[3].normalForeColor = yellow;
378 7257 : myStyles[3].selectBackColor = yellow;
379 7257 : myStyles[3].hiliteForeColor = yellow;
380 7257 : myStyles[6] = myStyles[3];
381 7257 : myStyles[6].style = STYLE_UNDERLINE;
382 : // set GLDebug text style
383 7257 : myStyles[7] = myStyles[0];
384 7257 : myStyles[7].normalForeColor = fuchsia;
385 7257 : myStyles[7].selectBackColor = fuchsia;
386 7257 : myStyles[7].hiliteForeColor = fuchsia;
387 7257 : }
388 :
389 : /****************************************************************************/
|