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