btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / apps / webpositive / DownloadWindow.cpp
blobf164b9936cd882ad336259214c740c996f5d65a9
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 "DownloadWindow.h"
9 #include <stdio.h>
11 #include <Alert.h>
12 #include <Button.h>
13 #include <Catalog.h>
14 #include <ControlLook.h>
15 #include <Entry.h>
16 #include <File.h>
17 #include <FindDirectory.h>
18 #include <GroupLayout.h>
19 #include <GroupLayoutBuilder.h>
20 #include <Locale.h>
21 #include <MenuBar.h>
22 #include <MenuItem.h>
23 #include <Path.h>
24 #include <Roster.h>
25 #include <ScrollView.h>
26 #include <SeparatorView.h>
27 #include <SpaceLayoutItem.h>
29 #include "BrowserApp.h"
30 #include "BrowserWindow.h"
31 #include "DownloadProgressView.h"
32 #include "SettingsKeys.h"
33 #include "SettingsMessage.h"
34 #include "WebDownload.h"
35 #include "WebPage.h"
38 #undef B_TRANSLATION_CONTEXT
39 #define B_TRANSLATION_CONTEXT "Download Window"
41 enum {
42 INIT = 'init',
43 OPEN_DOWNLOADS_FOLDER = 'odnf',
44 REMOVE_FINISHED_DOWNLOADS = 'rmfd',
45 REMOVE_MISSING_DOWNLOADS = 'rmmd'
49 class DownloadsContainerView : public BGroupView {
50 public:
51 DownloadsContainerView()
53 BGroupView(B_VERTICAL, 0.0)
55 SetFlags(Flags() | B_PULSE_NEEDED);
56 SetViewColor(245, 245, 245);
57 AddChild(BSpaceLayoutItem::CreateGlue());
60 virtual BSize MinSize()
62 BSize minSize = BGroupView::MinSize();
63 return BSize(minSize.width, 80);
66 virtual void Pulse()
68 DownloadProgressView::SpeedVersusEstimatedFinishTogglePulse();
71 protected:
72 virtual void DoLayout()
74 BGroupView::DoLayout();
75 if (BScrollBar* scrollBar = ScrollBar(B_VERTICAL)) {
76 BSize minSize = BGroupView::MinSize();
77 float height = Bounds().Height();
78 float max = minSize.height - height;
79 scrollBar->SetRange(0, max);
80 if (minSize.height > 0)
81 scrollBar->SetProportion(height / minSize.height);
82 else
83 scrollBar->SetProportion(1);
89 class DownloadContainerScrollView : public BScrollView {
90 public:
91 DownloadContainerScrollView(BView* target)
93 BScrollView("Downloads scroll view", target, 0, false, true,
94 B_NO_BORDER)
98 protected:
99 virtual void DoLayout()
101 BScrollView::DoLayout();
102 // Tweak scroll bar layout to hide part of the frame for better looks.
103 BScrollBar* scrollBar = ScrollBar(B_VERTICAL);
104 scrollBar->MoveBy(1, -1);
105 scrollBar->ResizeBy(0, 2);
106 Target()->ResizeBy(1, 0);
107 // Set the scroll steps
108 if (BView* item = Target()->ChildAt(0)) {
109 scrollBar->SetSteps(item->MinSize().height + 1,
110 item->MinSize().height + 1);
116 // #pragma mark -
119 DownloadWindow::DownloadWindow(BRect frame, bool visible,
120 SettingsMessage* settings)
121 : BWindow(frame, B_TRANSLATE("Downloads"),
122 B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
123 B_AUTO_UPDATE_SIZE_LIMITS | B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE),
124 fMinimizeOnClose(false)
126 SetPulseRate(1000000);
128 settings->AddListener(BMessenger(this));
129 BPath downloadPath;
130 if (find_directory(B_DESKTOP_DIRECTORY, &downloadPath) != B_OK)
131 downloadPath.SetTo("/boot/home/Desktop");
132 fDownloadPath = settings->GetValue(kSettingsKeyDownloadPath,
133 downloadPath.Path());
134 settings->SetValue(kSettingsKeyDownloadPath, fDownloadPath);
136 SetLayout(new BGroupLayout(B_VERTICAL, 0.0));
138 DownloadsContainerView* downloadsGroupView = new DownloadsContainerView();
139 fDownloadViewsLayout = downloadsGroupView->GroupLayout();
141 BMenuBar* menuBar = new BMenuBar("Menu bar");
142 BMenu* menu = new BMenu(B_TRANSLATE("Downloads"));
143 menu->AddItem(new BMenuItem(B_TRANSLATE("Open downloads folder"),
144 new BMessage(OPEN_DOWNLOADS_FOLDER)));
145 BMessage* newWindowMessage = new BMessage(NEW_WINDOW);
146 newWindowMessage->AddString("url", "");
147 BMenuItem* newWindowItem = new BMenuItem(B_TRANSLATE("New browser window"),
148 newWindowMessage, 'N');
149 menu->AddItem(newWindowItem);
150 newWindowItem->SetTarget(be_app);
151 menu->AddSeparatorItem();
152 menu->AddItem(new BMenuItem(B_TRANSLATE("Close"),
153 new BMessage(B_QUIT_REQUESTED), 'D'));
154 menuBar->AddItem(menu);
156 fDownloadsScrollView = new DownloadContainerScrollView(downloadsGroupView);
158 fRemoveFinishedButton = new BButton(B_TRANSLATE("Remove finished"),
159 new BMessage(REMOVE_FINISHED_DOWNLOADS));
160 fRemoveFinishedButton->SetEnabled(false);
162 fRemoveMissingButton = new BButton(B_TRANSLATE("Remove missing"),
163 new BMessage(REMOVE_MISSING_DOWNLOADS));
164 fRemoveMissingButton->SetEnabled(false);
166 const float spacing = be_control_look->DefaultItemSpacing();
168 AddChild(BGroupLayoutBuilder(B_VERTICAL, 0.0)
169 .Add(menuBar)
170 .Add(fDownloadsScrollView)
171 .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER))
172 .Add(BGroupLayoutBuilder(B_HORIZONTAL, spacing)
173 .AddGlue()
174 .Add(fRemoveMissingButton)
175 .Add(fRemoveFinishedButton)
176 .SetInsets(12, 5, 12, 5)
180 PostMessage(INIT);
182 if (!visible)
183 Hide();
184 Show();
185 MoveOnScreen(B_MOVE_IF_PARTIALLY_OFFSCREEN);
189 DownloadWindow::~DownloadWindow()
191 // Only necessary to save the current progress of unfinished downloads:
192 _SaveSettings();
196 void
197 DownloadWindow::DispatchMessage(BMessage* message, BHandler* target)
199 // We need to intercept mouse down events inside the area of download
200 // progress views (regardless of whether they have children at the click),
201 // so that they may display a context menu.
202 BPoint where;
203 int32 buttons;
204 if (message->what == B_MOUSE_DOWN
205 && message->FindPoint("screen_where", &where) == B_OK
206 && message->FindInt32("buttons", &buttons) == B_OK
207 && (buttons & B_SECONDARY_MOUSE_BUTTON) != 0) {
208 for (int32 i = fDownloadViewsLayout->CountItems() - 1;
209 BLayoutItem* item = fDownloadViewsLayout->ItemAt(i); i--) {
210 DownloadProgressView* view = dynamic_cast<DownloadProgressView*>(
211 item->View());
212 if (!view)
213 continue;
214 BPoint viewWhere(where);
215 view->ConvertFromScreen(&viewWhere);
216 if (view->Bounds().Contains(viewWhere)) {
217 view->ShowContextMenu(where);
218 return;
222 BWindow::DispatchMessage(message, target);
226 void
227 DownloadWindow::MessageReceived(BMessage* message)
229 switch (message->what) {
230 case INIT:
232 _LoadSettings();
233 // Small trick to get the correct enabled status of the Remove
234 // finished button
235 _DownloadFinished(NULL);
236 break;
238 case B_DOWNLOAD_ADDED:
240 BWebDownload* download;
241 if (message->FindPointer("download", reinterpret_cast<void**>(
242 &download)) == B_OK) {
243 _DownloadStarted(download);
245 break;
247 case B_DOWNLOAD_REMOVED:
249 BWebDownload* download;
250 if (message->FindPointer("download", reinterpret_cast<void**>(
251 &download)) == B_OK) {
252 _DownloadFinished(download);
254 break;
256 case OPEN_DOWNLOADS_FOLDER:
258 entry_ref ref;
259 status_t status = get_ref_for_path(fDownloadPath.String(), &ref);
260 if (status == B_OK)
261 status = be_roster->Launch(&ref);
262 if (status != B_OK && status != B_ALREADY_RUNNING) {
263 BString errorString(B_TRANSLATE_COMMENT("The downloads folder could "
264 "not be opened.\n\nError: %error", "Don't translate "
265 "variable %error"));
266 errorString.ReplaceFirst("%error", strerror(status));
267 BAlert* alert = new BAlert(B_TRANSLATE("Error opening downloads "
268 "folder"), errorString.String(), B_TRANSLATE("OK"));
269 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
270 alert->Go(NULL);
272 break;
274 case REMOVE_FINISHED_DOWNLOADS:
275 _RemoveFinishedDownloads();
276 break;
277 case REMOVE_MISSING_DOWNLOADS:
278 _RemoveMissingDownloads();
279 break;
280 case SAVE_SETTINGS:
281 _ValidateButtonStatus();
282 _SaveSettings();
283 break;
285 case SETTINGS_VALUE_CHANGED:
287 BString string;
288 if (message->FindString("name", &string) == B_OK
289 && string == kSettingsKeyDownloadPath
290 && message->FindString("value", &string) == B_OK) {
291 fDownloadPath = string;
293 break;
295 default:
296 BWindow::MessageReceived(message);
297 break;
302 bool
303 DownloadWindow::QuitRequested()
305 if (fMinimizeOnClose) {
306 if (!IsMinimized())
307 Minimize(true);
308 } else {
309 if (!IsHidden())
310 Hide();
312 return false;
316 bool
317 DownloadWindow::DownloadsInProgress()
319 bool downloadsInProgress = false;
320 if (!Lock())
321 return downloadsInProgress;
323 for (int32 i = fDownloadViewsLayout->CountItems() - 1;
324 BLayoutItem* item = fDownloadViewsLayout->ItemAt(i); i--) {
325 DownloadProgressView* view = dynamic_cast<DownloadProgressView*>(
326 item->View());
327 if (!view)
328 continue;
329 if (view->Download() != NULL) {
330 downloadsInProgress = true;
331 break;
335 Unlock();
337 return downloadsInProgress;
341 void
342 DownloadWindow::SetMinimizeOnClose(bool minimize)
344 if (Lock()) {
345 fMinimizeOnClose = minimize;
346 Unlock();
351 // #pragma mark - private
354 void
355 DownloadWindow::_DownloadStarted(BWebDownload* download)
357 download->Start(BPath(fDownloadPath.String()));
359 int32 finishedCount = 0;
360 int32 missingCount = 0;
361 int32 index = 0;
362 for (int32 i = fDownloadViewsLayout->CountItems() - 1;
363 BLayoutItem* item = fDownloadViewsLayout->ItemAt(i); i--) {
364 DownloadProgressView* view = dynamic_cast<DownloadProgressView*>(
365 item->View());
366 if (!view)
367 continue;
368 if (view->URL() == download->URL()) {
369 index = i;
370 view->RemoveSelf();
371 delete view;
372 continue;
374 if (view->IsFinished())
375 finishedCount++;
376 if (view->IsMissing())
377 missingCount++;
379 fRemoveFinishedButton->SetEnabled(finishedCount > 0);
380 fRemoveMissingButton->SetEnabled(missingCount > 0);
381 DownloadProgressView* view = new DownloadProgressView(download);
382 if (!view->Init()) {
383 delete view;
384 return;
386 fDownloadViewsLayout->AddView(index, view);
388 // Scroll new download into view
389 if (BScrollBar* scrollBar = fDownloadsScrollView->ScrollBar(B_VERTICAL)) {
390 float min;
391 float max;
392 scrollBar->GetRange(&min, &max);
393 float viewHeight = view->MinSize().height + 1;
394 float scrollOffset = min + index * viewHeight;
395 float scrollBarHeight = scrollBar->Bounds().Height() - 1;
396 float value = scrollBar->Value();
397 if (scrollOffset < value)
398 scrollBar->SetValue(scrollOffset);
399 else if (scrollOffset + viewHeight > value + scrollBarHeight) {
400 float diff = scrollOffset + viewHeight - (value + scrollBarHeight);
401 scrollBar->SetValue(value + diff);
405 _SaveSettings();
407 SetWorkspaces(B_CURRENT_WORKSPACE);
408 if (IsHidden())
409 Show();
411 Activate(true);
415 void
416 DownloadWindow::_DownloadFinished(BWebDownload* download)
418 int32 finishedCount = 0;
419 int32 missingCount = 0;
420 for (int32 i = 0;
421 BLayoutItem* item = fDownloadViewsLayout->ItemAt(i); i++) {
422 DownloadProgressView* view = dynamic_cast<DownloadProgressView*>(
423 item->View());
424 if (!view)
425 continue;
426 if (download && view->Download() == download) {
427 view->DownloadFinished();
428 finishedCount++;
429 continue;
431 if (view->IsFinished())
432 finishedCount++;
433 if (view->IsMissing())
434 missingCount++;
436 fRemoveFinishedButton->SetEnabled(finishedCount > 0);
437 fRemoveMissingButton->SetEnabled(missingCount > 0);
438 if (download)
439 _SaveSettings();
443 void
444 DownloadWindow::_RemoveFinishedDownloads()
446 int32 missingCount = 0;
447 for (int32 i = fDownloadViewsLayout->CountItems() - 1;
448 BLayoutItem* item = fDownloadViewsLayout->ItemAt(i); i--) {
449 DownloadProgressView* view = dynamic_cast<DownloadProgressView*>(
450 item->View());
451 if (!view)
452 continue;
453 if (view->IsFinished()) {
454 view->RemoveSelf();
455 delete view;
456 } else if (view->IsMissing())
457 missingCount++;
459 fRemoveFinishedButton->SetEnabled(false);
460 fRemoveMissingButton->SetEnabled(missingCount > 0);
461 _SaveSettings();
465 void
466 DownloadWindow::_RemoveMissingDownloads()
468 int32 finishedCount = 0;
469 for (int32 i = fDownloadViewsLayout->CountItems() - 1;
470 BLayoutItem* item = fDownloadViewsLayout->ItemAt(i); i--) {
471 DownloadProgressView* view = dynamic_cast<DownloadProgressView*>(
472 item->View());
473 if (!view)
474 continue;
475 if (view->IsMissing()) {
476 view->RemoveSelf();
477 delete view;
478 } else if (view->IsFinished())
479 finishedCount++;
481 fRemoveMissingButton->SetEnabled(false);
482 fRemoveFinishedButton->SetEnabled(finishedCount > 0);
483 _SaveSettings();
487 void
488 DownloadWindow::_ValidateButtonStatus()
490 int32 finishedCount = 0;
491 int32 missingCount = 0;
492 for (int32 i = fDownloadViewsLayout->CountItems() - 1;
493 BLayoutItem* item = fDownloadViewsLayout->ItemAt(i); i--) {
494 DownloadProgressView* view = dynamic_cast<DownloadProgressView*>(
495 item->View());
496 if (!view)
497 continue;
498 if (view->IsFinished())
499 finishedCount++;
500 if (view->IsMissing())
501 missingCount++;
503 fRemoveFinishedButton->SetEnabled(finishedCount > 0);
504 fRemoveMissingButton->SetEnabled(missingCount > 0);
508 void
509 DownloadWindow::_SaveSettings()
511 BFile file;
512 if (!_OpenSettingsFile(file, B_ERASE_FILE | B_CREATE_FILE | B_WRITE_ONLY))
513 return;
514 BMessage message;
515 for (int32 i = fDownloadViewsLayout->CountItems() - 1;
516 BLayoutItem* item = fDownloadViewsLayout->ItemAt(i); i--) {
517 DownloadProgressView* view = dynamic_cast<DownloadProgressView*>(
518 item->View());
519 if (!view)
520 continue;
522 BMessage downloadArchive;
523 if (view->SaveSettings(&downloadArchive) == B_OK)
524 message.AddMessage("download", &downloadArchive);
526 message.Flatten(&file);
530 void
531 DownloadWindow::_LoadSettings()
533 BFile file;
534 if (!_OpenSettingsFile(file, B_READ_ONLY))
535 return;
536 BMessage message;
537 if (message.Unflatten(&file) != B_OK)
538 return;
539 BMessage downloadArchive;
540 for (int32 i = 0;
541 message.FindMessage("download", i, &downloadArchive) == B_OK;
542 i++) {
543 DownloadProgressView* view = new DownloadProgressView(
544 &downloadArchive);
545 if (!view->Init(&downloadArchive))
546 continue;
547 fDownloadViewsLayout->AddView(0, view);
552 bool
553 DownloadWindow::_OpenSettingsFile(BFile& file, uint32 mode)
555 BPath path;
556 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK
557 || path.Append(kApplicationName) != B_OK
558 || path.Append("Downloads") != B_OK) {
559 return false;
561 return file.SetTo(path.Path(), mode) == B_OK;