source: trunk/beos/TRWindow.cpp @ 1125

Last change on this file since 1125 was 261, checked in by titer, 16 years ago

Updated svn:keywords

  • Property svn:keywords set to Date Rev Author Id
File size: 13.7 KB
Line 
1// $Id: TRWindow.cpp 261 2006-05-29 21:27:31Z livings124 $
2
3#include "TRWindow.h"
4
5#include <stdio.h>
6
7#include <Alert.h>
8#include <Application.h>
9#include <File.h>
10#include <MenuBar.h>
11#include <MenuItem.h>
12#include <NodeMonitor.h>
13#include <ScrollView.h>
14#include <String.h>
15
16#include <malloc.h>
17
18#include "Prefs.h"
19#include "TRApplication.h"
20#include "TRTransfer.h"
21#include "TRInfoWindow.h"
22
23/**
24 * The Transmission Window! Yay!
25 */
26TRWindow::TRWindow() : BWindow(BRect(10, 40, 350, 110), "Transmission", B_TITLED_WINDOW,
27                               B_ASYNCHRONOUS_CONTROLS , B_CURRENT_WORKSPACE)
28{
29        engine = NULL;
30        Prefs prefs(TRANSMISSION_SETTINGS);
31       
32        BRect *rectFrame = new BRect();
33        if (prefs.FindRect("window.frame", rectFrame) == B_OK) {
34                MoveTo(rectFrame->LeftTop());
35                ResizeTo(rectFrame->Width(), rectFrame->Height());
36        } else {
37                rectFrame->Set(10, 40, 350, 110);
38        }
39        Lock();
40       
41        BRect viewRect(0, 0, rectFrame->Width(), rectFrame->Height());
42       
43        BMenuBar *menubar = new BMenuBar(viewRect, "MenuBar");
44        BMenu *menu = new BMenu("File");
45        menu->AddItem(new BMenuItem("Open", new BMessage(TR_OPEN), 'O', B_COMMAND_KEY));
46        menu->FindItem(TR_OPEN)->SetTarget(be_app_messenger); // send OPEN to the be_app.
47        menu->AddSeparatorItem();
48        menu->AddItem(new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED), 'Q', B_COMMAND_KEY));
49        menubar->AddItem(menu);
50       
51        menu = new BMenu("Torrent");
52        menu->AddItem(new BMenuItem("Get Info", new BMessage(TR_INFO), 'I', B_COMMAND_KEY));
53        menu->FindItem(TR_INFO)->SetEnabled(false);
54        menu->AddSeparatorItem();
55        menu->AddItem(new BMenuItem("Resume", new BMessage(TR_RESUME)));
56        menu->AddItem(new BMenuItem("Pause", new BMessage(TR_PAUSE)));
57        menu->AddItem(new BMenuItem("Remove", new BMessage(TR_REMOVE)));
58        menubar->AddItem(menu);
59       
60        menu = new BMenu("Tools");
61        menu->AddItem(new BMenuItem("Settings", new BMessage(TR_SETTINGS)));
62        menu->FindItem(TR_SETTINGS)->SetTarget(be_app_messenger);
63        menu->AddSeparatorItem();
64        menu->AddItem(new BMenuItem("About Transmission", new BMessage(B_ABOUT_REQUESTED)));
65        menu->FindItem(B_ABOUT_REQUESTED)->SetTarget(be_app_messenger);
66        menubar->AddItem(menu);
67       
68        AddChild(menubar);
69        SetKeyMenuBar(menubar);
70       
71        // TODO: Tool Bar? (Well after everything is working based on Menus)
72       
73        // Setup the transfers ListView
74        viewRect.Set(2, menubar->Frame().bottom + 3, rectFrame->Width() - 2 - B_V_SCROLL_BAR_WIDTH, rectFrame->Height() - 2);
75        transfers = new BListView(viewRect, "TorrentList", B_SINGLE_SELECTION_LIST, B_FOLLOW_ALL);
76        transfers->SetSelectionMessage(new BMessage(TR_SELECT));
77        AddChild(new BScrollView("TransferScroller", transfers, B_FOLLOW_ALL, 0, false, true));
78       
79        Unlock();
80        delete rectFrame;
81       
82        // Bring up the Transmission Engine
83        engine = tr_init();
84        LoadSettings();
85       
86        UpdateList(-1, true);
87       
88        // Start the message loop without showing the window.
89        Hide();
90        Show();
91}
92
93TRWindow::~TRWindow() {
94        tr_close(engine);
95        stop_watching(this);
96}
97
98
99void TRWindow::LoadSettings() {
100        if (engine != NULL) {
101                Prefs prefs(TRANSMISSION_SETTINGS);
102               
103                int32 bindPort;
104                if (prefs.FindInt32("transmission.bindPort", &bindPort) != B_OK) {
105                        bindPort = 9000;
106                        prefs.SetInt32("transmission.bindPort", bindPort);
107                }
108                tr_setBindPort(engine, (int)bindPort);
109               
110                int32 uploadLimit;
111                if (prefs.FindInt32("transmission.uploadLimit", &uploadLimit) != B_OK) {
112                        uploadLimit = 20;
113                        prefs.SetInt32("transmission.uploadLimit", uploadLimit);
114                }
115                tr_setUploadLimit(engine, (int)uploadLimit);
116        }
117}
118
119
120/**
121 * Rescans the active Torrents folder, and will add all the torrents there to the
122 * engine. Called during initial Application Start & Stop.
123 */
124void TRWindow::RescanTorrents() {
125        if (Lock()) {
126                TRApplication *app = dynamic_cast<TRApplication*>(be_app);
127                BEntry *torrentEntry = new BEntry();
128                status_t err;
129               
130                if (app->TorrentDir()->InitCheck() == B_OK) {
131                        err = app->TorrentDir()->Rewind();
132                        while (err == B_OK) {
133                                err = app->TorrentDir()->GetNextEntry(torrentEntry, true);
134                                if (err != B_ENTRY_NOT_FOUND) {
135                                        AddEntry(torrentEntry);
136                                }
137                        }
138                }
139                delete torrentEntry;
140                Unlock();
141        }
142}
143
144
145/**
146 * Adds the file specified by *torrent to the Transmission engine.
147 * Then adds a new TRTransfer item in the transfers list.
148 * This item holds cached information about the torrent entry and node.
149 * These TRTransmission items are _NOT_ guaranteed to render the entry
150 * they were created from.
151 */
152void TRWindow::AddEntry(BEntry *torrent) {
153        node_ref node;
154        if (torrent->GetNodeRef(&node) == B_OK) {
155                if (watch_node(&node, B_WATCH_NAME, this) == B_OK) {
156                        BPath path;
157                        torrent->GetPath(&path);
158                       
159                        // Try adding the torrent to the engine.
160                        int addStatus = tr_torrentInit(engine, path.Path());
161                        if (addStatus == 0 && Lock()) { // Success. Add the TRTorrent item.
162                                transfers->AddItem(new TRTransfer(path.Path(), node));
163                               
164                                bool autoStart = true;
165                               
166                                // Decide if we should auto-start this torrent or not.
167                                BString prefName("download.");
168                                prefName << path.Path() << ".running";
169                               
170                                Prefs *prefs = new Prefs(TRANSMISSION_SETTINGS);
171                                if (prefs->FindBool(prefName.String(), &autoStart) != B_OK) {
172                                        autoStart = true;
173                                }
174                                delete prefs;
175                               
176                                if (autoStart) {
177                                        // Start the newly added torrent.
178                                        worker_info *startData = (worker_info*)calloc(1, sizeof(worker_info));
179                                        startData->window = this;
180                                        startData->index = tr_torrentCount(engine) - 1;
181                                        thread_id start_thread = spawn_thread(TRWindow::AsynchStartTorrent, "BirthCanal",
182                                                                              B_NORMAL_PRIORITY, (void *)startData);
183                                        if (!((start_thread) < B_OK)) {
184                                                resume_thread(start_thread);
185                                        } else { // Fallback and start the old way.
186                                                StartTorrent(startData->index);
187                                                free(startData);
188                                        }
189                                }
190                                Unlock();
191                        } else {
192                                bool duplicate = false;
193                                TRTransfer* tr;
194                                for (int32 i = 0; i < transfers->CountItems(); i++) {
195                                        tr = (TRTransfer*)transfers->ItemAt(i);
196                                        if (tr->GetCachedNodeRef() == node) {
197                                                duplicate = true;
198                                        }
199                                }
200                                if (!duplicate) {
201                                        BString errmsg("An error occurred trying to read ");
202                                        char namebuf[B_FILE_NAME_LENGTH];
203                                        torrent->GetName(namebuf);
204                                        errmsg << namebuf;
205                                        errmsg << ".";
206                                       
207                                        BAlert *error = new BAlert("Error Opening Torrent", 
208                                               errmsg.String(), 
209                                               "Ok", NULL, NULL, 
210                                               B_WIDTH_AS_USUAL, B_WARNING_ALERT);
211                                        error->Go();
212                                        torrent->Remove();
213                                }
214                        }
215                }
216        }
217}
218
219
220void TRWindow::MessageReceived(BMessage *msg) {
221        /*
222         * The only messages we receive from the node_monitor are if we need to
223         * stop watching the node. Basically, if it's been moved or removed we stop.
224         */
225        if (msg->what == B_NODE_MONITOR) {
226                node_ref node;
227                ino_t fromDir;
228                ino_t toDir;
229                int32 opcode;
230               
231                if ((msg->FindInt32("opcode", &opcode) == B_OK) &&
232                        (msg->FindInt64("node", &node.node) == B_OK) &&
233                    (msg->FindInt32("device", &node.device) == B_OK))
234                {
235                        bool stop = (opcode == B_ENTRY_REMOVED);
236                       
237                        if (stop) {
238                                msg->FindInt64("directory", &toDir);
239                        } else { // It must have moved.
240                                stop = ((msg->FindInt64("from directory", &fromDir) == B_OK) &&
241                                        (msg->FindInt64("to directory", &toDir) == B_OK) &&
242                                        (toDir != fromDir));
243                        }
244                       
245                        if (stop) {
246                                watch_node(&node, B_STOP_WATCHING, this);
247                               
248                                /* Find the full path from the TRTorrents.
249                                 * The index of the TRTorrent that is caching the information
250                                 * IS NOT the index of the torrent in the engine. These are
251                                 * Totally decoupled, due to the way transmission is written.
252                                 */
253                                char path[B_FILE_NAME_LENGTH];
254                                TRTransfer* item;
255                                for (int32 i = 0; i < transfers->CountItems(); i++) {
256                                        item = (TRTransfer*)transfers->ItemAt(i);
257                                        if (item->GetCachedNodeRef() == node) {
258                                                strcpy(path, item->GetCachedPath());
259                                        }
260                                }
261                               
262                                // Look for the torrent info in the engine with the matching
263                                // path name.
264                                tr_stat_t *stats;
265                                int max = tr_torrentStat(engine, &stats);
266                                int index;
267                                for (index = 0; index < max; index++) {
268                                        if (strcmp(stats[index].info.torrent, path) == 0) {
269                                                tr_torrentClose(engine, index);
270                                                transfers->RemoveItem(index);
271                                                break;
272                                        }
273                                }
274                                free(stats);
275                        }
276                }
277        } else if (msg->what == TR_INFO) {
278                // Display an Info Window.
279                tr_stat_t *s;
280                tr_torrentStat(engine, &s);
281               
282                TRInfoWindow *info = new TRInfoWindow(s[transfers->CurrentSelection()]);
283                info->MoveTo(Frame().LeftTop() + BPoint(20, 25));
284                info->Show();
285        } else if (msg->what == TR_SELECT) {
286                // Setup the Torrent Menu enabled / disabled state.
287                int32 selection;
288                msg->FindInt32("index", &selection);
289                UpdateList(selection, true);
290        } else if (msg->what == TR_RESUME) {
291                worker_info *startData = (worker_info*)calloc(1, sizeof(worker_info));
292                startData->window = this;
293                startData->index = (int)transfers->CurrentSelection();
294                thread_id start_thread = spawn_thread(TRWindow::AsynchStartTorrent, "BirthCanal",
295                                                      B_NORMAL_PRIORITY, (void *)startData);
296                if (!((start_thread) < B_OK)) {
297                        resume_thread(start_thread);
298                } else { // Fallback and start the old way.
299                        StartTorrent(startData->index);
300                        free(startData);
301                }
302        } else if (msg->what == TR_PAUSE) {
303                worker_info *stopData = (worker_info*)calloc(1, sizeof(worker_info));
304                stopData->window = this;
305                stopData->index = (int)transfers->CurrentSelection();
306                thread_id stop_thread = spawn_thread(TRWindow::AsynchStopTorrent, "InUtero",
307                                                     B_NORMAL_PRIORITY, (void *)stopData);
308                if (!((stop_thread) < B_OK)) {
309                        resume_thread(stop_thread);
310                } else { // Fallback and stop it the old way.
311                        StopTorrent(stopData->index);
312                        free(stopData);
313                }
314        } else if (msg->what == TR_REMOVE) {
315                int32 index = transfers->CurrentSelection();
316               
317                tr_torrentClose(engine, (int)index);
318               
319                // Remove the file from the filesystem.
320                TRTransfer *item = (TRTransfer*)transfers->RemoveItem(index);
321                BEntry *entry = new BEntry(item->GetCachedPath(), true);
322                entry->Remove();
323                delete entry;
324                delete item;
325               
326               
327               
328                UpdateList(transfers->CurrentSelection(), true);
329        } else if (msg->what == B_SIMPLE_DATA) {
330                be_app->RefsReceived(msg);
331        }
332               
333        BWindow::MessageReceived(msg);
334}
335
336/**
337 * Handles QuitRequests.
338 * Displays a BAlert asking if the user really wants to quit if torrents are running.
339 * If affimative, then we'll stop all the running torrents.
340 */
341bool TRWindow::QuitRequested() {
342        bool quit = false;
343       
344        bool running = false;
345        tr_stat_t *s;
346        int max = tr_torrentStat(engine, &s);
347        for (int i = 0; i < max && !running; i++) {
348                running = (s[i].status &
349                          (TR_STATUS_CHECK | TR_STATUS_DOWNLOAD | TR_STATUS_SEED));
350        }
351        free(s);
352       
353        if (running) {
354                BString quitMsg("");
355                quitMsg << "There's " << max << " torrent";
356                if (max > 1) {
357                        quitMsg << "s";
358                }
359                quitMsg << " currently running.\n"
360                        << "What would you like to do?";
361               
362                BAlert *confirmQuit = new BAlert("Confirm Quit", quitMsg.String(),
363                                                 "Cancel", "Quit", NULL,
364                                                 B_WIDTH_AS_USUAL, B_WARNING_ALERT);
365                quit = (confirmQuit->Go() == 1);
366        } else {
367                quit = true;
368        }
369       
370        if (quit) {
371                Prefs *prefs = new Prefs(TRANSMISSION_SETTINGS);
372                prefs->SetRect("window.frame", Frame());
373               
374                BString strItem("");
375                for (int i = 0; i < tr_torrentStat(engine, &s); i++) {
376                        strItem = "download.";
377                        strItem << s[i].info.torrent << ".running";
378                        if (s[i].status & (TR_STATUS_CHECK | TR_STATUS_DOWNLOAD | TR_STATUS_SEED)) {
379                                prefs->SetBool(strItem.String(), true);
380                                tr_torrentStop(engine, i);
381                        } else {
382                                prefs->SetBool(strItem.String(), false);
383                        }
384                }
385                free(s);
386                delete prefs;
387               
388                be_app->PostMessage(new BMessage(B_QUIT_REQUESTED));
389        }
390        return quit;
391}
392
393void TRWindow::FrameResized(float width, float height) {
394        transfers->Invalidate();
395}
396
397
398/**
399 * Called from the StopTorrent thread.
400 */
401void TRWindow::StopTorrent(int index) {
402        tr_torrentStop(engine, index);
403       
404        UpdateList(index, true);
405}
406
407/**
408 * Called from StartTorrent thread.
409 */
410void TRWindow::StartTorrent(int index) {
411        // Read the settings.
412        BString folder("");
413        Prefs *prefs = new Prefs(TRANSMISSION_SETTINGS);
414        if (prefs->FindString("download.folder", &folder) != B_OK) {
415                prefs->SetString("download.folder", "/boot/home/Downloads");
416                folder << "/boot/home/Downloads";
417        }
418        tr_torrentSetFolder(engine, index, folder.String());
419        tr_torrentStart(engine, index);
420       
421        if (transfers->CurrentSelection() >= 0) {
422                UpdateList(index, true);
423        }
424       
425        delete prefs;
426}
427
428/**
429 * Called from the be_app Pulse();
430 * This will update the data structures that the TRTorrents use to render,
431 * and invalidate the view.
432 */
433void TRWindow::UpdateList(int32 selection = -1, bool menus = true) {
434        bool running = false;
435       
436        tr_stat_t * s;
437        int i = 0;
438        int max = tr_torrentStat(engine, &s);
439        bool invalid[max];
440       
441        for (i = 0; i < max; i++) {
442                invalid[i] = ((TRTransfer*)transfers->ItemAt(i))->SetStatus(&(s[i]), (i % 2 != 0));
443               
444                if (menus && i == (int)selection) {
445                        running = (s[selection].status & 
446                                  (TR_STATUS_CHECK | TR_STATUS_DOWNLOAD | TR_STATUS_SEED));
447                }
448        }
449        free(s);
450       
451        if (menus) {
452                KeyMenuBar()->FindItem(TR_INFO)->SetEnabled(selection >= 0);
453                KeyMenuBar()->FindItem(TR_RESUME)->SetEnabled(selection >= 0 && !running);
454                KeyMenuBar()->FindItem(TR_PAUSE)->SetEnabled(selection >= 0 && running);
455                KeyMenuBar()->FindItem(TR_REMOVE)->SetEnabled(selection >= 0 && !running);
456        }
457       
458        if (Lock()) {
459                for (i = 0; i < max; i++) {
460                        if (invalid[i]) {
461                                transfers->InvalidateItem(i);
462                        }
463                }
464                Unlock();
465        }
466}
467
468/**
469 * Thread Function to stop Torrents. This can be expensive and causes the event loop to
470 * choke.
471 */
472int32 TRWindow::AsynchStopTorrent(void *data) {
473        worker_info* stopData = (worker_info*)data;
474        stopData->window->StopTorrent(stopData->index);
475        free(stopData);
476        return B_OK;
477}
478
479/**
480 * Thread Function to start Torrents. This can be expensive and causes the event loop to
481 * choke.
482 */
483int32 TRWindow::AsynchStartTorrent(void *data) {
484        worker_info* startData = (worker_info*)data;
485        startData->window->StartTorrent(startData->index);
486        free(startData);
487        return B_OK;
488}
Note: See TracBrowser for help on using the repository browser.