1/*
2 * Copyright 2014-2023 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Zhuowei Zhang
7 *		Humdinger
8 */
9#include "ConsoleWindow.h"
10
11#include <Catalog.h>
12#include <Clipboard.h>
13#include <Message.h>
14#include <Button.h>
15#include <GroupLayout.h>
16#include <GroupLayoutBuilder.h>
17#include <LayoutBuilder.h>
18#include <SeparatorView.h>
19#include <StringFormat.h>
20#include <TextControl.h>
21#include <ListView.h>
22#include <ScrollView.h>
23
24#include "BrowserWindow.h"
25#include "BrowserApp.h"
26#include "WebViewConstants.h"
27
28
29#undef B_TRANSLATION_CONTEXT
30#define B_TRANSLATION_CONTEXT "Console Window"
31
32
33enum {
34	EVAL_CONSOLE_WINDOW_COMMAND = 'ecwc',
35	CLEAR_CONSOLE_MESSAGES = 'ccms'
36};
37
38
39ConsoleWindow::ConsoleWindow(BRect frame)
40	:
41	BWindow(frame, B_TRANSLATE("Script console"), B_TITLED_WINDOW,
42		B_NORMAL_WINDOW_FEEL, B_AUTO_UPDATE_SIZE_LIMITS
43			| B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE),
44	fPreviousText(""),
45	fRepeatCounter(0)
46{
47	SetLayout(new BGroupLayout(B_VERTICAL, 0.0));
48
49	fMessagesListView = new BListView("Console messages",
50		B_MULTIPLE_SELECTION_LIST);
51
52	fClearMessagesButton = new BButton(B_TRANSLATE("Clear"),
53		new BMessage(CLEAR_CONSOLE_MESSAGES));
54	fCopyMessagesButton = new BButton(B_TRANSLATE("Copy"),
55		new BMessage(B_COPY));
56
57	AddChild(BGroupLayoutBuilder(B_VERTICAL, 0.0)
58		.Add(new BScrollView("Console messages scroll",
59			fMessagesListView, 0, true, true))
60		.Add(BGroupLayoutBuilder(B_HORIZONTAL, B_USE_SMALL_SPACING)
61			.AddGlue()
62			.Add(fClearMessagesButton)
63			.Add(fCopyMessagesButton)
64			.AddGlue()
65			.SetInsets(0, B_USE_SMALL_SPACING, 0, 0))
66		.SetInsets(B_USE_SMALL_SPACING, B_USE_SMALL_SPACING,
67			B_USE_SMALL_SPACING, B_USE_SMALL_SPACING)
68	);
69	if (!frame.IsValid())
70		CenterOnScreen();
71}
72
73
74void
75ConsoleWindow::MessageReceived(BMessage* message)
76{
77	switch (message->what) {
78		case ADD_CONSOLE_MESSAGE:
79		{
80			BString source = message->FindString("source");
81			int32 lineNumber = message->FindInt32("line");
82			int32 columnNumber = message->FindInt32("column");
83			BString text = message->FindString("string");
84			BString finalText;
85			finalText.SetToFormat("%s:%" B_PRIi32 ":%" B_PRIi32 ": %s\n",
86				source.String(), lineNumber, columnNumber, text.String());
87
88			if (finalText == fPreviousText) {
89				finalText = "";
90				static BStringFormat format(B_TRANSLATE("{0, plural,"
91					"one{Last line repeated # time.}"
92					"other{Last line repeated # times.}}"));
93				format.Format(finalText, ++fRepeatCounter);
94				// preserve the repeated line
95				if (fRepeatCounter > 1) {
96					int32 index = fMessagesListView->CountItems() - 1;
97					BStringItem* item = (BStringItem*)fMessagesListView->ItemAt(index);
98					item->SetText(finalText.String());
99					fMessagesListView->InvalidateItem(index);
100					break;
101				}
102			} else {
103				fPreviousText = finalText;
104				fRepeatCounter = 0;
105			}
106			fMessagesListView->AddItem(new BStringItem(finalText.String()));
107			break;
108		}
109		case CLEAR_CONSOLE_MESSAGES:
110		{
111			fPreviousText = "";
112			int count = fMessagesListView->CountItems();
113			for (int i = count - 1; i >= 0; i--)
114				delete fMessagesListView->RemoveItem(i);
115			break;
116		}
117		case B_COPY:
118		{
119			_CopyToClipboard();
120			break;
121		}
122		default:
123			BWindow::MessageReceived(message);
124			break;
125	}
126}
127
128
129bool
130ConsoleWindow::QuitRequested()
131{
132	if (!IsHidden())
133		Hide();
134	return false;
135}
136
137
138void
139ConsoleWindow::_CopyToClipboard()
140{
141	BString text;
142	int32 index;
143	if (fMessagesListView->CurrentSelection() == -1) {
144		for (int32 i = 0; i < fMessagesListView->CountItems(); i++) {
145			BStringItem* item = (BStringItem*)fMessagesListView->ItemAt(i);
146			text << item->Text();
147		}
148	} else {
149		for (int32 i = 0; (index = fMessagesListView->CurrentSelection(i)) >= 0; i++) {
150			BStringItem* item = (BStringItem*)fMessagesListView->ItemAt(index);
151			text << item->Text();
152		}
153	}
154
155	ssize_t textLen = text.Length();
156	if (be_clipboard->Lock()) {
157		be_clipboard->Clear();
158		BMessage* clip = be_clipboard->Data();
159		if (clip != NULL) {
160			clip->AddData("text/plain", B_MIME_TYPE, text.String(), textLen);
161			be_clipboard->Commit();
162		}
163		be_clipboard->Unlock();
164	}
165}
166