1/*
2 * Copyright 2010 Wim van der Meer <WPJvanderMeer@gmail.com>
3 * Copyright Karsten Heimrich, host.haiku@gmx.de. All rights reserved.
4 * Distributed under the terms of the MIT License.
5 *
6 * Authors:
7 *		Karsten Heimrich
8 *		Fredrik Modéen
9 *		Christophe Huriaux
10 *		Wim van der Meer
11 */
12
13
14#include "Utility.h"
15
16#include <Bitmap.h>
17#include <BitmapStream.h>
18#include <Catalog.h>
19#include <Clipboard.h>
20#include <Entry.h>
21#include <File.h>
22#include <FindDirectory.h>
23#include <Locale.h>
24#include <Looper.h>
25#include <MimeType.h>
26#include <NodeInfo.h>
27#include <Path.h>
28#include <Region.h>
29#include <Screen.h>
30#include <String.h>
31#include <Translator.h>
32#include <View.h>
33
34
35#undef B_TRANSLATION_CONTEXT
36#define B_TRANSLATION_CONTEXT "Screenshot"
37
38
39const char* Utility::sDefaultFileNameBase =
40	B_TRANSLATE_MARK_COMMENT("screenshot",
41	"Base filename of screenshot files");
42
43
44Utility::Utility()
45	:
46	wholeScreen(NULL),
47	cursorBitmap(NULL),
48	cursorAreaBitmap(NULL)
49{
50}
51
52
53Utility::~Utility()
54{
55	delete wholeScreen;
56	delete cursorBitmap;
57	delete cursorAreaBitmap;
58}
59
60
61void
62Utility::CopyToClipboard(const BBitmap& screenshot) const
63{
64	if (be_clipboard->Lock()) {
65		be_clipboard->Clear();
66		BMessage* clipboard = be_clipboard->Data();
67		if (clipboard) {
68			BMessage* bitmap = new BMessage();
69			screenshot.Archive(bitmap);
70			clipboard->AddMessage("image/bitmap", bitmap);
71			be_clipboard->Commit();
72		}
73		be_clipboard->Unlock();
74	}
75}
76
77
78/*!
79	Save the screenshot to the file with the specified filename and type.
80	Note that any existing file with the same filename will be overwritten
81	without warning.
82*/
83status_t
84Utility::Save(BBitmap** screenshot, const char* fileName, uint32 imageType)
85	const
86{
87	BString fileNameString(fileName);
88
89	// Generate a default filename when none is given
90	if (fileNameString.Compare("") == 0) {
91		BPath homePath;
92		if (find_directory(B_USER_DIRECTORY, &homePath) != B_OK)
93			return B_ERROR;
94
95		BEntry entry;
96		int32 index = 1;
97		BString extension = GetFileNameExtension(imageType);
98		do {
99			fileNameString.SetTo(homePath.Path());
100			fileNameString << "/" << B_TRANSLATE_NOCOLLECT(sDefaultFileNameBase)
101				<< index++ << extension;
102			entry.SetTo(fileNameString);
103		} while (entry.Exists());
104	}
105
106	// Create the file
107	BFile file(fileNameString, B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY);
108	if (file.InitCheck() != B_OK)
109		return B_ERROR;
110
111	// Write the screenshot bitmap to the file
112	BBitmapStream stream(*screenshot);
113	BTranslatorRoster* roster = BTranslatorRoster::Default();
114	roster->Translate(&stream, NULL, NULL, &file, imageType,
115		B_TRANSLATOR_BITMAP);
116	*screenshot = NULL;
117
118	// Set the file MIME attribute
119	BNodeInfo nodeInfo(&file);
120	if (nodeInfo.InitCheck() != B_OK)
121		return B_ERROR;
122
123	nodeInfo.SetType(_GetMimeString(imageType));
124
125	return B_OK;
126}
127
128
129BBitmap*
130Utility::MakeScreenshot(bool includeMouse, bool activeWindow,
131	bool includeBorder) const
132{
133	if (wholeScreen == NULL)
134		return NULL;
135
136	int cursorWidth = 0;
137	int cursorHeight = 0;
138
139	if (cursorBitmap != NULL) {
140		BRect bounds = cursorBitmap->Bounds();
141		cursorWidth = bounds.IntegerWidth() + 1;
142		cursorHeight = bounds.IntegerHeight() + 1;
143	}
144
145	if (includeMouse && cursorBitmap != NULL) {
146		// Import the cursor bitmap into wholeScreen
147		wholeScreen->ImportBits(cursorBitmap->Bits(),
148			cursorBitmap->BitsLength(), cursorBitmap->BytesPerRow(),
149			cursorBitmap->ColorSpace(), BPoint(0, 0), cursorPosition,
150			cursorWidth, cursorHeight);
151
152	} else if (cursorAreaBitmap != NULL) {
153		// Import the cursor area bitmap into wholeScreen
154		wholeScreen->ImportBits(cursorAreaBitmap->Bits(),
155			cursorAreaBitmap->BitsLength(), cursorAreaBitmap->BytesPerRow(),
156			cursorAreaBitmap->ColorSpace(), BPoint(0, 0), cursorPosition,
157			cursorWidth, cursorHeight);
158	}
159
160	BBitmap* screenshot = NULL;
161
162	if (activeWindow && activeWindowFrame.IsValid()) {
163
164		BRect frame(activeWindowFrame);
165		if (includeBorder) {
166			frame.InsetBy(-borderSize, -borderSize);
167			frame.top -= tabFrame.bottom - tabFrame.top;
168		}
169
170		screenshot = new BBitmap(frame.OffsetToCopy(B_ORIGIN), B_RGBA32, true);
171
172		if (screenshot->ImportBits(wholeScreen->Bits(),
173			wholeScreen->BitsLength(), wholeScreen->BytesPerRow(),
174			wholeScreen->ColorSpace(), frame.LeftTop(),
175			BPoint(0, 0), frame.IntegerWidth() + 1,
176			frame.IntegerHeight() + 1) != B_OK) {
177			delete screenshot;
178			return NULL;
179		}
180
181		if (includeBorder)
182			_MakeTabSpaceTransparent(screenshot, frame);
183	} else
184		screenshot = new BBitmap(wholeScreen);
185
186	return screenshot;
187}
188
189
190BString
191Utility::GetFileNameExtension(uint32 imageType) const
192{
193	BMimeType mimeType(_GetMimeString(imageType));
194	BString extension("");
195
196	BMessage message;
197	if (mimeType.GetFileExtensions(&message) == B_OK) {
198		const char* ext;
199		if (message.FindString("extensions", 0, &ext) == B_OK) {
200			extension.SetTo(ext);
201			extension.Prepend(".");
202		} else
203			extension.SetTo("");
204	}
205
206	return extension;
207}
208
209
210BString
211Utility::_GetMimeString(uint32 imageType) const
212{
213	const char *dummy = "";
214	translator_id* translators = NULL;
215	int32 numTranslators = 0;
216	BTranslatorRoster* roster = BTranslatorRoster::Default();
217	status_t status = roster->GetAllTranslators(&translators, &numTranslators);
218	if (status != B_OK)
219		return dummy;
220
221	for (int32 x = 0; x < numTranslators; x++) {
222		const translation_format* formats = NULL;
223		int32 numFormats;
224
225		if (roster->GetOutputFormats(translators[x], &formats, &numFormats)
226				== B_OK) {
227			for (int32 i = 0; i < numFormats; ++i) {
228				if (formats[i].type == imageType) {
229					delete [] translators;
230					return formats[i].MIME;
231				}
232			}
233		}
234	}
235	delete [] translators;
236	return dummy;
237}
238
239
240void
241Utility::_MakeTabSpaceTransparent(BBitmap* screenshot, BRect frame) const
242{
243	if (!frame.IsValid() || screenshot->ColorSpace() != B_RGBA32)
244		return;
245
246	if (!frame.Contains(tabFrame))
247		return;
248
249	float tabHeight = tabFrame.bottom - tabFrame.top;
250
251	BRegion tabSpace(frame);
252	frame.OffsetBy(0, tabHeight);
253	tabSpace.Exclude(frame);
254	tabSpace.Exclude(tabFrame);
255	frame.OffsetBy(0, -tabHeight);
256	tabSpace.OffsetBy(-frame.left, -frame.top);
257	BScreen screen;
258	BRect screenFrame = screen.Frame();
259	tabSpace.OffsetBy(-screenFrame.left, -screenFrame.top);
260
261	BView view(screenshot->Bounds(), "bitmap", B_FOLLOW_ALL_SIDES, 0);
262	screenshot->AddChild(&view);
263	if (view.Looper() && view.Looper()->Lock()) {
264		view.SetDrawingMode(B_OP_COPY);
265		view.SetHighColor(B_TRANSPARENT_32_BIT);
266
267		for (int i = 0; i < tabSpace.CountRects(); i++)
268			view.FillRect(tabSpace.RectAt(i));
269
270		view.Sync();
271		view.Looper()->Unlock();
272	}
273	screenshot->RemoveChild(&view);
274}
275