1 /*****************************************************************************/ 2 // SlideShowSaver 3 // Written by Michael Wilber 4 // Slide show code derived from ShowImage code, written by Michael Pfeiffer 5 // 6 // SlideShowSaver.cpp 7 // 8 // 9 // Copyright (C) Haiku 10 // 11 // Permission is hereby granted, free of charge, to any person obtaining a 12 // copy of this software and associated documentation files (the "Software"), 13 // to deal in the Software without restriction, including without limitation 14 // the rights to use, copy, modify, merge, publish, distribute, sublicense, 15 // and/or sell copies of the Software, and to permit persons to whom the 16 // Software is furnished to do so, subject to the following conditions: 17 // 18 // The above copyright notice and this permission notice shall be included 19 // in all copies or substantial portions of the Software. 20 // 21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 22 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 24 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 // DEALINGS IN THE SOFTWARE. 28 /*****************************************************************************/ 29 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <BitmapStream.h> 34 #include <TranslatorRoster.h> 35 #include <Directory.h> 36 #include <File.h> 37 #include <Path.h> 38 #include <List.h> 39 #include <StringView.h> 40 #include "SlideShowSaver.h" 41 #include "SlideShowConfigView.h" 42 43 // Called by system to get the screen saver 44 extern "C" _EXPORT BScreenSaver * 45 instantiate_screen_saver(BMessage *message, image_id id) 46 { 47 return new SlideShowSaver(message, id); 48 } 49 50 // returns B_ERROR if problems reading ref 51 // B_OK if ref is not a directory 52 // B_OK + 1 if ref is a directory 53 status_t 54 ent_is_dir(const entry_ref *ref) 55 { 56 BEntry ent(ref); 57 if (ent.InitCheck() != B_OK) 58 return B_ERROR; 59 60 struct stat st; 61 if (ent.GetStat(&st) != B_OK) 62 return B_ERROR; 63 64 return S_ISDIR(st.st_mode) ? (B_OK + 1) : B_OK; 65 } 66 67 int CompareEntries(const void* a, const void* b) 68 { 69 entry_ref *r1, *r2; 70 r1 = *(entry_ref**)a; 71 r2 = *(entry_ref**)b; 72 return strcasecmp(r1->name, r2->name); 73 } 74 75 // Default settings for the Translator 76 LiveSetting gDefaultSettings[] = { 77 LiveSetting(CHANGE_CAPTION, SAVER_SETTING_CAPTION, true), 78 // Show image caption by default 79 LiveSetting(CHANGE_BORDER, SAVER_SETTING_BORDER, true), 80 // Show image border by default 81 LiveSetting(CHANGE_DIRECTORY, SAVER_SETTING_DIRECTORY, "/boot/home"), 82 // Set default image directory to home 83 LiveSetting(CHANGE_DELAY, SAVER_SETTING_DELAY, (int32) 3000) 84 // Default delay: 3 seconds 85 }; 86 87 SlideShowSaver::SlideShowSaver(BMessage *archive, image_id image) 88 : BScreenSaver(archive, image), fLock("SlideShow Lock") 89 { 90 fNewDirectory = true; 91 fBitmap = NULL; 92 fShowBorder = true; 93 fShowCaption = true; 94 95 fSettings = new LiveSettings("SlideShowSaver_Settings", 96 gDefaultSettings, sizeof(gDefaultSettings) / sizeof(LiveSetting)); 97 fSettings->LoadSettings(); 98 // load settings from the settings file 99 100 fSettings->AddObserver(this); 101 } 102 103 SlideShowSaver::~SlideShowSaver() 104 { 105 delete fBitmap; 106 fBitmap = NULL; 107 108 fSettings->RemoveObserver(this); 109 fSettings->Release(); 110 } 111 112 // Called by fSettings to notify that someone has changed 113 // a setting. For example, if the user changes a setting 114 // on the config panel, this will be called to notify this 115 // object. 116 void 117 SlideShowSaver::SettingChanged(uint32 setting) 118 { 119 switch (setting) { 120 case CHANGE_CAPTION: 121 UpdateShowCaption(); 122 break; 123 case CHANGE_BORDER: 124 UpdateShowBorder(); 125 break; 126 case CHANGE_DIRECTORY: 127 UpdateDirectory(); 128 break; 129 case CHANGE_DELAY: 130 UpdateTickSize(); 131 break; 132 133 default: 134 break; 135 } 136 } 137 138 status_t 139 SlideShowSaver::UpdateTickSize() 140 { 141 // Tick size is in microseconds, but is stored in settings as 142 // milliseconds 143 bigtime_t ticks = static_cast<bigtime_t> 144 (fSettings->SetGetInt32(SAVER_SETTING_DELAY)) * 1000; 145 SetTickSize(ticks); 146 147 return B_OK; 148 } 149 150 status_t 151 SlideShowSaver::UpdateShowCaption() 152 { 153 fShowCaption = fSettings->SetGetBool(SAVER_SETTING_CAPTION); 154 return B_OK; 155 } 156 157 status_t 158 SlideShowSaver::UpdateShowBorder() 159 { 160 fShowBorder = fSettings->SetGetBool(SAVER_SETTING_BORDER); 161 return B_OK; 162 } 163 164 status_t 165 SlideShowSaver::UpdateDirectory() 166 { 167 status_t result = B_OK; 168 169 fLock.Lock(); 170 171 BString strDirectory; 172 fSettings->GetString(SAVER_SETTING_DIRECTORY, strDirectory); 173 BDirectory dir(strDirectory.String()); 174 if (dir.InitCheck() != B_OK || dir.GetNextRef(&fCurrentRef) != B_OK) 175 result = B_ERROR; 176 // Use ShowNextImage to find which translatable image is 177 // alphabetically first in the given directory, and load it 178 if (result == B_OK && ShowNextImage(true, true) == false) 179 result = B_ERROR; 180 181 fNewDirectory = true; 182 183 fLock.Unlock(); 184 185 return result; 186 } 187 188 void 189 SlideShowSaver::StartConfig(BView *view) 190 { 191 view->AddChild(new SlideShowConfigView( 192 BRect(10, 10, 250, 300), "SlideShowSaver Config", 193 B_FOLLOW_ALL, B_WILL_DRAW, fSettings->Acquire())); 194 } 195 196 status_t 197 SlideShowSaver::StartSaver(BView *view, bool preview) 198 { 199 UpdateShowCaption(); 200 UpdateShowBorder(); 201 202 if (UpdateDirectory() != B_OK) 203 return B_ERROR; 204 205 // Read ticksize setting and set it as the delay 206 UpdateTickSize(); 207 208 return B_OK; 209 } 210 211 void 212 SlideShowSaver::Draw(BView *view, int32 frame) 213 { 214 fLock.Lock(); 215 216 view->SetLowColor(0, 0, 0); 217 view->SetHighColor(192, 192, 192); 218 view->SetViewColor(192, 192, 192); 219 220 bool bResult = false; 221 if (fNewDirectory == true) { 222 // Already have a bitmap on the first frame 223 bResult = true; 224 } else { 225 bResult = ShowNextImage(true, false); 226 // try rewinding to beginning 227 if (bResult == false) 228 bResult = ShowNextImage(true, true); 229 } 230 fNewDirectory = false; 231 232 if (bResult == true && fBitmap != NULL) { 233 BRect destRect(0, 0, fBitmap->Bounds().Width(), fBitmap->Bounds().Height()), 234 vwBounds = view->Bounds(); 235 236 if (destRect.Width() < vwBounds.Width()) { 237 destRect.OffsetBy((vwBounds.Width() - destRect.Width()) / 2, 0); 238 } 239 if (destRect.Height() < vwBounds.Height()) { 240 destRect.OffsetBy(0, (vwBounds.Height() - destRect.Height()) / 2); 241 } 242 243 BRect border = destRect, bounds = view->Bounds(); 244 // top 245 view->FillRect(BRect(0, 0, bounds.right, border.top-1), B_SOLID_LOW); 246 // left 247 view->FillRect(BRect(0, border.top, border.left-1, border.bottom), B_SOLID_LOW); 248 // right 249 view->FillRect(BRect(border.right+1, border.top, bounds.right, border.bottom), B_SOLID_LOW); 250 // bottom 251 view->FillRect(BRect(0, border.bottom+1, bounds.right, bounds.bottom), B_SOLID_LOW); 252 253 if (fShowBorder == true) { 254 BRect strokeRect = destRect; 255 strokeRect.InsetBy(-1, -1); 256 view->StrokeRect(strokeRect); 257 } 258 259 view->DrawBitmap(fBitmap, fBitmap->Bounds(), destRect); 260 261 if (fShowCaption == true) 262 DrawCaption(view); 263 } 264 265 fLock.Unlock(); 266 } 267 268 status_t 269 SlideShowSaver::SetImage(const entry_ref *pref) 270 { 271 entry_ref ref; 272 if (!pref) 273 ref = fCurrentRef; 274 else 275 ref = *pref; 276 277 BTranslatorRoster *proster = BTranslatorRoster::Default(); 278 if (!proster) 279 return B_ERROR; 280 281 if (ent_is_dir(pref) != B_OK) 282 // if ref is erroneous or a directory, return error 283 return B_ERROR; 284 285 BFile file(&ref, B_READ_ONLY); 286 translator_info info; 287 memset(&info, 0, sizeof(translator_info)); 288 BMessage ioExtension; 289 //if (ref != fCurrentRef) 290 // if new image, reset to first document 291 // fDocumentIndex = 1; 292 if (ioExtension.AddInt32("/documentIndex", 1 /*fDocumentIndex*/) != B_OK) 293 return B_ERROR; 294 if (proster->Identify(&file, &ioExtension, &info, 0, NULL, 295 B_TRANSLATOR_BITMAP) != B_OK) 296 return B_ERROR; 297 298 // Translate image data and create a new ShowImage window 299 BBitmapStream outstream; 300 if (proster->Translate(&file, &info, &ioExtension, &outstream, 301 B_TRANSLATOR_BITMAP) != B_OK) 302 return B_ERROR; 303 BBitmap *newBitmap = NULL; 304 if (outstream.DetachBitmap(&newBitmap) != B_OK) 305 return B_ERROR; 306 307 // Now that I've successfully loaded the new bitmap, 308 // I can be sure it is safe to delete the old one, 309 // and clear everything 310 delete fBitmap; 311 fBitmap = newBitmap; 312 newBitmap = NULL; 313 fCurrentRef = ref; 314 315 // Get path to use in caption 316 fCaption = "<< Unable to read the path >>"; 317 BEntry entry(&fCurrentRef); 318 if (entry.InitCheck() == B_OK) { 319 BPath path(&entry); 320 if (path.InitCheck() == B_OK) { 321 fCaption = path.Path(); 322 } 323 } 324 325 return B_OK; 326 } 327 328 // Function originally from Haiku ShowImage 329 bool 330 SlideShowSaver::ShowNextImage(bool next, bool rewind) 331 { 332 bool found; 333 entry_ref curRef, imgRef; 334 335 curRef = fCurrentRef; 336 found = FindNextImage(&curRef, &imgRef, next, rewind); 337 if (found) { 338 // Keep trying to load images until: 339 // 1. The image loads successfully 340 // 2. The last file in the directory is found (for find next or find first) 341 // 3. The first file in the directory is found (for find prev) 342 // 4. The call to FindNextImage fails for any other reason 343 while (SetImage(&imgRef) != B_OK) { 344 curRef = imgRef; 345 found = FindNextImage(&curRef, &imgRef, next, false); 346 if (!found) 347 return false; 348 } 349 return true; 350 } 351 return false; 352 } 353 354 // Function taken from Haiku ShowImage, 355 // function originally written by Michael Pfeiffer 356 bool 357 SlideShowSaver::IsImage(const entry_ref *pref) 358 { 359 if (!pref) 360 return false; 361 362 if (ent_is_dir(pref) != B_OK) 363 // if ref is erroneous or a directory, return false 364 return false; 365 366 BFile file(pref, B_READ_ONLY); 367 if (file.InitCheck() != B_OK) 368 return false; 369 370 BTranslatorRoster *proster = BTranslatorRoster::Default(); 371 if (!proster) 372 return false; 373 374 BMessage ioExtension; 375 if (ioExtension.AddInt32("/documentIndex", 1) != B_OK) 376 return false; 377 378 translator_info info; 379 memset(&info, 0, sizeof(translator_info)); 380 if (proster->Identify(&file, &ioExtension, &info, 0, NULL, 381 B_TRANSLATOR_BITMAP) != B_OK) 382 return false; 383 384 return true; 385 } 386 387 // Function taken from Haiku ShowImage, 388 // function originally written by Michael Pfeiffer 389 bool 390 SlideShowSaver::FindNextImage(entry_ref *in_current, entry_ref *out_image, bool next, bool rewind) 391 { 392 // ASSERT(next || !rewind); 393 BEntry curImage(in_current); 394 entry_ref entry, *ref; 395 BDirectory parent; 396 BList entries; 397 bool found = false; 398 int32 cur; 399 400 if (curImage.GetParent(&parent) != B_OK) 401 return false; 402 403 while (parent.GetNextRef(&entry) == B_OK) { 404 if (entry != *in_current) { 405 entries.AddItem(new entry_ref(entry)); 406 } else { 407 // insert current ref, so we can find it easily after sorting 408 entries.AddItem(in_current); 409 } 410 } 411 412 entries.SortItems(CompareEntries); 413 414 cur = entries.IndexOf(in_current); 415 // ASSERT(cur >= 0); 416 417 // remove it so FreeEntries() does not delete it 418 entries.RemoveItem(in_current); 419 420 if (next) { 421 // find the next image in the list 422 if (rewind) cur = 0; // start with first 423 for (; (ref = (entry_ref*)entries.ItemAt(cur)) != NULL; cur ++) { 424 if (IsImage(ref)) { 425 found = true; 426 *out_image = (const entry_ref)*ref; 427 break; 428 } 429 } 430 } else { 431 // find the previous image in the list 432 cur --; 433 for (; cur >= 0; cur --) { 434 ref = (entry_ref*)entries.ItemAt(cur); 435 if (IsImage(ref)) { 436 found = true; 437 *out_image = (const entry_ref)*ref; 438 break; 439 } 440 } 441 } 442 443 FreeEntries(&entries); 444 return found; 445 } 446 447 // Function taken from Haiku ShowImage, 448 // function originally written by Michael Pfeiffer 449 void 450 SlideShowSaver::FreeEntries(BList *entries) 451 { 452 const int32 n = entries->CountItems(); 453 for (int32 i = 0; i < n; i ++) { 454 entry_ref *ref = (entry_ref *)entries->ItemAt(i); 455 delete ref; 456 } 457 entries->MakeEmpty(); 458 } 459 460 void 461 SlideShowSaver::LayoutCaption(BView *view, BFont &font, BPoint &pos, BRect &rect) 462 { 463 font_height fontHeight; 464 float width, height; 465 BRect bounds(view->Bounds()); 466 font = be_plain_font; 467 width = font.StringWidth(fCaption.String()) + 1; // 1 for text shadow 468 font.GetHeight(&fontHeight); 469 height = fontHeight.ascent + fontHeight.descent; 470 // center text horizontally 471 pos.x = (bounds.left + bounds.right - width)/2; 472 // flush bottom 473 pos.y = bounds.bottom - fontHeight.descent - 5; 474 475 // background rectangle 476 rect.Set(0, 0, (width-1)+2, (height-1)+2+1); // 2 for border and 1 for text shadow 477 rect.OffsetTo(pos); 478 rect.OffsetBy(-1, -1-fontHeight.ascent); // -1 for border 479 } 480 481 void 482 SlideShowSaver::DrawCaption(BView *view) 483 { 484 BFont font; 485 BPoint pos; 486 BRect rect; 487 LayoutCaption(view, font, pos, rect); 488 489 view->PushState(); 490 // draw background 491 view->SetDrawingMode(B_OP_ALPHA); 492 view->SetHighColor(0, 0, 255, 128); 493 view->FillRect(rect); 494 // draw text 495 view->SetDrawingMode(B_OP_OVER); 496 view->SetFont(&font); 497 view->SetLowColor(B_TRANSPARENT_COLOR); 498 // text shadow 499 pos += BPoint(1, 1); 500 view->SetHighColor(0, 0, 0); 501 view->SetPenSize(1); 502 view->DrawString(fCaption.String(), pos); 503 // text 504 pos -= BPoint(1, 1); 505 view->SetHighColor(255, 255, 0); 506 view->DrawString(fCaption.String(), pos); 507 view->PopState(); 508 } 509 510 511