blob: fc2970821b1ecaa8a0e65e0a0d1d94e3b8d627ce [file] [log] [blame]
/*
* Copyright (C) 2023 Adrien Destugues <pulkomandy@pulkomandy.tk>
*
* Distributed under terms of the MIT license.
*/
#include <private/interface/AboutWindow.h>
#include <Application.h>
#include <private/interface/ColumnListView.h>
#include <private/interface/ColumnTypes.h>
#include <GroupLayout.h>
#include <LayoutBuilder.h>
#include <MenuBar.h>
#include <Window.h>
#include <ctype.h>
#include "../BeDC.h"
class BColorStringField: public BStringField
{
public:
BColorStringField(const char* string, rgb_color color)
: BStringField(string)
, fColor(color)
{
}
rgb_color fColor;
};
class BColorStringColumn: public BStringColumn
{
public:
BColorStringColumn(const char* title, float w, float minw, float maxw)
: BStringColumn(title, w, minw, maxw, false)
{
}
void DrawField(BField* field, BRect rect, BView* parent) override
{
BColorStringField* csField = dynamic_cast<BColorStringField*>(field);
if (csField) {
parent->SetHighColor(csField->fColor);
}
BStringColumn::DrawField(field, rect, parent);
}
};
static char sanechar(int32 input) {
input = input & 0xff;
if (isprint(input))
return (char)input;
else
return '.';
}
class LoggerWindow: public BWindow
{
public:
LoggerWindow()
: BWindow(BRect(100, 100, 900, 700), "Dev console", B_DOCUMENT_WINDOW,
B_QUIT_ON_WINDOW_CLOSE | B_AUTO_UPDATE_SIZE_LIMITS)
{
BGroupLayout* layout = new BGroupLayout(B_VERTICAL);
SetLayout(layout);
layout->SetSpacing(0);
fEventList = new BColumnListView("messages", 0, B_NO_BORDER);
fEventList->AddColumn(new BColorStringColumn("Name", 100, 50, 200), 0);
fEventList->AddColumn(new BColorStringColumn("Text", INT16_MAX, 50, INT16_MAX), 1);
BMenuBar* mainMenu = new BMenuBar("main menu");
BMessage* separatorMessage = new BMessage(BEDC_MESSAGE);
separatorMessage->AddInt8("bedc_type", DC_SEPARATOR);
BMessage* clearMessage = new BMessage(BEDC_MESSAGE);
clearMessage->AddInt8("bedc_type", DC_CLEAR);
BLayoutBuilder::Menu<>(mainMenu)
.AddMenu("File")
.AddItem("New window", new BMessage(), 'N')
.AddItem("Activate window", new BMessage(), 'S')
.SetEnabled(false)
.AddSeparator()
.AddItem("About…", new BMessage(B_ABOUT_REQUESTED))
.AddSeparator()
.AddItem("Quit", new BMessage(B_QUIT_REQUESTED), 'Q')
.End()
.AddMenu("Edit")
.AddItem("Cut", new BMessage(), 'X')
.AddItem("Copy", new BMessage(), 'C')
.AddItem("Delete", new BMessage(), 'D')
.AddSeparator()
.AddItem("Clear", clearMessage, 'E')
.AddItem("Select all", new BMessage(), 'A')
.AddSeparator()
.AddItem("Preferences", new BMessage())
.End()
.AddMenu("Tools")
.AddItem("Add separator", separatorMessage, 'S')
.AddSeparator()
.AddItem("Save to file…", new BMessage())
.AddItem("Save selection to file…", new BMessage())
.End()
.End();
BLayoutBuilder::Group<>(layout)
.SetInsets(0, -1, -1, -1)
.Add(mainMenu)
.Add(fEventList)
.End();
// TODO make all menu items work
// TODO add status bar with online/offline status and line counter
// Newest created window becomes active, deactivates all other
// Actve window can be switched from menu
// Active window has its 'activate window' menu disabled, using the shortcut adds a
// separator instead
// TODO add line numbers in list view
}
void MessageReceived(BMessage* message) override
{
switch(message->what) {
case BEDC_MESSAGE:
{
// TODO parse bedc_ver to handle mulitple protocol versions
BString name = message->FindString("bedc_name");
BString text = message->FindString("bedc_text");
int8 colorEnum = message->FindInt8("bedc_color");
int8 type = message->FindInt8("bedc_type");
int8 main_type = message->FindInt8("bedc_main_type");
if (type == DC_CLEAR) {
fEventList->Clear();
return;
}
if (main_type == BEDC_BMESSAGE && text.IsEmpty()) {
int32 what = message->FindInt32("bedc_what");
BString description = message->FindString("bedc_desc");
if (description.IsEmpty())
description = "BMessage";
text.SetToFormat("%s(what=%#010x, %d, '%c%c%c%c')", description.String(),
what, what,
sanechar(what >> 24), sanechar(what >> 16),
sanechar(what >> 8), sanechar(what >> 0));
}
BRow* row = new BRow();
if (colorEnum == 0) {
row->SetField(new BStringField(name), 0);
} else {
rgb_color color;
switch(colorEnum) {
case DC_WHITE:
color = make_color(128, 128, 128, 255);
break;
case DC_BLACK:
color = make_color(64, 64, 64, 255);
break;
case DC_BLUE:
color = make_color(0, 0, 128, 255);
break;
case DC_RED:
color = make_color(0, 128, 0, 255);
break;
case DC_YELLOW:
color = make_color(128, 128, 0, 255);
break;
case DC_GREEN:
color = make_color(128, 0, 0, 255);
break;
}
row->SetField(new BColorStringField(name, color), 0);
}
if (type == DC_SUCCESS)
row->SetField(new BColorStringField(text, ui_color(B_SUCCESS_COLOR)), 1);
else if (type == DC_ERROR)
row->SetField(new BColorStringField(text, ui_color(B_FAILURE_COLOR)), 1);
else if (type == DC_MESSAGE)
row->SetField(new BStringField(text), 1);
else if (type == DC_SEPARATOR)
row->SetField(new BStringField("-----"), 1);
else {
message->PrintToStream();
}
fEventList->AddRow(row, -1);
// For BEDC_MESSAGE, there is no bedc_text, but instead all non-bedc fields of the
// message should be added to the view. We add them in subrows that can be folded.
if (main_type == BEDC_BMESSAGE) {
int index = 0;
char* nameFound;
type_code typeFound;
int32 countFound;
while (message->GetInfo(B_ANY_TYPE, index++, &nameFound, &typeFound, &countFound) == B_OK) {
// Don't display all the bedc_ internal stuff
if (BString(nameFound).StartsWith("bedc_"))
continue;
for (int j = 0; j < countFound; j++) {
name.SetToFormat("%s[%d] (%c%c%c%c)", nameFound, j,
(typeFound >> 24) & 0xFF, (typeFound >> 16) & 0xFF,
(typeFound >> 8) & 0xFF, (typeFound >> 0) & 0xFF
);
switch(typeFound) {
#if 0
B_AFFINE_TRANSFORM_TYPE = 'AMTX',
B_ALIGNMENT_TYPE = 'ALGN',
B_ANY_TYPE = 'ANYT',
B_ATOM_TYPE = 'ATOM',
B_ATOMREF_TYPE = 'ATMR',
B_BOOL_TYPE = 'BOOL',
B_CHAR_TYPE = 'CHAR',
B_COLOR_8_BIT_TYPE = 'CLRB',
B_DOUBLE_TYPE = 'DBLE',
B_FLOAT_TYPE = 'FLOT',
B_GRAYSCALE_8_BIT_TYPE = 'GRYB',
B_INT16_TYPE = 'SHRT',
B_INT64_TYPE = 'LLNG',
B_INT8_TYPE = 'BYTE',
B_LARGE_ICON_TYPE = 'ICON',
B_MEDIA_PARAMETER_GROUP_TYPE = 'BMCG',
B_MEDIA_PARAMETER_TYPE = 'BMCT',
B_MEDIA_PARAMETER_WEB_TYPE = 'BMCW',
B_MESSAGE_TYPE = 'MSGG',
B_MESSENGER_TYPE = 'MSNG',
B_MIME_TYPE = 'MIME',
B_MINI_ICON_TYPE = 'MICN',
B_MONOCHROME_1_BIT_TYPE = 'MNOB',
B_OBJECT_TYPE = 'OPTR',
B_OFF_T_TYPE = 'OFFT',
B_PATTERN_TYPE = 'PATN',
B_POINTER_TYPE = 'PNTR',
B_PROPERTY_INFO_TYPE = 'SCTD',
B_RAW_TYPE = 'RAWT',
B_RECT_TYPE = 'RECT',
B_REF_TYPE = 'RREF',
B_NODE_REF_TYPE = 'NREF',
B_RGB_32_BIT_TYPE = 'RGBB',
B_RGB_COLOR_TYPE = 'RGBC',
B_SIZE_TYPE = 'SIZE',
B_SIZE_T_TYPE = 'SIZT',
B_SSIZE_T_TYPE = 'SSZT',
B_STRING_LIST_TYPE = 'STRL',
B_TIME_TYPE = 'TIME',
B_UINT16_TYPE = 'USHT',
B_UINT32_TYPE = 'ULNG',
B_UINT64_TYPE = 'ULLG',
B_UINT8_TYPE = 'UBYT',
B_VECTOR_ICON_TYPE = 'VICN',
B_XATTR_TYPE = 'XATR',
B_NETWORK_ADDRESS_TYPE = 'NWAD',
B_MIME_STRING_TYPE = 'MIMS',
#endif
case B_STRING_TYPE:
text = message->FindString(nameFound, j);
break;
case B_INT32_TYPE:
{
int32 val = message->FindInt32(nameFound, j);
text.SetToFormat("%d (%#010x)", val, val);
break;
}
case B_POINT_TYPE:
{
BPoint val = message->FindPoint(nameFound, j);
text.SetToFormat("X: %f Y: %f", val.x, val.y);
break;
}
default:
text.SetTo("TODO: unhandled message content type");
break;
}
BRow* subRow = new BRow();
subRow->SetField(new BStringField(name), 0);
subRow->SetField(new BStringField(text), 1);
fEventList->AddRow(subRow, row);
}
}
}
break;
}
case B_ABOUT_REQUESTED:
{
be_app_messenger.SendMessage(B_ABOUT_REQUESTED);
break;
}
default:
BWindow::MessageReceived(message);
break;
}
}
private:
BColumnListView* fEventList;
};
class LoggerApp: public BApplication
{
public:
LoggerApp()
: BApplication("application/x-vnd.ml-BeDCApp")
{
}
void ReadyToRun() override
{
BApplication::ReadyToRun();
fActiveLogWindow = new LoggerWindow();
fActiveLogWindow->Show();
}
void AboutRequested() override
{
BAboutWindow* about = new BAboutWindow("Dev Console", "application/x-vnd.ml-BeDCApp");
about->AddDescription("Display log messages from other applications");
about->AddCopyright(2023, "Adrien Destugues");
about->Show();
}
void MessageReceived(BMessage* message) override
{
switch(message->what) {
case BEDC_MESSAGE:
DetachCurrentMessage();
fActiveLogWindow->PostMessage(message);
break;
default:
BApplication::MessageReceived(message);
break;
}
}
private:
BWindow* fActiveLogWindow;
};
int main(void)
{
LoggerApp app;
app.Run();
}