* Copyright 2007-2014, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ryan Leavengood
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Bitmap.h>
#include <Catalog.h>
#include <DefaultSettingsView.h>
#include <Font.h>
#include <ObjectList.h>
#include <Picture.h>
#include <Screen.h>
#include <ScreenSaver.h>
#include <String.h>
#include <StringList.h>
#include <TextView.h>
#include <View.h>
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Screensaver Message"
const pattern kCheckered =
{ { 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0x33, 0x33 } };
BString *get_message()
{
BString *result = new BString();
FILE *file = popen("/bin/fortune", "r");
if (file) {
char buf[512];
int bytesRead;
while (!feof(file)) {
bytesRead = fread(buf, 1, 512, file);
result->Append(buf, bytesRead);
}
pclose(file);
}
if (result->Length() <= 0) {
result->Append(B_TRANSLATE("Insert clever anecdote or phrase here!"));
}
return result;
}
int
get_lines(BString *message, BStringList& list, int *longestLine)
{
BString copy(*message);
copy.ReplaceAll("\t", " ");
if (copy.Split("\n", false, list)) {
int maxLength = 0;
int32 count = list.CountStrings();
for (int32 i = 0; i < count; i++) {
int32 length = list.StringAt(i).Length();
if (length > maxLength) {
maxLength = length;
*longestLine = i;
}
}
return count;
}
return 0;
}
class Message : public BScreenSaver
{
public:
Message(BMessage *archive, image_id);
~Message();
void Draw(BView *view, int32 frame);
void StartConfig(BView *view);
status_t StartSaver(BView *view, bool preview);
private:
struct font_family_wrapper {
font_family val;
};
BObjectList<font_family_wrapper> fFontFamilies;
float fScaleFactor;
bool fPreview;
};
BScreenSaver *instantiate_screen_saver(BMessage *msg, image_id image)
{
return new Message(msg, image);
}
Message::Message(BMessage *archive, image_id id)
: BScreenSaver(archive, id)
{
}
Message::~Message()
{
for (int32 i = 0; i < fFontFamilies.CountItems(); i++) {
if (fFontFamilies.ItemAt(i))
delete fFontFamilies.ItemAt(i);
}
}
void
Message::StartConfig(BView *view)
{
BPrivate::BuildDefaultSettingsView(view, "Message",
B_TRANSLATE("by Ryan Leavengood"));
}
status_t
Message::StartSaver(BView *view, bool preview)
{
fPreview = preview;
fScaleFactor = view->Bounds().Height() / 1024;
int numFamilies = count_font_families();
for (int32 i = 0; i < numFamilies; i++) {
font_family_wrapper* family = new font_family_wrapper;
uint32 flags;
if (get_font_family(i, &(family->val), &flags) == B_OK
&& (flags & B_IS_FIXED) == 0) {
fFontFamilies.AddItem(family);
} else
delete family;
}
srand((int)system_time());
SetTickSize(30000000);
return B_OK;
}
void
Message::Draw(BView *view, int32 frame)
{
if (view == NULL || view->Window() == NULL || !view->Window()->IsLocked())
return;
BScreen screen(view->Window());
if (!screen.IsValid())
return;
BBitmap buffer(view->Bounds(), screen.ColorSpace(), true);
if (buffer.InitCheck() != B_OK)
return;
BView offscreen(view->Bounds(), NULL, 0, 0);
buffer.AddChild(&offscreen);
buffer.Lock();
rgb_color base_color = {(uint8)(rand() % 25), (uint8)(rand() % 25),
(uint8)(rand() % 25)};
offscreen.SetHighColor(base_color);
offscreen.SetLowColor(tint_color(base_color, 0.815F));
offscreen.FillRect(offscreen.Bounds(), kCheckered);
rgb_color colors[8] = {
tint_color(base_color, B_LIGHTEN_1_TINT),
tint_color(base_color, 0.795F),
tint_color(base_color, 0.851F),
tint_color(base_color, 0.926F),
tint_color(base_color, 1.05F),
tint_color(base_color, B_DARKEN_1_TINT),
tint_color(base_color, B_DARKEN_2_TINT),
tint_color(base_color, B_DARKEN_3_TINT),
};
offscreen.SetDrawingMode(B_OP_OVER);
BFont font;
offscreen.GetFont(&font);
font.SetFace(B_BOLD_FACE);
font.SetFamilyAndStyle(
fFontFamilies.ItemAt(rand() % fFontFamilies.CountItems())->val, NULL);
offscreen.SetFont(&font);
BString *message = get_message();
BString *origMessage = new BString();
message->CopyInto(*origMessage, 0, message->Length());
message->ReplaceSet("\n\t", ' ');
int height = (int) offscreen.Bounds().Height();
int width = (int) offscreen.Bounds().Width();
int32 iterations = (rand() % 8) + 14;
for (int32 i = 0; i < iterations; i++) {
BFont font;
offscreen.GetFont(&font);
float fontSize = ((rand() % 320) + 42) * fScaleFactor;
font.SetSize(fontSize);
if (rand() % 2 == 1)
font.SetShear((float) ((rand() % 135) + (rand() % 45)));
else
font.SetShear(90.0);
offscreen.SetFont(&font);
int x = (rand() % width) - (rand() % width/((rand() % 8)+1));
int y = rand() % height;
offscreen.SetHighColor(colors[rand() % 8]);
int strLength = message->Length();
float strWidth = offscreen.StringWidth(message->String());
int drawingLength = (int) (strLength * (width / strWidth));
int start = 0;
if (drawingLength >= strLength)
drawingLength = strLength;
else
start = rand() % (strLength - drawingLength);
char *toDraw = new char[drawingLength+1];
strncpy(toDraw, message->String()+start, drawingLength);
toDraw[drawingLength] = 0;
offscreen.DrawString(toDraw, BPoint(x, y));
delete[] toDraw;
}
if (!fPreview) {
BFont font(be_fixed_font);
font.SetSize(14.0);
offscreen.SetFont(&font);
font_height fontHeight;
font.GetHeight(&fontHeight);
float lineHeight = fontHeight.ascent + fontHeight.descent
+ fontHeight.leading;
BStringList lines;
int longestLine = 0;
int32 count = get_lines(origMessage, lines, &longestLine);
float stringWidth =
font.StringWidth(lines.StringAt(longestLine).String());
BRect box(0, 0, stringWidth + 20, (lineHeight * count) + 20);
box.OffsetTo((width - box.Width()) / 2, height - box.Height() - 40);
offscreen.SetDrawingMode(B_OP_ALPHA);
base_color.alpha = 128;
offscreen.SetHighColor(base_color);
offscreen.FillRoundRect(box, 8, 8);
offscreen.SetHighColor(205, 205, 205);
BPoint start = box.LeftTop();
start.x += 10;
start.y += 10 + fontHeight.ascent + fontHeight.leading;
for (int i = 0; i < count; i++) {
offscreen.DrawString(lines.StringAt(i).String(), start);
start.y += lineHeight;
}
}
delete origMessage;
delete message;
offscreen.Sync();
buffer.Unlock();
view->DrawBitmap(&buffer);
buffer.RemoveChild(&offscreen);
}