xref: /haiku/src/apps/screenshot/Utility.cpp (revision e015add1850997ecfd2e0ebfa288bbc37cc7d5a1)
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_TRANSLATE_CONTEXT
36 #define B_TRANSLATE_CONTEXT "Screenshot"
37 
38 
39 const char* Utility::sDefaultFileNameBase =
40 	B_TRANSLATE_MARK_COMMENT("screenshot",
41 	"Base filename of screenshot files");
42 
43 
44 Utility::Utility()
45 	:
46 	wholeScreen(NULL),
47 	cursorBitmap(NULL),
48 	cursorAreaBitmap(NULL)
49 {
50 }
51 
52 
53 Utility::~Utility()
54 {
55 	delete wholeScreen;
56 	delete cursorBitmap;
57 	delete cursorAreaBitmap;
58 }
59 
60 
61 void
62 Utility::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 */
83 status_t
84 Utility::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.String());
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 
129 BBitmap*
130 Utility::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 
190 const char*
191 Utility::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.String();
207 }
208 
209 
210 const char*
211 Utility::_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(x, &formats, &numFormats) == B_OK) {
226 			for (int32 i = 0; i < numFormats; ++i) {
227 				if (formats[i].type == imageType) {
228 					delete [] translators;
229 					return formats[i].MIME;
230 				}
231 			}
232 		}
233 	}
234 	delete [] translators;
235 	return dummy;
236 }
237 
238 
239 void
240 Utility::_MakeTabSpaceTransparent(BBitmap* screenshot, BRect frame) const
241 {
242 	if (!frame.IsValid() || screenshot->ColorSpace() != B_RGBA32)
243 		return;
244 
245 	if (!frame.Contains(tabFrame))
246 		return;
247 
248 	float tabHeight = tabFrame.bottom - tabFrame.top;
249 
250 	BRegion tabSpace(frame);
251 	frame.OffsetBy(0, tabHeight);
252 	tabSpace.Exclude(frame);
253 	tabSpace.Exclude(tabFrame);
254 	frame.OffsetBy(0, -tabHeight);
255 	tabSpace.OffsetBy(-frame.left, -frame.top);
256 	BScreen screen;
257 	BRect screenFrame = screen.Frame();
258 	tabSpace.OffsetBy(-screenFrame.left, -screenFrame.top);
259 
260 	BView view(screenshot->Bounds(), "bitmap", B_FOLLOW_ALL_SIDES, 0);
261 	screenshot->AddChild(&view);
262 	if (view.Looper() && view.Looper()->Lock()) {
263 		view.SetDrawingMode(B_OP_COPY);
264 		view.SetHighColor(B_TRANSPARENT_32_BIT);
265 
266 		for (int i = 0; i < tabSpace.CountRects(); i++)
267 			view.FillRect(tabSpace.RectAt(i));
268 
269 		view.Sync();
270 		view.Looper()->Unlock();
271 	}
272 	screenshot->RemoveChild(&view);
273 }
274