1 /* 2 * Copyright 2003-2010, Haiku, Inc. All Rights Reserved. 3 * Copyright 2004-2005 yellowTAB GmbH. All Rights Reserverd. 4 * Copyright 2006 Bernd Korz. All Rights Reserved 5 * Distributed under the terms of the MIT License. 6 * 7 * Authors: 8 * Fernando Francisco de Oliveira 9 * Michael Wilber 10 * Michael Pfeiffer 11 * Ryan Leavengood 12 * yellowTAB GmbH 13 * Bernd Korz 14 * Stephan Aßmus <superstippi@gmx.de> 15 * Axel Dörfler, axeld@pinc-software.de 16 */ 17 18 19 #include "ImageFileNavigator.h" 20 21 #include <new> 22 23 #include <stdio.h> 24 25 #include <BitmapStream.h> 26 #include <Directory.h> 27 #include <Entry.h> 28 #include <File.h> 29 #include <ObjectList.h> 30 #include <TranslatorRoster.h> 31 32 #include <tracker_private.h> 33 34 #include "ProgressWindow.h" 35 #include "ShowImageConstants.h" 36 37 38 class Navigator { 39 public: 40 Navigator(); 41 virtual ~Navigator(); 42 43 virtual bool FindNextImage(const entry_ref& currentRef, 44 entry_ref& ref, bool next, bool rewind) = 0; 45 virtual void UpdateSelection(const entry_ref& ref) = 0; 46 47 protected: 48 bool IsImage(const entry_ref& ref); 49 }; 50 51 52 // Navigation to the next/previous image file is based on 53 // communication with Tracker, the folder containing the current 54 // image needs to be open for this to work. The routine first tries 55 // to find the next candidate file, then tries to load it as image. 56 // As long as loading fails, the operation is repeated for the next 57 // candidate file. 58 59 class TrackerNavigator : public Navigator { 60 public: 61 TrackerNavigator( 62 const BMessenger& trackerMessenger); 63 virtual ~TrackerNavigator(); 64 65 virtual bool FindNextImage(const entry_ref& currentRef, 66 entry_ref& ref, bool next, bool rewind); 67 virtual void UpdateSelection(const entry_ref& ref); 68 69 private: 70 BMessenger fTrackerMessenger; 71 // of the window that this was launched from 72 }; 73 74 75 class FolderNavigator : public Navigator { 76 public: 77 FolderNavigator(entry_ref& ref); 78 virtual ~FolderNavigator(); 79 80 virtual bool FindNextImage(const entry_ref& currentRef, 81 entry_ref& ref, bool next, bool rewind); 82 virtual void UpdateSelection(const entry_ref& ref); 83 84 private: 85 void _BuildEntryList(); 86 static int _CompareRefs(const entry_ref* refA, 87 const entry_ref* refB); 88 89 private: 90 BDirectory fFolder; 91 BObjectList<entry_ref> fEntries; 92 }; 93 94 95 // TODO: Remove this and use Tracker's Command.h once it is moved into the 96 // private headers! 97 namespace BPrivate { 98 const uint32 kMoveToTrash = 'Ttrs'; 99 } 100 101 102 static bool 103 entry_ref_is_file(const entry_ref& ref) 104 { 105 BEntry entry(&ref, true); 106 if (entry.InitCheck() != B_OK) 107 return false; 108 109 return entry.IsFile(); 110 } 111 112 113 // #pragma mark - 114 115 116 Navigator::Navigator() 117 { 118 } 119 120 121 Navigator::~Navigator() 122 { 123 } 124 125 126 bool 127 Navigator::IsImage(const entry_ref& ref) 128 { 129 if (!entry_ref_is_file(ref)) 130 return false; 131 132 BFile file(&ref, B_READ_ONLY); 133 if (file.InitCheck() != B_OK) 134 return false; 135 136 BTranslatorRoster* roster = BTranslatorRoster::Default(); 137 if (roster == NULL) 138 return false; 139 140 translator_info info; 141 memset(&info, 0, sizeof(translator_info)); 142 return roster->Identify(&file, NULL, &info, 0, NULL, 143 B_TRANSLATOR_BITMAP) == B_OK; 144 } 145 146 147 // #pragma mark - 148 149 150 TrackerNavigator::TrackerNavigator(const BMessenger& trackerMessenger) 151 : 152 fTrackerMessenger(trackerMessenger) 153 { 154 } 155 156 157 TrackerNavigator::~TrackerNavigator() 158 { 159 } 160 161 162 bool 163 TrackerNavigator::FindNextImage(const entry_ref& currentRef, entry_ref& ref, 164 bool next, bool rewind) 165 { 166 // Based on GetTrackerWindowFile function from BeMail 167 if (!fTrackerMessenger.IsValid()) 168 return false; 169 170 // Ask the Tracker what the next/prev file in the window is. 171 // Continue asking for the next reference until a valid 172 // image is found. 173 entry_ref nextRef = currentRef; 174 bool foundRef = false; 175 while (!foundRef) { 176 BMessage request(B_GET_PROPERTY); 177 BMessage specifier; 178 if (rewind) 179 specifier.what = B_DIRECT_SPECIFIER; 180 else if (next) 181 specifier.what = 'snxt'; 182 else 183 specifier.what = 'sprv'; 184 specifier.AddString("property", "Entry"); 185 if (rewind) { 186 // if rewinding, ask for the ref to the 187 // first item in the directory 188 specifier.AddInt32("data", 0); 189 } else 190 specifier.AddRef("data", &nextRef); 191 request.AddSpecifier(&specifier); 192 193 BMessage reply; 194 if (fTrackerMessenger.SendMessage(&request, &reply) != B_OK) 195 return false; 196 if (reply.FindRef("result", &nextRef) != B_OK) 197 return false; 198 199 if (IsImage(nextRef)) 200 foundRef = true; 201 202 rewind = false; 203 // stop asking for the first ref in the directory 204 } 205 206 ref = nextRef; 207 return foundRef; 208 } 209 210 211 void 212 TrackerNavigator::UpdateSelection(const entry_ref& ref) 213 { 214 BMessage setSelection(B_SET_PROPERTY); 215 setSelection.AddSpecifier("Selection"); 216 setSelection.AddRef("data", &ref); 217 fTrackerMessenger.SendMessage(&setSelection); 218 } 219 220 221 // #pragma mark - 222 223 224 FolderNavigator::FolderNavigator(entry_ref& ref) 225 : 226 fEntries(true) 227 { 228 BEntry entry(&ref); 229 if (entry.IsDirectory()) 230 fFolder.SetTo(&ref); 231 else { 232 node_ref nodeRef; 233 nodeRef.device = ref.device; 234 nodeRef.node = ref.directory; 235 236 fFolder.SetTo(&nodeRef); 237 } 238 239 _BuildEntryList(); 240 241 // TODO: monitor the directory for changes, sort it naturally 242 243 if (entry.IsDirectory()) 244 FindNextImage(ref, ref, false, true); 245 } 246 247 248 FolderNavigator::~FolderNavigator() 249 { 250 } 251 252 253 bool 254 FolderNavigator::FindNextImage(const entry_ref& currentRef, entry_ref& nextRef, 255 bool next, bool rewind) 256 { 257 int32 index; 258 if (rewind) { 259 index = next ? fEntries.CountItems() : 0; 260 next = !next; 261 } else { 262 index = fEntries.BinarySearchIndex(currentRef, 263 &FolderNavigator::_CompareRefs); 264 if (next) 265 index++; 266 else 267 index--; 268 } 269 270 while (index < fEntries.CountItems() && index >= 0) { 271 const entry_ref& ref = *fEntries.ItemAt(index); 272 if (IsImage(ref)) { 273 nextRef = ref; 274 return true; 275 } else { 276 // remove non-image entries 277 delete fEntries.RemoveItemAt(index); 278 if (!next) 279 index--; 280 } 281 } 282 283 return false; 284 } 285 286 287 void 288 FolderNavigator::UpdateSelection(const entry_ref& ref) 289 { 290 // nothing to do for us here 291 } 292 293 294 void 295 FolderNavigator::_BuildEntryList() 296 { 297 fEntries.MakeEmpty(); 298 fFolder.Rewind(); 299 300 while (true) { 301 entry_ref* ref = new entry_ref(); 302 status_t status = fFolder.GetNextRef(ref); 303 if (status != B_OK) { 304 delete ref; 305 break; 306 } 307 308 fEntries.AddItem(ref); 309 } 310 311 fEntries.SortItems(&FolderNavigator::_CompareRefs); 312 } 313 314 315 /*static*/ int 316 FolderNavigator::_CompareRefs(const entry_ref* refA, const entry_ref* refB) 317 { 318 // TODO: natural sorting? Collating via current locale? 319 return strcasecmp(refA->name, refB->name); 320 } 321 322 323 // #pragma mark - 324 325 326 ImageFileNavigator::ImageFileNavigator(const entry_ref& ref, 327 const BMessenger& trackerMessenger) 328 : 329 fCurrentRef(ref), 330 fDocumentIndex(1), 331 fDocumentCount(1) 332 { 333 // TODO: allow selecting a folder from Tracker as well! 334 if (trackerMessenger.IsValid()) 335 fNavigator = new TrackerNavigator(trackerMessenger); 336 else 337 fNavigator = new FolderNavigator(fCurrentRef); 338 } 339 340 341 ImageFileNavigator::~ImageFileNavigator() 342 { 343 delete fNavigator; 344 } 345 346 347 void 348 ImageFileNavigator::SetTo(const entry_ref& ref, int32 page, int32 pageCount) 349 { 350 fCurrentRef = ref; 351 fDocumentIndex = page; 352 fDocumentCount = pageCount; 353 } 354 355 356 int32 357 ImageFileNavigator::CurrentPage() 358 { 359 return fDocumentIndex; 360 } 361 362 363 int32 364 ImageFileNavigator::PageCount() 365 { 366 return fDocumentCount; 367 } 368 369 370 bool 371 ImageFileNavigator::FirstPage() 372 { 373 if (fDocumentIndex != 1) { 374 fDocumentIndex = 1; 375 return true; 376 } 377 return false; 378 } 379 380 381 bool 382 ImageFileNavigator::LastPage() 383 { 384 if (fDocumentIndex != fDocumentCount) { 385 fDocumentIndex = fDocumentCount; 386 return true; 387 } 388 return false; 389 } 390 391 392 bool 393 ImageFileNavigator::NextPage() 394 { 395 if (fDocumentIndex < fDocumentCount) { 396 fDocumentIndex++; 397 return true; 398 } 399 return false; 400 } 401 402 403 bool 404 ImageFileNavigator::PreviousPage() 405 { 406 if (fDocumentIndex > 1) { 407 fDocumentIndex--; 408 return true; 409 } 410 return false; 411 } 412 413 414 bool 415 ImageFileNavigator::GoToPage(int32 page) 416 { 417 if (page > 0 && page <= fDocumentCount && page != fDocumentIndex) { 418 fDocumentIndex = page; 419 return true; 420 } 421 return false; 422 } 423 424 425 bool 426 ImageFileNavigator::FirstFile() 427 { 428 entry_ref ref; 429 if (fNavigator->FindNextImage(fCurrentRef, ref, false, true)) { 430 SetTo(ref, 1, 1); 431 fNavigator->UpdateSelection(fCurrentRef); 432 return true; 433 } 434 435 return false; 436 } 437 438 439 bool 440 ImageFileNavigator::NextFile() 441 { 442 entry_ref ref; 443 if (fNavigator->FindNextImage(fCurrentRef, ref, true, false)) { 444 SetTo(ref, 1, 1); 445 fNavigator->UpdateSelection(fCurrentRef); 446 return true; 447 } 448 449 return false; 450 } 451 452 453 bool 454 ImageFileNavigator::PreviousFile() 455 { 456 entry_ref ref; 457 if (fNavigator->FindNextImage(fCurrentRef, ref, false, false)) { 458 SetTo(ref, 1, 1); 459 fNavigator->UpdateSelection(fCurrentRef); 460 return true; 461 } 462 463 return false; 464 } 465 466 467 bool 468 ImageFileNavigator::HasNextFile() 469 { 470 entry_ref ref; 471 return fNavigator->FindNextImage(fCurrentRef, ref, true, false); 472 } 473 474 475 bool 476 ImageFileNavigator::HasPreviousFile() 477 { 478 entry_ref ref; 479 return fNavigator->FindNextImage(fCurrentRef, ref, false, false); 480 } 481 482 483 bool 484 ImageFileNavigator::GetNextFile(const entry_ref& ref, entry_ref& nextRef) 485 { 486 return fNavigator->FindNextImage(ref, nextRef, true, false); 487 } 488 489 490 bool 491 ImageFileNavigator::GetPreviousFile(const entry_ref& ref, 492 entry_ref& previousRef) 493 { 494 return fNavigator->FindNextImage(ref, previousRef, false, false); 495 } 496 497 498 /*! Moves the current file into the trash. 499 Returns true if a new file should be loaded, false if not. 500 */ 501 bool 502 ImageFileNavigator::MoveFileToTrash() 503 { 504 entry_ref nextRef; 505 if (!fNavigator->FindNextImage(fCurrentRef, nextRef, true, false) 506 && !fNavigator->FindNextImage(fCurrentRef, nextRef, false, false)) 507 nextRef.device = -1; 508 509 // Move image to Trash 510 BMessage trash(BPrivate::kMoveToTrash); 511 trash.AddRef("refs", &fCurrentRef); 512 513 // We create our own messenger because the member fTrackerMessenger 514 // could be invalid 515 BMessenger tracker(kTrackerSignature); 516 if (tracker.SendMessage(&trash) != B_OK) 517 return false; 518 519 if (nextRef.device != -1) { 520 SetTo(nextRef, 1, 1); 521 return true; 522 } 523 524 return false; 525 } 526