blob: 8c7989b28794555de7e54f1faf2b208f3a02b814 [file] [log] [blame]
PulkoMandy69eec3e2023-04-10 15:22:28 +02001/*
2 * Copyright (C) 2023 Adrien Destugues <pulkomandy@pulkomandy.tk>
3 *
4 * Distributed under terms of the MIT license.
5 */
6
PulkoMandyee68b1e2023-04-21 22:54:09 +02007#include <private/interface/AboutWindow.h>
PulkoMandy69eec3e2023-04-10 15:22:28 +02008#include <Application.h>
PulkoMandy768645a2023-06-04 21:43:01 +02009#include <Clipboard.h>
PulkoMandy69eec3e2023-04-10 15:22:28 +020010#include <private/interface/ColumnListView.h>
11#include <private/interface/ColumnTypes.h>
12#include <GroupLayout.h>
13#include <LayoutBuilder.h>
14#include <MenuBar.h>
15#include <Window.h>
16
17#include <ctype.h>
18
19#include "../BeDC.h"
20
21
22class BColorStringField: public BStringField
23{
24public:
25 BColorStringField(const char* string, rgb_color color)
26 : BStringField(string)
27 , fColor(color)
28 {
29 }
30
31 rgb_color fColor;
32};
33
34
35class BColorStringColumn: public BStringColumn
36{
37public:
38 BColorStringColumn(const char* title, float w, float minw, float maxw)
39 : BStringColumn(title, w, minw, maxw, false)
40 {
41 }
42
43 void DrawField(BField* field, BRect rect, BView* parent) override
44 {
45 BColorStringField* csField = dynamic_cast<BColorStringField*>(field);
46 if (csField) {
47 parent->SetHighColor(csField->fColor);
48 }
49 BStringColumn::DrawField(field, rect, parent);
50 }
51};
52
53
54static char sanechar(int32 input) {
55 input = input & 0xff;
56 if (isprint(input))
57 return (char)input;
58 else
59 return '.';
60}
61
62
63class LoggerWindow: public BWindow
64{
65public:
66 LoggerWindow()
67 : BWindow(BRect(100, 100, 900, 700), "Dev console", B_DOCUMENT_WINDOW,
68 B_QUIT_ON_WINDOW_CLOSE | B_AUTO_UPDATE_SIZE_LIMITS)
69 {
70 BGroupLayout* layout = new BGroupLayout(B_VERTICAL);
71 SetLayout(layout);
72 layout->SetSpacing(0);
73
74 fEventList = new BColumnListView("messages", 0, B_NO_BORDER);
75 fEventList->AddColumn(new BColorStringColumn("Name", 100, 50, 200), 0);
76 fEventList->AddColumn(new BColorStringColumn("Text", INT16_MAX, 50, INT16_MAX), 1);
77
PulkoMandyee68b1e2023-04-21 22:54:09 +020078 BMenuBar* mainMenu = new BMenuBar("main menu");
79
80 BMessage* separatorMessage = new BMessage(BEDC_MESSAGE);
81 separatorMessage->AddInt8("bedc_type", DC_SEPARATOR);
82
83 BMessage* clearMessage = new BMessage(BEDC_MESSAGE);
84 clearMessage->AddInt8("bedc_type", DC_CLEAR);
85
86 BLayoutBuilder::Menu<>(mainMenu)
87 .AddMenu("File")
88 .AddItem("New window", new BMessage(), 'N')
89 .AddItem("Activate window", new BMessage(), 'S')
90 .SetEnabled(false)
91 .AddSeparator()
92 .AddItem("About…", new BMessage(B_ABOUT_REQUESTED))
93 .AddSeparator()
94 .AddItem("Quit", new BMessage(B_QUIT_REQUESTED), 'Q')
95 .End()
96 .AddMenu("Edit")
PulkoMandy768645a2023-06-04 21:43:01 +020097 .AddItem("Cut", new BMessage(B_CUT), 'X')
98 .AddItem("Copy", new BMessage(B_COPY), 'C')
PulkoMandyee68b1e2023-04-21 22:54:09 +020099 .AddItem("Delete", new BMessage(), 'D')
100 .AddSeparator()
101 .AddItem("Clear", clearMessage, 'E')
PulkoMandy768645a2023-06-04 21:43:01 +0200102 .AddItem("Select all", new BMessage(B_SELECT_ALL), 'A')
PulkoMandyee68b1e2023-04-21 22:54:09 +0200103 .AddSeparator()
104 .AddItem("Preferences", new BMessage())
105 .End()
106 .AddMenu("Tools")
107 .AddItem("Add separator", separatorMessage, 'S')
108 .AddSeparator()
109 .AddItem("Save to file…", new BMessage())
110 .AddItem("Save selection to file…", new BMessage())
111 .End()
112 .End();
113
PulkoMandy69eec3e2023-04-10 15:22:28 +0200114 BLayoutBuilder::Group<>(layout)
115 .SetInsets(0, -1, -1, -1)
PulkoMandyee68b1e2023-04-21 22:54:09 +0200116 .Add(mainMenu)
PulkoMandy69eec3e2023-04-10 15:22:28 +0200117 .Add(fEventList)
118 .End();
PulkoMandyee68b1e2023-04-21 22:54:09 +0200119
120 // TODO make all menu items work
121 // TODO add status bar with online/offline status and line counter
122 // Newest created window becomes active, deactivates all other
123 // Actve window can be switched from menu
124 // Active window has its 'activate window' menu disabled, using the shortcut adds a
125 // separator instead
126 // TODO add line numbers in list view
PulkoMandy69eec3e2023-04-10 15:22:28 +0200127 }
128
129 void MessageReceived(BMessage* message) override
130 {
131 switch(message->what) {
132 case BEDC_MESSAGE:
133 {
PulkoMandyee68b1e2023-04-21 22:54:09 +0200134 // TODO parse bedc_ver to handle mulitple protocol versions
PulkoMandy69eec3e2023-04-10 15:22:28 +0200135 BString name = message->FindString("bedc_name");
136 BString text = message->FindString("bedc_text");
137 int8 colorEnum = message->FindInt8("bedc_color");
138 int8 type = message->FindInt8("bedc_type");
139 int8 main_type = message->FindInt8("bedc_main_type");
140
141 if (type == DC_CLEAR) {
142 fEventList->Clear();
143 return;
144 }
145
146 if (main_type == BEDC_BMESSAGE && text.IsEmpty()) {
147 int32 what = message->FindInt32("bedc_what");
PulkoMandyee68b1e2023-04-21 22:54:09 +0200148 BString description = message->FindString("bedc_desc");
149 if (description.IsEmpty())
150 description = "BMessage";
PulkoMandy69eec3e2023-04-10 15:22:28 +0200151
PulkoMandyee68b1e2023-04-21 22:54:09 +0200152 text.SetToFormat("%s(what=%#010x, %d, '%c%c%c%c')", description.String(),
153 what, what,
PulkoMandy69eec3e2023-04-10 15:22:28 +0200154 sanechar(what >> 24), sanechar(what >> 16),
155 sanechar(what >> 8), sanechar(what >> 0));
156 }
157
PulkoMandy69eec3e2023-04-10 15:22:28 +0200158 BRow* row = new BRow();
159 if (colorEnum == 0) {
160 row->SetField(new BStringField(name), 0);
161 } else {
162 rgb_color color;
163 switch(colorEnum) {
164 case DC_WHITE:
165 color = make_color(128, 128, 128, 255);
166 break;
167 case DC_BLACK:
168 color = make_color(64, 64, 64, 255);
169 break;
170 case DC_BLUE:
171 color = make_color(0, 0, 128, 255);
172 break;
173 case DC_RED:
174 color = make_color(0, 128, 0, 255);
175 break;
176 case DC_YELLOW:
177 color = make_color(128, 128, 0, 255);
178 break;
179 case DC_GREEN:
180 color = make_color(128, 0, 0, 255);
181 break;
182 }
183 row->SetField(new BColorStringField(name, color), 0);
184 }
185
186 if (type == DC_SUCCESS)
187 row->SetField(new BColorStringField(text, ui_color(B_SUCCESS_COLOR)), 1);
188 else if (type == DC_ERROR)
189 row->SetField(new BColorStringField(text, ui_color(B_FAILURE_COLOR)), 1);
190 else if (type == DC_MESSAGE)
191 row->SetField(new BStringField(text), 1);
192 else if (type == DC_SEPARATOR)
193 row->SetField(new BStringField("-----"), 1);
194 else {
195 message->PrintToStream();
196 }
197 fEventList->AddRow(row, -1);
198
PulkoMandyee68b1e2023-04-21 22:54:09 +0200199 // For BEDC_MESSAGE, there is no bedc_text, but instead all non-bedc fields of the
200 // message should be added to the view. We add them in subrows that can be folded.
PulkoMandy69eec3e2023-04-10 15:22:28 +0200201 if (main_type == BEDC_BMESSAGE) {
202 int index = 0;
203 char* nameFound;
204 type_code typeFound;
205 int32 countFound;
206 while (message->GetInfo(B_ANY_TYPE, index++, &nameFound, &typeFound, &countFound) == B_OK) {
207 // Don't display all the bedc_ internal stuff
208 if (BString(nameFound).StartsWith("bedc_"))
209 continue;
210
211 for (int j = 0; j < countFound; j++) {
212 name.SetToFormat("%s[%d] (%c%c%c%c)", nameFound, j,
213 (typeFound >> 24) & 0xFF, (typeFound >> 16) & 0xFF,
214 (typeFound >> 8) & 0xFF, (typeFound >> 0) & 0xFF
215 );
216
217 switch(typeFound) {
218#if 0
219 B_AFFINE_TRANSFORM_TYPE = 'AMTX',
220 B_ALIGNMENT_TYPE = 'ALGN',
221 B_ANY_TYPE = 'ANYT',
222 B_ATOM_TYPE = 'ATOM',
223 B_ATOMREF_TYPE = 'ATMR',
224 B_BOOL_TYPE = 'BOOL',
225 B_CHAR_TYPE = 'CHAR',
226 B_COLOR_8_BIT_TYPE = 'CLRB',
227 B_DOUBLE_TYPE = 'DBLE',
228 B_FLOAT_TYPE = 'FLOT',
229 B_GRAYSCALE_8_BIT_TYPE = 'GRYB',
230 B_INT16_TYPE = 'SHRT',
231 B_INT64_TYPE = 'LLNG',
232 B_INT8_TYPE = 'BYTE',
233 B_LARGE_ICON_TYPE = 'ICON',
234 B_MEDIA_PARAMETER_GROUP_TYPE = 'BMCG',
235 B_MEDIA_PARAMETER_TYPE = 'BMCT',
236 B_MEDIA_PARAMETER_WEB_TYPE = 'BMCW',
237 B_MESSAGE_TYPE = 'MSGG',
238 B_MESSENGER_TYPE = 'MSNG',
239 B_MIME_TYPE = 'MIME',
240 B_MINI_ICON_TYPE = 'MICN',
241 B_MONOCHROME_1_BIT_TYPE = 'MNOB',
242 B_OBJECT_TYPE = 'OPTR',
243 B_OFF_T_TYPE = 'OFFT',
244 B_PATTERN_TYPE = 'PATN',
245 B_POINTER_TYPE = 'PNTR',
246 B_PROPERTY_INFO_TYPE = 'SCTD',
247 B_RAW_TYPE = 'RAWT',
248 B_RECT_TYPE = 'RECT',
249 B_REF_TYPE = 'RREF',
250 B_NODE_REF_TYPE = 'NREF',
251 B_RGB_32_BIT_TYPE = 'RGBB',
252 B_RGB_COLOR_TYPE = 'RGBC',
253 B_SIZE_TYPE = 'SIZE',
254 B_SIZE_T_TYPE = 'SIZT',
255 B_SSIZE_T_TYPE = 'SSZT',
256 B_STRING_LIST_TYPE = 'STRL',
257 B_TIME_TYPE = 'TIME',
258 B_UINT16_TYPE = 'USHT',
259 B_UINT32_TYPE = 'ULNG',
260 B_UINT64_TYPE = 'ULLG',
261 B_UINT8_TYPE = 'UBYT',
262 B_VECTOR_ICON_TYPE = 'VICN',
263 B_XATTR_TYPE = 'XATR',
264 B_NETWORK_ADDRESS_TYPE = 'NWAD',
265 B_MIME_STRING_TYPE = 'MIMS',
266#endif
267 case B_STRING_TYPE:
268 text = message->FindString(nameFound, j);
269 break;
270 case B_INT32_TYPE:
271 {
272 int32 val = message->FindInt32(nameFound, j);
273 text.SetToFormat("%d (%#010x)", val, val);
274 break;
275 }
276 case B_POINT_TYPE:
277 {
278 BPoint val = message->FindPoint(nameFound, j);
279 text.SetToFormat("X: %f Y: %f", val.x, val.y);
280 break;
281 }
282 default:
283 text.SetTo("TODO: unhandled message content type");
284 break;
285 }
286
287 BRow* subRow = new BRow();
288 subRow->SetField(new BStringField(name), 0);
289 subRow->SetField(new BStringField(text), 1);
290 fEventList->AddRow(subRow, row);
291 }
292
293 }
294 }
295
296 break;
297 }
PulkoMandy768645a2023-06-04 21:43:01 +0200298 case B_CUT:
299 case B_COPY:
300 {
301 BRow* row = nullptr;
302
303 BString fullMessage;
304
305 // TODO copy all if selection is empty
306 while (row = fEventList->CurrentSelection(row)) {
307 BStringField* field = dynamic_cast<BStringField*>(row->GetField(1));
308 const char* string = field->String();
309 fullMessage.Prepend("\n");
310 fullMessage.Prepend(string);
311 }
312
313 be_clipboard->Lock();
314 be_clipboard->Clear();
315 BMessage* clip = be_clipboard->Data();
316
317 clip->AddData("text/plain", B_MIME_TYPE, fullMessage.String(), fullMessage.Length());
318
319 be_clipboard->Commit();
320 be_clipboard->Unlock();
321
322 if (message->what == B_CUT) {
323 while (row = fEventList->CurrentSelection(nullptr)) {
324 fEventList->RemoveRow(row);
325 delete row;
326 }
327 }
328
329 break;
330 }
PulkoMandyee68b1e2023-04-21 22:54:09 +0200331 case B_ABOUT_REQUESTED:
332 {
333 be_app_messenger.SendMessage(B_ABOUT_REQUESTED);
334 break;
335 }
PulkoMandy69eec3e2023-04-10 15:22:28 +0200336 default:
337 BWindow::MessageReceived(message);
338 break;
339 }
340 }
341
342private:
343 BColumnListView* fEventList;
344};
345
346
347class LoggerApp: public BApplication
348{
349public:
350 LoggerApp()
351 : BApplication("application/x-vnd.ml-BeDCApp")
352 {
PulkoMandyee68b1e2023-04-21 22:54:09 +0200353 }
354
355 void ReadyToRun() override
356 {
357 BApplication::ReadyToRun();
358 fActiveLogWindow = new LoggerWindow();
359 fActiveLogWindow->Show();
360 }
361
362 void AboutRequested() override
363 {
364 BAboutWindow* about = new BAboutWindow("Dev Console", "application/x-vnd.ml-BeDCApp");
365 about->AddDescription("Display log messages from other applications");
366 about->AddCopyright(2023, "Adrien Destugues");
367 about->Show();
PulkoMandy69eec3e2023-04-10 15:22:28 +0200368 }
369
370 void MessageReceived(BMessage* message) override
371 {
372 switch(message->what) {
373 case BEDC_MESSAGE:
PulkoMandyee68b1e2023-04-21 22:54:09 +0200374 DetachCurrentMessage();
375 fActiveLogWindow->PostMessage(message);
PulkoMandy69eec3e2023-04-10 15:22:28 +0200376 break;
377 default:
378 BApplication::MessageReceived(message);
379 break;
380 }
381 }
PulkoMandyee68b1e2023-04-21 22:54:09 +0200382
383private:
384 BWindow* fActiveLogWindow;
PulkoMandy69eec3e2023-04-10 15:22:28 +0200385};
386
387
388int main(void)
389{
390 LoggerApp app;
391 app.Run();
392}