btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / apps / webpositive / DownloadProgressView.cpp
blob927461d6df30bbe26352bcde6420cd749a7a790e
1 /*
2 * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
4 * All rights reserved. Distributed under the terms of the MIT License.
5 */
7 #include "DownloadProgressView.h"
9 #include <stdio.h>
11 #include <Alert.h>
12 #include <Application.h>
13 #include <Bitmap.h>
14 #include <Button.h>
15 #include <Catalog.h>
16 #include <Clipboard.h>
17 #include <Directory.h>
18 #include <DateTimeFormat.h>
19 #include <DurationFormat.h>
20 #include <Entry.h>
21 #include <FindDirectory.h>
22 #include <GroupLayoutBuilder.h>
23 #include <Locale.h>
24 #include <MenuItem.h>
25 #include <NodeInfo.h>
26 #include <NodeMonitor.h>
27 #include <Notification.h>
28 #include <PopUpMenu.h>
29 #include <Roster.h>
30 #include <SpaceLayoutItem.h>
31 #include <StatusBar.h>
32 #include <StringView.h>
33 #include <TimeFormat.h>
35 #include "BrowserWindow.h"
36 #include "WebDownload.h"
37 #include "WebPage.h"
38 #include "StringForSize.h"
41 #undef B_TRANSLATION_CONTEXT
42 #define B_TRANSLATION_CONTEXT "Download Window"
44 enum {
45 OPEN_DOWNLOAD = 'opdn',
46 RESTART_DOWNLOAD = 'rsdn',
47 CANCEL_DOWNLOAD = 'cndn',
48 REMOVE_DOWNLOAD = 'rmdn',
49 COPY_URL_TO_CLIPBOARD = 'curl',
50 OPEN_CONTAINING_FOLDER = 'opfd',
53 const bigtime_t kMaxUpdateInterval = 100000LL;
54 const bigtime_t kSpeedReferenceInterval = 500000LL;
55 const bigtime_t kShowSpeedInterval = 8000000LL;
56 const bigtime_t kShowEstimatedFinishInterval = 4000000LL;
58 bigtime_t DownloadProgressView::sLastEstimatedFinishSpeedToggleTime = -1;
59 bool DownloadProgressView::sShowSpeed = true;
60 static const time_t kSecondsPerDay = 24 * 60 * 60;
61 static const time_t kSecondsPerHour = 60 * 60;
64 class IconView : public BView {
65 public:
66 IconView(const BEntry& entry)
68 BView("Download icon", B_WILL_DRAW),
69 fIconBitmap(BRect(0, 0, 31, 31), 0, B_RGBA32),
70 fDimmedIcon(false)
72 SetDrawingMode(B_OP_OVER);
73 SetTo(entry);
76 IconView()
78 BView("Download icon", B_WILL_DRAW),
79 fIconBitmap(BRect(0, 0, 31, 31), 0, B_RGBA32),
80 fDimmedIcon(false)
82 SetDrawingMode(B_OP_OVER);
83 memset(fIconBitmap.Bits(), 0, fIconBitmap.BitsLength());
86 IconView(BMessage* archive)
88 BView("Download icon", B_WILL_DRAW),
89 fIconBitmap(archive),
90 fDimmedIcon(true)
92 SetDrawingMode(B_OP_OVER);
95 void SetTo(const BEntry& entry)
97 BNode node(&entry);
98 BNodeInfo info(&node);
99 info.GetTrackerIcon(&fIconBitmap, B_LARGE_ICON);
100 Invalidate();
103 void SetIconDimmed(bool iconDimmed)
105 if (fDimmedIcon != iconDimmed) {
106 fDimmedIcon = iconDimmed;
107 Invalidate();
111 bool IsIconDimmed() const
113 return fDimmedIcon;
116 status_t SaveSettings(BMessage* archive)
118 return fIconBitmap.Archive(archive);
121 virtual void AttachedToWindow()
123 AdoptParentColors();
126 virtual void Draw(BRect updateRect)
128 if (fDimmedIcon) {
129 SetDrawingMode(B_OP_ALPHA);
130 SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY);
131 SetHighColor(0, 0, 0, 100);
133 DrawBitmapAsync(&fIconBitmap);
136 virtual BSize MinSize()
138 return BSize(fIconBitmap.Bounds().Width(),
139 fIconBitmap.Bounds().Height());
142 virtual BSize PreferredSize()
144 return MinSize();
147 virtual BSize MaxSize()
149 return MinSize();
152 BBitmap* Bitmap()
154 return &fIconBitmap;
157 private:
158 BBitmap fIconBitmap;
159 bool fDimmedIcon;
163 class SmallButton : public BButton {
164 public:
165 SmallButton(const char* label, BMessage* message = NULL)
167 BButton(label, message)
169 BFont font;
170 GetFont(&font);
171 float size = ceilf(font.Size() * 0.8);
172 font.SetSize(max_c(8, size));
173 SetFont(&font, B_FONT_SIZE);
178 // #pragma mark - DownloadProgressView
181 DownloadProgressView::DownloadProgressView(BWebDownload* download)
183 BGroupView(B_HORIZONTAL, 8),
184 fDownload(download),
185 fURL(download->URL()),
186 fPath(download->Path())
191 DownloadProgressView::DownloadProgressView(const BMessage* archive)
193 BGroupView(B_HORIZONTAL, 8),
194 fDownload(NULL),
195 fURL(),
196 fPath()
198 const char* string;
199 if (archive->FindString("path", &string) == B_OK)
200 fPath.SetTo(string);
201 if (archive->FindString("url", &string) == B_OK)
202 fURL = string;
206 bool
207 DownloadProgressView::Init(BMessage* archive)
209 fCurrentSize = 0;
210 fExpectedSize = 0;
211 fLastUpdateTime = 0;
212 fBytesPerSecond = 0.0;
213 for (size_t i = 0; i < kBytesPerSecondSlots; i++)
214 fBytesPerSecondSlot[i] = 0.0;
215 fCurrentBytesPerSecondSlot = 0;
216 fLastSpeedReferenceSize = 0;
217 fEstimatedFinishReferenceSize = 0;
219 fProcessStartTime = fLastSpeedReferenceTime
220 = fEstimatedFinishReferenceTime = system_time();
222 SetViewColor(245, 245, 245);
223 SetFlags(Flags() | B_FULL_UPDATE_ON_RESIZE | B_WILL_DRAW);
225 if (archive) {
226 fStatusBar = new BStatusBar("download progress", fPath.Leaf());
227 float value;
228 if (archive->FindFloat("value", &value) == B_OK)
229 fStatusBar->SetTo(value);
230 } else
231 fStatusBar = new BStatusBar("download progress", "Download");
232 fStatusBar->SetMaxValue(100);
233 fStatusBar->SetBarHeight(12);
235 // fPath is only valid when constructed from archive (fDownload == NULL)
236 BEntry entry(fPath.Path());
238 if (archive) {
239 if (!entry.Exists())
240 fIconView = new IconView(archive);
241 else
242 fIconView = new IconView(entry);
243 } else
244 fIconView = new IconView();
246 if (!fDownload && (fStatusBar->CurrentValue() < 100 || !entry.Exists())) {
247 fTopButton = new SmallButton(B_TRANSLATE("Restart"),
248 new BMessage(RESTART_DOWNLOAD));
249 } else {
250 fTopButton = new SmallButton(B_TRANSLATE("Open"),
251 new BMessage(OPEN_DOWNLOAD));
252 fTopButton->SetEnabled(fDownload == NULL);
254 if (fDownload) {
255 fBottomButton = new SmallButton(B_TRANSLATE("Cancel"),
256 new BMessage(CANCEL_DOWNLOAD));
257 } else {
258 fBottomButton = new SmallButton(B_TRANSLATE("Remove"),
259 new BMessage(REMOVE_DOWNLOAD));
260 fBottomButton->SetEnabled(fDownload == NULL);
263 fInfoView = new BStringView("info view", "");
264 fInfoView->SetViewColor(ViewColor());
266 BSize topButtonSize = fTopButton->PreferredSize();
267 BSize bottomButtonSize = fBottomButton->PreferredSize();
268 if (bottomButtonSize.width < topButtonSize.width)
269 fBottomButton->SetExplicitMaxSize(topButtonSize);
270 else
271 fTopButton->SetExplicitMaxSize(bottomButtonSize);
273 BGroupLayout* layout = GroupLayout();
274 layout->SetInsets(8, 5, 5, 6);
275 layout->AddView(fIconView);
276 BView* verticalGroup = BGroupLayoutBuilder(B_VERTICAL, 3)
277 .Add(fStatusBar)
278 .Add(fInfoView)
279 .TopView()
281 verticalGroup->SetViewColor(ViewColor());
282 layout->AddView(verticalGroup);
284 verticalGroup = BGroupLayoutBuilder(B_VERTICAL, 3)
285 .Add(fTopButton)
286 .Add(fBottomButton)
287 .TopView()
289 verticalGroup->SetViewColor(ViewColor());
290 layout->AddView(verticalGroup);
292 BFont font;
293 fInfoView->GetFont(&font);
294 float fontSize = font.Size() * 0.8f;
295 font.SetSize(max_c(8.0f, fontSize));
296 fInfoView->SetFont(&font, B_FONT_SIZE);
297 fInfoView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
299 return true;
303 status_t
304 DownloadProgressView::SaveSettings(BMessage* archive)
306 if (!archive)
307 return B_BAD_VALUE;
308 status_t ret = archive->AddString("path", fPath.Path());
309 if (ret == B_OK)
310 ret = archive->AddString("url", fURL.String());
311 if (ret == B_OK)
312 ret = archive->AddFloat("value", fStatusBar->CurrentValue());
313 if (ret == B_OK)
314 ret = fIconView->SaveSettings(archive);
315 return ret;
319 void
320 DownloadProgressView::AttachedToWindow()
322 if (fDownload) {
323 fDownload->SetProgressListener(BMessenger(this));
324 // Will start node monitor upon receiving the B_DOWNLOAD_STARTED
325 // message.
326 } else {
327 BEntry entry(fPath.Path());
328 if (entry.Exists())
329 _StartNodeMonitor(entry);
332 fTopButton->SetTarget(this);
333 fBottomButton->SetTarget(this);
337 void
338 DownloadProgressView::DetachedFromWindow()
340 _StopNodeMonitor();
344 void
345 DownloadProgressView::AllAttached()
347 fStatusBar->SetLowColor(ViewColor());
348 fInfoView->SetLowColor(ViewColor());
349 fInfoView->SetHighColor(0, 0, 0, 255);
351 SetViewColor(B_TRANSPARENT_COLOR);
352 SetLowColor(245, 245, 245);
353 SetHighColor(tint_color(LowColor(), B_DARKEN_1_TINT));
357 void
358 DownloadProgressView::Draw(BRect updateRect)
360 BRect bounds(Bounds());
361 bounds.bottom--;
362 FillRect(bounds, B_SOLID_LOW);
363 bounds.bottom++;
364 StrokeLine(bounds.LeftBottom(), bounds.RightBottom());
368 void
369 DownloadProgressView::MessageReceived(BMessage* message)
371 switch (message->what) {
372 case B_DOWNLOAD_STARTED:
374 BString path;
375 if (message->FindString("path", &path) != B_OK)
376 break;
377 fPath.SetTo(path);
378 BEntry entry(fPath.Path());
379 fIconView->SetTo(entry);
380 fStatusBar->Reset(fPath.Leaf());
381 _StartNodeMonitor(entry);
383 // Immediately switch to speed display whenever a new download
384 // starts.
385 sShowSpeed = true;
386 sLastEstimatedFinishSpeedToggleTime
387 = fProcessStartTime = fLastSpeedReferenceTime
388 = fEstimatedFinishReferenceTime = system_time();
389 break;
391 case B_DOWNLOAD_PROGRESS:
393 int64 currentSize;
394 int64 expectedSize;
395 if (message->FindInt64("current size", &currentSize) == B_OK
396 && message->FindInt64("expected size", &expectedSize) == B_OK) {
397 _UpdateStatus(currentSize, expectedSize);
399 break;
401 case B_DOWNLOAD_REMOVED:
402 // TODO: This is a bit asymetric. The removed notification
403 // arrives here, but it would be nicer if it arrived
404 // at the window...
405 Window()->PostMessage(message);
406 break;
407 case OPEN_DOWNLOAD:
409 // TODO: In case of executable files, ask the user first!
410 entry_ref ref;
411 status_t status = get_ref_for_path(fPath.Path(), &ref);
412 if (status == B_OK)
413 status = be_roster->Launch(&ref);
414 if (status != B_OK && status != B_ALREADY_RUNNING) {
415 BAlert* alert = new BAlert(B_TRANSLATE("Open download error"),
416 B_TRANSLATE("The download could not be opened."),
417 B_TRANSLATE("OK"));
418 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
419 alert->Go(NULL);
421 break;
423 case RESTART_DOWNLOAD:
425 // We can't create a download without a full web context (mainly
426 // because it needs to access the cookie jar), and when we get here
427 // the original context is long gone (possibly the browser was
428 // restarted). So we create a new window to restart the download
429 // in a fresh context.
430 // FIXME this has of course the huge downside of leaving the new
431 // window open with a blank page. I can't think of a better
432 // solution right now...
433 BMessage* request = new BMessage(NEW_WINDOW);
434 request->AddString("url", fURL);
435 be_app->PostMessage(request);
436 break;
439 case CANCEL_DOWNLOAD:
440 CancelDownload();
441 break;
443 case REMOVE_DOWNLOAD:
445 Window()->PostMessage(SAVE_SETTINGS);
446 RemoveSelf();
447 delete this;
448 // TOAST!
449 return;
451 case B_NODE_MONITOR:
453 int32 opCode;
454 if (message->FindInt32("opcode", &opCode) != B_OK)
455 break;
456 switch (opCode) {
457 case B_ENTRY_REMOVED:
458 fIconView->SetIconDimmed(true);
459 CancelDownload();
460 break;
461 case B_ENTRY_MOVED:
463 // Follow the entry to the new location
464 dev_t device;
465 ino_t directory;
466 const char* name;
467 if (message->FindInt32("device",
468 reinterpret_cast<int32*>(&device)) != B_OK
469 || message->FindInt64("to directory",
470 reinterpret_cast<int64*>(&directory)) != B_OK
471 || message->FindString("name", &name) != B_OK
472 || strlen(name) == 0) {
473 break;
475 // Construct the BEntry and update fPath
476 entry_ref ref(device, directory, name);
477 BEntry entry(&ref);
478 if (entry.GetPath(&fPath) != B_OK)
479 break;
481 // Find out if the directory is the Trash for this
482 // volume
483 char trashPath[B_PATH_NAME_LENGTH];
484 if (find_directory(B_TRASH_DIRECTORY, device, false,
485 trashPath, B_PATH_NAME_LENGTH) == B_OK) {
486 BPath trashDirectory(trashPath);
487 BPath parentDirectory;
488 fPath.GetParent(&parentDirectory);
489 if (parentDirectory == trashDirectory) {
490 // The entry was moved into the Trash.
491 // If the download is still in progress,
492 // cancel it.
493 fIconView->SetIconDimmed(true);
494 CancelDownload();
495 break;
496 } else if (fIconView->IsIconDimmed()) {
497 // Maybe it was moved out of the trash.
498 fIconView->SetIconDimmed(false);
502 // Inform download of the new path
503 if (fDownload)
504 fDownload->HasMovedTo(fPath);
506 float value = fStatusBar->CurrentValue();
507 fStatusBar->Reset(name);
508 fStatusBar->SetTo(value);
509 Window()->PostMessage(SAVE_SETTINGS);
510 break;
512 case B_ATTR_CHANGED:
514 BEntry entry(fPath.Path());
515 fIconView->SetIconDimmed(false);
516 fIconView->SetTo(entry);
517 break;
520 break;
523 // Context menu messages
524 case COPY_URL_TO_CLIPBOARD:
525 if (be_clipboard->Lock()) {
526 BMessage* data = be_clipboard->Data();
527 if (data != NULL) {
528 be_clipboard->Clear();
529 data->AddData("text/plain", B_MIME_TYPE, fURL.String(),
530 fURL.Length());
532 be_clipboard->Commit();
533 be_clipboard->Unlock();
535 break;
536 case OPEN_CONTAINING_FOLDER:
537 if (fPath.InitCheck() == B_OK) {
538 BEntry selected(fPath.Path());
539 if (!selected.Exists())
540 break;
542 BPath containingFolder;
543 if (fPath.GetParent(&containingFolder) != B_OK)
544 break;
545 entry_ref ref;
546 if (get_ref_for_path(containingFolder.Path(), &ref) != B_OK)
547 break;
549 // Ask Tracker to open the containing folder and select the
550 // file inside it.
551 BMessenger trackerMessenger("application/x-vnd.Be-TRAK");
553 if (trackerMessenger.IsValid()) {
554 BMessage selectionCommand(B_REFS_RECEIVED);
555 selectionCommand.AddRef("refs", &ref);
557 node_ref selectedRef;
558 if (selected.GetNodeRef(&selectedRef) == B_OK) {
559 selectionCommand.AddData("nodeRefToSelect", B_RAW_TYPE,
560 (void*)&selectedRef, sizeof(node_ref));
563 trackerMessenger.SendMessage(&selectionCommand);
566 break;
568 default:
569 BGroupView::MessageReceived(message);
574 void
575 DownloadProgressView::ShowContextMenu(BPoint screenWhere)
577 screenWhere += BPoint(2, 2);
579 BPopUpMenu* contextMenu = new BPopUpMenu("download context");
580 BMenuItem* copyURL = new BMenuItem(B_TRANSLATE("Copy URL to clipboard"),
581 new BMessage(COPY_URL_TO_CLIPBOARD));
582 copyURL->SetEnabled(fURL.Length() > 0);
583 contextMenu->AddItem(copyURL);
584 BMenuItem* openFolder = new BMenuItem(B_TRANSLATE("Open containing folder"),
585 new BMessage(OPEN_CONTAINING_FOLDER));
586 contextMenu->AddItem(openFolder);
588 contextMenu->SetTargetForItems(this);
589 contextMenu->Go(screenWhere, true, true, true);
593 BWebDownload*
594 DownloadProgressView::Download() const
596 return fDownload;
600 const BString&
601 DownloadProgressView::URL() const
603 return fURL;
607 bool
608 DownloadProgressView::IsMissing() const
610 return fIconView->IsIconDimmed();
614 bool
615 DownloadProgressView::IsFinished() const
617 return !fDownload && fStatusBar->CurrentValue() == 100;
621 void
622 DownloadProgressView::DownloadFinished()
624 fDownload = NULL;
625 if (fExpectedSize == -1) {
626 fStatusBar->SetTo(100.0);
627 fExpectedSize = fCurrentSize;
629 fTopButton->SetEnabled(true);
630 fBottomButton->SetLabel(B_TRANSLATE("Remove"));
631 fBottomButton->SetMessage(new BMessage(REMOVE_DOWNLOAD));
632 fBottomButton->SetEnabled(true);
633 fInfoView->SetText("");
634 fStatusBar->SetBarColor(ui_color(B_SUCCESS_COLOR));
636 BNotification success(B_INFORMATION_NOTIFICATION);
637 success.SetGroup(B_TRANSLATE("WebPositive"));
638 success.SetTitle(B_TRANSLATE("Download finished"));
639 success.SetContent(fPath.Leaf());
640 BEntry entry(fPath.Path());
641 entry_ref ref;
642 entry.GetRef(&ref);
643 success.SetOnClickFile(&ref);
644 success.SetIcon(fIconView->Bitmap());
645 success.Send();
650 void
651 DownloadProgressView::CancelDownload()
653 // Show the cancel notification, and set the progress bar red, only if the
654 // download was still running. In cases where the file is deleted after
655 // the download was finished, we don't want these things to happen.
656 if (fDownload) {
657 // Also cancel the download
658 fDownload->Cancel();
659 BNotification success(B_ERROR_NOTIFICATION);
660 success.SetGroup(B_TRANSLATE("WebPositive"));
661 success.SetTitle(B_TRANSLATE("Download aborted"));
662 success.SetContent(fPath.Leaf());
663 // Don't make a click on the notification open the file: it is not
664 // complete
665 success.SetIcon(fIconView->Bitmap());
666 success.Send();
668 fStatusBar->SetBarColor(ui_color(B_FAILURE_COLOR));
671 fDownload = NULL;
672 fTopButton->SetLabel(B_TRANSLATE("Restart"));
673 fTopButton->SetMessage(new BMessage(RESTART_DOWNLOAD));
674 fTopButton->SetEnabled(true);
675 fBottomButton->SetLabel(B_TRANSLATE("Remove"));
676 fBottomButton->SetMessage(new BMessage(REMOVE_DOWNLOAD));
677 fBottomButton->SetEnabled(true);
678 fInfoView->SetText("");
680 fPath.Unset();
684 /*static*/ void
685 DownloadProgressView::SpeedVersusEstimatedFinishTogglePulse()
687 bigtime_t now = system_time();
688 if (sShowSpeed
689 && sLastEstimatedFinishSpeedToggleTime + kShowSpeedInterval
690 <= now) {
691 sShowSpeed = false;
692 sLastEstimatedFinishSpeedToggleTime = now;
693 } else if (!sShowSpeed
694 && sLastEstimatedFinishSpeedToggleTime
695 + kShowEstimatedFinishInterval <= now) {
696 sShowSpeed = true;
697 sLastEstimatedFinishSpeedToggleTime = now;
702 // #pragma mark - private
705 void
706 DownloadProgressView::_UpdateStatus(off_t currentSize, off_t expectedSize)
708 fCurrentSize = currentSize;
709 fExpectedSize = expectedSize;
711 fStatusBar->SetTo(100.0 * currentSize / expectedSize);
713 bigtime_t currentTime = system_time();
714 if ((currentTime - fLastUpdateTime) > kMaxUpdateInterval) {
715 fLastUpdateTime = currentTime;
717 if (currentTime >= fLastSpeedReferenceTime + kSpeedReferenceInterval) {
718 // update current speed every kSpeedReferenceInterval
719 fCurrentBytesPerSecondSlot
720 = (fCurrentBytesPerSecondSlot + 1) % kBytesPerSecondSlots;
721 fBytesPerSecondSlot[fCurrentBytesPerSecondSlot]
722 = (double)(currentSize - fLastSpeedReferenceSize)
723 * 1000000LL / (currentTime - fLastSpeedReferenceTime);
724 fLastSpeedReferenceSize = currentSize;
725 fLastSpeedReferenceTime = currentTime;
726 fBytesPerSecond = 0.0;
727 size_t count = 0;
728 for (size_t i = 0; i < kBytesPerSecondSlots; i++) {
729 if (fBytesPerSecondSlot[i] != 0.0) {
730 fBytesPerSecond += fBytesPerSecondSlot[i];
731 count++;
734 if (count > 0)
735 fBytesPerSecond /= count;
737 _UpdateStatusText();
742 void
743 DownloadProgressView::_UpdateStatusText()
745 fInfoView->SetText("");
746 BString buffer;
747 if (sShowSpeed && fBytesPerSecond != 0.0) {
748 // Draw speed info
749 char sizeBuffer[128];
750 buffer = "(";
751 // Get strings for current and expected size and remove the unit
752 // from the current size string if it's the same as the expected
753 // size unit.
754 BString currentSize = string_for_size((double)fCurrentSize, sizeBuffer,
755 sizeof(sizeBuffer));
756 BString expectedSize = string_for_size((double)fExpectedSize, sizeBuffer,
757 sizeof(sizeBuffer));
758 int currentSizeUnitPos = currentSize.FindLast(' ');
759 int expectedSizeUnitPos = expectedSize.FindLast(' ');
760 if (currentSizeUnitPos >= 0 && expectedSizeUnitPos >= 0
761 && strcmp(currentSize.String() + currentSizeUnitPos,
762 expectedSize.String() + expectedSizeUnitPos) == 0) {
763 currentSize.Truncate(currentSizeUnitPos);
765 buffer << currentSize;
766 buffer << " ";
767 buffer << B_TRANSLATE_COMMENT("of", "...as in '12kB of 256kB'");
768 buffer << " ";
769 buffer << expectedSize;
770 buffer << ", ";
771 buffer << string_for_size(fBytesPerSecond, sizeBuffer,
772 sizeof(sizeBuffer));
773 buffer << B_TRANSLATE_COMMENT("/s)", "...as in 'per second'");
774 float stringWidth = fInfoView->StringWidth(buffer.String());
775 if (stringWidth < fInfoView->Bounds().Width())
776 fInfoView->SetText(buffer.String());
777 else {
778 // complete string too wide, try with shorter version
779 buffer << string_for_size(fBytesPerSecond, sizeBuffer,
780 sizeof(sizeBuffer));
781 buffer << B_TRANSLATE_COMMENT("/s)", "...as in 'per second'");
782 stringWidth = fInfoView->StringWidth(buffer.String());
783 if (stringWidth < fInfoView->Bounds().Width())
784 fInfoView->SetText(buffer.String());
786 } else if (!sShowSpeed && fCurrentSize < fExpectedSize) {
787 double totalBytesPerSecond = (double)(fCurrentSize
788 - fEstimatedFinishReferenceSize)
789 * 1000000LL / (system_time() - fEstimatedFinishReferenceTime);
790 double secondsRemaining = (fExpectedSize - fCurrentSize)
791 / totalBytesPerSecond;
792 time_t now = (time_t)real_time_clock();
793 time_t finishTime = (time_t)(now + secondsRemaining);
795 BString timeText;
796 if (finishTime - now > kSecondsPerDay) {
797 BDateTimeFormat().Format(timeText, finishTime,
798 B_MEDIUM_DATE_FORMAT, B_MEDIUM_TIME_FORMAT);
799 } else {
800 BTimeFormat().Format(timeText, finishTime,
801 B_MEDIUM_TIME_FORMAT);
804 BString statusString;
805 BDurationFormat formatter;
806 BString finishString;
807 if (finishTime - now > kSecondsPerHour) {
808 statusString.SetTo(B_TRANSLATE("(Finish: %date - Over %duration left)"));
809 formatter.Format(finishString, now * 1000000LL, finishTime * 1000000LL);
810 } else {
811 statusString.SetTo(B_TRANSLATE("(Finish: %date - %duration left)"));
812 formatter.Format(finishString, now * 1000000LL, finishTime * 1000000LL);
815 statusString.ReplaceFirst("%date", timeText);
816 statusString.ReplaceFirst("%duration", finishString);
818 float stringWidth = fInfoView->StringWidth(statusString.String());
819 if (stringWidth < fInfoView->Bounds().Width())
820 fInfoView->SetText(statusString.String());
821 else {
822 // complete string too wide, try with shorter version
823 statusString.SetTo(B_TRANSLATE("(Finish: %date)"));
824 statusString.ReplaceFirst("%date", timeText);
825 stringWidth = fInfoView->StringWidth(statusString.String());
826 if (stringWidth < fInfoView->Bounds().Width())
827 fInfoView->SetText(statusString.String());
833 void
834 DownloadProgressView::_StartNodeMonitor(const BEntry& entry)
836 node_ref nref;
837 if (entry.GetNodeRef(&nref) == B_OK)
838 watch_node(&nref, B_WATCH_ALL, this);
842 void
843 DownloadProgressView::_StopNodeMonitor()
845 stop_watching(this);