Changeset 1505 for trunk/beos


Ignore:
Timestamp:
Feb 22, 2007, 12:49:34 PM (15 years ago)
Author:
bvarner
Message:

Brought BeOS GUI up to date with latest libtransmission. There's still some optimization that could be performed here, but the GUI is working again, and better than before thanks to the internal changes on libtransmission.

Location:
trunk/beos
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/beos/TRApplication.cpp

    r261 r1505  
    220220        }
    221221       
    222         be_app_messenger.SendMessage(refs);
     222        be_app_messenger.SendMessage(&refs);
    223223}
    224224
  • trunk/beos/TRApplication.h

    r261 r1505  
    2828                virtual void ReadyToRun();
    2929                virtual void RefsReceived(BMessage *message);
     30                virtual void ArgvReceived(int32 _argc, char** _argv);
    3031                virtual bool QuitRequested();
    3132
  • trunk/beos/TRInfoWindow.cpp

    r261 r1505  
    1212
    1313
    14 TRInfoWindow::TRInfoWindow(tr_stat_t status) : BWindow(BRect(0, 0, 250, 175), "Info",
     14TRInfoWindow::TRInfoWindow(tr_stat_t *status, tr_info_t *info, char *folder) : BWindow(BRect(0, 0, 250, 175), "Info",
    1515                          B_FLOATING_WINDOW, B_ASYNCHRONOUS_CONTROLS | /*B_NOT_RESIZABLE*/  B_NOT_ZOOMABLE,
    1616                          B_CURRENT_WORKSPACE)
     
    2323        font_height fh;
    2424        headerFont.GetHeight(&fh);
    25         if (headerFont.StringWidth(status.info.name) > Bounds().Width() - 10) {
    26                 ResizeBy(headerFont.StringWidth(status.info.name) - Bounds().Width() + 10, 0);
     25        if (headerFont.StringWidth(info->name) > Bounds().Width() - 10) {
     26                ResizeBy(headerFont.StringWidth(info->name) - Bounds().Width() + 10, 0);
    2727        }
    2828       
    2929        viewRect = Bounds();
    3030        viewRect.bottom = fh.ascent + fh.descent;
    31         BStringView *strView = new BStringView(viewRect, "header", status.info.name, B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP);
     31        BStringView *strView = new BStringView(viewRect, "header", info->name, B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP);
    3232        strView->SetFont(&headerFont);
    3333        strView->SetAlignment(B_ALIGN_CENTER);
     
    3939        txtView->MakeEditable(false);
    4040       
    41         BString strTracker(status.info.trackerAddress);
    42         strTracker << ":" << status.info.trackerPort;
     41        BString strTracker(status->trackerAddress);
     42        strTracker << ":" << status->trackerPort;
    4343
    4444        BString strPieceSize("");
    45         StringForFileSize(status.info.pieceSize, &strPieceSize);
     45        StringForFileSize(info->pieceSize, &strPieceSize);
    4646
    4747        BString strTotalSize("");
    48         StringForFileSize(status.info.totalSize, &strTotalSize);
     48        StringForFileSize(info->totalSize, &strTotalSize);
    4949
    5050        BString strDownloaded("");
    51         StringForFileSize(status.downloaded, &strDownloaded);
     51        StringForFileSize(status->downloaded, &strDownloaded);
    5252
    5353        BString strUploaded("");
    54         StringForFileSize(status.uploaded, &strUploaded);
     54        StringForFileSize(status->uploaded, &strUploaded);
    5555
    56         BString info("");
    57         info << "Tracker: " << strTracker << "\n"
    58              << "Announce: " << status.info.trackerAnnounce << "\n"
    59              << "Piece Size: " << strPieceSize << "\n"
    60              << "Pieces: " << status.info.pieceCount << "\n"
    61              << "Total Size: " << strTotalSize << "\n"
    62              << "\n"
    63              << "Folder: " << status.folder << "\n"
    64              << "Downloaded: " << strDownloaded << "\n"
    65              << "Uploaded: " << strUploaded << "\n";
    66         txtView->SetText(info.String());
     56        BString infoStr("");
     57        infoStr << "Tracker: " << strTracker << "\n"
     58                << "Announce: " << status->trackerAnnounce << "\n"
     59                << "Piece Size: " << strPieceSize << "\n"
     60                << "Pieces: " << info->pieceCount << "\n"
     61                << "Total Size: " << strTotalSize << "\n"
     62                << "\n"
     63                << "Folder: " << folder << "\n"
     64                << "Downloaded: " << strDownloaded << "\n"
     65                << "Uploaded: " << strUploaded << "\n";
     66        txtView->SetText(infoStr.String());
    6767       
    6868        Lock();
  • trunk/beos/TRInfoWindow.h

    r261 r1505  
    1212class TRInfoWindow : public BWindow {
    1313public:
    14         TRInfoWindow(tr_stat_t status);
     14        TRInfoWindow(tr_stat_t *status, tr_info_t *info, char *folder);
    1515        ~TRInfoWindow();
    1616       
  • trunk/beos/TRTransfer.cpp

    r261 r1505  
    1111 * BListItem that renders Transfer status.
    1212 */
    13 TRTransfer::TRTransfer(const char *fullpath, node_ref node) : BListItem(0, false), cachedNodeRef(node) {
     13TRTransfer::TRTransfer(const char *fullpath, node_ref node, tr_torrent_t *torrentRef) : BListItem(0, false), cachedNodeRef(node) {
    1414        fBaselineOffset = 0.0f;
    1515        fLineSpacing = 0.0f;
     16        torrent = torrentRef;
    1617       
    1718        cachedPath = new BString(fullpath);
     
    1920       
    2021        fStatus = (tr_stat_t*)calloc(1, sizeof(tr_stat_t));
     22        tr_info_t *info = tr_torrentInfo(torrent);
     23        fName = new BString("<unknown name>");
     24        fName->SetTo(info->name);
    2125       
    2226        fBarColor.red = 50;
     
    3741        }
    3842        delete cachedPath;
     43        delete fName;
    3944}
    4045
     
    6570 * local fStatus requires a successful Lock on fStatusLock.
    6671 */
    67 bool TRTransfer::SetStatus(tr_stat_t *stat, bool shade) {
     72bool TRTransfer::UpdateStatus(tr_stat_t *stat, bool shade) {
    6873        bool dirty = false;
    6974        if (fStatusLock->Lock()) {
     
    8287}
    8388
     89bool TRTransfer::IsRunning() {
     90        return (fStatus != NULL &&
     91                        (fStatus->status &
     92                                  (TR_STATUS_CHECK | TR_STATUS_DOWNLOAD | TR_STATUS_SEED)));
     93}
     94
    8495
    8596/**
     
    111122       
    112123        if (fStatus != NULL && fStatusLock->Lock()) {
    113                 owner->DrawString(fStatus->info.name, textLoc);
     124                owner->DrawString(fName->String(), textLoc);
    114125               
    115126                if (fStatus->status & TR_STATUS_PAUSE ) {
  • trunk/beos/TRTransfer.h

    r261 r1505  
    1414class TRTransfer : public BListItem {
    1515public: // Construction and Controll methods.
    16         TRTransfer(const char *fullpath, node_ref node);
     16        TRTransfer(const char *fullpath, node_ref node, tr_torrent_t *torrentRef);
    1717        ~TRTransfer();
    1818
    1919        inline node_ref GetCachedNodeRef() { return cachedNodeRef; };
    2020        inline const char* GetCachedPath() { return cachedPath->String(); };
     21        inline tr_torrent_t* GetTorrent()  { return torrent; };
    2122       
    22         bool SetStatus(tr_stat_t *stat, bool shade);
     23        bool UpdateStatus(tr_stat_t *stat, bool shade);
     24        bool IsRunning();
    2325       
    2426public: // BListItem
     
    2729
    2830private:
    29         /*
    30          * Cached data. The items stored here are _NOT_ necessairly
    31          * the torrent we'll be rendering. It's likely they will be,
    32          * but NOT guaranteed. They are not used for anything relating
    33          * to rendering.
    34          *
    35          * Specifically we needed a way to cache the node_ref and
    36          * reverse-lookup the node from the string path in the
    37          * transmission structs. This seemed the logical place to store
    38          * that information, since it ends up in a BList(View).
    39          */
    4031        node_ref cachedNodeRef;
    4132        BString *cachedPath;
     33        tr_torrent_t *torrent;
    4234
    4335private: // Private members used for rendering.
     
    4739        BLocker *fStatusLock;
    4840        tr_stat_t *fStatus;
     41        BString *fName;
    4942       
    5043        rgb_color fBarColor;
  • trunk/beos/TRWindow.cpp

    r261 r1505  
    2121#include "TRInfoWindow.h"
    2222
     23BListView *TRWindow::transfers = NULL;
     24
    2325/**
    2426 * The Transmission Window! Yay!
     
    7375        // Setup the transfers ListView
    7476        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        TRWindow::transfers = new BListView(viewRect, "TorrentList", B_SINGLE_SELECTION_LIST, B_FOLLOW_ALL);
     78        TRWindow::transfers->SetSelectionMessage(new BMessage(TR_SELECT));
    7779        AddChild(new BScrollView("TransferScroller", transfers, B_FOLLOW_ALL, 0, false, true));
    7880       
     
    113115                        prefs.SetInt32("transmission.uploadLimit", uploadLimit);
    114116                }
    115                 tr_setUploadLimit(engine, (int)uploadLimit);
     117                tr_setGlobalUploadLimit(engine, (int)uploadLimit);
    116118        }
    117119}
     
    158160                       
    159161                        // 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));
     162                        int error;
     163                        tr_torrent_t *nTorrent = tr_torrentInit(engine, path.Path(), 0, &error);
     164                        if (nTorrent != NULL && Lock()) { // Success. Add the TRTorrent item.
     165                                transfers->AddItem(new TRTransfer(path.Path(), node, nTorrent));
    163166                               
    164167                                bool autoStart = true;
     
    178181                                        worker_info *startData = (worker_info*)calloc(1, sizeof(worker_info));
    179182                                        startData->window = this;
    180                                         startData->index = tr_torrentCount(engine) - 1;
     183                                        startData->torrent = nTorrent;
    181184                                        thread_id start_thread = spawn_thread(TRWindow::AsynchStartTorrent, "BirthCanal",
    182185                                                                              B_NORMAL_PRIORITY, (void *)startData);
     
    184187                                                resume_thread(start_thread);
    185188                                        } else { // Fallback and start the old way.
    186                                                 StartTorrent(startData->index);
     189                                                StartTorrent(startData->torrent);
    187190                                                free(startData);
    188191                                        }
     
    251254                                 * Totally decoupled, due to the way transmission is written.
    252255                                 */
    253                                 char path[B_FILE_NAME_LENGTH];
     256                                remove_info *removeData = (remove_info*)calloc(1, sizeof(remove_info));
     257                                removeData->window = this;
    254258                                TRTransfer* item;
    255259                                for (int32 i = 0; i < transfers->CountItems(); i++) {
    256260                                        item = (TRTransfer*)transfers->ItemAt(i);
    257261                                        if (item->GetCachedNodeRef() == node) {
    258                                                 strcpy(path, item->GetCachedPath());
     262                                                strcpy(removeData->path, item->GetCachedPath());
    259263                                        }
    260264                                }
    261265                               
    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);
     266                                transfers->DoForEach(TRWindow::RemovePath, (void *)removeData);
     267                               
     268                                free(removeData);
    275269                        }
    276270                }
    277271        } else if (msg->what == TR_INFO) {
    278272                // Display an Info Window.
    279                 tr_stat_t *s;
    280                 tr_torrentStat(engine, &s);
    281                
    282                 TRInfoWindow *info = new TRInfoWindow(s[transfers->CurrentSelection()]);
     273                TRTransfer *transfer = dynamic_cast<TRTransfer*>(transfers->ItemAt(transfers->CurrentSelection()));
     274                tr_stat_t *s = tr_torrentStat(transfer->GetTorrent());
     275                tr_info_t *i = tr_torrentInfo(transfer->GetTorrent());
     276               
     277                TRInfoWindow *info = new TRInfoWindow(s, i, tr_torrentGetFolder(transfer->GetTorrent()));
    283278                info->MoveTo(Frame().LeftTop() + BPoint(20, 25));
    284279                info->Show();
     
    291286                worker_info *startData = (worker_info*)calloc(1, sizeof(worker_info));
    292287                startData->window = this;
    293                 startData->index = (int)transfers->CurrentSelection();
     288                startData->torrent = (dynamic_cast<TRTransfer*>(transfers->ItemAt(transfers->CurrentSelection())))->GetTorrent();
    294289                thread_id start_thread = spawn_thread(TRWindow::AsynchStartTorrent, "BirthCanal",
    295290                                                      B_NORMAL_PRIORITY, (void *)startData);
     
    297292                        resume_thread(start_thread);
    298293                } else { // Fallback and start the old way.
    299                         StartTorrent(startData->index);
     294                        StartTorrent(startData->torrent);
    300295                        free(startData);
    301296                }
     
    303298                worker_info *stopData = (worker_info*)calloc(1, sizeof(worker_info));
    304299                stopData->window = this;
    305                 stopData->index = (int)transfers->CurrentSelection();
     300                stopData->torrent = (dynamic_cast<TRTransfer*>(transfers->ItemAt(transfers->CurrentSelection())))->GetTorrent();
    306301                thread_id stop_thread = spawn_thread(TRWindow::AsynchStopTorrent, "InUtero",
    307302                                                     B_NORMAL_PRIORITY, (void *)stopData);
     
    309304                        resume_thread(stop_thread);
    310305                } else { // Fallback and stop it the old way.
    311                         StopTorrent(stopData->index);
     306                        StopTorrent(stopData->torrent);
    312307                        free(stopData);
    313308                }
     
    315310                int32 index = transfers->CurrentSelection();
    316311               
    317                 tr_torrentClose(engine, (int)index);
    318                
    319312                // Remove the file from the filesystem.
    320313                TRTransfer *item = (TRTransfer*)transfers->RemoveItem(index);
     314                tr_torrentClose(engine, item->GetTorrent());
    321315                BEntry *entry = new BEntry(item->GetCachedPath(), true);
    322316                entry->Remove();
     
    342336        bool quit = false;
    343337       
    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) {
     338        quit_info *quitData = (quit_info*)calloc(1, sizeof(quit_info));
     339        quitData->running = 0;
     340        transfers->DoForEach(TRWindow::CheckQuitStatus, (void *)quitData);
     341       
     342        if (quitData->running > 0) {
    354343                BString quitMsg("");
    355                 quitMsg << "There's " << max << " torrent";
    356                 if (max > 1) {
     344                quitMsg << "There's " << quitData->running << " torrent";
     345                if (quitData->running > 1) {
    357346                        quitMsg << "s";
    358347                }
     
    367356                quit = true;
    368357        }
     358        free(quitData);
    369359       
    370360        if (quit) {
     
    373363               
    374364                BString strItem("");
    375                 for (int i = 0; i < tr_torrentStat(engine, &s); i++) {
     365                for (int i = 0; i < transfers->CountItems(); i++) {
    376366                        strItem = "download.";
    377                         strItem << s[i].info.torrent << ".running";
    378                         if (s[i].status & (TR_STATUS_CHECK | TR_STATUS_DOWNLOAD | TR_STATUS_SEED)) {
     367                        tr_torrent_t *torrent = (dynamic_cast<TRTransfer*>(transfers->ItemAt(i)))->GetTorrent();
     368                        tr_info_t *info = tr_torrentInfo(torrent);
     369                        tr_stat_t *stat = tr_torrentStat(torrent);
     370                       
     371                        strItem << info->torrent << ".running";
     372                        if (stat->status & (TR_STATUS_CHECK | TR_STATUS_DOWNLOAD | TR_STATUS_SEED)) {
    379373                                prefs->SetBool(strItem.String(), true);
    380                                 tr_torrentStop(engine, i);
     374                                tr_torrentStop(torrent);
    381375                        } else {
    382376                                prefs->SetBool(strItem.String(), false);
    383377                        }
    384378                }
    385                 free(s);
    386379                delete prefs;
    387380               
     
    399392 * Called from the StopTorrent thread.
    400393 */
    401 void TRWindow::StopTorrent(int index) {
    402         tr_torrentStop(engine, index);
    403        
    404         UpdateList(index, true);
     394void TRWindow::StopTorrent(tr_torrent_t *torrent) {
     395        tr_torrentStop(torrent);
     396       
     397        UpdateList(transfers->CurrentSelection(), true);
    405398}
    406399
     
    408401 * Called from StartTorrent thread.
    409402 */
    410 void TRWindow::StartTorrent(int index) {
     403void TRWindow::StartTorrent(tr_torrent_t *torrent) {
    411404        // Read the settings.
    412405        BString folder("");
     
    416409                folder << "/boot/home/Downloads";
    417410        }
    418         tr_torrentSetFolder(engine, index, folder.String());
    419         tr_torrentStart(engine, index);
     411        tr_torrentSetFolder(torrent, folder.String());
     412        tr_torrentStart(torrent);
    420413       
    421414        if (transfers->CurrentSelection() >= 0) {
    422                 UpdateList(index, true);
     415                UpdateList(transfers->CurrentSelection(), true);
    423416        }
    424417       
     
    432425 */
    433426void 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);
     427        update_info *upData = (update_info*)calloc(1, sizeof(update_info));
     428        upData->running = false;
     429        upData->selected = selection;
     430        upData->invalid = 0;
     431       
     432        transfers->DoForEach(TRWindow::UpdateStats, (void *)upData);
    450433       
    451434        if (menus) {
    452435                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 }
     436                KeyMenuBar()->FindItem(TR_RESUME)->SetEnabled(selection >= 0 && !upData->running);
     437                KeyMenuBar()->FindItem(TR_PAUSE)->SetEnabled(selection >= 0 && upData->running);
     438                KeyMenuBar()->FindItem(TR_REMOVE)->SetEnabled(selection >= 0 && !upData->running);
     439        }
     440       
     441        if (upData->invalid > 0 && transfers->LockLooper()) {
     442                transfers->Invalidate();
     443                transfers->UnlockLooper();
     444        }
     445       
     446        free(upData);
     447}
     448
     449
    467450
    468451/**
     
    472455int32 TRWindow::AsynchStopTorrent(void *data) {
    473456        worker_info* stopData = (worker_info*)data;
    474         stopData->window->StopTorrent(stopData->index);
     457        stopData->window->StopTorrent(stopData->torrent);
    475458        free(stopData);
    476459        return B_OK;
     
    483466int32 TRWindow::AsynchStartTorrent(void *data) {
    484467        worker_info* startData = (worker_info*)data;
    485         startData->window->StartTorrent(startData->index);
     468        startData->window->StartTorrent(startData->torrent);
    486469        free(startData);
    487470        return B_OK;
    488471}
     472
     473/**
     474 * Invoked by DoForEach upon the transfers list. This will
     475 * remove the item that is caching the path specified by
     476 * path.
     477 */
     478bool TRWindow::RemovePath(BListItem *item, void *data) {
     479        remove_info* removeData = (remove_info*)data;
     480        TRTransfer *transfer = dynamic_cast<TRTransfer*>(item);
     481       
     482        if (strcmp(transfer->GetCachedPath(), removeData->path) == 0) {
     483                removeData->window->transfers->RemoveItem(transfer);
     484                return true;
     485        }
     486        return false;
     487}
     488
     489/**
     490 * Invoked during QuitRequested, iterates all Transfers and
     491 * checks to see how many are running.
     492 */
     493bool TRWindow::CheckQuitStatus(BListItem *item, void *data) {
     494        quit_info* quitData = (quit_info*)data;
     495        TRTransfer *transfer = dynamic_cast<TRTransfer*>(item);
     496       
     497        if (transfer->IsRunning()) {
     498                quitData->running++;
     499        }
     500        return false;
     501}
     502
     503/**
     504 * Invoked during UpdateList()
     505 */
     506bool TRWindow::UpdateStats(BListItem *item, void *data) {
     507        update_info* upData = (update_info*)data;
     508        TRTransfer *transfer = dynamic_cast<TRTransfer*>(item);
     509       
     510        int32 index = transfers->IndexOf(transfer);
     511        if (transfer->UpdateStatus(tr_torrentStat(transfer->GetTorrent()), index % 2 == 0)) {
     512                upData->invalid++;
     513        }
     514       
     515        if (index == upData->selected) {
     516                upData->running = transfer->IsRunning();
     517        }
     518       
     519        return false;
     520}
  • trunk/beos/TRWindow.h

    r261 r1505  
    77#include <FilePanel.h>
    88#include <ListView.h>
     9#include <ListItem.h>
    910#include <Window.h>
    1011
     
    4243        void LoadSettings();
    4344       
    44         void StopTorrent(int index);
    45         void StartTorrent(int index);
     45        void StopTorrent(tr_torrent_t *torrent);
     46        void StartTorrent(tr_torrent_t *torrent);
    4647       
    4748        static int32 AsynchStopTorrent(void *data);
     
    5152
    5253private:
    53         BListView *transfers;
     54        static BListView *transfers;
    5455        BFilePanel *openPanel;
    5556       
     
    5758       
    5859        TRPrefsWindow *fSettings;
     60       
     61        static bool RemovePath(BListItem *item, void *data);
     62        static bool CheckQuitStatus(BListItem *item, void *data);
     63        static bool UpdateStats(BListItem *item, void *data);
    5964};
    6065
     
    6368 */
    6469struct worker_info {
     70        TRWindow     *window;
     71        tr_torrent_t *torrent;
     72};
     73
     74struct remove_info {
    6575        TRWindow *window;
    66         int      index;
     76        char     path[B_FILE_NAME_LENGTH];
     77};
     78
     79struct quit_info {
     80        TRWindow *window;
     81        int       running;
     82};
     83
     84struct update_info {
     85        TRWindow *window;
     86        bool     running;
     87        int      selected;
     88        int      invalid;
    6789};
    6890
Note: See TracChangeset for help on using the changeset viewer.