xref: /haiku/src/add-ons/screen_savers/message/Message.cpp (revision 385ee03ba83b7a40d315e17b03031b3ca37820c0)
1 /*
2  * Copyright 2007-2014, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Ryan Leavengood
7  */
8 
9 
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 
14 #include <Bitmap.h>
15 #include <Catalog.h>
16 #include <DefaultSettingsView.h>
17 #include <Font.h>
18 #include <ObjectList.h>
19 #include <Picture.h>
20 #include <Screen.h>
21 #include <ScreenSaver.h>
22 #include <String.h>
23 #include <StringList.h>
24 #include <TextView.h>
25 #include <View.h>
26 
27 
28 #undef B_TRANSLATION_CONTEXT
29 #define B_TRANSLATION_CONTEXT "Screensaver Message"
30 
31 
32 // Double brackets to satisfy a compiler warning
33 const pattern kCheckered = { { 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0x33, 0x33 } };
34 
35 // Get a clever message from fortune
36 BString *get_message()
37 {
38 	BString *result = new BString();
39 	FILE *file = popen("/bin/fortune", "r");
40 	if (file) {
41 		char buf[512];
42 		int bytesRead;
43 		while (!feof(file)) {
44 			bytesRead = fread(buf, 1, 512, file);
45 			result->Append(buf, bytesRead);
46 		}
47 		pclose(file);
48 	}
49 
50 	// Just in case
51 	if (result->Length() <= 0) {
52 		result->Append(B_TRANSLATE("Insert clever anecdote or phrase here!"));
53 	}
54 
55 	return result;
56 }
57 
58 
59 int
60 get_lines(BString *message, BStringList& list, int *longestLine)
61 {
62 	BString copy(*message);
63 	// Convert tabs to 4 spaces
64 	copy.ReplaceAll("\t", "    ");
65 	if (copy.Split("\n", false, list)) {
66 		int maxLength = 0;
67 		int32 count = list.CountStrings();
68 		for (int32 i = 0; i < count; i++) {
69 			int32 length = list.StringAt(i).Length();
70 			if (length  > maxLength) {
71 				maxLength = length;
72 				*longestLine = i;
73 			}
74 		}
75 		return count;
76 	}
77 	return 0;
78 }
79 
80 
81 // Inspired by the classic BeOS screensaver, of course.
82 // Thanks to Jon Watte for writing the original.
83 class Message : public BScreenSaver
84 {
85 	public:
86 					Message(BMessage *archive, image_id);
87 					~Message();
88 		void		Draw(BView *view, int32 frame);
89 		void		StartConfig(BView *view);
90 		status_t	StartSaver(BView *view, bool preview);
91 
92 	private:
93 		BObjectList<font_family>	fFontFamilies;
94 		float						fScaleFactor;
95 		bool						fPreview;
96 };
97 
98 
99 BScreenSaver *instantiate_screen_saver(BMessage *msg, image_id image)
100 {
101 	return new Message(msg, image);
102 }
103 
104 
105 Message::Message(BMessage *archive, image_id id)
106  :	BScreenSaver(archive, id)
107 {
108 }
109 
110 
111 Message::~Message()
112 {
113 	for (int32 i = 0; i < fFontFamilies.CountItems(); i++) {
114 		if (fFontFamilies.ItemAt(i))
115 			delete fFontFamilies.ItemAt(i);
116 	}
117 }
118 
119 
120 void
121 Message::StartConfig(BView *view)
122 {
123 	BPrivate::BuildDefaultSettingsView(view, "Message",
124 		B_TRANSLATE("by Ryan Leavengood"));
125 }
126 
127 
128 status_t
129 Message::StartSaver(BView *view, bool preview)
130 {
131 	fPreview = preview;
132 	// Scale factor is based on the system I developed this on, in
133 	// other words other factors below depend on this.
134 	fScaleFactor = view->Bounds().Height() / 1024;
135 
136 	// Get font families
137 	int numFamilies = count_font_families();
138 	for (int32 i = 0; i < numFamilies; i++) {
139 		font_family* family = new font_family[1];
140 		uint32 flags;
141 		if (get_font_family(i, family, &flags) == B_OK
142 			&& (flags & B_IS_FIXED) == 0) {
143 			// Do not add fixed fonts
144 			fFontFamilies.AddItem(family);
145 		} else
146 			delete[] family;
147 	}
148 
149 	// Seed the random number generator
150 	srand((int)system_time());
151 
152 	// Set tick size to 30,000,000 microseconds = 30 seconds
153 	SetTickSize(30000000);
154 
155 	return B_OK;
156 }
157 
158 
159 void
160 Message::Draw(BView *view, int32 frame)
161 {
162 	if (view == NULL || view->Window() == NULL || !view->Window()->IsLocked())
163 		return;
164 
165 	BScreen screen(view->Window());
166 	if (!screen.IsValid())
167 		return;
168 
169 	// Double-buffered drawing
170 	BBitmap buffer(view->Bounds(), screen.ColorSpace(), true);
171 	if (buffer.InitCheck() != B_OK)
172 		return;
173 
174 	BView offscreen(view->Bounds(), NULL, 0, 0);
175 	buffer.AddChild(&offscreen);
176 	buffer.Lock();
177 
178 	// Set up the colors
179 	rgb_color base_color = {(uint8)(rand() % 25), (uint8)(rand() % 25),
180 		(uint8)(rand() % 25)};
181 	offscreen.SetHighColor(base_color);
182 	offscreen.SetLowColor(tint_color(base_color, 0.815F));
183 	offscreen.FillRect(offscreen.Bounds(), kCheckered);
184 	rgb_color colors[8] = {
185 		tint_color(base_color, B_LIGHTEN_1_TINT),
186 		tint_color(base_color, 0.795F),
187 		tint_color(base_color, 0.851F),
188 		tint_color(base_color, 0.926F),
189 		tint_color(base_color, 1.05F),
190 		tint_color(base_color, B_DARKEN_1_TINT),
191 		tint_color(base_color, B_DARKEN_2_TINT),
192 		tint_color(base_color, B_DARKEN_3_TINT),
193 	};
194 
195 	offscreen.SetDrawingMode(B_OP_OVER);
196 
197 	// Set the basic font parameters, including random font family
198 	BFont font;
199 	offscreen.GetFont(&font);
200 	font.SetFace(B_BOLD_FACE);
201 	font.SetFamilyAndStyle(*(fFontFamilies.ItemAt(rand() % fFontFamilies.CountItems())), NULL);
202 	offscreen.SetFont(&font);
203 
204 	// Get the message
205 	BString *message = get_message();
206 	BString *origMessage = new BString();
207 	message->CopyInto(*origMessage, 0, message->Length());
208 	// Replace newlines and tabs with spaces
209 	message->ReplaceSet("\n\t", ' ');
210 
211 	int height = (int) offscreen.Bounds().Height();
212 	int width = (int) offscreen.Bounds().Width();
213 
214 	// From 14 to 22 iterations
215 	int32 iterations = (rand() % 8) + 14;
216 	for (int32 i = 0; i < iterations; i++) {
217 		// Randomly set font size and shear
218 		BFont font;
219 		offscreen.GetFont(&font);
220 		float fontSize = ((rand() % 320) + 42) * fScaleFactor;
221 		font.SetSize(fontSize);
222 		// Set the shear off 90 about 1/2 of the time
223 		if (rand() % 2 == 1)
224 			font.SetShear((float) ((rand() % 135) + (rand() % 45)));
225 		else
226 			font.SetShear(90.0);
227 		offscreen.SetFont(&font);
228 
229 		// Randomly set drawing location
230 		int x = (rand() % width) - (rand() % width/((rand() % 8)+1));
231 		int y = rand() % height;
232 
233 		// Draw new text
234 		offscreen.SetHighColor(colors[rand() % 8]);
235 		int strLength = message->Length();
236 		// See how wide this string is with the current font
237 		float strWidth = offscreen.StringWidth(message->String());
238 		int drawingLength = (int) (strLength * (width / strWidth));
239 		int start = 0;
240 		if (drawingLength >= strLength)
241 			drawingLength = strLength;
242 		else
243 			start = rand() % (strLength - drawingLength);
244 		char *toDraw = new char[drawingLength+1];
245 		strncpy(toDraw, message->String()+start, drawingLength);
246 		toDraw[drawingLength] = 0;
247 		offscreen.DrawString(toDraw, BPoint(x, y));
248 		delete[] toDraw;
249 	}
250 
251 	// Now draw the full message in a nice translucent box, but only
252 	// if this isn't preview mode
253 	if (!fPreview) {
254 		BFont font(be_fixed_font);
255 		font.SetSize(14.0);
256 		offscreen.SetFont(&font);
257 		font_height fontHeight;
258 		font.GetHeight(&fontHeight);
259 		float lineHeight = fontHeight.ascent + fontHeight.descent
260 			+ fontHeight.leading;
261 
262 		BStringList lines;
263 		int longestLine = 0;
264 		int32 count = get_lines(origMessage, lines, &longestLine);
265 
266 		float stringWidth = font.StringWidth(lines.StringAt(longestLine).String());
267 		BRect box(0, 0, stringWidth + 20, (lineHeight * count) + 20);
268 		box.OffsetTo((width - box.Width()) / 2, height - box.Height() - 40);
269 
270 		offscreen.SetDrawingMode(B_OP_ALPHA);
271 		base_color.alpha = 128;
272 		offscreen.SetHighColor(base_color);
273 		offscreen.FillRoundRect(box, 8, 8);
274 		offscreen.SetHighColor(205, 205, 205);
275 		BPoint start = box.LeftTop();
276 		start.x += 10;
277 		start.y += 10 + fontHeight.ascent + fontHeight.leading;
278 		for (int i = 0; i < count; i++) {
279 			offscreen.DrawString(lines.StringAt(i).String(), start);
280 			start.y += lineHeight;
281 		}
282 	}
283 
284 	delete origMessage;
285 	delete message;
286 
287 	offscreen.Sync();
288 	buffer.Unlock();
289 	view->DrawBitmap(&buffer);
290 	buffer.RemoveChild(&offscreen);
291 }
292 
293