Changeset 2149


Ignore:
Timestamp:
Jun 18, 2007, 3:40:41 AM (14 years ago)
Author:
livings124
Message:

Merge file selection and torrent creation into the main branch.

The new code for these features is under a new license.

Location:
trunk
Files:
83 edited
17 copied

Legend:

Unmodified
Added
Removed
  • trunk/LICENSE

    r1217 r2149  
    1 The Transmission binaries and source code are distributed under the MIT
    2 license.
     1The Transmission binaries and most of its source code is
     2distributed under the MIT license.
     3
     4Some files are copyrighted by Charles Kerr and are covered by
     5the GPL version 2.  Works owned by the Transmission project
     6are granted a special exemption to clause 2(b) so that the bulk
     7of its code can remain under the MIT license.  This exemption does
     8not extend to original or derived works not owned by the
     9Transmission project.
     10
     11The remainder of this text pertains only to the MIT-licensed
     12portions of Transmission.
    313
    414-----
  • trunk/NEWS

    r1821 r2149  
    11NEWS file for Transmission <http://transmission.m0k.org/>
     2
     30.80 ()
     4 - Ability to selectively download and prioritize files
     5 - Torrent file creation
     6 - Partial licensing change -- see the LICENSE file for details
     7 - Fix to UPnP
     8 - Rechecking torrents is now done one torrent at a time
     9   to avoid hammering the disk
     10 - Many miscellaneous bugfixes and small improvements
     11 - OS X:
     12   + Overlay when dragging torrent files onto window
     13   + Ability to set an amount of time to considered a transfer stalled
     14   + Various smaller interface changes and improvements
     15 - GTK:
     16   + Interface significantly reworked to closer match the Mac version
     17   + Torrent inspector
     18   + Other minor improvements
    219
    3200.72 (2007/04/30)
  • trunk/Transmission.xcodeproj/project.pbxproj

    r1994 r2149  
    88
    99/* Begin PBXBuildFile section */
     10                002C9EE60C19CD2500C2F6F4 /* fastresume.c in Sources */ = {isa = PBXBuildFile; fileRef = 002C9EE50C19CD2500C2F6F4 /* fastresume.c */; };
    1011                35B038130AC5B6EB00A10FDF /* ResumeNoWaitOn.png in Resources */ = {isa = PBXBuildFile; fileRef = 35B037F90AC5B53800A10FDF /* ResumeNoWaitOn.png */; };
    1112                35B038140AC5B6EC00A10FDF /* ResumeNoWaitOff.png in Resources */ = {isa = PBXBuildFile; fileRef = 35B037FA0AC5B53800A10FDF /* ResumeNoWaitOff.png */; };
     
    105106                A2AA579D0ADFCAB400CA59F6 /* PiecesImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = A2AA579B0ADFCAB400CA59F6 /* PiecesImageView.m */; };
    106107                A2AF1C390A3D0F6200F1575D /* FileOutlineView.m in Sources */ = {isa = PBXBuildFile; fileRef = A2AF1C370A3D0F6200F1575D /* FileOutlineView.m */; };
     108                A2BE9C520C1E4AF5002D16E6 /* makemeta.c in Sources */ = {isa = PBXBuildFile; fileRef = A2BE9C4E0C1E4ADA002D16E6 /* makemeta.c */; };
     109                A2BE9C530C1E4AF7002D16E6 /* makemeta.h in Headers */ = {isa = PBXBuildFile; fileRef = A2BE9C4F0C1E4ADA002D16E6 /* makemeta.h */; };
    107110                A2BF07910B066E0800757C92 /* SpeedLimitToTurtleIconTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = A2BF078F0B066E0800757C92 /* SpeedLimitToTurtleIconTransformer.m */; };
    108111                A2C655650A04FEDC00E9FD82 /* BottomBorder.png in Resources */ = {isa = PBXBuildFile; fileRef = A2C655640A04FEDC00E9FD82 /* BottomBorder.png */; };
     
    110113                A2D4F0830A915F6600890C32 /* RedDot.tiff in Resources */ = {isa = PBXBuildFile; fileRef = A2D4F0820A915F6600890C32 /* RedDot.tiff */; };
    111114                A2D4F0850A915F7200890C32 /* GreenDot.tiff in Resources */ = {isa = PBXBuildFile; fileRef = A2D4F0840A915F7200890C32 /* GreenDot.tiff */; };
     115                A2DF37060C220D03006523C1 /* CreatorWindowController.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = A2DF37040C220D03006523C1 /* CreatorWindowController.h */; };
     116                A2DF37070C220D03006523C1 /* CreatorWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = A2DF37050C220D03006523C1 /* CreatorWindowController.m */; };
     117                A2DF377C0C222E2D006523C1 /* Creator.nib in Resources */ = {isa = PBXBuildFile; fileRef = A2DF377A0C222E2D006523C1 /* Creator.nib */; };
     118                A2E9AA760C249AF400085DCF /* Create.png in Resources */ = {isa = PBXBuildFile; fileRef = A2E9AA750C249AF400085DCF /* Create.png */; };
    112119                A2F40AE40A361C00006B8288 /* Transmission.icns in Resources */ = {isa = PBXBuildFile; fileRef = 4D2784360905709500687951 /* Transmission.icns */; };
    113120                A2F6DB090A55F31C0058D1E5 /* SpeedLimitButton.png in Resources */ = {isa = PBXBuildFile; fileRef = A2F6DB070A55F31C0058D1E5 /* SpeedLimitButton.png */; };
     
    243250                                A261F1E40A69A1B10002815A /* Growl.framework in CopyFiles */,
    244251                                A24F19210A3A796800C9C145 /* Sparkle.framework in CopyFiles */,
     252                                A2DF37060C220D03006523C1 /* CreatorWindowController.h in CopyFiles */,
    245253                        );
    246254                        runOnlyForDeploymentPostprocessing = 0;
     
    249257
    250258/* Begin PBXFileReference section */
     259                002C9EE50C19CD2500C2F6F4 /* fastresume.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = fastresume.c; path = libtransmission/fastresume.c; sourceTree = "<group>"; };
    251260                089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = macosx/English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
    252261                1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
     
    304313                4DFBC2DE09C0970D00D5C571 /* Torrent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Torrent.m; path = macosx/Torrent.m; sourceTree = "<group>"; };
    305314                8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; name = Info.plist; path = macosx/Info.plist; sourceTree = "<group>"; };
    306                 8D1107320486CEB800E47090 /* Transmission.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Transmission.app; sourceTree = BUILT_PRODUCTS_DIR; };
     315                8D1107320486CEB800E47090 /* Transmission.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = Transmission.app; sourceTree = BUILT_PRODUCTS_DIR; };
    307316                A200B8390A2263BA007BBB1E /* InfoWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = InfoWindowController.h; path = macosx/InfoWindowController.h; sourceTree = "<group>"; };
    308317                A200B83A0A2263BA007BBB1E /* InfoWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = InfoWindowController.m; path = macosx/InfoWindowController.m; sourceTree = "<group>"; };
     
    407416                A2AF1C360A3D0F6200F1575D /* FileOutlineView.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = FileOutlineView.h; path = macosx/FileOutlineView.h; sourceTree = "<group>"; };
    408417                A2AF1C370A3D0F6200F1575D /* FileOutlineView.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = FileOutlineView.m; path = macosx/FileOutlineView.m; sourceTree = "<group>"; };
     418                A2BE9C4E0C1E4ADA002D16E6 /* makemeta.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = makemeta.c; path = libtransmission/makemeta.c; sourceTree = "<group>"; };
     419                A2BE9C4F0C1E4ADA002D16E6 /* makemeta.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = makemeta.h; path = libtransmission/makemeta.h; sourceTree = "<group>"; };
    409420                A2BF078E0B066E0800757C92 /* SpeedLimitToTurtleIconTransformer.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = SpeedLimitToTurtleIconTransformer.h; path = macosx/SpeedLimitToTurtleIconTransformer.h; sourceTree = "<group>"; };
    410421                A2BF078F0B066E0800757C92 /* SpeedLimitToTurtleIconTransformer.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = SpeedLimitToTurtleIconTransformer.m; path = macosx/SpeedLimitToTurtleIconTransformer.m; sourceTree = "<group>"; };
     
    413424                A2D4F0820A915F6600890C32 /* RedDot.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = RedDot.tiff; path = macosx/Images/RedDot.tiff; sourceTree = "<group>"; };
    414425                A2D4F0840A915F7200890C32 /* GreenDot.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = GreenDot.tiff; path = macosx/Images/GreenDot.tiff; sourceTree = "<group>"; };
     426                A2DF37040C220D03006523C1 /* CreatorWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CreatorWindowController.h; path = macosx/CreatorWindowController.h; sourceTree = "<group>"; };
     427                A2DF37050C220D03006523C1 /* CreatorWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CreatorWindowController.m; path = macosx/CreatorWindowController.m; sourceTree = "<group>"; };
     428                A2DF377B0C222E2D006523C1 /* Creator.nib */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = Creator.nib; path = macosx/English.lproj/Creator.nib; sourceTree = "<group>"; };
     429                A2E9AA750C249AF400085DCF /* Create.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Create.png; path = macosx/Images/Create.png; sourceTree = "<group>"; };
    415430                A2F6DB070A55F31C0058D1E5 /* SpeedLimitButton.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = SpeedLimitButton.png; path = macosx/Images/SpeedLimitButton.png; sourceTree = "<group>"; };
    416431                A2F8951E0A2D4BA500ED2127 /* Credits.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = Credits.rtf; path = macosx/Credits.rtf; sourceTree = "<group>"; };
     
    592607                                A256588A0A9A695400E8A03B /* MessageWindowController.h */,
    593608                                A256588B0A9A695400E8A03B /* MessageWindowController.m */,
     609                                A2DF37040C220D03006523C1 /* CreatorWindowController.h */,
     610                                A2DF37050C220D03006523C1 /* CreatorWindowController.m */,
    594611                                E1B6FBF80C0D719B0015FE4D /* Info Window */,
    595612                                E1B6FBFD0C0D72430015FE4D /* Prefs Window */,
     
    647664                                4DF7500808A103AD007B0D70 /* Info.png */,
    648665                                4DF7500708A103AD007B0D70 /* Open.png */,
     666                                A2E9AA750C249AF400085DCF /* Create.png */,
    649667                                4DF7500908A103AD007B0D70 /* Remove.png */,
    650668                                35B037F90AC5B53800A10FDF /* ResumeNoWaitOn.png */,
     
    671689                                A200B9620A227FD0007BBB1E /* InfoWindow.nib */,
    672690                                A21567EB0A9A5034004DECD6 /* MessageWindow.nib */,
     691                                A2DF377A0C222E2D006523C1 /* Creator.nib */,
    673692                                A2C655640A04FEDC00E9FD82 /* BottomBorder.png */,
    674693                                A289EB0B0A33C56D00B082A3 /* ButtonBorder.png */,
     
    714733                        isa = PBXGroup;
    715734                        children = (
     735                                A2BE9C4E0C1E4ADA002D16E6 /* makemeta.c */,
     736                                A2BE9C4F0C1E4ADA002D16E6 /* makemeta.h */,
     737                                002C9EE50C19CD2500C2F6F4 /* fastresume.c */,
    716738                                A24D2A770C0A65C400A0ED9F /* ipcparse.c */,
    717739                                A24D2A780C0A65C400A0ED9F /* ipcparse.h */,
     
    939961                                BEFC1E5E0C07861A00B0BB3C /* bsdqueue.h in Headers */,
    940962                                BEFC1E5F0C07861A00B0BB3C /* trcompat.h in Headers */,
     963                                A2BE9C530C1E4AF7002D16E6 /* makemeta.h in Headers */,
    941964                        );
    942965                        runOnlyForDeploymentPostprocessing = 0;
     
    11451168                                A24103070AF80E390001C8D0 /* FilterButtonPressedLeft.png in Resources */,
    11461169                                A241528B0C0261B8007DD3B4 /* Globe.tiff in Resources */,
     1170                                A2DF377C0C222E2D006523C1 /* Creator.nib in Resources */,
     1171                                A2E9AA760C249AF400085DCF /* Create.png in Resources */,
    11471172                        );
    11481173                        runOnlyForDeploymentPostprocessing = 0;
     
    12021227                                BEFC1E5A0C07861A00B0BB3C /* choking.c in Sources */,
    12031228                                BEFC1E5D0C07861A00B0BB3C /* bencode.c in Sources */,
     1229                                002C9EE60C19CD2500C2F6F4 /* fastresume.c in Sources */,
     1230                                A2BE9C520C1E4AF5002D16E6 /* makemeta.c in Sources */,
    12041231                        );
    12051232                        runOnlyForDeploymentPostprocessing = 0;
     
    12531280                                A24838820C0BA608005CC3FE /* FilterBarView.m in Sources */,
    12541281                                A215760B0C0D449A0057A26A /* BezierPathAdditions.m in Sources */,
     1282                                A2DF37070C220D03006523C1 /* CreatorWindowController.m in Sources */,
    12551283                        );
    12561284                        runOnlyForDeploymentPostprocessing = 0;
     
    13931421                        sourceTree = "<group>";
    13941422                };
     1423                A2DF377A0C222E2D006523C1 /* Creator.nib */ = {
     1424                        isa = PBXVariantGroup;
     1425                        children = (
     1426                                A2DF377B0C222E2D006523C1 /* Creator.nib */,
     1427                        );
     1428                        name = Creator.nib;
     1429                        sourceTree = "<group>";
     1430                };
    13951431/* End PBXVariantGroup section */
    13961432
     
    13991435                        isa = XCBuildConfiguration;
    14001436                        buildSettings = {
     1437                                ARCHS = (
     1438                                        ppc,
     1439                                        i386,
     1440                                );
    14011441                                CONFIGURATION_BUILD_DIR = "$(SRCROOT)/build/Debug";
    14021442                                FRAMEWORK_SEARCH_PATHS = (
     
    14221462                        isa = XCBuildConfiguration;
    14231463                        buildSettings = {
     1464                                ARCHS = (
     1465                                        ppc,
     1466                                        i386,
     1467                                );
    14241468                                CONFIGURATION_BUILD_DIR = "$(SRCROOT)/build/Debug";
    14251469                                LIBRARY_SEARCH_PATHS = "$(SRCROOT)/build/Debug";
     
    14391483                        isa = XCBuildConfiguration;
    14401484                        buildSettings = {
     1485                                ARCHS = (
     1486                                        ppc,
     1487                                        i386,
     1488                                );
    14411489                                CONFIGURATION_BUILD_DIR = "$(SRCROOT)/macosx";
    14421490                                FRAMEWORK_SEARCH_PATHS = (
     
    14741522                                GCC_WARN_ABOUT_RETURN_TYPE = NO;
    14751523                                GCC_WARN_SIGN_COMPARE = NO;
     1524                                GCC_WARN_UNUSED_FUNCTION = NO;
     1525                                GCC_WARN_UNUSED_LABEL = NO;
     1526                                GCC_WARN_UNUSED_PARAMETER = NO;
     1527                                GCC_WARN_UNUSED_VALUE = NO;
    14761528                                GCC_WARN_UNUSED_VARIABLE = NO;
    14771529                                MACOSX_DEPLOYMENT_TARGET_i386 = 10.4;
     
    14871539                        isa = XCBuildConfiguration;
    14881540                        buildSettings = {
     1541                                ARCHS = (
     1542                                        ppc,
     1543                                        i386,
     1544                                );
    14891545                                CONFIGURATION_BUILD_DIR = "$(SRCROOT)/build/Debug";
    14901546                                COPY_PHASE_STRIP = NO;
     
    15181574                        isa = XCBuildConfiguration;
    15191575                        buildSettings = {
     1576                                ARCHS = (
     1577                                        ppc,
     1578                                        i386,
     1579                                );
    15201580                                CONFIGURATION_BUILD_DIR = "$(SRCROOT)/build/Debug";
    15211581                                COPY_PHASE_STRIP = NO;
     
    15511611                        isa = XCBuildConfiguration;
    15521612                        buildSettings = {
     1613                                ARCHS = (
     1614                                        ppc,
     1615                                        i386,
     1616                                );
    15531617                                CONFIGURATION_BUILD_DIR = "$(SRCROOT)/build/Debug";
    15541618                                COPY_PHASE_STRIP = NO;
  • trunk/beos/TRTransfer.cpp

    r1973 r2149  
    6262 *
    6363 * The view is determined to be dirty if the transfer
    64  * status, progress, eta or the "shade" (even or odd)
     64 * status, percentDone, eta or the "shade" (even or odd)
    6565 * position in the list changes from the previous state.
    6666 * If the tr_stat_t is in fact different then the new, full
     
    7474        if (fStatusLock->Lock()) {
    7575                if (fStatus->status != stat->status ||
    76                     fStatus->progress != stat->progress ||
     76                    fStatus->percentDone != stat->percentDone ||
    7777                    fStatus->eta != stat->eta ||
    7878                    fShade != shade)
     
    125125               
    126126                if (fStatus->status & TR_STATUS_PAUSE ) {
    127                         sprintf(fTimeStr, "Paused (%.2f %%)", 100 * fStatus->progress);
     127                        sprintf(fTimeStr, "Paused (%.2f %%)", 100 * fStatus->percentDone);
    128128                } else if (fStatus->status & TR_STATUS_CHECK_WAIT ) {
    129129                        sprintf(fTimeStr, "Waiting To Check Existing Files (%.2f %%)",
    130                                 100 * fStatus->progress);
     130                                100 * fStatus->percentDone);
    131131                } else if (fStatus->status & TR_STATUS_CHECK ) {
    132132                        sprintf(fTimeStr, "Checking Existing Files (%.2f %%)",
    133                                 100 * fStatus->progress);
     133                                100 * fStatus->percentDone);
    134134                } else if (fStatus->status & TR_STATUS_DOWNLOAD) {
    135135                        if (fStatus->eta < 0 ) {
    136136                                sprintf(fTimeStr, "--:--:-- Remaining (%.2f %%Complete)",
    137                                         100 * fStatus->progress);
     137                                        100 * fStatus->percentDone);
    138138                        } else {
    139139                                sprintf(fTimeStr, "%02d:%02d:%02d Remaining (%.2f %%Complete)",
    140140                                        fStatus->eta / 3600, (fStatus->eta / 60) % 60,
    141                                         fStatus->eta % 60, 100 * fStatus->progress);
     141                                        fStatus->eta % 60, 100 * fStatus->percentDone);
    142142                        }
    143143                } else if (fStatus->status & TR_STATUS_SEED) {
     
    215215                owner->FillRect(rect);
    216216       
    217                 if (fStatus->progress != 0.0f) {
    218                         rect.right = rect.left + (float)ceil(fStatus->progress * (rect.Width() - 4)),
     217                if (fStatus->percentDone != 0.0f) {
     218                        rect.right = rect.left + (float)ceil(fStatus->percentDone * (rect.Width() - 4)),
    219219       
    220220                        // Bevel
  • trunk/cli/transmissioncli.c

    r1973 r2149  
    3030#include <signal.h>
    3131#include <transmission.h>
     32#include <makemeta.h>
    3233#ifdef SYS_BEOS
    3334#include <kernel/OS.h>
     
    3536#endif
    3637
    37 #define USAGE \
    38 "Usage: %s [options] file.torrent [options]\n\n" \
    39 "Options:\n" \
    40 "  -d, --download <int> Maximum download rate (-1 = no limit, default = -1)\n"\
    41 "  -f, --finish <shell script> Command you wish to run on completion\n" \
    42 "  -h, --help           Print this help and exit\n" \
    43 "  -i, --info           Print metainfo and exit\n" \
    44 "  -n  --nat-traversal  Attempt NAT traversal using NAT-PMP or UPnP IGD\n" \
    45 "  -p, --port <int>     Port we should listen on (default = %d)\n" \
    46 "  -s, --scrape         Print counts of seeders/leechers and exit\n" \
    47 "  -u, --upload <int>   Maximum upload rate (-1 = no limit, default = 20)\n" \
    48 "  -v, --verbose <int>  Verbose level (0 to 2, default = 0)\n"
     38/* macro to shut up "unused parameter" warnings */
     39#ifdef __GNUC__
     40#define UNUSED                  __attribute__((unused))
     41#else
     42#define UNUSED
     43#endif
     44
     45const char * USAGE =
     46"Usage: %s [options] file.torrent [options]\n\n"
     47"Options:\n"
     48"  -c, --create-from <file>  Create torrent from the specified source file.\n"
     49"  -a, --announce <url> Used in conjunction with -c.\n"
     50"  -r, --private        Used in conjunction with -c.\n"
     51"  -m, --comment <text> Adds an optional comment when creating a torrent.\n"
     52"  -d, --download <int> Maximum download rate (-1 = no limit, default = -1)\n"
     53"  -f, --finish <shell script> Command you wish to run on completion\n"
     54"  -h, --help           Print this help and exit\n"
     55"  -i, --info           Print metainfo and exit\n"
     56"  -n  --nat-traversal  Attempt NAT traversal using NAT-PMP or UPnP IGD\n"
     57"  -p, --port <int>     Port we should listen on (default = %d)\n"
     58"  -s, --scrape         Print counts of seeders/leechers and exit\n"
     59"  -u, --upload <int>   Maximum upload rate (-1 = no limit, default = 20)\n"
     60"  -v, --verbose <int>  Verbose level (0 to 2, default = 0)\n";
    4961
    5062static int           showHelp      = 0;
    5163static int           showInfo      = 0;
    5264static int           showScrape    = 0;
     65static int           isPrivate     = 0;
    5366static int           verboseLevel  = 0;
    5467static int           bindPort      = TR_DEFAULT_PORT;
     
    6174
    6275static char          * finishCall   = NULL;
     76static char          * announce     = NULL;
     77static char          * sourceFile   = NULL;
     78static char          * comment      = NULL;
    6379
    6480static int  parseCommandLine ( int argc, char ** argv );
     
    123139    /* Initialize libtransmission */
    124140    h = tr_init( "cli" );
     141
     142    if( sourceFile && *sourceFile ) /* creating a torrent */
     143    {
     144        int ret;
     145        tr_metainfo_builder_t* builder = tr_metaInfoBuilderCreate( h, sourceFile );
     146        tr_makeMetaInfo( builder, NULL, announce, comment, isPrivate );
     147        while( !builder->isDone ) {
     148            usleep( 1 );
     149            printf( "." );
     150        }
     151        ret = !builder->failed;
     152        tr_metaInfoBuilderFree( builder );
     153        return ret;
     154    }
    125155
    126156    /* Open and parse torrent file */
     
    224254        {
    225255            chars = snprintf( string, sizeof string,
    226                 "Waiting to check files... %.2f %%", 100.0 * s->progress );
     256                "Waiting to check files... %.2f %%", 100.0 * s->percentDone );
    227257        }
    228258        else if( s->status & TR_STATUS_CHECK )
    229259        {
    230260            chars = snprintf( string, sizeof string,
    231                 "Checking files... %.2f %%", 100.0 * s->progress );
     261                "Checking files... %.2f %%", 100.0 * s->percentDone );
    232262        }
    233263        else if( s->status & TR_STATUS_DOWNLOAD )
     
    235265            chars = snprintf( string, sizeof string,
    236266                "Progress: %.2f %%, %d peer%s, dl from %d (%.2f KB/s), "
    237                 "ul to %d (%.2f KB/s) [%s]", 100.0 * s->progress,
     267                "ul to %d (%.2f KB/s) [%s]", 100.0 * s->percentDone,
    238268                s->peersTotal, ( s->peersTotal == 1 ) ? "" : "s",
    239269                s->peersUploading, s->rateDownload,
     
    268298        }
    269299       
    270         if( tr_getFinished( tor ) )
     300        if( tr_getDone(tor) || tr_getComplete(tor) )
    271301        {
    272302            result = system(finishCall);
     
    303333            { "info",     no_argument,       NULL, 'i' },
    304334            { "scrape",   no_argument,       NULL, 's' },
     335            { "private",  no_argument,       NULL, 'r' },
    305336            { "verbose",  required_argument, NULL, 'v' },
    306337            { "port",     required_argument, NULL, 'p' },
     
    308339            { "download", required_argument, NULL, 'd' },
    309340            { "finish",   required_argument, NULL, 'f' },
     341            { "create",   required_argument, NULL, 'c' },
     342            { "comment",  required_argument, NULL, 'm' },
     343            { "announce", required_argument, NULL, 'a' },
    310344            { "nat-traversal", no_argument,  NULL, 'n' },
    311345            { 0, 0, 0, 0} };
    312346
    313347        int c, optind = 0;
    314         c = getopt_long( argc, argv, "hisv:p:u:d:f:n", long_options, &optind );
     348        c = getopt_long( argc, argv, "hisrv:p:u:d:f:c:m:a:n:",
     349                         long_options, &optind );
    315350        if( c < 0 )
    316351        {
     
    328363                showScrape = 1;
    329364                break;
     365            case 'r':
     366                isPrivate = 1;
     367                break;
    330368            case 'v':
    331369                verboseLevel = atoi( optarg );
     
    342380            case 'f':
    343381                finishCall = optarg;
     382                break;
     383            case 'c':
     384                sourceFile = optarg;
     385                break;
     386            case 'm':
     387                comment = optarg;
     388                break;
     389            case 'a':
     390                announce = optarg;
    344391                break;
    345392            case 'n':
  • trunk/daemon/server.c

    r2038 r2149  
    755755{
    756756    tr_stat_t * st;
    757     tr_info_t * inf;
    758757
    759758    st = torrent_stat( id );
     
    762761        return 0;
    763762    }
    764     inf = torrent_info( id );
    765     assert( NULL != inf );
    766 
    767     return ipc_addstat( list, id, inf, st, types );
     763
     764    return ipc_addstat( list, id, st, types );
    768765}
    769766
  • trunk/gtk/hig.c

    r2001 r2149  
    1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
    21/*
    3  * Pan - A Newsreader for Gtk+
    4  * Copyright (C) 2002  Charles Kerr <charles@rebelbase.com>
     2 * This file Copyright (C) 2007 Charles Kerr <charles@rebelbase.com>
    53 *
    6  * This program is free software; you can redistribute it and/or modify
    7  * it under the terms of the GNU General Public License as published by
    8  * the Free Software Foundation; version 2 of the License.
    9  *
    10  * This program is distributed in the hope that it will be useful,
    11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  * GNU General Public License for more details.
    14  *
    15  * You should have received a copy of the GNU General Public License
    16  * along with this program; if not, write to the Free Software
    17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     4 * This file is licensed by the GPL version 2.  Works owned by the
     5 * Transmission project are granted a special exemption to clause 2(b)
     6 * so that the bulk of its code can remain under the MIT license.
     7 * This exemption does not extend to derived works not owned by
     8 * the Transmission project.
    189 */
    1910
     
    10394{
    10495  if (GTK_IS_MISC(l))
    105     gtk_misc_set_alignment (GTK_MISC(l), 0.0f, 0.0f);
     96    gtk_misc_set_alignment (GTK_MISC(l), 0.0f, 0.5f);
    10697  if (GTK_IS_LABEL(l))
    10798    gtk_label_set_use_markup (GTK_LABEL(l), TRUE);
     
    167158  gtk_table_attach_defaults (GTK_TABLE(table), w, 0, 4, *row, *row+1);
    168159}
     160
     161void
     162hig_message_dialog_set_text (GtkMessageDialog * dialog,
     163                             const char       * primary,
     164                             const char       * secondary)
     165{
     166#if GTK_CHECK_VERSION(2,6,0)
     167  gtk_message_dialog_set_markup (dialog, primary);
     168  gtk_message_dialog_format_secondary_text (dialog, "%s", secondary);
     169#else
     170  char * pch = g_strdup_printf ("<b>%s</b>\n \n%s", primary, secondary);
     171  gtk_message_dialog_set_markup (dialog, pch);
     172  g_free (pch);
     173#endif
     174}
  • trunk/gtk/hig.h

    r1998 r2149  
    1 /******************************************************************************
    2  * $Id:$
     1/*
     2 * This file Copyright (C) 2007 Charles Kerr <charles@rebelbase.com>
    33 *
    4  * Copyright (c) 2007 Transmission authors and contributors
    5  *
    6  * Permission is hereby granted, free of charge, to any person obtaining a
    7  * copy of this software and associated documentation files (the "Software"),
    8  * to deal in the Software without restriction, including without limitation
    9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
    10  * and/or sell copies of the Software, and to permit persons to whom the
    11  * Software is furnished to do so, subject to the following conditions:
    12  *
    13  * The above copyright notice and this permission notice shall be included in
    14  * all copies or substantial portions of the Software.
    15  *
    16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    22  * DEALINGS IN THE SOFTWARE.
    23  *****************************************************************************/
     4 * This file is licensed by the GPL version 2.  Works owned by the
     5 * Transmission project are granted a special exemption to clause 2(b)
     6 * so that the bulk of its code can remain under the MIT license.
     7 * This exemption does not extend to derived works not owned by
     8 * the Transmission project.
     9 */
    2410
    2511#ifndef __HIG_H__
     
    9379                     int         * row);
    9480
     81void
     82hig_message_dialog_set_text (GtkMessageDialog * dialog,
     83                             const char       * primary,
     84                             const char       * secondary);
     85
     86
    9587/**
    9688***
  • trunk/gtk/ipc.c

    r2042 r2149  
    789789         benc_val_t * val )
    790790{
    791     tr_info_t * inf;
    792     tr_stat_t * st;
    793 
    794     inf = tr_torrent_info( tor );
    795791    if( IPC_MSG_INFO == msgid )
    796792    {
     793        tr_info_t * inf = tr_torrent_info( tor );
    797794        return ipc_addinfo( val, torid, inf, types );
    798795    }
    799796    else
    800797    {
    801         st = tr_torrent_stat( tor );
    802         return ipc_addstat( val, torid, inf, st, types );
     798        tr_stat_t * st = tr_torrent_stat( tor );
     799        return ipc_addstat( val, torid, st, types );
    803800    }
    804801}
  • trunk/gtk/main.c

    r2007 r2149  
    3737#include <glib/gstdio.h>
    3838
     39#include "actions.h"
    3940#include "conf.h"
    4041#include "dialogs.h"
    4142#include "ipc.h"
     43#include "makemeta-ui.h"
    4244#include "msgwin.h"
    4345#include "torrent-inspector.h"
     
    4951#include "tr_window.h"
    5052#include "util.h"
     53#include "ui.h"
    5154
    5255#include "transmission.h"
    5356#include "version.h"
    54 
    55 #include "img_icon_full.h"
    5657
    5758/* time in seconds to wait for torrents to stop when exiting */
     
    7071#define SHOW_LICENSE
    7172static const char * LICENSE =
    72 "The Transmission binaries and source code are distributed under the MIT "
     73"The Transmission binaries and most of its source code is distributed "
    7374"license. "
     75"\n\n"
     76"Some files are copyrighted by Charles Kerr and are covered by "
     77"the GPL version 2.  Works owned by the Transmission project "
     78"are granted a special exemption to clause 2(b) so that the bulk "
     79"of its code can remain under the MIT license.  This exemption does "
     80"not extend to original or derived works not owned by the "
     81"Transmission project. "
    7482"\n\n"
    7583"Permission is hereby granted, free of charge, to any person obtaining "
     
    96104    GtkWindow    * wind;
    97105    TrCore       * core;
    98     TrIcon       * icon;
     106    GtkWidget    * icon;
    99107    TrPrefs      * prefs;
    100108    guint          timer;
     
    110118};
    111119
    112 enum
    113 {
    114     ACT_OPEN = 0,
    115     ACT_START,
    116     ACT_STOP,
    117     ACT_DELETE,
    118     ACT_SEPARATOR1,
    119     ACT_INFO,
    120     ACT_DEBUG,
    121     ACT_ABOUT,
    122     ACT_SEPARATOR2,
    123     ACT_PREF,
    124     ACT_SEPARATOR3,
    125     ACT_CLOSE,
    126     ACT_QUIT,
    127     ACT_ICON,
    128     ACTION_COUNT,
    129 };
    130 
    131 #if !GTK_CHECK_VERSION(2,8,0)
    132 #define GTK_STOCK_INFO NULL
    133 #endif
    134 
    135 struct
    136 {
    137     const char * label;
    138     const char * icon;
    139     guint        key;
    140     int          flags;
    141     const char * tooltip;
    142 }
    143 actions[] =
    144 {
    145     { NULL,        GTK_STOCK_ADD,       'o', ACTF_WHEREVER | ACTF_ALWAYS,
    146       N_("Add a new torrent") },
    147     { N_("Start"), GTK_STOCK_EXECUTE,     0, ACTF_WHEREVER | ACTF_INACTIVE,
    148       N_("Start a torrent that is not running") },
    149     { NULL,        GTK_STOCK_STOP,        0, ACTF_WHEREVER | ACTF_ACTIVE,
    150       N_("Stop a torrent that is running") },
    151     { NULL,        GTK_STOCK_REMOVE,      0, ACTF_WHEREVER | ACTF_WHATEVER,
    152       N_("Remove a torrent") },
    153     { NULL,        NULL,                  0, ACTF_SEPARATOR, NULL },
    154     { NULL,        GTK_STOCK_PROPERTIES,  0, ACTF_WHEREVER | ACTF_WHATEVER,
    155       N_("Show additional information about a torrent") },
    156     { N_("Open debug window"), GTK_STOCK_INFO,      0, ACTF_MENU     | ACTF_ALWAYS,
    157       NULL },
    158     { NULL,        GTK_STOCK_ABOUT,      0, ACTF_MENU | ACTF_ALWAYS,
    159       N_("About Transmission") },
    160     { NULL,        NULL,                  0, ACTF_SEPARATOR, NULL },
    161     { NULL,        GTK_STOCK_PREFERENCES, 0, ACTF_WHEREVER | ACTF_ALWAYS,
    162       N_("Customize application behavior") },
    163     { NULL,        NULL,                  0, ACTF_SEPARATOR, NULL },
    164     { NULL,        GTK_STOCK_CLOSE,       0, ACTF_MENU     | ACTF_ALWAYS,
    165       N_("Close the main window") },
    166     { NULL,        GTK_STOCK_QUIT,        0, ACTF_MENU     | ACTF_ALWAYS,
    167       N_("Exit the program") },
    168     /* this isn't a terminator for the list, it's ACT_ICON */
    169     { NULL,        NULL,                  0, 0, NULL },
    170 };
    171 
    172120#define CBDATA_PTR              "callback-data-pointer"
     121
     122static GtkUIManager * myUIManager = NULL;
    173123
    174124static sig_atomic_t global_sigcount = 0;
     
    179129sendremote( GList * files, gboolean sendquit );
    180130static void
    181 gtksetup( int * argc, char *** argv );
    182 static void
    183 appsetup( TrWindow * wind, benc_val_t * state, GList * args, gboolean paused );
     131gtksetup( int * argc, char *** argv, struct cbdata* );
     132static void
     133appsetup( TrWindow * wind, benc_val_t * state, GList * args,
     134          struct cbdata * , gboolean paused );
    184135static void
    185136winsetup( struct cbdata * cbdata, TrWindow * wind );
    186137static void
    187 iconclick( struct cbdata * cbdata );
    188 static void
    189138makeicon( struct cbdata * cbdata );
    190 static gboolean
    191 winclose( GtkWidget * widget, GdkEvent * event, gpointer gdata );
    192139static void
    193140wannaquit( void * vdata );
     
    217164static void
    218165boolwindclosed(GtkWidget *widget, gpointer gdata);
    219 static void
    220 windact(GtkWidget *widget, int action, gpointer gdata);
    221166static GList *
    222167getselection( struct cbdata * cbdata );
    223 static void
    224 handleaction( struct cbdata *data, int action );
    225168
    226169static void
     
    230173static void
    231174fatalsig(int sig);
     175
     176static void
     177accumulateStatusForeach (GtkTreeModel * model,
     178                         GtkTreePath  * path UNUSED,
     179                         GtkTreeIter  * iter,
     180                         gpointer       accumulated_status)
     181{
     182    int status = 0;
     183    gtk_tree_model_get( model, iter, MC_STAT, &status, -1 );
     184    *(int*)accumulated_status |= status;
     185}
     186
     187static void
     188refreshTorrentActions( GtkTreeSelection * s )
     189{
     190    int status = 0;
     191    gtk_tree_selection_selected_foreach( s, accumulateStatusForeach, &status );
     192    action_sensitize( "stop-torrent", (status & TR_STATUS_ACTIVE) != 0);
     193    action_sensitize( "start-torrent", (status & TR_STATUS_INACTIVE) != 0);
     194    action_sensitize( "remove-torrent", status != 0);
     195    action_sensitize( "show-torrent-inspector", status != 0);
     196}
     197
     198static void
     199selectionChangedCB( GtkTreeSelection * s, gpointer unused UNUSED )
     200{
     201    refreshTorrentActions( s );
     202}
    232203
    233204int
    234205main( int argc, char ** argv )
    235206{
    236     GtkWindow  * mainwind;
     207    struct cbdata * cbdata = g_new (struct cbdata, 1);
    237208    char       * err;
    238209    benc_val_t * state;
     
    250221    }
    251222    setupsighandlers();         /* set up handlers for fatal signals */
    252     gtksetup( &argc, &argv );   /* set up gtk and gettext */
     223    gtksetup( &argc, &argv, cbdata );   /* set up gtk and gettext */
    253224
    254225    if( ( didinit || cf_init( tr_getPrefsDirectory(), &err ) ) &&
    255226        ( didlock || cf_lock( &err ) ) )
    256227    {
     228        GtkWindow  * mainwind;
     229
    257230        /* create main window now to be a parent to any error dialogs */
    258         mainwind = GTK_WINDOW( tr_window_new() );
     231        mainwind = GTK_WINDOW( tr_window_new( myUIManager ) );
    259232
    260233        /* try to load prefs and saved state */
     
    273246
    274247        msgwin_loadpref();      /* set message level here before tr_init() */
    275         appsetup( TR_WINDOW( mainwind ), state, argfiles, startpaused );
     248        appsetup( mainwind, state, argfiles, cbdata, startpaused );
    276249        cf_freestate( state );
    277250    }
     
    379352
    380353static void
    381 gtksetup( int * argc, char *** argv )
    382 {
    383     GdkPixbuf * icon;
    384 
    385     gtk_init( argc, argv );
     354gtksetup( int * argc, char *** argv, struct cbdata * callback_data )
     355{
    386356
    387357    bindtextdomain( "transmission-gtk", LOCALEDIR );
     
    390360
    391361    g_set_application_name( _("Transmission") );
     362    gtk_init( argc, argv );
     363
     364    /* connect up the actions */
     365    myUIManager = gtk_ui_manager_new ();
     366    actions_init ( myUIManager, callback_data );
     367    gtk_ui_manager_add_ui_from_string (myUIManager, fallback_ui_file, -1, NULL);
     368    gtk_ui_manager_ensure_update (myUIManager);
    392369
    393370    /* tweak some style properties in dialogs to get closer to the GNOME HiG */
     
    401378        "widget \"TransmissionDialog\" style \"transmission-standard\"\n" );
    402379
    403     icon = gdk_pixbuf_new_from_inline( -1, tr_icon_full, FALSE, NULL );
    404     gtk_window_set_default_icon( icon );
    405     g_object_unref( icon );
    406 }
    407 
    408 static void
    409 appsetup( TrWindow * wind, benc_val_t * state, GList * args, gboolean paused )
    410 {
    411     struct cbdata        * cbdata;
     380    gtk_window_set_default_icon_name ( "ICON_TRANSMISSION" );
     381}
     382
     383static void
     384appsetup( TrWindow * wind, benc_val_t * state, GList * args,
     385          struct cbdata * cbdata, gboolean paused )
     386{
    412387    enum tr_torrent_action action;
    413388
    414389    /* fill out cbdata */
    415     cbdata = g_new0( struct cbdata, 1 );
    416390    cbdata->wind       = NULL;
    417391    cbdata->core       = tr_core_new();
     
    460434
    461435    /* show the window */
    462     tr_window_show( wind );
     436    gtk_widget_show( GTK_WIDGET(wind) );
     437}
     438
     439static gboolean
     440winclose( GtkWidget * widget UNUSED, GdkEvent * event UNUSED, gpointer gdata )
     441{
     442    struct cbdata * cbdata = (struct cbdata *) gdata;
     443
     444    if( cbdata->icon != NULL )
     445        gtk_widget_hide( GTK_WIDGET( cbdata->wind ) );
     446    else
     447        askquit( cbdata->wind, wannaquit, cbdata );
     448
     449    return TRUE; /* don't propagate event further */
     450}
     451
     452static void
     453rowChangedCB( GtkTreeModel  * model UNUSED,
     454              GtkTreePath   * path UNUSED,
     455              GtkTreeIter   * iter UNUSED,
     456              gpointer        sel)
     457{
     458    refreshTorrentActions( GTK_TREE_SELECTION(sel) );
    463459}
    464460
     
    466462winsetup( struct cbdata * cbdata, TrWindow * wind )
    467463{
    468     int ii;
    469     GtkWidget  * drag;
    470 
    471     g_assert( ACTION_COUNT == ALEN( actions ) );
     464    GtkTreeModel * model;
     465    GtkTreeSelection * sel;
     466
    472467    g_assert( NULL == cbdata->wind );
    473468    cbdata->wind = GTK_WINDOW( wind );
    474     for( ii = 0; ii < ALEN( actions ); ii++ )
    475     {
    476         tr_window_action_add( wind, ii, actions[ii].flags,
    477                               gettext( actions[ii].label ), actions[ii].icon,
    478                               gettext( actions[ii].tooltip ),
    479                               actions[ii].key );
    480     }
    481     g_object_set( wind, "model", tr_core_model( cbdata->core ),
    482                         "double-click-action", ACT_INFO, NULL);
    483 
    484     g_signal_connect( wind, "action",       G_CALLBACK( windact  ), cbdata );
     469
     470    sel = tr_window_get_selection( cbdata->wind );
     471    g_signal_connect( sel, "changed", G_CALLBACK(selectionChangedCB), NULL );
     472    selectionChangedCB( sel, NULL );
     473    model = tr_core_model( cbdata->core );
     474    gtk_tree_view_set_model ( gtk_tree_selection_get_tree_view(sel), model );
     475    g_signal_connect( model, "row-changed", G_CALLBACK(rowChangedCB), sel );
    485476    g_signal_connect( wind, "delete-event", G_CALLBACK( winclose ), cbdata );
    486477   
    487     g_object_get( wind, "drag-widget", &drag, NULL );
    488     setupdrag( drag, cbdata );
    489 }
    490 
    491 static void
    492 iconclick( struct cbdata * cbdata )
    493 {
    494     GtkWidget * win;
    495 
    496     if( NULL != cbdata->wind )
    497     {
    498         /* close window  */
    499         winclose( NULL, NULL, cbdata );
    500     }
    501     else
    502     {
    503         /* create window */
    504         win = tr_window_new();
    505         winsetup( cbdata, TR_WINDOW( win ) );
    506         tr_window_show( TR_WINDOW( win ) );
    507     }
     478    setupdrag( GTK_WIDGET(wind), cbdata );
    508479}
    509480
     
    511482makeicon( struct cbdata * cbdata )
    512483{
    513     TrIcon * icon;
    514     int      ii;
    515 
    516     if( NULL != cbdata->icon )
    517     {
    518         return;
    519     }
    520 
    521     icon = tr_icon_new();
    522     for( ii = 0; ii < ALEN( actions ); ii++ )
    523     {
    524         tr_icon_action_add( TR_ICON( icon ), ii, actions[ii].flags,
    525                               gettext( actions[ii].label ), actions[ii].icon );
    526     }
    527     g_object_set( icon, "activate-action", ACT_ICON, NULL);
    528     g_signal_connect( icon, "action", G_CALLBACK( windact ), cbdata );
    529 
    530     cbdata->icon = icon;
    531 }
    532 
    533 static gboolean
    534 winclose( GtkWidget * widget SHUTUP, GdkEvent * event SHUTUP, gpointer gdata )
    535 {
    536     struct cbdata * cbdata;
    537 
    538     cbdata = gdata;
    539 
    540     if( NULL != cbdata->icon && tr_icon_docked( cbdata->icon ) )
    541     {
    542         gtk_widget_destroy( GTK_WIDGET( cbdata->wind ) );
    543         cbdata->wind = NULL;
    544     }
    545     else
    546     {
    547         askquit( cbdata->wind, wannaquit, cbdata );
    548     }
    549 
    550     /* don't propagate event further */
    551     return TRUE;
     484    if( NULL == cbdata->icon )
     485        cbdata->icon = tr_icon_new( );
    552486}
    553487
     
    863797            else if( NULL != cbdata->icon )
    864798            {
     799g_message ("foo");
    865800                g_object_unref( cbdata->icon );
    866801                cbdata->icon = NULL;
     
    910845  {
    911846      tr_torrentRates( tr_core_handle( data->core ), &down, &up );
    912       tr_window_update( TR_WINDOW( data->wind ), down, up );
     847      tr_window_update( data->wind, down, up );
    913848  }
    914849
     
    926861}
    927862
    928 static void
    929 windact( GtkWidget * wind SHUTUP, int action, gpointer gdata )
    930 {
    931     g_assert( 0 <= action );
    932     handleaction( gdata, action );
    933 }
    934 
    935863/* returns a GList containing a GtkTreeRowReference to each selected row */
    936864static GList *
    937865getselection( struct cbdata * cbdata )
    938866{
    939     GtkTreeSelection    * sel;
    940     GList               * rows, * ii;
    941     GtkTreeRowReference * ref;
    942 
    943     if( NULL == cbdata->wind )
    944     {
    945         return NULL;
    946     }
    947     g_object_get( cbdata->wind, "selection", &sel, NULL );
    948     rows = gtk_tree_selection_get_selected_rows( sel, NULL );
    949     for( ii = rows; NULL != ii; ii = ii->next )
    950     {
    951         ref = gtk_tree_row_reference_new( tr_core_model( cbdata->core ), ii->data );
    952         gtk_tree_path_free( ii->data );
    953         ii->data = ref;
     867    GList * rows = NULL;
     868
     869    if( NULL != cbdata->wind )
     870    {
     871        GList * ii;
     872        GtkTreeSelection *s = tr_window_get_selection(cbdata->wind);
     873        GtkTreeModel * model = tr_core_model( cbdata->core );
     874        rows = gtk_tree_selection_get_selected_rows( s, NULL );
     875        for( ii = rows; NULL != ii; ii = ii->next )
     876        {
     877            GtkTreeRowReference * ref = gtk_tree_row_reference_new(
     878                model, ii->data );
     879            gtk_tree_path_free( ii->data );
     880            ii->data = ref;
     881        }
    954882    }
    955883
     
    984912
    985913static void
    986 handleaction( struct cbdata * data, int act )
    987 {
    988   GList *rows, *ii;
    989   GtkTreeModel * model;
    990   GtkTreePath *path;
    991   GtkTreeIter iter;
    992   TrTorrent *tor;
    993   int status;
    994   gboolean changed;
    995   GtkWidget * win;
    996 
    997   g_assert( ACTION_COUNT > act );
    998 
    999   switch( act )
    1000   {
    1001       case ACT_ABOUT:
    1002           about ();
    1003           return;
    1004       case ACT_OPEN:
    1005           makeaddwind( data->wind, data->core );
    1006           return;
    1007       case ACT_PREF:
    1008           if( NULL != data->prefs )
    1009           {
    1010               return;
    1011           }
    1012           data->prefs = tr_prefs_new_with_parent( G_OBJECT( data->core ),
    1013                                                   data->wind );
    1014           g_signal_connect( data->prefs, "destroy",
    1015                             G_CALLBACK( gtk_widget_destroyed ), &data->prefs );
    1016           gtk_widget_show( GTK_WIDGET( data->prefs ) );
    1017           return;
    1018       case ACT_DEBUG:
    1019           if( !data->msgwinopen )
    1020           {
    1021               data->msgwinopen = TRUE;
    1022               win = msgwin_create();
    1023               g_signal_connect( win, "destroy", G_CALLBACK( boolwindclosed ),
     914startTorrentForeach (GtkTreeModel * model,
     915                     GtkTreePath  * path UNUSED,
     916                     GtkTreeIter  * iter,
     917                     gpointer       data UNUSED)
     918{
     919    TrTorrent * tor = NULL;
     920    gtk_tree_model_get( model, iter, MC_TORRENT, &tor, -1 );
     921    tr_torrent_start( tor );
     922    g_object_unref( G_OBJECT( tor ) );
     923}
     924
     925static void
     926stopTorrentForeach (GtkTreeModel * model,
     927                    GtkTreePath  * path UNUSED,
     928                    GtkTreeIter  * iter,
     929                    gpointer       data UNUSED)
     930{
     931    TrTorrent * tor = NULL;
     932    gtk_tree_model_get( model, iter, MC_TORRENT, &tor, -1 );
     933    tr_torrent_stop( tor );
     934    g_object_unref( G_OBJECT( tor ) );
     935}
     936
     937static void
     938showInfoForeach (GtkTreeModel * model,
     939                 GtkTreePath  * path UNUSED,
     940                 GtkTreeIter  * iter,
     941                 gpointer       data UNUSED)
     942{
     943    TrTorrent * tor = NULL;
     944    GtkWidget * w;
     945    gtk_tree_model_get( model, iter, MC_TORRENT, &tor, -1 );
     946    w = torrent_inspector_new( GTK_WINDOW(data), tor );
     947    gtk_widget_show( w );
     948    g_object_unref( G_OBJECT( tor ) );
     949}
     950
     951static void
     952recheckTorrentForeach (GtkTreeModel * model,
     953                       GtkTreePath  * path UNUSED,
     954                       GtkTreeIter  * iter,
     955                       gpointer       data UNUSED)
     956{
     957    TrTorrent * gtor = NULL;
     958    int status = 0;
     959    tr_torrent_t * tor;
     960    gtk_tree_model_get( model, iter, MC_TORRENT, &gtor, MC_STAT, &status, -1 );
     961    tor = tr_torrent_handle( gtor );
     962    if( status & TR_STATUS_ACTIVE )
     963        tr_torrentStop( tor );
     964    tr_torrentRemoveFastResume( tor );
     965    tr_torrentStart( tor );
     966    g_object_unref( G_OBJECT( gtor ) );
     967}
     968
     969void
     970doAction ( const char * action_name, gpointer user_data )
     971{
     972    struct cbdata * data = (struct cbdata *) user_data;
     973    gboolean changed = FALSE;
     974
     975    if (!strcmp (action_name, "add-torrent"))
     976    {
     977        makeaddwind( data->wind, data->core );
     978    }
     979    else if (!strcmp (action_name, "start-torrent"))
     980    {
     981        GtkTreeSelection * s = tr_window_get_selection(data->wind);
     982        gtk_tree_selection_selected_foreach( s, startTorrentForeach, NULL );
     983        changed |= gtk_tree_selection_count_selected_rows( s ) != 0;
     984    }
     985    else if (!strcmp (action_name, "stop-torrent"))
     986    {
     987        GtkTreeSelection * s = tr_window_get_selection(data->wind);
     988        gtk_tree_selection_selected_foreach( s, stopTorrentForeach, NULL );
     989        changed |= gtk_tree_selection_count_selected_rows( s ) != 0;
     990    }
     991    else if (!strcmp (action_name, "recheck-torrent"))
     992    {
     993        GtkTreeSelection * s = tr_window_get_selection(data->wind);
     994        gtk_tree_selection_selected_foreach( s, recheckTorrentForeach, NULL );
     995        changed |= gtk_tree_selection_count_selected_rows( s ) != 0;
     996    }
     997    else if (!strcmp (action_name, "show-torrent-inspector"))
     998    {
     999        GtkTreeSelection * s = tr_window_get_selection(data->wind);
     1000        gtk_tree_selection_selected_foreach( s, showInfoForeach, data->wind );
     1001    }
     1002    else if (!strcmp (action_name, "create-torrent"))
     1003    {
     1004        GtkWidget * w = make_meta_ui( GTK_WINDOW( data->wind ), tr_core_handle( data->core ) );
     1005        gtk_widget_show_all( w );
     1006    }
     1007    else if (!strcmp (action_name, "remove-torrent"))
     1008    {
     1009        /* this modifies the model's contents, so can't use foreach */
     1010        GList *l, *sel = getselection( data );
     1011        GtkTreeModel *model = tr_core_model( data->core );
     1012        for( l=sel; l!=NULL; l=l->next )
     1013        {
     1014            GtkTreeIter iter;
     1015            GtkTreeRowReference * reference = (GtkTreeRowReference *) l->data;
     1016            GtkTreePath * path = gtk_tree_row_reference_get_path( reference );
     1017            gtk_tree_model_get_iter( model, &iter, path );
     1018            tr_core_delete_torrent( data->core, &iter );
     1019            gtk_tree_row_reference_free( reference );
     1020            changed = TRUE;
     1021        }
     1022        g_list_free( sel );
     1023    }
     1024    else if (!strcmp (action_name, "close"))
     1025    {
     1026        if( data->wind != NULL )
     1027            winclose( NULL, NULL, data );
     1028    }
     1029    else if (!strcmp (action_name, "quit"))
     1030    {
     1031        askquit( data->wind, wannaquit, data );
     1032    }
     1033    else if (!strcmp (action_name, "edit-preferences"))
     1034    {
     1035        if( NULL == data->prefs )
     1036        {
     1037            data->prefs = tr_prefs_new_with_parent( G_OBJECT( data->core ),
     1038                                                    data->wind );
     1039            g_signal_connect( data->prefs, "destroy",
     1040                             G_CALLBACK( gtk_widget_destroyed ), &data->prefs );
     1041            gtk_widget_show( GTK_WIDGET( data->prefs ) );
     1042        }
     1043    }
     1044    else if (!strcmp (action_name, "show-debug-window"))
     1045    {
     1046        if( !data->msgwinopen )
     1047        {
     1048            GtkWidget * win = msgwin_create();
     1049            g_signal_connect( win, "destroy", G_CALLBACK( boolwindclosed ),
    10241050                                &data->msgwinopen );
    1025           }
    1026           return;
    1027       case ACT_ICON:
    1028           iconclick( data );
    1029           return;
    1030       case ACT_CLOSE:
    1031           if( NULL != data->wind )
    1032           {
    1033               winclose( NULL, NULL, data );
    1034           }
    1035           return;
    1036       case ACT_QUIT:
    1037           askquit( data->wind, wannaquit, data );
    1038           return;
    1039       case ACT_SEPARATOR1:
    1040       case ACT_SEPARATOR2:
    1041       case ACT_SEPARATOR3:
    1042           return;
    1043       case ACT_START:
    1044       case ACT_STOP:
    1045       case ACT_DELETE:
    1046       case ACT_INFO:
    1047       case ACTION_COUNT:
    1048           break;
    1049   }
    1050 
    1051   /* get a list of references to selected rows */
    1052   rows = getselection( data );
    1053 
    1054   model = tr_core_model( data->core );
    1055   changed = FALSE;
    1056   for(ii = rows; NULL != ii; ii = ii->next) {
    1057     if(NULL != (path = gtk_tree_row_reference_get_path(ii->data)) &&
    1058        gtk_tree_model_get_iter( model, &iter, path ) )
    1059     {
    1060       gtk_tree_model_get( model , &iter, MC_TORRENT, &tor,
    1061                           MC_STAT, &status, -1 );
    1062       if( ACT_ISAVAIL( actions[act].flags, status ) )
    1063 
    1064       {
    1065           switch( act )
    1066           {
    1067               case ACT_START:
    1068                   tr_torrent_start( tor );
    1069                   changed = TRUE;
    1070                   break;
    1071               case ACT_STOP:
    1072                   tr_torrent_stop( tor );
    1073                   changed = TRUE;
    1074                   break;
    1075               case ACT_DELETE:
    1076                   tr_core_delete_torrent( data->core, &iter );
    1077                   changed = TRUE;
    1078                   break;
    1079               case ACT_INFO:
    1080                   gtk_widget_show (torrent_inspector_new (data->wind, tor));
    1081                   break;
    1082               case ACT_OPEN:
    1083               case ACT_PREF:
    1084               case ACT_DEBUG:
    1085               case ACT_ICON:
    1086               case ACT_CLOSE:
    1087               case ACT_QUIT:
    1088               case ACT_SEPARATOR1:
    1089               case ACT_SEPARATOR2:
    1090               case ACT_SEPARATOR3:
    1091               case ACTION_COUNT:
    1092                   break;
    1093           }
    1094       }
    1095       g_object_unref(tor);
    1096     }
    1097     if(NULL != path)
    1098       gtk_tree_path_free(path);
    1099     gtk_tree_row_reference_free(ii->data);
    1100   }
    1101   g_list_free(rows);
    1102 
    1103   if(changed) {
    1104     updatemodel(data);
    1105     tr_core_save( data->core );
    1106   }
    1107 }
     1051            data->msgwinopen = TRUE;
     1052        }
     1053    }
     1054    else if (!strcmp (action_name, "show-about-dialog"))
     1055    {
     1056        about();
     1057    }
     1058    else if (!strcmp (action_name, "toggle-main-window"))
     1059    {
     1060        GtkWidget * w = GTK_WIDGET (data->wind);
     1061        if (GTK_WIDGET_VISIBLE(w))
     1062            gtk_widget_hide (w);
     1063        else
     1064            gtk_window_present (GTK_WINDOW(w));
     1065    }
     1066    else g_error ("Unhandled action: %s", action_name );
     1067
     1068    if(changed)
     1069    {
     1070        updatemodel( data );
     1071        tr_core_save( data->core );
     1072    }
     1073}
     1074
    11081075
    11091076static void
  • trunk/gtk/torrent-inspector.c

    r2003 r2149  
    2727#include <glib/gi18n.h>
    2828#include <gtk/gtk.h>
     29
    2930#include "transmission.h"
    3031#include "platform.h" /* for tr_getTorrentsDirectory */
     32
     33#include "actions.h"
    3134#include "tr_torrent.h"
    3235#include "dot-icons.h"
     
    216219  gdk_draw_rectangle (da->window, gcs[GRAY], FALSE,
    217220                      grid_bounds.x, grid_bounds.y,
    218                       grid_bounds.width, grid_bounds.height-1);
     221                      grid_bounds.width-1, grid_bounds.height-1);
    219222
    220223  g_free (pieces);
     
    597600                                       GTK_SHADOW_IN);
    598601  gtk_container_add (GTK_CONTAINER(w), v);
    599   gtk_widget_set_usize (w, 500u, 0u);
    600602
    601603
     
    803805  Activity * a = (Activity*) g_object_get_data (G_OBJECT(top), "activity-data");
    804806  const tr_stat_t * stat = tr_torrent_stat( a->gtor );
    805   const tr_info_t * info = tr_torrent_info( a->gtor );
    806807  guint64 size;
    807808  char *pch;
     
    811812  g_free (pch);
    812813
    813   size = info->totalSize - stat->left;
     814  size = stat->downloadedValid;
    814815  pch = readablesize (size);
    815816  gtk_label_set_text (GTK_LABEL(a->valid_dl_lb), pch);
     
    941942  FC_INDEX,
    942943  FC_SIZE,
     944  FC_PRIORITY,
    943945  N_FILE_COLS
    944946};
     
    949951  GtkTreeModel * model; /* same object as store, but recast */
    950952  GtkTreeStore * store; /* same object as model, but recast */
     953  GtkTreeSelection * selection;
    951954}
    952955FileData;
    953956
    954 static void
    955 parsepath( GtkTreeStore  * store,
    956            GtkTreeIter   * ret,
    957            const char    * path,
    958            int             index,
    959            uint64_t        size )
     957static const char*
     958priorityToString( const int priority )
     959{
     960    switch( priority ) {
     961        case TR_PRI_HIGH:   return _("High");
     962        case TR_PRI_NORMAL: return _("Normal");
     963        case TR_PRI_LOW:    return _("Low");
     964        case TR_PRI_DND:    return _("Don't Get");
     965        default:            return "BUG!";
     966    }
     967}
     968
     969static tr_priority_t
     970stringToPriority( const char* str )
     971{
     972    if( !strcmp( str, _( "High" ) ) ) return TR_PRI_HIGH;
     973    if( !strcmp( str, _( "Low" ) ) ) return TR_PRI_LOW;
     974    if( !strcmp( str, _( "Don't Get" ) ) ) return TR_PRI_DND;;
     975    return TR_PRI_NORMAL;
     976}
     977
     978static void
     979parsepath( const tr_torrent_t  * tor,
     980           GtkTreeStore        * store,
     981           GtkTreeIter         * ret,
     982           const char          * path,
     983           int                   index,
     984           uint64_t              size )
    960985{
    961986    GtkTreeModel * model;
     
    963988    char         * file, * lower, * mykey, *escaped=0;
    964989    const char   * stock;
     990    int            priority = 0;
    965991
    966992    model  = GTK_TREE_MODEL( store );
     
    970996    {
    971997        char * dir = g_path_get_dirname( path );
    972         parsepath( store, &start, dir, index, size );
     998        parsepath( tor, store, &start, dir, index, size );
    973999        parent = &start;
    9741000        g_free( dir );
     
    10001026    }
    10011027
     1028    if (index != -1)
     1029        priority = tr_torrentGetFilePriority( tor, index );
     1030
    10021031    escaped = g_markup_escape_text (file, -1);
    10031032    gtk_tree_store_set( store, &iter, FC_INDEX, index, FC_LABEL, escaped,
    1004                         FC_KEY, mykey, FC_STOCK, stock, FC_SIZE, size, -1 );
     1033                        FC_KEY, mykey, FC_STOCK, stock,
     1034                        FC_PRIORITY, priorityToString(priority),
     1035                        FC_SIZE, size, -1 );
    10051036  done:
    10061037    g_free( escaped );
     
    10611092    if( gtk_tree_model_iter_children( model, &iter, parent ) ) do
    10621093    {
     1094        int oldProg, newProg;
    10631095        guint64 subGot, subTotal;
    10641096
     
    10801112        if (!subTotal) subTotal = 1; /* avoid div by zero */
    10811113        g_assert (subGot <= subTotal);
    1082         gtk_tree_store_set (store, &iter,
    1083                             FC_PROG, (int)(100.0*subGot/subTotal + 0.5), -1);
     1114
     1115        /* why not just set it every time?
     1116           because that causes the "priorities" combobox to pop down */
     1117        gtk_tree_model_get (model, &iter, FC_PROG, &oldProg, -1);
     1118        newProg = (int)(100.0*subGot/subTotal + 0.5);
     1119        if (oldProg != newProg)
     1120          gtk_tree_store_set (store, &iter,
     1121                              FC_PROG, (int)(100.0*subGot/subTotal + 0.5), -1);
    10841122
    10851123        gotSize += subGot;
     
    10921130}
    10931131
     1132static GtkTreeModel*
     1133priority_model_new (void)
     1134{
     1135  GtkTreeIter iter;
     1136  GtkListStore * store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
     1137  gtk_list_store_append (store, &iter);
     1138  gtk_list_store_set (store, &iter, 0, _("High"), 1, TR_PRI_HIGH, -1);
     1139  gtk_list_store_append (store, &iter);
     1140  gtk_list_store_set (store, &iter, 0, _("Normal"), 1, TR_PRI_NORMAL, -1);
     1141  gtk_list_store_append (store, &iter);
     1142  gtk_list_store_set (store, &iter, 0, _("Low"), 1, TR_PRI_LOW, -1);
     1143  gtk_list_store_append (store, &iter);
     1144  gtk_list_store_set (store, &iter, 0, _("Don't Get"), 1, TR_PRI_DND, -1);
     1145  return GTK_TREE_MODEL (store);
     1146}
     1147
     1148static void
     1149refreshPriorityActions( GtkTreeSelection * sel )
     1150{
     1151    GtkTreeIter iter;
     1152    GtkTreeModel * model;
     1153    const gboolean has_selection = gtk_tree_selection_get_selected( sel, &model, &iter );
     1154
     1155    action_sensitize ( "priority-high", has_selection );
     1156    action_sensitize ( "priority-normal", has_selection );
     1157    action_sensitize ( "priority-low", has_selection );
     1158    action_sensitize ( "priority-dnd", has_selection );
     1159
     1160    if( has_selection )
     1161    {
     1162        /* set the action priority base on the model's values */
     1163        char * pch = NULL;
     1164        const char * key;
     1165        gtk_tree_model_get( model, &iter, FC_PRIORITY, &pch, -1 );
     1166        switch( stringToPriority( pch ) ) {
     1167            case TR_PRI_HIGH:   key = "priority-high";   break;
     1168            case TR_PRI_LOW:    key = "priority-low";    break;
     1169            case TR_PRI_DND:    key = "priority-dnd";    break;
     1170            default:            key = "priority-normal"; break;
     1171        }
     1172        action_toggle( key, TRUE );
     1173        g_free( pch );
     1174    }
     1175}
     1176
     1177static void
     1178set_priority (GtkTreeSelection * selection,
     1179              GtkTreeStore * store,
     1180              GtkTreeIter * iter,
     1181              tr_torrent_t * tor,
     1182              int priority_val,
     1183              const char * priority_str)
     1184{
     1185    int index;
     1186    GtkTreeIter child;
     1187
     1188    gtk_tree_model_get( GTK_TREE_MODEL(store), iter, FC_INDEX, &index, -1  );
     1189    if (index >= 0)
     1190      tr_torrentSetFilePriority( tor, index, priority_val );
     1191    gtk_tree_store_set( store, iter, FC_PRIORITY, priority_str, -1 );
     1192
     1193    if( gtk_tree_model_iter_children( GTK_TREE_MODEL(store), &child, iter ) ) do
     1194      set_priority( selection, store, &child, tor, priority_val, priority_str );
     1195    while( gtk_tree_model_iter_next( GTK_TREE_MODEL(store), &child ) );
     1196
     1197    refreshPriorityActions( selection );
     1198}
     1199
     1200static void
     1201priority_changed_cb (GtkCellRendererText * cell UNUSED,
     1202                     const gchar         * path,
     1203                     const gchar         * value,
     1204                     void                * file_data)
     1205{
     1206  GtkTreeIter iter;
     1207  FileData * d = (FileData*) file_data;
     1208  if (gtk_tree_model_get_iter_from_string (d->model, &iter, path))
     1209  {
     1210    tr_torrent_t  * tor = tr_torrent_handle( d->gtor );
     1211    const tr_priority_t priority = stringToPriority( value );
     1212    set_priority( d->selection, d->store, &iter, tor, priority, value );
     1213  }
     1214}
     1215
     1216/* FIXME: NULL this back out when popup goes down */
     1217static GtkWidget * popupView = NULL;
     1218
     1219static void
     1220on_popup_menu ( GtkWidget * view, GdkEventButton * event )
     1221{
     1222    GtkWidget * menu = action_get_widget ( "/file-popup" );
     1223    popupView = view;
     1224    gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL,
     1225                    (event ? event->button : 0),
     1226                    (event ? event->time : 0));
     1227}
     1228
     1229static void
     1230fileSelectionChangedCB( GtkTreeSelection * sel, gpointer unused UNUSED )
     1231{
     1232    refreshPriorityActions( sel );
     1233}
     1234
     1235void
     1236set_selected_file_priority ( tr_priority_t priority_val )
     1237{
     1238    if( popupView && GTK_IS_TREE_VIEW(popupView) )
     1239    {
     1240        GtkTreeView * view = GTK_TREE_VIEW( popupView );
     1241        tr_torrent_t * tor = (tr_torrent_t*)
     1242            g_object_get_data (G_OBJECT(view), "torrent-handle");
     1243        const char * priority_str = priorityToString( priority_val );
     1244        GtkTreeModel * model;
     1245        GtkTreeIter iter;
     1246        GtkTreeSelection * sel = gtk_tree_view_get_selection (view);
     1247        gtk_tree_selection_get_selected( sel, &model, &iter );
     1248
     1249        set_priority( sel, GTK_TREE_STORE(model), &iter,
     1250                      tor, priority_val, priority_str );
     1251    }
     1252}
     1253
    10941254GtkWidget *
    10951255file_page_new ( TrTorrent * gtor )
    10961256{
    1097     GtkWidget * ret;
    1098     FileData * data;
    1099     tr_info_t         * inf;
    1100     GtkTreeStore      * store;
    1101     int                 ii;
    1102     GtkWidget         * view, * scroll;
    1103     GtkCellRenderer   * rend;
    1104     GtkTreeViewColumn * col;
    1105     GtkTreeSelection  * sel;
     1257    GtkWidget           * ret;
     1258    FileData            * data;
     1259    tr_info_t           * inf;
     1260    tr_torrent_t        * tor;
     1261    GtkTreeStore        * store;
     1262    int                   ii;
     1263    GtkWidget           * view, * scroll;
     1264    GtkCellRenderer     * rend;
     1265    GtkTreeViewColumn   * col;
     1266    GtkTreeSelection    * sel;
     1267    GtkTreeModel        * model;
    11061268
    11071269    store = gtk_tree_store_new ( N_FILE_COLS,
     
    11111273                                 G_TYPE_STRING,  /* key */
    11121274                                 G_TYPE_INT,     /* index */
    1113                                  G_TYPE_UINT64); /* size */
     1275                                 G_TYPE_UINT64,  /* size */
     1276                                 G_TYPE_STRING); /* priority */
    11141277
    11151278    /* set up the model */
     1279    tor = tr_torrent_handle( gtor );
    11161280    inf = tr_torrent_info( gtor );
    11171281    for( ii = 0; ii < inf->fileCount; ii++ )
    11181282    {
    1119         parsepath( store, NULL, STRIPROOT( inf->files[ii].name ),
     1283        parsepath( tor, store, NULL, STRIPROOT( inf->files[ii].name ),
    11201284                   ii, inf->files[ii].length );
    11211285    }
     
    11261290    /* create the view */
    11271291    view = gtk_tree_view_new_with_model( GTK_TREE_MODEL( store ) );
     1292    g_object_set_data (G_OBJECT(view), "torrent-handle", tor );
     1293    g_signal_connect( view, "popup-menu",
     1294                      G_CALLBACK(on_popup_menu), NULL );
     1295    g_signal_connect( view, "button-press-event",
     1296                      G_CALLBACK(on_tree_view_button_pressed), on_popup_menu);
     1297
    11281298    /* add file column */
    11291299   
     
    11511321    gtk_tree_view_expand_all( GTK_TREE_VIEW( view ) );
    11521322    gtk_tree_view_set_search_column( GTK_TREE_VIEW( view ), FC_LABEL );
     1323    g_signal_connect( sel, "changed", G_CALLBACK(fileSelectionChangedCB), NULL );
     1324    fileSelectionChangedCB( sel, NULL );
     1325
     1326
     1327    /* add priority column */
     1328    model = priority_model_new ();
     1329    col = gtk_tree_view_column_new ();
     1330    gtk_tree_view_column_set_title (col, _("Priority"));
     1331    rend = gtk_cell_renderer_combo_new ();
     1332    gtk_tree_view_column_pack_start (col, rend, TRUE);
     1333    g_object_set (G_OBJECT(rend), "model", model,
     1334                                  "editable", TRUE,
     1335                                  "has-entry", FALSE,
     1336                                  "text-column", 0,
     1337                                  NULL);
     1338    g_object_unref (G_OBJECT(model));
     1339    gtk_tree_view_column_add_attribute (col, rend, "text", FC_PRIORITY);
     1340    gtk_tree_view_append_column( GTK_TREE_VIEW( view ), col );
    11531341
    11541342    /* create the scrolled window and stick the view in it */
     
    11671355    data->model = GTK_TREE_MODEL(store);
    11681356    data->store = store;
     1357    data->selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) );
    11691358    g_object_set_data_full (G_OBJECT(ret), "file-data", data, g_free);
     1359    g_signal_connect (G_OBJECT(rend), "edited", G_CALLBACK(priority_changed_cb), data);
    11701360    return ret;
    11711361}
     
    12721462  hig_workarea_add_section_spacer (t, row, 1);
    12731463
    1274     tb = gtk_check_button_new_with_mnemonic (_("Stop Seeding at Ratio:"));
     1464    tb = gtk_check_button_new_with_mnemonic (_("_Stop Seeding at Ratio:"));
    12751465    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(tb), gtor->seeding_cap_enabled);
    12761466    g_signal_connect (tb, "toggled", G_CALLBACK(seeding_cap_toggled_cb), gtor);
  • trunk/gtk/torrent-inspector.h

    r1998 r2149  
    2929#include "tr_torrent.h"
    3030
     31struct TrTorrent;
     32
    3133GtkWidget* torrent_inspector_new ( GtkWindow * parent, TrTorrent * tor );
    3234
     35void set_selected_file_priority ( tr_priority_t );
     36
    3337#endif /* TG_PREFS_H */
  • trunk/gtk/tr_core.c

    r1998 r2149  
    219219        /* info->name, info->totalSize, info->hashString, status, */
    220220        G_TYPE_STRING, G_TYPE_UINT64,   G_TYPE_STRING,    G_TYPE_INT,
    221         /* error,   errorString,   progress,     rateDownload, rateUpload, */
    222         G_TYPE_INT, G_TYPE_STRING, G_TYPE_FLOAT, G_TYPE_FLOAT, G_TYPE_FLOAT,
     221        /* error,   errorString,   percentComplete, percentDone,  rateDownload, rateUpload, */
     222        G_TYPE_INT, G_TYPE_STRING, G_TYPE_FLOAT,    G_TYPE_FLOAT, G_TYPE_FLOAT, G_TYPE_FLOAT,
    223223        /* eta,     peersTotal, peersUploading, peersDownloading, seeders, */
    224224        G_TYPE_INT, G_TYPE_INT, G_TYPE_INT,     G_TYPE_INT,       G_TYPE_INT,
     
    265265
    266266    /* sever all remaining torrents in the model */
    267     if( gtk_tree_model_get_iter_first( self->model, &iter ) )
    268     {
    269         do
    270         {
    271             gtk_tree_model_get( self->model, &iter, MC_TORRENT, &tor, -1 );
    272             tr_torrent_sever( tor );
    273             g_object_unref( tor );
    274         }
    275         while( gtk_tree_model_iter_next( self->model, &iter ) );
    276     }
     267    if( gtk_tree_model_get_iter_first( self->model, &iter ) ) do
     268    {
     269        gtk_tree_model_get( self->model, &iter, MC_TORRENT, &tor, -1 );
     270        tr_torrent_sever( tor );
     271        g_object_unref( tor );
     272    }
     273    while( gtk_tree_model_iter_next( self->model, &iter ) );
    277274    g_object_unref( self->model );
    278275
     
    309306tr_core_model( TrCore * self )
    310307{
    311     TR_IS_CORE( self );
    312 
    313     if( self->disposed )
    314     {
    315         return NULL;
    316     }
    317 
    318     return self->model;
     308    g_return_val_if_fail (TR_IS_CORE(self), NULL);
     309
     310    return self->disposed ? NULL : self->model;
    319311}
    320312
     
    322314tr_core_handle( TrCore * self )
    323315{
    324     TR_IS_CORE( self );
    325 
    326     if( self->disposed )
    327     {
    328         return NULL;
    329     }
    330 
    331     return self->handle;
     316    g_return_val_if_fail (TR_IS_CORE(self), NULL);
     317
     318    return self->disposed ? NULL : self->handle;
    332319}
    333320
     
    747734                                MC_ERR,         st->error,
    748735                                MC_TERR,        st->errorString,
    749                                 MC_PROG,        st->progress,
     736                                MC_PROG_C,      st->percentComplete,
     737                                MC_PROG_D,      st->percentDone,
    750738                                MC_DRATE,       st->rateDownload,
    751739                                MC_URATE,       st->rateUpload,
  • trunk/gtk/tr_core.h

    r1938 r2149  
    189189enum {
    190190  MC_NAME, MC_SIZE, MC_HASH, MC_STAT, MC_ERR, MC_TERR,
    191   MC_PROG, MC_DRATE, MC_URATE, MC_ETA, MC_PEERS,
     191  MC_PROG_C, MC_PROG_D, MC_DRATE, MC_URATE, MC_ETA, MC_PEERS,
    192192  MC_UPEERS, MC_DPEERS, MC_SEED, MC_LEECH, MC_DONE,
    193193  MC_DOWN, MC_UP, MC_LEFT, MC_TRACKER, MC_TORRENT, MC_ID,
  • trunk/gtk/tr_icon.c

    r1673 r2149  
    2424
    2525#include <gtk/gtk.h>
    26 #include <glib/gi18n.h>
    27 
     26#include "actions.h"
    2827#include "tr_icon.h"
    2928#include "util.h"
    3029
    31 #define ITEM_ACTION             "tr-icon-item-action"
     30#ifndef STATUS_ICON_SUPPORTED
    3231
    33 enum
     32gpointer
     33tr_icon_new( void )
    3434{
    35     PROP_ICON = 1,
    36     PROP_DOCKED,
    37     PROP_CLICK,
    38 };
     35    return NULL;
     36}
     37
     38#else
    3939
    4040static void
    41 tr_icon_init( GTypeInstance * instance, gpointer g_class );
    42 static void
    43 tr_icon_set_property( GObject * object, guint property_id,
    44                       const GValue * value, GParamSpec * pspec );
    45 static void
    46 tr_icon_get_property( GObject * object, guint property_id,
    47                       GValue * value, GParamSpec * pspec);
    48 static void
    49 tr_icon_class_init( gpointer g_class, gpointer g_class_data );
    50 static void
    51 tr_icon_dispose( GObject * obj );
    52 static void
    53 itemclick( GObject * obj, gpointer data );
    54 static void
    55 emitaction( TrIcon * self, int id );
    56 #ifdef TR_ICON_SUPPORTED
    57 static void
    58 clicked( TrIcon * self, gpointer data );
    59 static void
    60 popup( TrIcon * self, guint button, guint when, gpointer data );
    61 #endif
    62 
    63 GType
    64 tr_icon_get_type( void )
     41activated ( GtkStatusIcon   * self        UNUSED,
     42            gpointer          user_data   UNUSED )
    6543{
    66     static GType type = 0;
    67 
    68     if( 0 == type )
    69     {
    70         static const GTypeInfo info =
    71         {
    72             sizeof( TrIconClass ),
    73             NULL,                       /* base_init */
    74             NULL,                       /* base_finalize */
    75             tr_icon_class_init,         /* class_init */
    76             NULL,                       /* class_finalize */
    77             NULL,                       /* class_data */
    78             sizeof( TrIcon ),
    79             0,                          /* n_preallocs */
    80             tr_icon_init,               /* instance_init */
    81             NULL,
    82         };
    83 #ifdef TR_ICON_SUPPORTED
    84         type = GTK_TYPE_STATUS_ICON;
    85 #else
    86         type = G_TYPE_OBJECT;
    87 #endif
    88         type = g_type_register_static( type, "TrIcon", &info, 0 );
    89     }
    90 
    91     return type;
     44    action_activate ("toggle-main-window");
    9245}
    9346
    9447static void
    95 tr_icon_class_init( gpointer g_class, gpointer g_class_data SHUTUP )
     48popup ( GtkStatusIcon  * self,
     49        guint            button,
     50        guint            when,
     51        gpointer         data    UNUSED )
    9652{
    97     GObjectClass * gobject_class;
    98     TrIconClass  * tricon_class;
    99     GParamSpec   * pspec;
    100 
    101     gobject_class = G_OBJECT_CLASS( g_class );
    102     gobject_class->set_property = tr_icon_set_property;
    103     gobject_class->get_property = tr_icon_get_property;
    104     gobject_class->dispose      = tr_icon_dispose;
    105 
    106     pspec = g_param_spec_boolean( "icon", "Icon",
    107                                   "Icon has been set from default window icon.",
    108                                   TRUE, G_PARAM_CONSTRUCT|G_PARAM_READWRITE );
    109     g_object_class_install_property( gobject_class, PROP_ICON, pspec );
    110 
    111     pspec = g_param_spec_boolean( "docked", "Docked",
    112                                  "Icon is docked in a system tray.",
    113                                   FALSE, G_PARAM_READABLE );
    114     g_object_class_install_property( gobject_class, PROP_DOCKED, pspec );
    115 
    116     pspec = g_param_spec_int( "activate-action", "Activate action",
    117                               "The action id to signal when icon is activated.",
    118                               G_MININT, G_MAXINT, -1, G_PARAM_READWRITE );
    119     g_object_class_install_property( gobject_class, PROP_CLICK, pspec );
    120 
    121     tricon_class = TR_ICON_CLASS( g_class );
    122     tricon_class->actionsig =
    123         g_signal_new( "action", G_TYPE_FROM_CLASS( g_class ),
    124                        G_SIGNAL_RUN_LAST, 0, NULL, NULL,
    125                        g_cclosure_marshal_VOID__INT,
    126                        G_TYPE_NONE, 1, G_TYPE_INT );
     53    GtkWidget * w = action_get_widget( "/icon-popup" );
     54    gtk_menu_popup (GTK_MENU(w), NULL, NULL,
     55                    gtk_status_icon_position_menu,
     56                    self, button, when );
    12757}
    12858
    129 static void
    130 tr_icon_init( GTypeInstance * instance, gpointer g_class SHUTUP )
    131 {
    132     TrIcon * self = ( TrIcon * )instance;
    133 
    134     self->clickact  = -1;
    135     self->menu      = NULL;
    136     self->actions   = NULL;
    137     self->disposed  = FALSE;
    138 
    139 #ifdef TR_ICON_SUPPORTED
    140     self->menu = gtk_menu_new();
    141     gtk_widget_show( self->menu );
    142     g_signal_connect( self, "activate", G_CALLBACK( clicked ), NULL );
    143     g_signal_connect( self, "popup-menu", G_CALLBACK( popup ), NULL );
    144 #endif
    145 }
    146 
    147 static void
    148 tr_icon_set_property( GObject * object, guint property_id,
    149                       const GValue * value, GParamSpec * pspec)
    150 {
    151     TrIcon * self = ( TrIcon * )object;
    152 
    153     if( self->disposed )
    154     {
    155         return;
    156     }
    157 
    158     switch( property_id )
    159     {
    160         case PROP_ICON:
    161 #ifdef TR_ICON_SUPPORTED
    162             if( g_value_get_boolean( value ) )
    163             {
    164                 GList  * icons = gtk_window_get_default_icon_list();
    165                 if( NULL != icons && NULL != icons->data )
    166                 {
    167                     gtk_status_icon_set_from_pixbuf( GTK_STATUS_ICON( self ),
    168                                                      icons->data );
    169                 }
    170                 g_list_free( icons );
    171             }
    172             else
    173             {
    174                 gtk_status_icon_set_from_pixbuf( GTK_STATUS_ICON( self ),
    175                                                  NULL );
    176             }
    177 #endif
    178             break;
    179         case PROP_CLICK:
    180             self->clickact = g_value_get_int( value );
    181             break;
    182         case PROP_DOCKED:
    183         default:
    184             G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, pspec );
    185             break;
    186     }
    187 }
    188 
    189 static void
    190 tr_icon_get_property( GObject * object, guint property_id,
    191                       GValue * value, GParamSpec * pspec )
    192 {
    193     TrIcon * self = ( TrIcon * )object;
    194 #ifdef TR_ICON_SUPPORTED
    195     GtkStatusIcon * icon;
    196 #endif
    197 
    198     if( self->disposed )
    199     {
    200         return;
    201     }
    202 
    203     switch( property_id )
    204     {
    205         case PROP_ICON:
    206 #ifdef TR_ICON_SUPPORTED
    207             icon = GTK_STATUS_ICON( self );
    208             if( GTK_IMAGE_PIXBUF == gtk_status_icon_get_storage_type( icon ) &&
    209                 NULL != gtk_status_icon_get_pixbuf( icon ) )
    210             {
    211                 g_value_set_boolean( value, TRUE );
    212             }
    213             else
    214 #endif
    215             {
    216                 g_value_set_boolean( value, FALSE );
    217             }
    218             break;
    219         case PROP_DOCKED:
    220 #ifdef TR_ICON_SUPPORTED
    221             if( gtk_status_icon_is_embedded( GTK_STATUS_ICON( self ) ) )
    222             {
    223                 g_value_set_boolean( value, TRUE );
    224             }
    225             else
    226 #endif
    227             {
    228                 g_value_set_boolean( value, FALSE );
    229             }
    230             break;
    231         case PROP_CLICK:
    232             g_value_set_int( value, self->clickact );
    233             break;
    234         default:
    235             G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, pspec );
    236             break;
    237     }
    238 }
    239 
    240 static void
    241 tr_icon_dispose( GObject * obj )
    242 {
    243     TrIcon       * self = ( TrIcon * )obj;
    244     GObjectClass * parent;
    245 
    246     if( self->disposed )
    247     {
    248         return;
    249     }
    250     self->disposed = TRUE;
    251 
    252     g_list_foreach( self->actions, ( GFunc )action_free, NULL );
    253     g_list_free( self->actions );
    254 
    255     /* Chain up to the parent class */
    256     parent = g_type_class_peek( g_type_parent( TR_ICON_TYPE ) );
    257     parent->dispose( obj );
    258 }
    259 
    260 TrIcon *
     59gpointer
    26160tr_icon_new( void )
    26261{
    263     return g_object_new( TR_ICON_TYPE, NULL );
    264 }
    265 
    266 gboolean
    267 tr_icon_docked( TrIcon * self )
    268 {
    269     gboolean ret;
    270 
    271     g_object_get( self, "docked", &ret, NULL );
    272 
     62    GtkStatusIcon * ret = gtk_status_icon_new_from_stock ("ICON_TRANSMISSION");
     63    g_signal_connect( ret, "activate", G_CALLBACK( activated ), NULL );
     64    g_signal_connect( ret, "popup-menu", G_CALLBACK( popup ), NULL );
    27365    return ret;
    27466}
    27567
    276 void
    277 tr_icon_action_add( TrIcon * self, int id, int flags, const char * name,
    278                     const char * icon )
    279 {
    280     struct action * act;
    281     GtkWidget     * item;
    282 
    283     TR_IS_ICON( self );
    284     if( self->disposed )
    285     {
    286         return;
    287     }
    288 
    289     act = action_new( id, flags, name, icon );
    290 
    291     if( NULL != self->menu )
    292     {
    293         if( ACTF_MENU & flags && ACTF_ALWAYS & flags )
    294         {
    295             item = action_makemenu( act, ITEM_ACTION, NULL, NULL, 0,
    296                                     G_CALLBACK( itemclick ), self );
    297             gtk_menu_shell_append( GTK_MENU_SHELL( self->menu ), item );
    298             act->menu = item;
    299         }
    300         else if( ACTF_SEPARATOR & flags )
    301         {
    302             item = gtk_separator_menu_item_new();
    303             gtk_widget_show( item );
    304             gtk_menu_shell_append( GTK_MENU_SHELL( self->menu ), item );
    305         }
    306     }
    307 
    308     self->actions = g_list_append( self->actions, act );
    309 }
    310 
    311 static void
    312 itemclick( GObject * obj, gpointer data )
    313 {
    314     TrIcon        * self;
    315     struct action * act;
    316 
    317     TR_IS_ICON( data );
    318     self = TR_ICON( data );
    319     act = g_object_get_data( obj, ITEM_ACTION );
    320 
    321     emitaction( self, act->id );
    322 }
    323 
    324 static void
    325 emitaction( TrIcon * self, int id )
    326 {
    327     TrIconClass * class;
    328 
    329     class = g_type_class_peek( TR_ICON_TYPE );
    330     g_signal_emit( self, class->actionsig, 0, id );
    331 }
    332 
    333 #ifdef TR_ICON_SUPPORTED
    334 
    335 static void
    336 clicked( TrIcon * self, gpointer data SHUTUP )
    337 {
    338     TR_IS_ICON( self );
    339 
    340     if( self->disposed || 0 > self->clickact )
    341     {
    342         return;
    343     }
    344 
    345     emitaction( self, self->clickact );
    346 }
    347 
    348 static void
    349 popup( TrIcon * self, guint button, guint when, gpointer data SHUTUP )
    350 {
    351     TR_IS_ICON( self );
    352 
    353     if( self->disposed )
    354     {
    355         return;
    356     }
    357 
    358     gtk_menu_popup( GTK_MENU( self->menu ), NULL, NULL,
    359                     gtk_status_icon_position_menu, self, button, when );
    360 }
    361 
    362 #endif /* TR_ICON_SUPPORTED */
     68#endif
  • trunk/gtk/tr_icon.h

    r1515 r2149  
    2828#include <gtk/gtk.h>
    2929
    30 #if GTK_MAJOR_VERSION > 2 || \
    31     ( GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 10 )
    32 #define TR_ICON_SUPPORTED
    33 #define tr_icon_supported()     (TRUE)
     30#if GTK_CHECK_VERSION(2,10,0)
     31#define STATUS_ICON_SUPPORTED
     32#define status_icon_supported() (TRUE)
    3433#else
    35 #define tr_icon_supported()    (FALSE)
     34#define status_icon_supported() (FALSE)
    3635#endif
    3736
    38 #define TR_ICON_TYPE              ( tr_icon_get_type() )
    39 
    40 #define TR_ICON( obj ) \
    41   ( G_TYPE_CHECK_INSTANCE_CAST( (obj),   TR_ICON_TYPE, TrIcon ) )
    42 
    43 #define TR_ICON_CLASS( class ) \
    44   ( G_TYPE_CHECK_CLASS_CAST(    (class), TR_ICON_TYPE, TrIconClass ) )
    45 
    46 #define TR_IS_ICON( obj ) \
    47   ( G_TYPE_CHECK_INSTANCE_TYPE( (obj),   TR_ICON_TYPE ) )
    48 
    49 #define TR_IS_ICON_CLASS( class ) \
    50   ( G_TYPE_CHECK_CLASS_TYPE(    (class), TR_ICON_TYPE ) )
    51 
    52 #define TR_ICON_GET_CLASS( obj ) \
    53   ( G_TYPE_INSTANCE_GET_CLASS(  (obj),   TR_ICON_TYPE, TrIconClass ) )
    54 
    55 typedef struct _TrIcon TrIcon;
    56 typedef struct _TrIconClass TrIconClass;
    57 
    58 /* treat the contents of this structure as private */
    59 struct _TrIcon
    60 {
    61 #ifdef TR_ICON_SUPPORTED
    62     GtkStatusIcon parent;
    63 #else
    64     GObject       parent;
    65 #endif
    66     GtkWidget   * menu;
    67     GList       * actions;
    68     int           clickact;
    69     gboolean      disposed;
    70 };
    71 
    72 struct _TrIconClass
    73 {
    74 #ifdef TR_ICON_SUPPORTED
    75     GtkStatusIconClass parent;
    76 #else
    77     GObjectClass       parent;
    78 #endif
    79     int                actionsig;
    80 };
    81 
    82 GType
    83 tr_icon_get_type( void );
    84 
    85 TrIcon *
    86 tr_icon_new( void );
    87 
    88 gboolean
    89 tr_icon_docked( TrIcon * icon );
    90 
    91 void
    92 tr_icon_action_add( TrIcon * icon, int id, int flags, const char * label,
    93                     const char * stock );
     37gpointer tr_icon_new( void );
    9438
    9539#endif
  • trunk/gtk/tr_prefs.c

    r2047 r2149  
    128128    /* PREF_ID_ICON */
    129129    { "use-tray-icon",          G_TYPE_BOOLEAN,
    130       ( tr_icon_supported() ? PR_ENABLED : PR_DISABLED ),    NULL,
     130      ( status_icon_supported() ? PR_ENABLED : PR_DISABLED ),    NULL,
    131131      N_("Display an _icon in the system tray"),
    132132      N_("Use a system tray / dock / notification area icon") },
  • trunk/gtk/tr_torrent.c

    r1998 r2149  
    648648    const int upeers = MAX (st->peersUploading, 0);
    649649    const int eta = st->eta;
    650     const double prog = st->progress * 100.0; /* [0...100] */
     650    const double prog = st->percentDone * 100.0; /* [0...100] */
    651651    const int status = st->status;
    652652
     
    673673        }
    674674    }
    675     else if(TR_STATUS_SEED & status)
     675    else if( TR_STATUS_SEED & status )
    676676    {
    677677        top = g_strdup_printf(
     
    680680            upeers, tpeers );
    681681    }
     682    else if( TR_STATUS_DONE & status )
     683    {
     684        top = g_strdup_printf(
     685            ngettext( "Uploading to %d of %d peer",
     686                      "Uploading to %d of %d peers", tpeers ),
     687            upeers, tpeers );
     688    }
    682689    else if( TR_STATUS_STOPPING & status )
    683690    {
  • trunk/gtk/tr_window.c

    r1998 r2149  
    3030#include "transmission.h"
    3131
     32#include "actions.h"
     33#include "hig.h"
    3234#include "tr_cell_renderer_progress.h"
    3335#include "tr_core.h"
     
    3638#include "util.h"
    3739
    38 #define ITEM_ACTION             "tr-window-item-action"
    39 
    40 enum
    41 {
    42     PROP_MODEL = 1,
    43     PROP_SELECTION,
    44     PROP_DOUBLECLICK,
    45     PROP_DRAG,
    46 };
    47 
    48 static void
    49 tr_window_init( GTypeInstance * instance, gpointer g_class );
    50 static void
    51 tr_window_set_property( GObject * object, guint property_id,
    52                         const GValue * value, GParamSpec * pspec );
    53 static void
    54 tr_window_get_property( GObject * object, guint property_id,
    55                         GValue * value, GParamSpec * pspec);
    56 static void
    57 tr_window_class_init( gpointer g_class, gpointer g_class_data );
    58 static void
    59 tr_window_dispose( GObject * obj );
    60 static GtkTreeView *
    61 makeview( TrWindow * self );
    62 static void
    63 stylekludge( GObject * obj, GParamSpec * spec, gpointer data );
    64 static void
    65 fixbuttons( GtkTreeSelection *sel, TrWindow * self );
    66 static void
    67 formatname( GtkTreeViewColumn * col, GtkCellRenderer * rend,
    68             GtkTreeModel * model, GtkTreeIter * iter, gpointer data );
    69 static void
    70 formatprog( GtkTreeViewColumn * col, GtkCellRenderer * rend,
    71             GtkTreeModel * model, GtkTreeIter * iter, gpointer data );
    72 static gboolean
    73 listclick( GtkWidget * view, GdkEventButton * event, gpointer data );
    74 static gboolean
    75 listpopup( GtkWidget * view SHUTUP, gpointer data );
    76 static void
    77 popupmenu( TrWindow * self, GdkEventButton * event );
    78 static void
    79 itemclick( GObject * obj, gpointer data );
    80 static void
    81 doubleclick( GtkWidget * view, GtkTreePath * path,
    82              GtkTreeViewColumn * col SHUTUP, gpointer data );
    83 static void
    84 emitaction( TrWindow * self, int id );
    85 static void
    86 orstatus( GtkTreeModel * model, GtkTreePath * path SHUTUP, GtkTreeIter * iter,
    87           gpointer data );
    88 static void
    89 istorsel( GtkTreeModel * model, GtkTreePath * path SHUTUP, GtkTreeIter * iter,
    90           gpointer data );
    91 
    92 GType
    93 tr_window_get_type( void )
    94 {
    95     static GType type = 0;
    96 
    97     if( 0 == type )
    98     {
    99         static const GTypeInfo info =
    100         {
    101             sizeof( TrWindowClass ),
    102             NULL,                       /* base_init */
    103             NULL,                       /* base_finalize */
    104             tr_window_class_init,       /* class_init */
    105             NULL,                       /* class_finalize */
    106             NULL,                       /* class_data */
    107             sizeof( TrWindow ),
    108             0,                          /* n_preallocs */
    109             tr_window_init,             /* instance_init */
    110             NULL,
    111         };
    112         type = g_type_register_static( GTK_TYPE_WINDOW, "TrWindow", &info, 0 );
    113     }
    114 
    115     return type;
    116 }
    117 
    118 static void
    119 tr_window_class_init( gpointer g_class, gpointer g_class_data SHUTUP )
    120 {
    121     GObjectClass  * gobject_class;
    122     TrWindowClass * trwindow_class;
    123     GParamSpec   * pspec;
    124 
    125     gobject_class = G_OBJECT_CLASS( g_class );
    126     gobject_class->set_property = tr_window_set_property;
    127     gobject_class->get_property = tr_window_get_property;
    128     gobject_class->dispose      = tr_window_dispose;
    129 
    130     pspec = g_param_spec_object( "model", "Model",
    131                                  "The GtkTreeModel for the list view.",
    132                                  GTK_TYPE_TREE_MODEL, G_PARAM_READWRITE );
    133     g_object_class_install_property( gobject_class, PROP_MODEL, pspec );
    134 
    135     pspec = g_param_spec_object( "selection", "Selection",
    136                                  "The GtkTreeSelection for the list view.",
    137                                  GTK_TYPE_TREE_SELECTION, G_PARAM_READABLE );
    138     g_object_class_install_property( gobject_class, PROP_SELECTION, pspec );
    139 
    140     pspec = g_param_spec_int( "double-click-action", "Double-click action",
    141                               "The action id to signal on a double click.",
    142                               G_MININT, G_MAXINT, -1, G_PARAM_READWRITE );
    143     g_object_class_install_property( gobject_class, PROP_DOUBLECLICK, pspec );
    144 
    145     pspec = g_param_spec_object( "drag-widget", "Drag widget",
    146                                  "The GtkWidget used for drag-and-drop.",
    147                                  GTK_TYPE_WIDGET, G_PARAM_READABLE );
    148     g_object_class_install_property( gobject_class, PROP_DRAG, pspec );
    149 
    150     trwindow_class = TR_WINDOW_CLASS( g_class );
    151     trwindow_class->actionsig =
    152         g_signal_new( "action", G_TYPE_FROM_CLASS( g_class ),
    153                        G_SIGNAL_RUN_LAST, 0, NULL, NULL,
    154                        g_cclosure_marshal_VOID__INT,
    155                        G_TYPE_NONE, 1, G_TYPE_INT );
    156 }
    157 
    158 static void
    159 tr_window_init( GTypeInstance * instance, gpointer g_class SHUTUP )
    160 {
    161     TrWindow * self = ( TrWindow * )instance;
    162     GtkWidget * vbox, * scroll, * status, * tools, * menu, * file, * item;
    163 
    164     vbox   = gtk_vbox_new( FALSE, 0 );
    165     scroll = gtk_scrolled_window_new( NULL, NULL );
    166     status = gtk_statusbar_new();
    167     tools  = gtk_toolbar_new();
    168     menu   = gtk_menu_bar_new();
    169     file   = gtk_menu_new();
    170     item   = gtk_menu_item_new_with_mnemonic( _("_File") );
    171 
    172     self->scroll          = GTK_SCROLLED_WINDOW( scroll );
    173     self->view            = makeview( self );
    174     self->status          = GTK_STATUSBAR( status );
    175     self->toolbar         = GTK_TOOLBAR( tools );
    176     self->menu            = GTK_MENU_SHELL( file );
    177     /* this should have been set by makeview() */
    178     g_assert( NULL != self->namerend );
    179     self->doubleclick     = -1;
    180     self->actions         = NULL;
    181     self->accel           = gtk_accel_group_new();
    182     self->stupidpopuphack = NULL;
    183     self->disposed        = FALSE;
    184 
    185     gtk_window_add_accel_group( GTK_WINDOW( self ), self->accel );
    186     gtk_menu_set_accel_group( GTK_MENU( self->menu ), self->accel );
    187 
    188     gtk_menu_item_set_submenu( GTK_MENU_ITEM( item ), file );
    189     gtk_menu_shell_append( GTK_MENU_SHELL( menu ), item );
    190     gtk_box_pack_start( GTK_BOX( vbox ), menu, FALSE, FALSE, 0 );
    191 
    192     gtk_toolbar_set_tooltips( self->toolbar, TRUE );
    193     gtk_toolbar_set_show_arrow( self->toolbar, FALSE );
    194     gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( self->toolbar ),
    195                         FALSE, FALSE, 0 );
    196 
    197     gtk_container_add( GTK_CONTAINER( scroll ), GTK_WIDGET( self->view ) );
    198     gtk_box_pack_start( GTK_BOX( vbox ), scroll, TRUE, TRUE, 0 );
    199 
    200     gtk_statusbar_push( self->status, 0, "" );
    201     gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( self->status ),
    202                         FALSE, FALSE, 0 );
    203 
    204     gtk_container_set_focus_child( GTK_CONTAINER( vbox ), scroll );
    205     gtk_widget_show_all( vbox );
    206     gtk_container_add( GTK_CONTAINER( self ), vbox );
    207     gtk_window_set_title( GTK_WINDOW( self ), g_get_application_name());
    208     gtk_window_set_role( GTK_WINDOW( self ), "tr-main" );
    209 }
    210 
    211 static void
    212 tr_window_set_property( GObject * object, guint property_id,
    213                         const GValue * value SHUTUP, GParamSpec * pspec)
    214 {
    215     TrWindow         * self = ( TrWindow * )object;
    216 
    217     if( self->disposed )
    218     {
    219         return;
    220     }
    221 
    222     switch( property_id )
    223     {
    224         case PROP_MODEL:
    225             gtk_tree_view_set_model( self->view, g_value_get_object( value ) );
    226             break;
    227         case PROP_DOUBLECLICK:
    228             self->doubleclick = g_value_get_int( value );
    229             break;
    230         default:
    231             G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, pspec );
    232             break;
    233     }
    234 }
    235 
    236 static void
    237 tr_window_get_property( GObject * object, guint property_id,
    238                         GValue * value SHUTUP, GParamSpec * pspec )
    239 {
    240     TrWindow         * self = ( TrWindow * )object;
    241 
    242     if( self->disposed )
    243     {
    244         return;
    245     }
    246 
    247     switch( property_id )
    248     {
    249         case PROP_MODEL:
    250             g_value_set_object( value, gtk_tree_view_get_model( self->view ) );
    251             break;
    252         case PROP_SELECTION:
    253             g_value_set_object( value,
    254                                 gtk_tree_view_get_selection( self->view ) );
    255             break;
    256         case PROP_DOUBLECLICK:
    257             g_value_set_int( value, self->doubleclick );
    258             break;
    259         case PROP_DRAG:
    260             g_value_set_object( value, self->view );
    261             break;
    262         default:
    263             G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, pspec );
    264             break;
    265     }
    266 }
    267 
    268 static void
    269 tr_window_dispose( GObject * obj )
    270 {
    271     TrWindow     * self = ( TrWindow * )obj;
    272     GObjectClass * parent;
    273 
    274     if( self->disposed )
    275     {
    276         return;
    277     }
    278     self->disposed = TRUE;
    279 
    280     g_list_foreach( self->actions, ( GFunc )action_free, NULL );
    281     g_list_free( self->actions );
    282     g_object_unref( self->accel );
    283     if( NULL != self->stupidpopuphack )
    284     {
    285         gtk_widget_destroy( self->stupidpopuphack );
    286     }
    287     self->stupidpopuphack = NULL;
    288 
    289     /* Chain up to the parent class */
    290     parent = g_type_class_peek( g_type_parent( TR_WINDOW_TYPE ) );
    291     parent->dispose( obj );
    292 }
    293 
    294 GtkWidget *
    295 tr_window_new( void )
    296 {
    297     return g_object_new( TR_WINDOW_TYPE, NULL );
    298 }
    299 
    300 void
    301 tr_window_action_add( TrWindow * self, int id, int flags, const char * name,
    302                       const char * icon, const char * description, guint key )
    303 {
    304     struct action * act;
    305     GtkWidget     * sep;
    306 
    307     TR_IS_WINDOW( self );
    308     if( self->disposed )
    309     {
    310         return;
    311     }
    312 
    313     act = action_new( id, flags, name, icon );
    314 
    315     if( ACTF_TOOL & flags )
    316     {
    317         act->tool = action_maketool( act, ITEM_ACTION,
    318                                      G_CALLBACK( itemclick ), self );
    319         gtk_tool_item_set_tooltip( GTK_TOOL_ITEM( act->tool ),
    320                                    self->toolbar->tooltips, description, "" );
    321         gtk_toolbar_insert( self->toolbar, GTK_TOOL_ITEM( act->tool ), -1 );
    322     }
    323 
    324     if( ACTF_MENU & flags )
    325     {
    326         act->menu = action_makemenu( act, ITEM_ACTION, self->accel,
    327                                      "<transmission-mainwind>/file", key,
    328                                      G_CALLBACK( itemclick ), self );
    329         gtk_menu_shell_append( self->menu, act->menu );
    330     }
    331 
    332     if( ACTF_SEPARATOR & flags )
    333     {
    334         sep = gtk_separator_menu_item_new();
    335         gtk_widget_show( sep );
    336         gtk_menu_shell_append( self->menu, sep );
    337     }
    338 
    339     self->actions = g_list_append( self->actions, act );
    340 }
    341 
    342 void
    343 tr_window_update( TrWindow * self, float downspeed, float upspeed )
    344 {
    345     char * downstr, * upstr, * str;
    346 
    347     TR_IS_WINDOW( self );
    348     if( self->disposed )
    349     {
    350         return;
    351     }
    352 
    353     /* update the status bar */
    354     downstr = readablespeed ( downspeed );
    355     upstr   = readablespeed ( upspeed );
    356     str     = g_strdup_printf( _("     Total DL: %s     Total UL: %s"),
    357                                downstr, upstr );
    358     g_free( downstr );
    359     g_free( upstr );
    360     gtk_statusbar_pop( self->status, 0 );
    361     gtk_statusbar_push( self->status, 0, str );
     40typedef struct
     41{
     42    GtkWidget * scroll;
     43    GtkWidget * view;
     44    GtkWidget * status;
     45    GtkTreeSelection * selection;
     46    GtkCellRenderer * namerend;
     47}
     48PrivateData;
     49
     50#define PRIVATE_DATA_KEY "private-data"
     51
     52PrivateData*
     53get_private_data( TrWindow * w )
     54{
     55    return (PrivateData*) g_object_get_data (G_OBJECT(w), PRIVATE_DATA_KEY);
     56}
     57
     58/***
     59****
     60***/
     61
     62/* kludge to have the progress bars notice theme changes */
     63static void
     64stylekludge( GObject * obj, GParamSpec * spec, gpointer data )
     65{
     66    if( 0 == strcmp( "style", spec->name ) )
     67    {
     68        tr_cell_renderer_progress_reset_style(
     69            TR_CELL_RENDERER_PROGRESS( data ) );
     70        gtk_widget_queue_draw( GTK_WIDGET( obj ) );
     71    }
     72}
     73
     74static void
     75formatname( GtkTreeViewColumn * col SHUTUP, GtkCellRenderer * rend,
     76            GtkTreeModel * model, GtkTreeIter * iter, gpointer data SHUTUP )
     77{
     78    TrTorrent * gtor;
     79    char  * name, * mb, * str, * top, * bottom;
     80    guint64 size;
     81    int     status, err, eta, tpeers, upeers, dpeers;
     82
     83    gtk_tree_model_get( model, iter, MC_NAME, &name, MC_STAT, &status,
     84                        MC_ERR, &err, MC_SIZE, &size,
     85                        MC_ETA, &eta, MC_PEERS, &tpeers, MC_UPEERS, &upeers,
     86                        MC_DPEERS, &dpeers, MC_TORRENT, &gtor, -1 );
     87
     88    tpeers = MAX( tpeers, 0 );
     89    upeers = MAX( upeers, 0 );
     90    dpeers = MAX( dpeers, 0 );
     91    mb = readablesize(size);
     92
     93    top = tr_torrent_status_str ( gtor );
     94
     95    if( TR_OK != err )
     96    {
     97        char * terr;
     98        gtk_tree_model_get( model, iter, MC_TERR, &terr, -1 );
     99        bottom = g_strconcat( _("Error: "), terr, NULL );
     100        g_free( terr );
     101    }
     102    else if( TR_STATUS_DOWNLOAD & status )
     103    {
     104        bottom = g_strdup_printf( ngettext( "Downloading from %i of %i peer",
     105                                            "Downloading from %i of %i peers",
     106                                            tpeers ), upeers, tpeers );
     107    }
     108    else
     109    {
     110        bottom = NULL;
     111    }
     112
     113    str = g_markup_printf_escaped( "<big>%s (%s)</big>\n<small>%s\n%s</small>",
     114                                   name, mb, top,
     115                                   ( NULL == bottom ? "" : bottom ) );
     116    g_object_set( rend, "markup", str, NULL );
     117    g_free( name );
     118    g_free( mb );
    362119    g_free( str );
    363 
    364     /* the selection's status may have changed so update the buttons */
    365     fixbuttons( NULL, self );
    366 }
    367 
    368 void
    369 tr_window_show( TrWindow * self )
    370 {
    371     TR_IS_WINDOW( self );
    372 
    373     sizingmagic( GTK_WINDOW( self ), self->scroll,
    374                  GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS );
    375     g_object_set( self->namerend, "ellipsize", PANGO_ELLIPSIZE_END, NULL );
    376     gtk_widget_show( GTK_WIDGET( self ) );
    377 }
    378 
    379 static GtkTreeView *
    380 makeview( TrWindow * self )
     120    g_free( top );
     121    g_free( bottom );
     122    g_object_unref( gtor );
     123}
     124
     125static void
     126formatprog( GtkTreeViewColumn * col SHUTUP, GtkCellRenderer * rend,
     127            GtkTreeModel * model, GtkTreeIter * iter, gpointer data SHUTUP )
     128{
     129    char  * dlstr, * ulstr, * str, * marked;
     130    gfloat  prog, dl, ul;
     131    guint64 down, up;
     132
     133    gtk_tree_model_get( model, iter, MC_PROG_D, &prog, MC_DRATE, &dl,
     134                        MC_URATE, &ul, MC_DOWN, &down, MC_UP, &up, -1 );
     135    prog = MAX( prog, 0.0 );
     136    prog = MIN( prog, 1.0 );
     137
     138    ulstr = readablespeed (ul);
     139    if( 1.0 == prog )
     140    {
     141        dlstr = ratiostr( down, up );
     142        str = g_strdup_printf( _("Ratio: %s\nUL: %s"), dlstr, ulstr );
     143    }
     144    else
     145    {
     146        dlstr = readablespeed( dl );
     147        str = g_strdup_printf( _("DL: %s\nUL: %s"), dlstr, ulstr );
     148    }
     149    marked = g_markup_printf_escaped( "<small>%s</small>", str );
     150    g_object_set( rend, "markup", str, "progress", prog, NULL );
     151    g_free( dlstr );
     152    g_free( ulstr );
     153    g_free( str );
     154    g_free( marked );
     155}
     156
     157static void
     158on_popup_menu ( GtkWidget * self UNUSED, GdkEventButton * event )
     159{
     160    GtkWidget * menu = action_get_widget ( "/main-window-popup" );
     161    gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL,
     162                    (event ? event->button : 0),
     163                    (event ? event->time : 0));
     164}
     165
     166static void
     167view_row_activated ( GtkTreeView       * tree_view  UNUSED,
     168                     GtkTreePath       * path       UNUSED,
     169                     GtkTreeViewColumn * column     UNUSED,
     170                     gpointer            user_data  UNUSED )
     171{
     172    action_activate( "show-torrent-inspector" );
     173}
     174
     175static GtkWidget*
     176makeview( PrivateData * p )
    381177{
    382178    GtkWidget         * view;
     
    386182    char              * str;
    387183
    388     TR_IS_WINDOW( self );
    389 
    390184    view     = gtk_tree_view_new();
     185
     186    p->selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(view) );
    391187    namerend = gtk_cell_renderer_text_new();
    392     self->namerend = G_OBJECT( namerend );
     188    p->namerend = namerend;
    393189    /* note that this renderer is set to ellipsize, just not here */
    394190    col = gtk_tree_view_column_new_with_attributes( _("Name"), namerend,
     
    421217                                 GTK_SELECTION_MULTIPLE );
    422218
    423     g_signal_connect( G_OBJECT( sel ), "changed",
    424                       G_CALLBACK( fixbuttons ), self );
    425     g_signal_connect( G_OBJECT( view ), "button-press-event",
    426                       G_CALLBACK( listclick ), self );
    427     g_signal_connect( G_OBJECT( view ), "popup-menu",
    428                       G_CALLBACK( listpopup ), self );
    429     g_signal_connect( G_OBJECT( view ), "row-activated",
    430                       G_CALLBACK( doubleclick ), self );
    431 
    432     return GTK_TREE_VIEW( view );
    433 }
    434 
    435 /* kludge to have the progress bars notice theme changes */
    436 static void
    437 stylekludge( GObject * obj, GParamSpec * spec, gpointer data )
    438 {
    439     if( 0 == strcmp( "style", spec->name ) )
    440     {
    441         tr_cell_renderer_progress_reset_style(
    442             TR_CELL_RENDERER_PROGRESS( data ) );
    443         gtk_widget_queue_draw( GTK_WIDGET( obj ) );
    444     }
    445 }
    446 
    447 /* disable buttons and menuitems the user shouldn't be able to click on */
    448 static void
    449 fixbuttons( GtkTreeSelection *sel, TrWindow * self ) {
    450     gboolean        selected, avail;
    451     GList         * ii;
    452     int             status;
    453     struct action * act;
    454 
    455     TR_IS_WINDOW( self );
    456     if( self->disposed )
    457     {
    458         return;
    459     }
    460 
    461     if( NULL == sel )
    462     {
    463         sel = gtk_tree_view_get_selection( self->view );
    464     }
    465     status = 0;
    466     gtk_tree_selection_selected_foreach( sel, orstatus, &status );
    467     selected = ( 0 < gtk_tree_selection_count_selected_rows( sel ) );
    468 
    469     for( ii = g_list_first( self->actions ); NULL != ii; ii = ii->next )
    470     {
    471         act = ii->data;
    472         if( ACTF_ALWAYS & act->flags )
    473         {
    474             continue;
    475         }
    476         avail = ACT_ISAVAIL( act->flags, status );
    477         if( ACTF_TOOL & act->flags )
    478         {
    479             g_assert( NULL != act->tool );
    480             gtk_widget_set_sensitive( act->tool, selected && avail );
    481         }
    482         if( ACTF_MENU & act->flags )
    483         {
    484             g_assert( NULL != act->menu );
    485             gtk_widget_set_sensitive( act->menu, selected && avail );
    486         }
    487     }
    488 }
    489 
    490 static void
    491 formatname( GtkTreeViewColumn * col SHUTUP, GtkCellRenderer * rend,
    492             GtkTreeModel * model, GtkTreeIter * iter, gpointer data SHUTUP )
    493 {
    494     TrTorrent * gtor;
    495     char  * name, * mb, * str, * top, * bottom;
    496     guint64 size;
    497     int     status, err, eta, tpeers, upeers, dpeers;
    498 
    499     gtk_tree_model_get( model, iter, MC_NAME, &name, MC_STAT, &status,
    500                         MC_ERR, &err, MC_SIZE, &size,
    501                         MC_ETA, &eta, MC_PEERS, &tpeers, MC_UPEERS, &upeers,
    502                         MC_DPEERS, &dpeers, MC_TORRENT, &gtor, -1 );
    503 
    504     tpeers = MAX( tpeers, 0 );
    505     upeers = MAX( upeers, 0 );
    506     dpeers = MAX( dpeers, 0 );
    507     mb = readablesize(size);
    508 
    509     top = tr_torrent_status_str ( gtor );
    510 
    511     if( TR_OK != err )
    512     {
    513         char * terr;
    514         gtk_tree_model_get( model, iter, MC_TERR, &terr, -1 );
    515         bottom = g_strconcat( _("Error: "), terr, NULL );
    516         g_free( terr );
    517     }
    518     else if( TR_STATUS_DOWNLOAD & status )
    519     {
    520         bottom = g_strdup_printf( ngettext( "Downloading from %i of %i peer",
    521                                             "Downloading from %i of %i peers",
    522                                             tpeers ), upeers, tpeers );
    523     }
    524     else
    525     {
    526         bottom = NULL;
    527     }
    528 
    529     str = g_markup_printf_escaped( "<big>%s (%s)</big>\n<small>%s\n%s</small>",
    530                                    name, mb, top,
    531                                    ( NULL == bottom ? "" : bottom ) );
    532     g_object_set( rend, "markup", str, NULL );
    533     g_free( name );
    534     g_free( mb );
     219    g_signal_connect( view, "popup-menu",
     220                      G_CALLBACK(on_popup_menu), NULL );
     221    g_signal_connect( view, "button-press-event",
     222                      G_CALLBACK(on_tree_view_button_pressed), on_popup_menu);
     223    g_signal_connect( view, "row-activated",
     224                      G_CALLBACK(view_row_activated), NULL);
     225
     226    return view;
     227}
     228
     229static void
     230realized_cb ( GtkWidget * wind, gpointer unused UNUSED )
     231{
     232    PrivateData * p = get_private_data( GTK_WINDOW( wind ) );
     233    sizingmagic( GTK_WINDOW(wind), GTK_SCROLLED_WINDOW( p->scroll ),
     234                 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS );
     235    g_object_set( p->namerend, "ellipsize", PANGO_ELLIPSIZE_END, NULL );
     236}
     237
     238/***
     239****  PUBLIC
     240***/
     241
     242GtkWidget *
     243tr_window_new( GtkUIManager * ui_manager )
     244{
     245    PrivateData * p = g_new( PrivateData, 1 );
     246    GtkWidget *vbox, *w, *self;
     247
     248    /* make the window */
     249    self = gtk_window_new (GTK_WINDOW_TOPLEVEL);
     250    g_object_set_data_full(G_OBJECT(self), PRIVATE_DATA_KEY, p, g_free );
     251    gtk_window_set_title( GTK_WINDOW( self ), g_get_application_name());
     252    gtk_window_set_role( GTK_WINDOW( self ), "tr-main" );
     253    gtk_window_add_accel_group (GTK_WINDOW(self),
     254                                gtk_ui_manager_get_accel_group (ui_manager));
     255    g_signal_connect( self, "realize", G_CALLBACK(realized_cb), NULL);
     256
     257    /* window's main container */
     258    vbox = gtk_vbox_new (FALSE, 0);
     259    gtk_container_add (GTK_CONTAINER(self), vbox);
     260
     261    /* main menu */
     262    w = action_get_widget( "/main-window-menu" );
     263    gtk_box_pack_start( GTK_BOX(vbox), w, FALSE, FALSE, 0 );
     264
     265    /* toolbar */
     266    w = action_get_widget( "/main-window-toolbar" );
     267    gtk_box_pack_start( GTK_BOX(vbox), w, FALSE, FALSE, 0 );
     268
     269    /* workarea */
     270    p->view = makeview( p );
     271    w = p->scroll = gtk_scrolled_window_new( NULL, NULL );
     272    gtk_container_add( GTK_CONTAINER(w), p->view );
     273    gtk_box_pack_start_defaults( GTK_BOX(vbox), w );
     274    gtk_container_set_focus_child( GTK_CONTAINER( vbox ), w );
     275
     276    /* statusbar */
     277    w = p->status = gtk_statusbar_new ();
     278    gtk_statusbar_push( GTK_STATUSBAR(w), 0, "" );
     279    gtk_box_pack_start( GTK_BOX(vbox), w, FALSE, FALSE, 0 );
     280
     281    /* show all but the window */
     282    gtk_widget_show_all( vbox );
     283
     284    return self;
     285}
     286
     287void
     288tr_window_update( TrWindow * self, float downspeed, float upspeed )
     289{
     290    PrivateData * p = get_private_data( self );
     291    GtkStatusbar * status = GTK_STATUSBAR( p->status );
     292
     293    /* update the status bar */
     294    char * downstr = readablespeed ( downspeed );
     295    char * upstr   = readablespeed ( upspeed );
     296    char * str     = g_strdup_printf( _("     Total DL: %s     Total UL: %s"),
     297                                      downstr, upstr );
     298    g_free( downstr );
     299    g_free( upstr );
     300    gtk_statusbar_pop( status, 0 );
     301    gtk_statusbar_push( status, 0, str );
    535302    g_free( str );
    536     g_free( top );
    537     g_free( bottom );
    538     g_object_unref( gtor );
    539 }
    540 
    541 static void
    542 formatprog( GtkTreeViewColumn * col SHUTUP, GtkCellRenderer * rend,
    543             GtkTreeModel * model, GtkTreeIter * iter, gpointer data SHUTUP )
    544 {
    545     char  * dlstr, * ulstr, * str, * marked;
    546     gfloat  prog, dl, ul;
    547     guint64 down, up;
    548 
    549     gtk_tree_model_get( model, iter, MC_PROG, &prog, MC_DRATE, &dl,
    550                         MC_URATE, &ul, MC_DOWN, &down, MC_UP, &up, -1 );
    551     prog = MAX( prog, 0.0 );
    552     prog = MIN( prog, 1.0 );
    553 
    554     ulstr = readablespeed (ul);
    555     if( 1.0 == prog )
    556     {
    557         dlstr = ratiostr( down, up );
    558         str = g_strdup_printf( _("Ratio: %s\nUL: %s"), dlstr, ulstr );
    559     }
    560     else
    561     {
    562         dlstr = readablespeed( dl );
    563         str = g_strdup_printf( _("DL: %s\nUL: %s"), dlstr, ulstr );
    564     }
    565     marked = g_markup_printf_escaped( "<small>%s</small>", str );
    566     g_object_set( rend, "markup", str, "progress", prog, NULL );
    567     g_free( dlstr );
    568     g_free( ulstr );
    569     g_free( str );
    570     g_free( marked );
    571 }
    572 
    573 /* show a popup menu for a right-click on the list */
    574 static gboolean
    575 listclick( GtkWidget * view, GdkEventButton * event, gpointer data )
    576 {
    577     TrWindow         * self;
    578     GtkTreeSelection * sel;
    579     GtkTreePath      * path;
    580     GtkTreeModel     * model;
    581     GtkTreeIter        iter;
    582     int                status;
    583     TrTorrent        * tor, * issel;
    584 
    585     if( GDK_BUTTON_PRESS != event->type || 3 != event->button )
    586     {
    587         return FALSE;
    588     }
    589 
    590     TR_IS_WINDOW( data );
    591     self = TR_WINDOW( data );
    592     if( self->disposed )
    593     {
    594         return FALSE;
    595     }
    596 
    597     sel = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) );
    598     model = gtk_tree_view_get_model( GTK_TREE_VIEW( view ) );
    599 
    600     /* find what row, if any, the user clicked on */
    601     if( gtk_tree_view_get_path_at_pos( GTK_TREE_VIEW( view ),
    602                                         event->x, event->y, &path,
    603                                         NULL, NULL, NULL ) )
    604     {
    605         if( gtk_tree_model_get_iter( model, &iter, path ) )
    606         {
    607             /* get torrent and status for the right-clicked row */
    608             gtk_tree_model_get( model, &iter, MC_TORRENT, &tor,
    609                                 MC_STAT, &status, -1 );
    610             issel = tor;
    611             gtk_tree_selection_selected_foreach( sel, istorsel, &issel );
    612             g_object_unref( tor );
    613             /* if the clicked row isn't selected, select only it */
    614             if( NULL != issel )
    615             {
    616                 gtk_tree_selection_unselect_all( sel );
    617                 gtk_tree_selection_select_iter( sel, &iter );
    618             }
    619         }
    620         gtk_tree_path_free( path );
    621     }
    622     else
    623     {
    624         gtk_tree_selection_unselect_all( sel );
    625     }
    626 
    627     popupmenu( self, event );
    628 
    629     return TRUE;
    630 }
    631 
    632 static gboolean
    633 listpopup( GtkWidget * view SHUTUP, gpointer data )
    634 {
    635     popupmenu( TR_WINDOW( data ), NULL );
    636     return TRUE;
    637 }
    638 
    639 static void
    640 popupmenu( TrWindow * self, GdkEventButton * event )
    641 {
    642     GtkTreeSelection * sel;
    643     int                count, status;
    644     GtkWidget        * menu, * item;
    645     GList            * ii;
    646     struct action    * act;
    647 
    648     TR_IS_WINDOW( self );
    649     if( self->disposed )
    650     {
    651         return;
    652     }
    653 
    654     sel   = gtk_tree_view_get_selection( self->view );
    655     count = gtk_tree_selection_count_selected_rows( sel );
    656     menu  = gtk_menu_new();
    657 
    658     if( NULL != self->stupidpopuphack )
    659     {
    660         gtk_widget_destroy( self->stupidpopuphack );
    661     }
    662     self->stupidpopuphack = menu;
    663 
    664     status = 0;
    665     gtk_tree_selection_selected_foreach( sel, orstatus, &status );
    666 
    667     for( ii = g_list_first( self->actions ); NULL != ii; ii = ii->next )
    668     {
    669         act = ii->data;
    670         if( ACTF_SEPARATOR & act->flags )
    671         {
    672             item = gtk_separator_menu_item_new();
    673             gtk_widget_show( item );
    674             gtk_menu_shell_append( GTK_MENU_SHELL( menu ), item );
    675         }
    676         else if( ACTF_MENU & act->flags && ACT_ISAVAIL( act->flags, status ) )
    677         {
    678             item = action_makemenu( act, ITEM_ACTION, NULL, NULL, 0,
    679                                     G_CALLBACK( itemclick ), self );
    680             gtk_menu_shell_append( GTK_MENU_SHELL( menu ), item );
    681         }
    682     }
    683 
    684     gtk_widget_show( menu );
    685 
    686     gtk_menu_popup( GTK_MENU( menu ), NULL, NULL, NULL, NULL,
    687                     ( NULL == event ? 0 : event->button ),
    688                     gdk_event_get_time( (GdkEvent*)event ) );
    689 }
    690 
    691 static void
    692 itemclick( GObject * obj, gpointer data )
    693 {
    694     TrWindow      * self;
    695     struct action * act;
    696 
    697     TR_IS_WINDOW( data );
    698     self = TR_WINDOW( data );
    699     act = g_object_get_data( obj, ITEM_ACTION );
    700 
    701     emitaction( self, act->id );
    702 }
    703 
    704 static void
    705 doubleclick( GtkWidget * view SHUTUP, GtkTreePath * path,
    706              GtkTreeViewColumn * col SHUTUP, gpointer data )
    707 {
    708     TrWindow         * self;
    709     GtkTreeSelection * sel;
    710 
    711     TR_IS_WINDOW( data );
    712     self = TR_WINDOW( data );
    713     if( self->disposed || 0 > self->doubleclick )
    714     {
    715         return;
    716     }
    717 
    718     sel = gtk_tree_view_get_selection( self->view );
    719     gtk_tree_selection_select_path( sel, path );
    720 
    721     emitaction( self, self->doubleclick );
    722 }
    723 
    724 static void
    725 emitaction( TrWindow * self, int id )
    726 {
    727     TrWindowClass * class;
    728 
    729     TR_IS_WINDOW( self );
    730     if( self->disposed )
    731     {
    732         return;
    733     }
    734 
    735     class = g_type_class_peek( TR_WINDOW_TYPE );
    736     g_signal_emit( self, class->actionsig, 0, id );
    737 }
    738 
    739 /* use with gtk_tree_selection_selected_foreach to | status of selected rows */
    740 static void
    741 orstatus( GtkTreeModel * model, GtkTreePath * path SHUTUP, GtkTreeIter * iter,
    742           gpointer data )
    743 {
    744     int * allstatus, thisstatus;
    745 
    746   allstatus = data;
    747   gtk_tree_model_get( model, iter, MC_STAT, &thisstatus, -1 );
    748   *allstatus |= thisstatus;
    749 }
    750 
    751 /* data should be a TrTorrent**, will set torrent to NULL if it's selected */
    752 static void
    753 istorsel( GtkTreeModel * model, GtkTreePath * path SHUTUP, GtkTreeIter * iter,
    754           gpointer data )
    755 {
    756     TrTorrent ** torref, * tor;
    757 
    758     torref = data;
    759     if( NULL != *torref )
    760     {
    761         gtk_tree_model_get( model, iter, MC_TORRENT, &tor, -1 );
    762         if( tor == *torref )
    763         {
    764             *torref = NULL;
    765         }
    766         g_object_unref( tor );
    767     }
    768 }
     303}
     304
     305GtkTreeSelection*
     306tr_window_get_selection ( TrWindow * w )
     307{
     308    return get_private_data(w)->selection;
     309}
  • trunk/gtk/tr_window.h

    r1647 r2149  
    2626#define TR_WINDOW_H
    2727
    28 #include <glib-object.h>
    2928#include <gtk/gtk.h>
    3029
    31 #define TR_WINDOW_TYPE            ( tr_window_get_type() )
     30typedef GtkWindow TrWindow;
    3231
    33 #define TR_WINDOW( obj ) \
    34   ( G_TYPE_CHECK_INSTANCE_CAST( (obj),   TR_WINDOW_TYPE, TrWindow ) )
     32GtkTreeSelection * tr_window_get_selection( TrWindow* wind );
    3533
    36 #define TR_WINDOW_CLASS( class ) \
    37   ( G_TYPE_CHECK_CLASS_CAST(    (class), TR_WINDOW_TYPE, TrWindowClass ) )
    38 
    39 #define TR_IS_WINDOW( obj ) \
    40   ( G_TYPE_CHECK_INSTANCE_TYPE( (obj),   TR_WINDOW_TYPE ) )
    41 
    42 #define TR_IS_WINDOW_CLASS( class ) \
    43   ( G_TYPE_CHECK_CLASS_TYPE(    (class), TR_WINDOW_TYPE ) )
    44 
    45 #define TR_WINDOW_GET_CLASS( obj ) \
    46   ( G_TYPE_INSTANCE_GET_CLASS(  (obj),   TR_WINDOW_TYPE, TrWindowClass ) )
    47 
    48 typedef struct _TrWindow TrWindow;
    49 typedef struct _TrWindowClass TrWindowClass;
    50 
    51 /* treat the contents of this structure as private */
    52 struct _TrWindow
    53 {
    54     GtkWindow           parent;
    55     GtkScrolledWindow * scroll;
    56     GtkTreeView       * view;
    57     GtkStatusbar      * status;
    58     GtkToolbar        * toolbar;
    59     GtkMenuShell      * menu;
    60     GObject           * namerend;
    61     int                 doubleclick;
    62     GList             * actions;
    63     GtkAccelGroup     * accel;
    64     GtkWidget         * stupidpopuphack;
    65     gboolean            disposed;
    66 };
    67 
    68 struct _TrWindowClass
    69 {
    70   GtkWindowClass parent;
    71   int            actionsig;
    72 };
    73 
    74 GType
    75 tr_window_get_type( void );
    76 
    77 GtkWidget *
    78 tr_window_new( void );
    79 
    80 void
    81 tr_window_action_add( TrWindow * wind, int id, int flags, const char * name,
    82                       const char * icon, const char * description, guint key );
     34GtkWidget * tr_window_new( GtkUIManager* );
    8335
    8436void
    8537tr_window_update( TrWindow * wind, float downspeed, float upspeed );
    8638
    87 /* some magic to show the window with a nice initial size */
    88 void
    89 tr_window_show( TrWindow * wind );
    90 
    9139#endif
  • trunk/gtk/transmission-gtk.1

    r1750 r2149  
    7878.An Josh Elsasser Aq josh@elsasser.org ,
    7979.An Eric Petit Aq titer@m0k.org ,
     80.An Mitchell Livingston Aq livings124@gmail.com ,
    8081and
    81 .An Mitchell Livingston Aq livings124@gmail.com .
     82.An Charles Kerr Aq charles@rebelbase.com .
    8283.Sh SEE ALSO
    8384.Xr transmissioncli 1 ,
  • trunk/gtk/util.c

    r2002 r2149  
    320320}
    321321
     322/**
     323 * don't use more than 50% the height of the screen, nor 80% the width.
     324 * but don't be too small, either -- set the minimums to 500 x 300
     325 */
    322326void
    323 sizingmagic( GtkWindow * wind, GtkScrolledWindow * scroll,
    324              GtkPolicyType hscroll, GtkPolicyType vscroll )
    325 {
     327sizingmagic( GtkWindow         * wind,
     328             GtkScrolledWindow * scroll,
     329             GtkPolicyType       hscroll,
     330             GtkPolicyType       vscroll )
     331{
     332    int width;
     333    int height;
    326334    GtkRequisition req;
    327     GdkScreen    * screen;
    328     int            width, height;
    329 
    330     screen = gtk_widget_get_screen( GTK_WIDGET( wind ) );
     335
     336    GdkScreen * screen = gtk_widget_get_screen( GTK_WIDGET( wind ) );
    331337
    332338    gtk_scrolled_window_set_policy( scroll, GTK_POLICY_NEVER,
    333                                     GTK_POLICY_NEVER );
     339                                            GTK_POLICY_NEVER );
    334340
    335341    gtk_widget_size_request( GTK_WIDGET( wind ), &req );
     342    req.height = MAX( req.height, 300 );
    336343    height = MIN( req.height, gdk_screen_get_height( screen ) / 5 * 4 );
    337344
    338345    gtk_scrolled_window_set_policy( scroll, GTK_POLICY_NEVER, vscroll );
    339 
    340346    gtk_widget_size_request( GTK_WIDGET( wind ), &req );
     347    req.width = MAX( req.width, 500 );
    341348    width = MIN( req.width, gdk_screen_get_width( screen ) / 2 );
    342349
    343350    gtk_window_set_default_size( wind, width, height );
    344 
    345351    gtk_scrolled_window_set_policy( scroll, hscroll, vscroll );
    346 }
    347 
    348 struct action *
    349 action_new( int id, int flags, const char * label, const char * stock )
    350 {
    351     struct action * act;
    352 
    353     act        = g_new0( struct action, 1 );
    354     act->id    = id;
    355     act->flags = flags;
    356     act->label = g_strdup( label );
    357     act->stock = g_strdup( stock );
    358     act->tool  = NULL;
    359     act->menu  = NULL;
    360 
    361     return act;
    362 }
    363 
    364 void
    365 action_free( struct action * act )
    366 {
    367     g_free( act->label );
    368     g_free( act->stock );
    369     g_free( act );
    370 }
    371 
    372 GtkWidget *
    373 action_maketool( struct action * act, const char * key,
    374                  GCallback func, gpointer data )
    375 {
    376     GtkToolItem * item;
    377 
    378     item = gtk_tool_button_new_from_stock( act->stock );
    379     if( NULL != act->label )
    380     {
    381         gtk_tool_button_set_label( GTK_TOOL_BUTTON( item ), act->label );
    382     }
    383     g_object_set_data( G_OBJECT( item ), key, act );
    384     g_signal_connect( item, "clicked", func, data );
    385     gtk_widget_show( GTK_WIDGET( item ) );
    386 
    387     return GTK_WIDGET( item );
    388 }
    389 
    390 GtkWidget *
    391 action_makemenu( struct action * act, const char * actkey,
    392                  GtkAccelGroup * accel, const char * path, guint keyval,
    393                  GCallback func, gpointer data )
    394 {
    395     GtkWidget  * item, * label;
    396     GdkModifierType mod;
    397     GtkStockItem stock;
    398     const char * name;
    399     char       * joined;
    400 
    401     mod = GDK_CONTROL_MASK;
    402     name = act->label;
    403     if( NULL == act->stock )
    404     {
    405         item = gtk_menu_item_new_with_label( act->label );
    406     }
    407     else
    408     {
    409         item = gtk_image_menu_item_new_from_stock( act->stock, NULL );
    410         if( NULL == act->label )
    411         {
    412             if( gtk_stock_lookup( act->stock, &stock ) )
    413             {
    414                 name = stock.label;
    415                 if( 0 == keyval )
    416                 {
    417                     keyval = stock.keyval;
    418                     mod    = stock.modifier;
    419                 }
    420             }
    421         }
    422         else
    423         {
    424             label = gtk_bin_get_child( GTK_BIN( item ) );
    425             gtk_label_set_text( GTK_LABEL( label ), act->label );
    426            
    427         }
    428     }
    429 
    430     if( NULL != accel && 0 < keyval && NULL != name )
    431     {
    432         joined = g_strjoin( "/", path, name, NULL );
    433         gtk_accel_map_add_entry( joined, keyval, mod );
    434         gtk_widget_set_accel_path( item, joined, accel );
    435         g_free( joined );
    436     }
    437     g_object_set_data( G_OBJECT( item ), actkey, act );
    438     g_signal_connect( item, "activate", func, data );
    439     gtk_widget_show( item );
    440 
    441     return item;
    442352}
    443353
     
    520430  gtk_widget_destroy(widget);
    521431}
     432
     433/* pop up the context menu if a user right-clicks.
     434   if the row they right-click on isn't selected, select it. */
     435gboolean
     436on_tree_view_button_pressed (GtkWidget       * view,
     437                             GdkEventButton  * event,
     438                             gpointer          func)
     439{
     440  GtkTreeView * tv = GTK_TREE_VIEW( view );
     441
     442  if (event->type == GDK_BUTTON_PRESS  &&  event->button == 3)
     443  {
     444    GtkTreeSelection * selection = gtk_tree_view_get_selection(tv);
     445    GtkTreePath *path;
     446    if (gtk_tree_view_get_path_at_pos (tv,
     447                                       (gint) event->x,
     448                                       (gint) event->y,
     449                                       &path, NULL, NULL, NULL))
     450    {
     451      if (!gtk_tree_selection_path_is_selected (selection, path))
     452      {
     453        gtk_tree_selection_unselect_all (selection);
     454        gtk_tree_selection_select_path (selection, path);
     455      }
     456      gtk_tree_path_free(path);
     457    }
     458   
     459    typedef void (PopupFunc)(GtkWidget*, GdkEventButton*);
     460    ((PopupFunc*)func)(view, event);
     461
     462    return TRUE;
     463  }
     464
     465  return FALSE;
     466}
     467
  • trunk/gtk/util.h

    r1998 r2149  
    4646/* used for a callback function with a data parameter */
    4747typedef void (*callbackfunc_t)(void*);
    48 
    49 /* flags indicating where and when an action is valid */
    50 #define ACTF_TOOL       ( 1 << 0 ) /* appear in the toolbar */
    51 #define ACTF_MENU       ( 1 << 1 ) /* appear in the popup menu */
    52 #define ACTF_ALWAYS     ( 1 << 2 ) /* available regardless of selection */
    53 #define ACTF_ACTIVE     ( 1 << 3 ) /* available for active torrent */
    54 #define ACTF_INACTIVE   ( 1 << 4 ) /* available for inactive torrent */
    55 #define ACTF_SEPARATOR  ( 1 << 5 ) /* dummy action to create menu separator */
    56 /* appear in the toolbar and the popup menu */
    57 #define ACTF_WHEREVER   ( ACTF_TOOL | ACTF_MENU )
    58 /* available if there is something selected */
    59 #define ACTF_WHATEVER   ( ACTF_ACTIVE | ACTF_INACTIVE )
    60 
    61 /* checking action flags against torrent status */
    62 #define ACT_ISAVAIL( flags, status ) \
    63     ( ( ACTF_ACTIVE   & (flags) && TR_STATUS_ACTIVE   & (status) ) || \
    64       ( ACTF_INACTIVE & (flags) && TR_STATUS_INACTIVE & (status) ) || \
    65         ACTF_ALWAYS   & (flags) )
    6648
    6749/* try to interpret a string as a textual representation of a boolean */
     
    132114#ifdef GTK_MAJOR_VERSION
    133115
    134 /* action handling */
    135 struct action
    136 {
    137     int            id;
    138     int            flags;
    139     char         * label;
    140     char         * stock;
    141     GtkWidget    * tool;
    142     GtkWidget    * menu;
    143     callbackfunc_t func;
    144     gpointer       data;
    145 };
    146 struct action *
    147 action_new( int id, int flags, const char * label, const char * stock );
    148 void
    149 action_free( struct action * act );
    150 GtkWidget *
    151 action_maketool( struct action * act, const char * key,
    152                  GCallback func, gpointer data );
    153 GtkWidget *
    154 action_makemenu( struct action * act, const char * actkey,
    155                  GtkAccelGroup * accel, const char * path, guint keyval,
    156                  GCallback func, gpointer data );
    157 
    158116/* here there be dragons */
    159117void
     
    185143              const char * format, va_list ap );
    186144
     145/* pop up the context menu if a user right-clicks.
     146   if the row they right-click on isn't selected, select it. */
     147gboolean
     148on_tree_view_button_pressed (GtkWidget       * view,
     149                             GdkEventButton  * event,
     150                             gpointer          unused);
     151
    187152#endif /* GTK_MAJOR_VERSION */
    188153
  • trunk/libtransmission/choking.c

    r2004 r2149  
    3030#endif
    3131
    32 /* We may try to allocate and free tables of size 0. Quick and dirty
    33    way to handle it... */
    34 void * tr_malloc( size_t size )
    35 {
    36     if( !size )
    37         return NULL;
    38     return malloc( size );
    39 }
    40 void tr_free( void * p )
    41 {
    42     if( p )
    43         free( p );
    44 }
     32/* We may try to allocate and free tables of size 0.
     33   Quick and dirty way to handle it... */
    4534#define malloc tr_malloc
    4635#define free   tr_free
  • trunk/libtransmission/completion.c

    r2004 r2149  
    2424
    2525#include "transmission.h"
     26#include "completion.h"
     27
     28struct tr_completion_s
     29{
     30    tr_torrent_t      * tor;
     31    tr_bitfield_t     * blockBitfield;
     32    uint8_t           * blockDownloaders;
     33    tr_bitfield_t     * pieceBitfield;
     34
     35    /* a block is missing if we don't have it AND there's not a request pending */
     36    int * missingBlocks;
     37
     38    /* a block is complete if and only if we have it */
     39    int * completeBlocks;
     40
     41    /* rather than calculating these over and over again in loops,
     42       just calculate them once */
     43    int nBlocksInPiece;
     44    int nBlocksInLastPiece;
     45};
     46
     47#define tr_cpCountBlocks(cp,piece) \
     48    (piece==cp->tor->info.pieceCount-1 ? cp->nBlocksInLastPiece : cp->nBlocksInPiece)
    2649
    2750tr_completion_t * tr_cpInit( tr_torrent_t * tor )
     
    3558    cp->pieceBitfield    = tr_bitfieldNew( tor->info.pieceCount );
    3659    cp->missingBlocks    = malloc( tor->info.pieceCount * sizeof( int ) );
     60    cp->completeBlocks   = malloc( tor->info.pieceCount * sizeof( int ) );
     61
     62    cp->nBlocksInLastPiece = tr_pieceCountBlocks ( tor->info.pieceCount - 1 );
     63    cp->nBlocksInPiece = tor->info.pieceCount==1 ? cp->nBlocksInLastPiece
     64                                                : tr_pieceCountBlocks( 0 );
    3765
    3866    tr_cpReset( cp );
     
    4775    tr_bitfieldFree( cp->pieceBitfield );
    4876    free(            cp->missingBlocks );
     77    free(            cp->completeBlocks );
    4978    free(            cp );
    5079}
     
    5584    int i;
    5685
    57     cp->blockCount = 0;
    5886    tr_bitfieldClear( cp->blockBitfield );
    5987    memset( cp->blockDownloaders, 0, tor->blockCount );
    6088    tr_bitfieldClear( cp->pieceBitfield );
    61     for( i = 0; i < tor->info.pieceCount; i++ )
    62     {
    63         cp->missingBlocks[i] = tr_pieceCountBlocks( i );
    64     }
    65 }
    66 
    67 float tr_cpCompletionAsFloat( tr_completion_t * cp )
    68 {
    69     return (float) cp->blockCount / (float) cp->tor->blockCount;
    70 }
    71 
    72 uint64_t tr_cpLeftBytes( tr_completion_t * cp )
    73 {
    74     tr_torrent_t * tor = cp->tor;
    75     uint64_t left;
    76     left = (uint64_t) ( cp->tor->blockCount - cp->blockCount ) *
    77            (uint64_t) tor->blockSize;
    78     if( !tr_bitfieldHas( cp->blockBitfield, cp->tor->blockCount - 1 ) &&
    79         tor->info.totalSize % tor->blockSize )
    80     {
    81         left += tor->info.totalSize % tor->blockSize;
    82         left -= tor->blockSize;
    83     }
    84     return left;
    85 }
    86 
    87 /* Pieces */
    88 int tr_cpPieceHasAllBlocks( tr_completion_t * cp, int piece )
    89 {
    90     tr_torrent_t * tor = cp->tor;
    91     int startBlock  = tr_pieceStartBlock( piece );
    92     int endBlock    = startBlock + tr_pieceCountBlocks( piece );
    93     int i;
    94 
    95     for( i = startBlock; i < endBlock; i++ )
    96     {
    97         if( !tr_bitfieldHas( cp->blockBitfield, i ) )
    98         {
    99             return 0;
    100         }
    101     }   
    102     return 1;
    103 }
    104 int tr_cpPieceIsComplete( tr_completion_t * cp, int piece )
    105 {
    106     return tr_bitfieldHas( cp->pieceBitfield, piece );
    107 }
    108 
    109 tr_bitfield_t * tr_cpPieceBitfield( tr_completion_t * cp )
     89    for( i = 0; i < tor->info.pieceCount; ++i ) {
     90        cp->missingBlocks[i] = tr_cpCountBlocks( cp, i );
     91        cp->completeBlocks[i] = 0;
     92    }
     93}
     94
     95int tr_cpPieceHasAllBlocks( const tr_completion_t * cp, int piece )
     96{
     97    return tr_cpPieceIsComplete( cp, piece );
     98}
     99
     100int tr_cpPieceIsComplete( const tr_completion_t * cp, int piece )
     101{
     102    const int total = tr_cpCountBlocks( cp, piece );
     103    const int have = cp->completeBlocks[piece];
     104    assert( have <= total );
     105    return have == total;
     106}
     107
     108const tr_bitfield_t * tr_cpPieceBitfield( const tr_completion_t * cp )
    110109{
    111110    return cp->pieceBitfield;
     
    114113void tr_cpPieceAdd( tr_completion_t * cp, int piece )
    115114{
    116     tr_torrent_t * tor = cp->tor;
    117     int startBlock, endBlock, i;
    118 
    119     startBlock = tr_pieceStartBlock( piece );
    120     endBlock   = startBlock + tr_pieceCountBlocks( piece );
    121     for( i = startBlock; i < endBlock; i++ )
    122     {
    123         tr_cpBlockAdd( cp, i );
    124     }
     115    int i;
     116    const tr_torrent_t * tor = cp->tor;
     117    const int n_blocks = tr_cpCountBlocks( cp, piece );
     118    const int startBlock = tr_pieceStartBlock( piece );
     119    const int endBlock   = startBlock + n_blocks;
     120
     121    cp->completeBlocks[piece] = n_blocks;
     122
     123    for( i=startBlock; i<endBlock; ++i )
     124        if( !cp->blockDownloaders[i] )
     125            --cp->missingBlocks[piece];
     126
     127    tr_bitfieldAddRange( cp->blockBitfield, startBlock, endBlock-1 );
    125128
    126129    tr_bitfieldAdd( cp->pieceBitfield, piece );
     
    129132void tr_cpPieceRem( tr_completion_t * cp, int piece )
    130133{
    131     tr_torrent_t * tor = cp->tor;
    132     int startBlock, endBlock, i;
    133 
    134     startBlock = tr_pieceStartBlock( piece );
    135     endBlock   = startBlock + tr_pieceCountBlocks( piece );
    136     for( i = startBlock; i < endBlock; i++ )
    137     {
    138         tr_cpBlockRem( cp, i );
    139     }
     134    int i;
     135    const tr_torrent_t * tor = cp->tor;
     136    const int n_blocks = tr_cpCountBlocks( cp, piece );
     137    const int startBlock = tr_pieceStartBlock( piece );
     138    const int endBlock   = startBlock + n_blocks;
     139
     140    cp->completeBlocks[piece] = 0;
     141
     142    for( i=startBlock; i<endBlock; ++i )
     143        if( !cp->blockDownloaders[i] )
     144            ++cp->missingBlocks[piece];
     145
     146    tr_bitfieldRemRange ( cp->blockBitfield, startBlock, endBlock-1 );
    140147
    141148    tr_bitfieldRem( cp->pieceBitfield, piece );
     
    155162void tr_cpDownloaderRem( tr_completion_t * cp, int block )
    156163{
    157     tr_torrent_t * tor = cp->tor;
     164    tr_torrent_t * tor;
     165
     166    assert( cp != NULL );
     167    assert( cp->tor != NULL );
     168    assert( 0 <= block );
     169
     170    tor = cp->tor;
    158171    (cp->blockDownloaders[block])--;
    159172    if( !cp->blockDownloaders[block] && !tr_cpBlockIsComplete( cp, block ) )
     
    165178int tr_cpBlockIsComplete( const tr_completion_t * cp, int block )
    166179{
     180    assert( cp != NULL );
     181    assert( 0 <= block );
     182
    167183    return tr_bitfieldHas( cp->blockBitfield, block );
    168184}
     
    170186void tr_cpBlockAdd( tr_completion_t * cp, int block )
    171187{
    172     tr_torrent_t * tor = cp->tor;
     188    const tr_torrent_t * tor;
     189
     190    assert( cp != NULL );
     191    assert( cp->tor != NULL );
     192    assert( 0 <= block );
     193
     194    tor = cp->tor;
     195
    173196    if( !tr_cpBlockIsComplete( cp, block ) )
    174197    {
    175         (cp->blockCount)++;
     198        const int piece = tr_blockPiece( block );
     199        ++cp->completeBlocks[piece];
     200
     201        if( cp->completeBlocks[piece] == tr_cpCountBlocks( cp, piece ) )
     202            tr_bitfieldAdd( cp->pieceBitfield, piece );
     203
    176204        if( !cp->blockDownloaders[block] )
    177         {
    178             (cp->missingBlocks[tr_blockPiece(block)])--;
    179         }
    180     }
    181     tr_bitfieldAdd( cp->blockBitfield, block );
    182 }
    183 
    184 void tr_cpBlockRem( tr_completion_t * cp, int block )
    185 {
    186     tr_torrent_t * tor = cp->tor;
    187     if( tr_cpBlockIsComplete( cp, block ) )
    188     {
    189         (cp->blockCount)--;
    190         if( !cp->blockDownloaders[block] )
    191         {
    192             (cp->missingBlocks[tr_blockPiece(block)])++;
    193         }
    194     }
    195     tr_bitfieldRem( cp->blockBitfield, block );
    196 }
    197 
    198 tr_bitfield_t * tr_cpBlockBitfield( tr_completion_t * cp )
    199 {
     205            cp->missingBlocks[piece]--;
     206
     207        tr_bitfieldAdd( cp->blockBitfield, block );
     208    }
     209}
     210
     211const tr_bitfield_t * tr_cpBlockBitfield( const tr_completion_t * cp )
     212{
     213    assert( cp != NULL );
     214
    200215    return cp->blockBitfield;
    201216}
    202217
    203 void tr_cpBlockBitfieldSet( tr_completion_t * cp, tr_bitfield_t * bitfield )
    204 {
    205     tr_torrent_t * tor = cp->tor;
    206     int i, j;
    207     int startBlock, endBlock;
    208     int pieceComplete;
    209 
    210     for( i = 0; i < cp->tor->info.pieceCount; i++ )
    211     {
    212         startBlock    = tr_pieceStartBlock( i );
    213         endBlock      = startBlock + tr_pieceCountBlocks( i );
    214         pieceComplete = 1;
    215 
    216         for( j = startBlock; j < endBlock; j++ )
    217         {
    218             if( tr_bitfieldHas( bitfield, j ) )
    219             {
    220                 tr_cpBlockAdd( cp, j );
    221             }
    222             else
    223             {
    224                 pieceComplete = 0;
    225             }
    226         }
    227         if( pieceComplete )
    228         {
    229             tr_cpPieceAdd( cp, i );
    230         }
    231     }
    232 }
    233 
    234 float tr_cpPercentBlocksInPiece( tr_completion_t * cp, int piece )
    235 {
    236     tr_torrent_t * tor = cp->tor;
    237     int i;
    238     int blockCount, startBlock, endBlock;
    239     int complete;
    240     tr_bitfield_t * bitfield;
    241    
    242     blockCount  = tr_pieceCountBlocks( piece );
    243     startBlock  = tr_pieceStartBlock( piece );
    244     endBlock    = startBlock + blockCount;
    245     complete    = 0;
    246    
    247     bitfield = cp->blockBitfield;
    248 
    249     for( i = startBlock; i < endBlock; i++ )
    250     {
     218void
     219tr_cpBlockBitfieldSet( tr_completion_t * cp, tr_bitfield_t * bitfield )
     220{
     221    int i;
     222
     223    assert( cp != NULL );
     224    assert( bitfield != NULL );
     225
     226    tr_cpReset( cp );
     227
     228    for( i=0; i < cp->tor->blockCount; ++i )
    251229        if( tr_bitfieldHas( bitfield, i ) )
    252         {
    253             complete++;
    254         }
    255     }   
    256 
    257     return (float)complete / (float)blockCount;
     230            tr_cpBlockAdd( cp, i );
     231}
     232
     233float tr_cpPercentBlocksInPiece( const tr_completion_t * cp, int piece )
     234{
     235    assert( cp != NULL );
     236
     237    return cp->completeBlocks[piece] / (double)tr_cpCountBlocks( cp, piece );
    258238}
    259239
    260240int tr_cpMissingBlockInPiece( const tr_completion_t * cp, int piece )
    261241{
     242    int i;
    262243    const tr_torrent_t * tor = cp->tor;
    263     int start, count, end, i;
    264 
    265     start = tr_pieceStartBlock( piece );
    266     count = tr_pieceCountBlocks( piece );
    267     end   = start + count;
    268 
    269     for( i = start; i < end; i++ )
    270     {
    271         if( tr_cpBlockIsComplete( cp, i ) || cp->blockDownloaders[i] )
    272         {
    273             continue;
    274         }
    275         return i;
    276     }
     244    const int start = tr_pieceStartBlock( piece );
     245    const int end   = start + tr_cpCountBlocks( cp, piece );
     246
     247    for( i = start; i < end; ++i )
     248        if( !tr_cpBlockIsComplete( cp, i ) && !cp->blockDownloaders[i] )
     249            return i;
    277250
    278251    return -1;
    279252}
    280253
    281 int tr_cpMostMissingBlockInPiece( tr_completion_t * cp, int piece,
    282                                   int * downloaders )
     254int tr_cpMostMissingBlockInPiece( const tr_completion_t * cp,
     255                                  int                     piece,
     256                                  int                   * downloaders )
    283257{
    284258    tr_torrent_t * tor = cp->tor;
     
    287261
    288262    start = tr_pieceStartBlock( piece );
    289     count = tr_pieceCountBlocks( piece );
     263    count = tr_cpCountBlocks( cp, piece );
    290264    end   = start + count;
    291265
     
    325299}
    326300
     301
     302int
     303tr_cpMissingBlocksForPiece( const tr_completion_t * cp, int piece )
     304{
     305    assert( cp != NULL );
     306
     307    return cp->missingBlocks[piece];
     308}
     309
     310/***
     311****
     312***/
     313
     314cp_status_t
     315tr_cpGetStatus ( const tr_completion_t * cp )
     316{
     317    int i;
     318    int ret = TR_CP_COMPLETE;
     319    const tr_info_t * info;
     320
     321    assert( cp != NULL );
     322    assert( cp->tor != NULL );
     323
     324    info = &cp->tor->info;
     325    for( i=0; i<info->pieceCount; ++i ) {
     326        if( tr_cpPieceIsComplete( cp, i ) )
     327            continue;
     328        if( info->pieces[i].priority != TR_PRI_DND)
     329            return TR_CP_INCOMPLETE;
     330        ret = TR_CP_DONE;
     331    }
     332
     333    return ret;
     334}
     335
     336uint64_t
     337tr_cpLeftUntilComplete ( const tr_completion_t * cp )
     338{
     339    int i;
     340    uint64_t b=0;
     341    const tr_torrent_t * tor;
     342    const tr_info_t * info;
     343
     344    assert( cp != NULL );
     345    assert( cp->tor != NULL );
     346
     347    tor = cp->tor;
     348    info = &tor->info;
     349    for( i=0; i<info->pieceCount; ++i )
     350        if( !tr_cpPieceIsComplete( cp, i ) )
     351            b += ( tr_cpCountBlocks( cp, i ) - cp->completeBlocks[ i ] );
     352
     353    return b * tor->blockSize;;
     354}
     355
     356uint64_t
     357tr_cpLeftUntilDone ( const tr_completion_t * cp )
     358{
     359    int i;
     360    uint64_t b=0;
     361    const tr_torrent_t * tor;
     362    const tr_info_t * info;
     363
     364    assert( cp != NULL );
     365    assert( cp->tor != NULL );
     366
     367    tor = cp->tor;
     368    info = &tor->info;
     369
     370    for( i=0; i<info->pieceCount; ++i )
     371        if( !tr_cpPieceIsComplete( cp, i ) && info->pieces[i].priority != TR_PRI_DND )
     372            b += tor->blockSize * (tr_cpCountBlocks( cp, i ) - cp->completeBlocks[ i ] );
     373
     374    return b;
     375}
     376
     377float
     378tr_cpPercentComplete ( const tr_completion_t * cp )
     379{
     380    int i;
     381    uint64_t have=0;
     382    const tr_torrent_t * tor;
     383
     384    assert( cp != NULL );
     385    assert( cp->tor != NULL );
     386
     387    tor = cp->tor;
     388    for( i=0; i<tor->info.pieceCount; ++i )
     389        have += cp->completeBlocks[ i ];
     390
     391    return tor->blockCount ? (float)have / (float)tor->blockCount : 0.0f;
     392}
     393
     394float
     395tr_cpPercentDone( const tr_completion_t * cp )
     396{
     397    int i;
     398    uint64_t have=0, total=0;
     399    const tr_torrent_t * tor;
     400
     401    assert( cp != NULL );
     402    assert( cp->tor != NULL );
     403
     404    tor = cp->tor;
     405
     406    for( i=0; i<tor->info.pieceCount; ++i ) {
     407        if( tor->info.pieces[i].priority != TR_PRI_DND) {
     408            total += tr_cpCountBlocks( cp, i );
     409            have += cp->completeBlocks[ i ];
     410        }
     411    }
     412
     413    return !total ? 0.0f : (float)have / (float)total;
     414}
     415
     416uint64_t
     417tr_cpDownloadedValid( const tr_completion_t * cp )
     418{
     419    int i, n;
     420    uint64_t b=0;
     421
     422    for( i=0, n=cp->tor->info.pieceCount; i<n; ++i )
     423        b += cp->completeBlocks[ i ];
     424
     425   return b * cp->tor->blockSize;
     426}
  • trunk/libtransmission/completion.h

    r2004 r2149  
    2323 *****************************************************************************/
    2424
    25 struct tr_completion_s
    26 {
    27     tr_torrent_t      * tor;
    28     tr_bitfield_t     * blockBitfield;
    29     uint8_t           * blockDownloaders;
    30     int                 blockCount;
    31     tr_bitfield_t     * pieceBitfield;
    32     int               * missingBlocks;
    33 };
     25#ifndef TR_COMPLETION_H
     26#define TR_COMPLETION_H
    3427
    35 tr_completion_t * tr_cpInit( tr_torrent_t * );
    36 void              tr_cpClose( tr_completion_t * );
    37 void              tr_cpReset( tr_completion_t * );
     28#include "transmission.h"
     29
     30tr_completion_t     * tr_cpInit( tr_torrent_t * );
     31void                  tr_cpClose( tr_completion_t * );
     32void                  tr_cpReset( tr_completion_t * );
    3833
    3934/* General */
    40 float             tr_cpCompletionAsFloat( tr_completion_t * );
    41 static inline int tr_cpIsSeeding( tr_completion_t * cp )
    42 {
    43     return ( cp->blockCount == cp->tor->blockCount );
    44 }
    45 uint64_t          tr_cpLeftBytes( tr_completion_t * );
     35
     36cp_status_t           tr_cpGetStatus ( const tr_completion_t * );
     37
     38uint64_t              tr_cpDownloadedValid( const tr_completion_t * );
     39uint64_t              tr_cpLeftUntilComplete( const tr_completion_t * );
     40uint64_t              tr_cpLeftUntilDone( const tr_completion_t * );
     41float                 tr_cpPercentComplete( const tr_completion_t * );
     42float                 tr_cpPercentDone( const tr_completion_t * );
    4643
    4744/* Pieces */
    48 int               tr_cpPieceHasAllBlocks( tr_completion_t *, int piece );
    49 int               tr_cpPieceIsComplete( tr_completion_t *, int piece );
    50 tr_bitfield_t   * tr_cpPieceBitfield( tr_completion_t * );
    51 void              tr_cpPieceAdd( tr_completion_t *, int piece );
    52 void              tr_cpPieceRem( tr_completion_t *, int piece );
     45int                   tr_cpPieceHasAllBlocks( const tr_completion_t *, int piece );
     46int                   tr_cpPieceIsComplete( const tr_completion_t *, int piece );
     47const tr_bitfield_t * tr_cpPieceBitfield( const tr_completion_t* );
     48void                  tr_cpPieceAdd( tr_completion_t *, int piece );
     49void                  tr_cpPieceRem( tr_completion_t *, int piece );
    5350
    5451/* Blocks */
    55 void              tr_cpDownloaderAdd( tr_completion_t *, int block );
    56 void              tr_cpDownloaderRem( tr_completion_t *, int block );
    57 int               tr_cpBlockIsComplete( const tr_completion_t *, int block );
    58 void              tr_cpBlockAdd( tr_completion_t *, int block );
    59 void              tr_cpBlockRem( tr_completion_t *, int block );
    60 tr_bitfield_t   * tr_cpBlockBitfield( tr_completion_t * );
    61 void              tr_cpBlockBitfieldSet( tr_completion_t *, tr_bitfield_t * );
    62 float             tr_cpPercentBlocksInPiece( tr_completion_t * cp, int piece );
     52void                  tr_cpDownloaderAdd( tr_completion_t *, int block );
     53void                  tr_cpDownloaderRem( tr_completion_t *, int block );
     54int                   tr_cpBlockIsComplete( const tr_completion_t *, int block );
     55void                  tr_cpBlockAdd( tr_completion_t *, int block );
     56const tr_bitfield_t * tr_cpBlockBitfield( const tr_completion_t * );
     57void                  tr_cpBlockBitfieldSet( tr_completion_t *, tr_bitfield_t * );
     58float                 tr_cpPercentBlocksInPiece( const tr_completion_t * cp, int piece );
    6359/* Missing = we don't have it and we are not getting it from any peer yet */
    64 static inline int tr_cpMissingBlocksForPiece( const tr_completion_t * cp, int piece )
    65 {
    66     return cp->missingBlocks[piece];
    67 }
    68 int               tr_cpMissingBlockInPiece( const tr_completion_t *, int piece );
    69 int               tr_cpMostMissingBlockInPiece( tr_completion_t *, int piece,
    70                                                 int * downloaders );
     60int                   tr_cpMissingBlocksForPiece( const tr_completion_t * cp, int piece );
     61int                   tr_cpMissingBlockInPiece( const tr_completion_t *, int piece );
     62int                   tr_cpMostMissingBlockInPiece( const tr_completion_t *, int piece,
     63                                                    int * downloaders );
     64
     65#endif
  • trunk/libtransmission/fastresume.h

    r1807 r2149  
    2323 *****************************************************************************/
    2424
    25 /***********************************************************************
    26  * Fast resume
    27  ***********************************************************************
    28  * The format of the resume file is a 4 byte format version (currently 1),
    29  * followed by several variable-sized blocks of data.  Each block is
    30  * preceded by a 1 byte ID and a 4 byte length.  The currently recognized
    31  * IDs are defined below by the FR_ID_* macros.  The length does not include
    32  * the 5 bytes for the ID and length.
    33  *
    34  * The name of the resume file is "resume.<hash>-<tag>", although
    35  * older files with a name of "resume.<hash>" will be recognized if
    36  * the former doesn't exist.
    37  *
    38  * All values are stored in the native endianness. Moving a
    39  * libtransmission resume file from an architecture to another will not
    40  * work, although it will not hurt either (the version will be wrong,
    41  * so the resume file will not be read).
    42  **********************************************************************/
     25#ifndef TR_FAST_RESUME_H
     26#define TR_FAST_RESUME_H
    4327
    44 /* progress data:
    45  *  - 4 bytes * number of files: mtimes of files
    46  *  - 1 bit * number of blocks: whether we have the block or not
    47  *  - 4 bytes * number of pieces (byte aligned): the pieces that have
    48  *    been completed or started in each slot
    49  */
    50 #define FR_ID_PROGRESS          0x01
    51 /* number of bytes downloaded */
    52 #define FR_ID_DOWNLOADED        0x02
    53 /* number of bytes uploaded */
    54 #define FR_ID_UPLOADED          0x03
    55 /* IPs and ports of connectable peers */
    56 #define FR_ID_PEERS             0x04
     28void fastResumeSave( const tr_torrent_t * tor );
    5729
    58 /* macros for the length of various pieces of the progress data */
    59 #define FR_MTIME_LEN( t ) \
    60   ( 4 * (t)->info.fileCount )
    61 #define FR_BLOCK_BITFIELD_LEN( t ) \
    62   ( ( (t)->blockCount + 7 ) / 8 )
    63 #define FR_SLOTPIECE_LEN( t ) \
    64   ( 4 * (t)->info.pieceCount )
    65 #define FR_PROGRESS_LEN( t ) \
    66   ( FR_MTIME_LEN( t ) + FR_BLOCK_BITFIELD_LEN( t ) + FR_SLOTPIECE_LEN( t ) )
     30int fastResumeLoad( tr_torrent_t   * tor,
     31                    tr_bitfield_t  * uncheckedPieces );
    6732
    68 static void
    69 fastResumeFileName( char * path, size_t size, tr_torrent_t * tor, int tag )
    70 {
    71     if( tag )
    72     {
    73         snprintf( path, size, "%s/resume.%s-%s", tr_getCacheDirectory(),
    74                   tor->info.hashString, tor->handle->tag );
    75     }
    76     else
    77     {
    78         snprintf( path, size, "%s/resume.%s", tr_getCacheDirectory(),
    79                   tor->info.hashString );
    80     }
    81 }
     33void fastResumeRemove( tr_torrent_t * tor );
    8234
    83 static int fastResumeMTimes( tr_io_t * io, int * tab )
    84 {
    85     tr_torrent_t * tor = io->tor;
    86     tr_info_t    * inf = &tor->info;
    87 
    88     int           i;
    89     char        * path;
    90     struct stat   sb;
    91 
    92     for( i = 0; i < inf->fileCount; i++ )
    93     {
    94         asprintf( &path, "%s/%s", tor->destination, inf->files[i].name );
    95         if( stat( path, &sb ) )
    96         {
    97             tab[i] = 0xFFFFFFFF;
    98         }
    99         else if( S_ISREG( sb.st_mode ) )
    100         {
    101 #ifdef SYS_DARWIN
    102             tab[i] = ( sb.st_mtimespec.tv_sec & 0x7FFFFFFF );
    103 #else
    104             tab[i] = ( sb.st_mtime & 0x7FFFFFFF );
    10535#endif
    106         }
    107         else
    108         {
    109             /* Empty folder */
    110             tab[i] = 0;
    111         }
    112         free( path );
    113     }
    114 
    115     return 0;
    116 }
    117 
    118 static inline void fastResumeWriteData( uint8_t id, void * data, uint32_t size,
    119                                         uint32_t count, FILE * file )
    120 {
    121     uint32_t  datalen = size * count;
    122 
    123     fwrite( &id, 1, 1, file );
    124     fwrite( &datalen, 4, 1, file );
    125     fwrite( data, size, count, file );
    126 }
    127 
    128 static void fastResumeSave( tr_io_t * io )
    129 {
    130     tr_torrent_t * tor = io->tor;
    131 
    132     char      path[MAX_PATH_LENGTH];
    133     FILE    * file;
    134     int       version = 1;
    135     uint8_t * buf;
    136     uint64_t  total;
    137     int       size;
    138     tr_bitfield_t * bitfield;
    139 
    140     buf = malloc( FR_PROGRESS_LEN( tor ) );
    141 
    142     /* Get file sizes */
    143     if( fastResumeMTimes( io, (int*)buf ) )
    144     {
    145         free( buf );
    146         return;
    147     }
    148 
    149     /* Create/overwrite the resume file */
    150     fastResumeFileName( path, sizeof path, tor, 1 );
    151     file = fopen( path, "w" );
    152     if( NULL == file )
    153     {
    154         tr_err( "Could not open '%s' for writing", path );
    155         free( buf );
    156         return;
    157     }
    158    
    159     /* Write format version */
    160     fwrite( &version, 4, 1, file );
    161 
    162     /* Build and copy the bitfield for blocks */
    163     bitfield = tr_cpBlockBitfield( tor->completion );
    164     assert( FR_BLOCK_BITFIELD_LEN( tor ) == bitfield->len );
    165     memcpy(buf + FR_MTIME_LEN( tor ), bitfield->bits, bitfield->len );
    166 
    167     /* Copy the 'slotPiece' table */
    168     memcpy(buf + FR_MTIME_LEN( tor ) + FR_BLOCK_BITFIELD_LEN( tor ),
    169            io->slotPiece, FR_SLOTPIECE_LEN( tor ) );
    170 
    171     /* Write progress data */
    172     fastResumeWriteData( FR_ID_PROGRESS, buf, 1, FR_PROGRESS_LEN( tor ), file );
    173     free( buf );
    174 
    175     /* Write download and upload totals */
    176     total = tor->downloadedCur + tor->downloadedPrev;
    177     fastResumeWriteData( FR_ID_DOWNLOADED, &total, 8, 1, file );
    178     total = tor->uploadedCur + tor->uploadedPrev;
    179     fastResumeWriteData( FR_ID_UPLOADED, &total, 8, 1, file );
    180 
    181     if( !( TR_FLAG_PRIVATE & tor->info.flags ) )
    182     {
    183         /* Write IPs and ports of connectable peers, if any */
    184         if( ( size = tr_peerGetConnectable( tor, &buf ) ) > 0 )
    185         {
    186             fastResumeWriteData( FR_ID_PEERS, buf, size, 1, file );
    187             free( buf );
    188         }
    189     }
    190 
    191     fclose( file );
    192 
    193     tr_dbg( "Resume file '%s' written", path );
    194 }
    195 
    196 static int fastResumeLoadProgress( tr_io_t * io, FILE * file )
    197 {
    198     tr_torrent_t * tor = io->tor;
    199     tr_info_t    * inf = &tor->info;
    200 
    201     int     * fileMTimes;
    202     int       i, j;
    203     uint8_t * buf;
    204     size_t    len;
    205     tr_bitfield_t bitfield;
    206 
    207     len = FR_PROGRESS_LEN( tor );
    208     buf = calloc( len, 1 );
    209     if( len != fread( buf, 1, len, file ) )
    210     {
    211         tr_inf( "Could not read from resume file" );
    212         free( buf );
    213         return 1;
    214     }
    215 
    216     /* Compare file mtimes */
    217     fileMTimes = malloc( FR_MTIME_LEN( tor ) );
    218     if( fastResumeMTimes( io, fileMTimes ) )
    219     {
    220         free( buf );
    221         free( fileMTimes );
    222         return 1;
    223     }
    224     if( memcmp( fileMTimes, buf, FR_MTIME_LEN( tor ) ) )
    225     {
    226         tr_inf( "File mtimes don't match" );
    227         free( buf );
    228         free( fileMTimes );
    229         return 1;
    230     }
    231     free( fileMTimes );
    232 
    233     /* Copy the bitfield for blocks and fill blockHave */
    234     memset( &bitfield, 0, sizeof bitfield );
    235     bitfield.len = FR_BLOCK_BITFIELD_LEN( tor );
    236     bitfield.bits = buf + FR_MTIME_LEN( tor );
    237     tr_cpBlockBitfieldSet( tor->completion, &bitfield );
    238 
    239     /* Copy the 'slotPiece' table */
    240     memcpy( io->slotPiece, buf + FR_MTIME_LEN( tor ) +
    241             FR_BLOCK_BITFIELD_LEN( tor ), FR_SLOTPIECE_LEN( tor ) );
    242 
    243     free( buf );
    244 
    245     /* Update io->pieceSlot, io->slotsUsed, and tor->bitfield */
    246     io->slotsUsed = 0;
    247     for( i = 0; i < inf->pieceCount; i++ )
    248     {
    249         io->pieceSlot[i] = -1;
    250         for( j = 0; j < inf->pieceCount; j++ )
    251         {
    252             if( io->slotPiece[j] == i )
    253             {
    254                 // tr_dbg( "Has piece %d in slot %d", i, j );
    255                 io->pieceSlot[i] = j;
    256                 io->slotsUsed = MAX( io->slotsUsed, j + 1 );
    257                 break;
    258             }
    259         }
    260     }
    261     // tr_dbg( "Slot used: %d", io->slotsUsed );
    262 
    263     return 0;
    264 }
    265 
    266 static int fastResumeLoadOld( tr_io_t * io, FILE * file )
    267 {
    268     tr_torrent_t * tor = io->tor;
    269    
    270     int size;
    271 
    272     /* Check the size */
    273     size = 4 + FR_PROGRESS_LEN( tor );
    274     fseek( file, 0, SEEK_END );
    275     if( ftell( file ) != size )
    276     {
    277         tr_inf( "Wrong size for resume file (%d bytes, %d expected)",
    278                 (int)ftell( file ), size );
    279         fclose( file );
    280         return 1;
    281     }
    282 
    283     /* load progress information */
    284     fseek( file, 4, SEEK_SET );
    285     if( fastResumeLoadProgress( io, file ) )
    286     {
    287         fclose( file );
    288         return 1;
    289     }
    290 
    291     fclose( file );
    292 
    293     tr_inf( "Fast resuming successful (version 0)" );
    294    
    295     return 0;
    296 }
    297 
    298 static int fastResumeLoad( tr_io_t * io )
    299 {
    300     tr_torrent_t * tor = io->tor;
    301 
    302     char      path[MAX_PATH_LENGTH];
    303     FILE    * file;
    304     int       version = 0;
    305     uint8_t   id;
    306     uint32_t  len;
    307     int       ret;
    308 
    309     /* Open resume file */
    310     fastResumeFileName( path, sizeof path, tor, 1 );
    311     file = fopen( path, "r" );
    312     if( NULL == file )
    313     {
    314         if( ENOENT == errno )
    315         {
    316             fastResumeFileName( path, sizeof path, tor, 0 );
    317             file = fopen( path, "r" );
    318             if( NULL != file )
    319             {
    320                 goto good;
    321             }
    322             fastResumeFileName( path, sizeof path, tor, 1 );
    323         }
    324         tr_inf( "Could not open '%s' for reading", path );
    325         return 1;
    326     }
    327   good:
    328     tr_dbg( "Resume file '%s' loaded", path );
    329 
    330     /* Check format version */
    331     fread( &version, 4, 1, file );
    332     if( 0 == version )
    333     {
    334         return fastResumeLoadOld( io, file );
    335     }
    336     if( 1 != version )
    337     {
    338         tr_inf( "Resume file has version %d, not supported", version );
    339         fclose( file );
    340         return 1;
    341     }
    342 
    343     ret = 1;
    344     /* read each block of data */
    345     while( 1 == fread( &id, 1, 1, file ) && 1 == fread( &len, 4, 1, file ) )
    346     {
    347         switch( id )
    348         {
    349             case FR_ID_PROGRESS:
    350                 /* read progress data */
    351                 if( (uint32_t)FR_PROGRESS_LEN( tor ) == len )
    352                 {
    353                     if( fastResumeLoadProgress( io, file ) )
    354                     {
    355                         if( feof( file ) || ferror( file ) )
    356                         {
    357                             fclose( file );
    358                             return 1;
    359                         }
    360                     }
    361                     else
    362                     {
    363                         ret = 0;
    364                     }
    365                     continue;
    366                 }
    367                 break;
    368 
    369             case FR_ID_DOWNLOADED:
    370                 /* read download total */
    371                 if( 8 == len)
    372                 {
    373                     if( 1 != fread( &tor->downloadedPrev, 8, 1, file ) )
    374                     {
    375                         fclose( file );
    376                         return 1;
    377                     }
    378                     continue;
    379                 }
    380                 break;
    381 
    382             case FR_ID_UPLOADED:
    383                 /* read upload total */
    384                 if( 8 == len)
    385                 {
    386                     if( 1 != fread( &tor->uploadedPrev, 8, 1, file ) )
    387                     {
    388                         fclose( file );
    389                         return 1;
    390                     }
    391                     continue;
    392                 }
    393                 break;
    394 
    395             case FR_ID_PEERS:
    396                 if( !( TR_FLAG_PRIVATE & tor->info.flags ) )
    397                 {
    398                     int used;
    399                     uint8_t * buf = malloc( len );
    400                     if( 1 != fread( buf, len, 1, file ) )
    401                     {
    402                         free( buf );
    403                         fclose( file );
    404                         return 1;
    405                     }
    406                     used = tr_torrentAddCompact( tor, TR_PEER_FROM_CACHE,
    407                                                  buf, len / 6 );
    408                     tr_dbg( "found %i peers in resume file, used %i",
    409                             len / 6, used );
    410                     free( buf );
    411                 }
    412                 continue;
    413 
    414             default:
    415                 break;
    416         }
    417 
    418         /* if we didn't read the data, seek past it */
    419         tr_inf( "Skipping resume data type %02x, %u bytes", id, len );
    420         fseek( file, len, SEEK_CUR );
    421     }
    422 
    423     fclose( file );
    424 
    425     if( !ret )
    426     {
    427         tr_inf( "Fast resuming successful" );
    428     }
    429    
    430     return ret;
    431 }
    432 
    433 static void fastResumeRemove( tr_torrent_t * tor )
    434 {
    435     char file[MAX_PATH_LENGTH];
    436     fastResumeFileName( file, sizeof file, tor, NULL != tor->handle->tag );
    437    
    438     if ( unlink( file ) )
    439     {
    440         tr_inf( "Removing fast resume file failed" );
    441     }
    442 }
  • trunk/libtransmission/inout.c

    r2076 r2149  
    1 /******************************************************************************
    2  * $Id$
     1/*
     2 * This file Copyright (C) 2007 Charles Kerr <charles@rebelbase.com>
    33 *
    4  * Copyright (c) 2005-2007 Transmission authors and contributors
    5  *
    6  * Permission is hereby granted, free of charge, to any person obtaining a
    7  * copy of this software and associated documentation files (the "Software"),
    8  * to deal in the Software without restriction, including without limitation
    9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
    10  * and/or sell copies of the Software, and to permit persons to whom the
    11  * Software is furnished to do so, subject to the following conditions:
    12  *
    13  * The above copyright notice and this permission notice shall be included in
    14  * all copies or substantial portions of the Software.
    15  *
    16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    22  * DEALINGS IN THE SOFTWARE.
    23  *****************************************************************************/
     4 * This file is licensed by the GPL version 2.  Works owned by the
     5 * Transmission project are granted a special exemption to clause 2(b)
     6 * so that the bulk of its code can remain under the MIT license.
     7 * This exemption does not extend to derived works not owned by
     8 * the Transmission project.
     9 */
    2410
    2511#include <stdlib.h> /* for calloc */
     
    3016    tr_torrent_t * tor;
    3117
    32     /* Position of pieces
    33        -1 = we haven't started to download this piece yet
    34         n = we have started or completed the piece in slot n */
    35     int         * pieceSlot;
    36 
    37     /* Pieces in slot
    38        -1 = unused slot
    39         n = piece n */
    40     int         * slotPiece;
    41 
    42     int           slotsUsed;
    43 
    44     int           writeCount;
    45     int           readCount;
    46     int           reorderCount;
    47     int           hitCount;
    48 
    49     /* private flag to keep from saving aborted fastResume data */
    50     char          checkFilesAborted;
     18    tr_bitfield_t * uncheckedPieces;
    5119};
    5220
    5321#include "fastresume.h"
    5422
    55 /***********************************************************************
    56  * Local prototypes
    57  **********************************************************************/
    58 static int  checkFiles( tr_io_t * );
    59 static void closeFiles( tr_io_t * );
    60 static int  readOrWriteBytes( tr_io_t *, uint64_t, int, uint8_t *, int );
    61 static int  readOrWriteSlot( tr_io_t * io, int slot, uint8_t * buf,
    62                              int * size, int write );
    63 static void findSlotForPiece( tr_io_t *, int );
    64 
    65 #define readBytes(io,o,s,b)  readOrWriteBytes(io,o,s,b,0)
    66 #define writeBytes(io,o,s,b) readOrWriteBytes(io,o,s,b,1)
    67 
    68 #define readSlot(io,sl,b,s)  readOrWriteSlot(io,sl,b,s,0)
    69 #define writeSlot(io,sl,b,s) readOrWriteSlot(io,sl,b,s,1)
    70 
    71 static int reorderPieces( tr_io_t * io );
    72 
    73 /***********************************************************************
    74  * tr_ioLoadResume
    75  ***********************************************************************
    76  * Try to load the fast resume file
    77  **********************************************************************/
    78 void tr_ioLoadResume( tr_torrent_t * tor )
    79 {
    80     tr_io_t * io;
    81     tr_info_t * inf = &tor->info;
    82 
    83     io      = calloc( 1, sizeof( tr_io_t ) );
    84     io->tor = tor;
    85 
    86     io->pieceSlot = malloc( inf->pieceCount * sizeof( int ) );
    87     io->slotPiece = malloc( inf->pieceCount * sizeof( int ) );
    88 
    89     fastResumeLoad( io );
    90     tor->ioLoaded = 1;
    91 
    92     free( io->pieceSlot );
    93     free( io->slotPiece );
    94     free( io );
    95 }
    96 
    97 void tr_ioRemoveResume( tr_torrent_t * tor )
    98 {
    99     if( !tor->io )
    100     {
    101         fastResumeRemove( tor );
    102     }
    103 }
    104 
    105 /***********************************************************************
    106  * tr_ioInit
    107  ***********************************************************************
    108  * Open all files we are going to write to
    109  **********************************************************************/
    110 tr_io_t * tr_ioInit( tr_torrent_t * tor )
    111 {
    112     tr_io_t * io;
    113 
    114     io      = calloc( 1, sizeof( tr_io_t ) );
    115     io->tor = tor;
    116 
    117     if( checkFiles( io ) )
    118     {
    119         free( io );
    120         io = NULL;
    121     }
    122 
    123     return io;
    124 }
    125 
    126 /***********************************************************************
    127  * tr_ioRead
    128  **********************************************************************/
    129 int tr_ioRead( tr_io_t * io, int index, int begin, int length,
    130                uint8_t * buf )
    131 {
    132     tr_info_t * inf = &io->tor->info;
    133     uint64_t    offset;
    134 
    135     offset = (uint64_t) io->pieceSlot[index] *
    136         (uint64_t) inf->pieceSize + (uint64_t) begin;
    137 
    138     return readBytes( io, offset, length, buf );
    139 }
    140 
    141 /***********************************************************************
    142  * tr_ioWrite
    143  **********************************************************************/
    144 int tr_ioWrite( tr_io_t * io, int index, int begin, int length,
    145                 uint8_t * buf )
    146 {
    147     tr_info_t    * inf = &io->tor->info;
    148     uint64_t       offset;
    149         int            rv;
    150         int            reorder = 0;
    151         int            slotsUsedOld;
    152 
    153         slotsUsedOld = io->slotsUsed;
    154 
    155         if( io->pieceSlot[index] < 0 )
    156     {
    157         findSlotForPiece( io, index );
    158         tr_inf( "Piece %d: starting in slot %d", index,
    159                 io->pieceSlot[index] );
    160                 reorder = 1;
    161                 io->writeCount += 1;
    162     }
    163 
    164     offset = (uint64_t) io->pieceSlot[index] *
    165         (uint64_t) inf->pieceSize + (uint64_t) begin;
    166 
    167     rv = writeBytes( io, offset, length, buf );
    168 
    169     if (reorder)
    170         {
    171                 int reorderRuns;
    172 
    173                 tr_dbg( "reorder pieces");
    174                 reorderRuns = reorderPieces( io );
    175 
    176                 if (io->slotsUsed == slotsUsedOld && reorderRuns > 0)
    177                         tr_err( "reorder runs should have been 0 but was: %d", reorderRuns );
    178 
    179 //              tr_inf( "STAT: filesize: %03d writeCount: %03d readCount: %03d hitCount: %03d reorderCount: %03d",
    180 //                              io->slotsUsed,io->writeCount,io->readCount,io->hitCount,io->reorderCount);
    181         }
    182 
    183         return rv;
    184 }
    185 
    186 /***********************************************************************
    187  * tr_ioHash
    188  **********************************************************************/
    189 int tr_ioHash( tr_io_t * io, int index )
    190 {
    191     tr_torrent_t * tor = io->tor;
    192     tr_info_t    * inf = &io->tor->info;
    193     int            pieceSize;
    194     uint8_t      * pieceBuf;
    195     uint8_t        hash[SHA_DIGEST_LENGTH];
    196     int            hashFailed;
    197     int            ret;
    198     int            i;
    199 
    200     pieceSize = tr_pieceSize( index );
    201     pieceBuf  = malloc( pieceSize );
    202     if( ( ret = readBytes( io, (uint64_t) io->pieceSlot[index] *
    203                     (uint64_t) inf->pieceSize, pieceSize, pieceBuf ) ) )
    204     {
    205         free( pieceBuf );
    206         return ret;
    207     }
    208     SHA1( pieceBuf, pieceSize, hash );
    209     free( pieceBuf );
    210 
    211     hashFailed = memcmp( hash, &inf->pieces[20*index], SHA_DIGEST_LENGTH );
    212     if( hashFailed )
    213     {
    214         tr_err( "Piece %d (slot %d): hash FAILED", index,
    215                 io->pieceSlot[index] );
    216         tr_cpPieceRem( tor->completion, index );
    217     }
     23/****
     24*****  Low-level IO functions
     25****/
     26
     27enum { TR_IO_READ, TR_IO_WRITE };
     28
     29static int
     30readOrWriteBytes ( const tr_torrent_t  * tor,
     31                   int                   ioMode,
     32                   int                   fileIndex,
     33                   size_t                fileOffset,
     34                   void                * buf,
     35                   size_t                buflen )
     36{
     37    const tr_info_t * info = &tor->info;
     38    const tr_file_t * file = &info->files[fileIndex];
     39    int fd, ret;
     40    typedef size_t (* iofunc) ( int, void *, size_t );
     41    iofunc func = ioMode == TR_IO_READ ? (iofunc)read : (iofunc)write;
     42
     43    assert ( 0<=fileIndex && fileIndex<info->fileCount );
     44    assert ( !file->length || (fileOffset < file->length));
     45    assert ( fileOffset + buflen <= file->length );
     46
     47    if( !file->length )
     48        return 0;
     49    else if ((fd = tr_fdFileOpen ( tor->destination, file->name, TRUE )) < 0)
     50        ret = fd;
     51    else if( lseek( fd, fileOffset, SEEK_SET ) == ((off_t)-1) )
     52        ret = TR_ERROR_IO_OTHER;
     53    else if( func( fd, buf, buflen ) != buflen )
     54        ret = tr_ioErrorFromErrno ();
    21855    else
    219     {
    220         tr_inf( "Piece %d (slot %d): hash OK", index,
    221                 io->pieceSlot[index] );
    222         tr_cpPieceAdd( tor->completion, index );
    223     }
    224 
    225     /* Assign blame or credit to peers */
    226     for( i = 0; i < tor->peerCount; i++ )
    227     {
    228         tr_peerBlame( tor->peers[i], index, !hashFailed );
    229     }
    230 
    231     return 0;
    232 }
    233 
    234 /***********************************************************************
    235  * tr_ioSync
    236  **********************************************************************/
    237 void tr_ioSync( tr_io_t * io )
    238 {
    239     closeFiles( io );
    240 
    241     if( !io->checkFilesAborted )
    242     {
    243         fastResumeSave( io );
    244     }
    245 }
    246 
    247 /***********************************************************************
    248  * tr_ioClose
    249  **********************************************************************/
    250 void tr_ioClose( tr_io_t * io )
    251 {
    252     tr_ioSync( io );
    253 
    254     free( io->pieceSlot );
    255     free( io->slotPiece );
    256     free( io );
    257 }
    258 
    259 /***********************************************************************
    260  * checkFiles
    261  ***********************************************************************
    262  * Look for pieces
    263  **********************************************************************/
    264 static int checkFiles( tr_io_t * io )
    265 {
    266     tr_torrent_t * tor = io->tor;
    267     tr_info_t    * inf = &tor->info;
     56        ret = TR_OK;
     57 
     58    if( fd >= 0 )
     59        tr_fdFileRelease( fd );
     60
     61    return ret;
     62}
     63
     64static void
     65findFileLocation ( const tr_torrent_t * tor,
     66                   int pieceIndex, size_t pieceOffset,
     67                   int * fileIndex, size_t * fileOffset )
     68{
     69    const tr_info_t * info = &tor->info;
    26870
    26971    int i;
    270     uint8_t * buf;
    271     uint8_t hash[SHA_DIGEST_LENGTH];
     72    uint64_t piecePos = ((uint64_t)pieceIndex * info->pieceSize) + pieceOffset;
     73
     74    assert ( 0<=pieceIndex && pieceIndex < info->pieceCount );
     75    assert ( pieceOffset < (size_t)tr_pieceSize(pieceIndex) );
     76    assert ( piecePos < info->totalSize );
     77
     78    for ( i=0; info->files[i].length<=piecePos; ++i )
     79      piecePos -= info->files[i].length;
     80
     81    *fileIndex = i;
     82    *fileOffset = piecePos;
     83
     84    assert ( 0<=*fileIndex && *fileIndex<info->fileCount );
     85    assert ( *fileOffset < info->files[i].length );
     86}
     87
     88static int
     89ensureMinimumFileSize ( const tr_torrent_t  * tor,
     90                        int                   fileIndex,
     91                        size_t                minSize ) /* in bytes */
     92{
     93    int fd;
     94    int ret;
    27295    struct stat sb;
    273 
    274     io->pieceSlot = malloc( inf->pieceCount * sizeof( int ) );
    275     io->slotPiece = malloc( inf->pieceCount * sizeof( int ) );
    276     io->checkFilesAborted = 0;
    277 
    278     if( !fastResumeLoad( io ) )
    279     {
    280         return 0;
    281     }
    282 
    283     tr_dbg( "Checking pieces..." );
    284 
    285     /* Yet we don't have anything */
    286     memset( io->pieceSlot, 0xFF, inf->pieceCount * sizeof( int ) );
    287     memset( io->slotPiece, 0xFF, inf->pieceCount * sizeof( int ) );
    288 
    289     /* Truncate files that are too large */
    290     for( i = 0; inf->fileCount > i; i++ )
    291     {
    292         if( 0 > stat( inf->files[i].name, &sb ) )
    293         {
    294             if( ENOENT == errno )
    295             {
    296                 continue;
    297             }
    298             tr_err( "Could not stat %s (%s)",
    299                     inf->files[i].name, strerror( errno ) );
    300             break;
    301         }
    302 
    303         if( sb.st_size > ( off_t )inf->files[i].length )
    304         {
    305             tr_dbg( "truncate %s from %"PRIu64" to %"PRIu64" bytes",
    306                     inf->files[i].name, sb.st_size, inf->files[i].length );
    307             truncate( inf->files[i].name, inf->files[i].length );
    308         }
    309     }
    310 
    311     /* Check pieces */
    312     io->slotsUsed = 0;
    313     buf           = malloc( inf->pieceSize );
    314     for( i = 0; i < inf->pieceCount; i++ )
    315     {
    316         int size, j;
    317 
    318         if( tor->status & TR_STATUS_STOPPING )
    319         {
    320             io->checkFilesAborted = 1;
    321             break;
    322         }
    323 
    324         if( readSlot( io, i, buf, &size ) )
    325         {
    326             break;
    327         }
    328 
    329         io->slotsUsed = i + 1;
    330         SHA1( buf, size, hash );
    331 
    332         for( j = i; j < inf->pieceCount - 1; j++ )
    333         {
    334             if( !memcmp( hash, &inf->pieces[20*j], SHA_DIGEST_LENGTH ) )
    335             {
    336                                 if ( io->pieceSlot[j] > 0 && j == i)
    337                                 {
    338                                         /* Only remove double piece when we found one sitting in the right slot */
    339 
    340                                         tr_inf( "found piece %d (slot: %d) already on slot %d",j,i,io->pieceSlot[j] );
    341                                         io->slotPiece[io->pieceSlot[j]] = -1;
    342 
    343                                         io->pieceSlot[j] = i;
    344                                         io->slotPiece[i] = j;
    345                                 }
    346                                 else /* We found no double */
    347                                 {
    348                                         io->pieceSlot[j] = i;
    349                                         io->slotPiece[i] = j;
    350                                 }
    351 
    352                 tr_cpPieceAdd( tor->completion, j );
    353                 break;
    354             }
    355         }
    356 
    357         if( io->slotPiece[i] > -1 )
    358         {
    359             continue;
    360         }
    361 
    362         /* Special case for the last piece */
    363         SHA1( buf, tr_pieceSize( inf->pieceCount - 1 ), hash );
    364         if( !memcmp( hash, &inf->pieces[20 * (inf->pieceCount - 1)],
    365                      SHA_DIGEST_LENGTH ) )
    366         {
    367             io->pieceSlot[inf->pieceCount - 1] = i;
    368             io->slotPiece[i]                   = inf->pieceCount - 1;
    369 
    370             tr_cpPieceAdd( tor->completion, inf->pieceCount - 1 );
    371         }
    372     }
    373     free( buf );
    374 
    375     return 0;
    376 }
    377 
    378 /***********************************************************************
    379  * closeFiles
    380  **********************************************************************/
    381 static void closeFiles( tr_io_t * io )
    382 {
    383     tr_torrent_t * tor = io->tor;
    384     tr_info_t    * inf = &tor->info;
    385 
    386     int i;
    387 
    388     for( i = 0; i < inf->fileCount; i++ )
    389     {
    390         tr_fdFileClose( tor->destination, inf->files[i].name );
    391     }
    392 }
    393 
    394 /***********************************************************************
    395  * readOrWriteBytes
    396  ***********************************************************************
    397  *
    398  **********************************************************************/
    399 typedef size_t (* iofunc) ( int, void *, size_t );
    400 static int readOrWriteBytes( tr_io_t * io, uint64_t offset, int size,
    401                              uint8_t * buf, int isWrite )
    402 {
    403     tr_torrent_t * tor = io->tor;
    404     tr_info_t    * inf = &tor->info;
    405 
    406     int    piece = offset / inf->pieceSize;
    407     int    begin = offset % inf->pieceSize;
    408     int    i;
    409     size_t cur;
    410     int    file;
    411     iofunc readOrWrite = isWrite ? (iofunc) write : (iofunc) read;
    412     int    ret = 0;
     96    const tr_file_t * file = &tor->info.files[fileIndex];
     97
     98    assert ( 0<=fileIndex && fileIndex<tor->info.fileCount );
     99    assert ( minSize <= file->length );
     100
     101    fd = tr_fdFileOpen( tor->destination, file->name, TRUE );
     102    if( fd < 0 ) /* bad fd */
     103        ret = fd;
     104    else if (fstat (fd, &sb) ) /* how big is the file? */
     105        ret = tr_ioErrorFromErrno ();
     106    else if ((size_t)sb.st_size >= minSize) /* already big enough */
     107        ret = TR_OK;
     108    else if (!ftruncate( fd, minSize )) /* grow it */
     109        ret = TR_OK;
     110    else /* couldn't grow it */
     111        ret = tr_ioErrorFromErrno ();
     112
     113    if( fd >= 0 )
     114        tr_fdFileRelease( fd );
     115
     116    return ret;
     117}
     118
     119static int
     120readOrWritePiece ( tr_torrent_t       * tor,
     121                   int                  ioMode,
     122                   int                  pieceIndex,
     123                   size_t               pieceOffset,
     124                   uint8_t            * buf,
     125                   size_t               buflen )
     126{
     127    int ret = 0;
     128    int fileIndex;
     129    size_t fileOffset;
     130    const tr_info_t * info = &tor->info;
     131
     132    assert( 0<=pieceIndex && pieceIndex<tor->info.pieceCount );
     133    assert( buflen <= (size_t) tr_pieceSize( pieceIndex ) );
    413134
    414135    /* Release the torrent lock so the UI can still update itself if
     
    416137    tr_lockUnlock( &tor->lock );
    417138
    418     /* We don't ever read or write more than a piece at a time */
    419     if( tr_pieceSize( piece ) < begin + size )
     139    findFileLocation ( tor, pieceIndex, pieceOffset, &fileIndex, &fileOffset );
     140
     141    while( buflen && !ret )
    420142    {
    421         tr_err( "readOrWriteBytes: trying to write more than a piece" );
    422         ret = TR_ERROR_ASSERT;
    423         goto cleanup;
    424     }
    425 
    426     /* Find which file we shall start reading/writing in */
    427     for( i = 0; i < inf->fileCount; i++ )
     143        const tr_file_t * file = &info->files[fileIndex];
     144        const size_t bytesThisPass = MIN( buflen, file->length - fileOffset );
     145
     146        if( ioMode == TR_IO_WRITE )
     147            ret = ensureMinimumFileSize( tor, fileIndex,
     148                                         fileOffset + bytesThisPass );
     149        if( !ret )
     150            ret = readOrWriteBytes( tor, ioMode,
     151                                    fileIndex, fileOffset, buf, bytesThisPass );
     152        buf += bytesThisPass;
     153        buflen -= bytesThisPass;
     154        fileIndex++;
     155        fileOffset = 0;
     156    }
     157
     158    tr_lockLock( &tor->lock );
     159
     160    return ret;
     161}
     162
     163int
     164tr_ioRead( tr_io_t * io, int pieceIndex, int begin, int len, uint8_t * buf )
     165{
     166    return readOrWritePiece ( io->tor, TR_IO_READ, pieceIndex, begin, buf, len );
     167}
     168
     169int
     170tr_ioWrite( tr_io_t * io, int pieceIndex, int begin, int len, uint8_t * buf )
     171{
     172    return readOrWritePiece ( io->tor, TR_IO_WRITE, pieceIndex, begin, buf, len );
     173}
     174
     175/****
     176*****
     177****/
     178
     179static int
     180tr_ioRecalculateHash ( tr_torrent_t  * tor,
     181                       int             pieceIndex,
     182                       uint8_t       * setme )
     183{
     184    int n;
     185    int ret;
     186    uint8_t * buf;
     187    const tr_info_t * info;
     188
     189    assert( tor != NULL );
     190    assert( setme != NULL );
     191    assert( 0<=pieceIndex && pieceIndex<tor->info.pieceCount );
     192
     193    info = &tor->info;
     194    n = tr_pieceSize( pieceIndex );
     195
     196    buf = malloc( n );
     197    ret = readOrWritePiece ( tor, TR_IO_READ, pieceIndex, 0, buf, n );
     198    if( !ret ) {
     199        SHA1( buf, n, setme );
     200    }
     201    free( buf );
     202
     203    return ret;
     204}
     205
     206static int
     207checkPiece ( tr_torrent_t * tor, int pieceIndex )
     208{
     209    uint8_t hash[SHA_DIGEST_LENGTH];
     210    int ret = tr_ioRecalculateHash( tor, pieceIndex, hash )
     211           || memcmp( hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH );
     212    tr_dbg ("torrent [%s] piece %d hash check: %s",
     213            tor->info.name, pieceIndex, (ret?"FAILED":"OK"));
     214    return ret;
     215}
     216
     217static void
     218checkFiles( tr_io_t * io )
     219{
     220    int i;
     221    tr_torrent_t * tor = io->tor;
     222
     223    tr_bitfieldClear( io->uncheckedPieces );
     224
     225    if ( fastResumeLoad( io->tor, io->uncheckedPieces ) )
     226        tr_bitfieldAddRange( io->uncheckedPieces, 0, tor->info.pieceCount-1 );
     227
     228    for( i=0; i<tor->info.pieceCount; ++i )
    428229    {
    429         if( offset < inf->files[i].length )
    430         {
    431             /* This is the file */
     230        if( tor->status & TR_STATUS_STOPPING )
    432231            break;
    433         }
    434         offset -= inf->files[i].length;
    435     }
    436     if( i >= inf->fileCount )
     232
     233        if( !tr_bitfieldHas( io->uncheckedPieces, i ) )
     234            continue;
     235
     236        tr_inf ( "Checking piece %d because it's not in fast-resume", i );
     237
     238        if( !checkPiece( tor, i ) )
     239           tr_cpPieceAdd( tor->completion, i );
     240        else
     241           tr_cpPieceRem( tor->completion, i );
     242
     243        tr_bitfieldRem( io->uncheckedPieces, i );
     244    }
     245}
     246
     247/****
     248*****  Life Cycle
     249****/
     250
     251tr_io_t*
     252tr_ioInit( tr_torrent_t * tor )
     253{
     254    tr_io_t * io = calloc( 1, sizeof( tr_io_t ) );
     255    io->uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
     256    io->tor = tor;
     257    checkFiles( io );
     258    return io;
     259}
     260
     261void
     262tr_ioSync( tr_io_t * io )
     263{
     264    int i;
     265    const tr_info_t * info = &io->tor->info;
     266
     267    for( i=0; i<info->fileCount; ++i )
     268        tr_fdFileClose( io->tor->destination, info->files[i].name );
     269
     270    if( tr_bitfieldIsEmpty( io->uncheckedPieces ) )
     271        fastResumeSave( io->tor );
     272}
     273
     274void
     275tr_ioClose( tr_io_t * io )
     276{
     277    tr_ioSync( io );
     278
     279    tr_bitfieldFree( io->uncheckedPieces );
     280    free( io );
     281}
     282
     283
     284/* try to load the fast resume file */
     285void
     286tr_ioLoadResume( tr_torrent_t * tor )
     287{
     288    tr_io_t * io = calloc( 1, sizeof( tr_io_t ) );
     289    io->uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
     290    io->tor = tor;
     291    fastResumeLoad( tor, io->uncheckedPieces );
     292    tor->ioLoaded = 1;
     293    tr_bitfieldFree( io->uncheckedPieces );
     294    free( io );
     295}
     296
     297void tr_ioRemoveResume( tr_torrent_t * tor )
     298{
     299    if( !tor->io )
     300        fastResumeRemove( tor );
     301}
     302
     303int
     304tr_ioHash( tr_io_t * io, int pieceIndex )
     305{
     306    int i;
     307
     308    tr_torrent_t * tor = io->tor;
     309    const int success = !checkPiece( tor, pieceIndex );
     310    if( success )
    437311    {
    438         /* Should not happen */
    439         tr_err( "readOrWriteBytes: offset out of range (%"PRIu64", %d, %d)",
    440                 offset, size, isWrite );
    441         ret = TR_ERROR_ASSERT;
    442         goto cleanup;
    443     }
    444 
    445     while( size > 0 )
     312        tr_inf( "Piece %d hash OK", pieceIndex );
     313        tr_cpPieceAdd( tor->completion, pieceIndex );
     314    }
     315    else
    446316    {
    447         /* How much can we put or take with this file */
    448         if( inf->files[i].length < offset + size )
    449         {
    450             cur = (int) ( inf->files[i].length - offset );
    451         }
    452         else
    453         {
    454             cur = size;
    455         }
    456 
    457         if( cur > 0 )
    458         {
    459             /* Now let's get a descriptor on the file... */
    460             file = tr_fdFileOpen( tor->destination, inf->files[i].name,
    461                                   isWrite );
    462             if( file < 0 )
    463             {
    464                 ret = file;
    465                 goto cleanup;
    466             }
    467 
    468             /* seek to the right offset... */
    469             if( lseek( file, offset, SEEK_SET ) < 0 )
    470             {
    471                 tr_fdFileRelease( file );
    472                 ret = TR_ERROR_IO_OTHER;
    473                 goto cleanup;
    474             }
    475 
    476             /* do what we are here to do... */
    477             if( readOrWrite( file, buf, cur ) != cur )
    478             {
    479                 ret = tr_ioErrorFromErrno();
    480                 tr_fdFileRelease( file );
    481                 goto cleanup;
    482             }
    483 
    484             /* and release the descriptor. */
    485             tr_fdFileRelease( file );
    486         }
    487 
    488         /* 'cur' bytes done, 'size - cur' bytes to go with the next file */
    489         i      += 1;
    490         offset  = 0;
    491         size   -= cur;
    492         buf    += cur;
    493     }
    494 
    495 cleanup:
    496     tr_lockLock( &tor->lock );
    497 
    498         if( ret )
    499     {
    500                 tr_err( "readOrWriteBytes: ret: %d",ret );
    501     }
    502         return ret;
    503 }
    504 
    505 /***********************************************************************
    506  * readSlot
    507  ***********************************************************************
    508  *
    509  **********************************************************************/
    510 static int readOrWriteSlot( tr_io_t * io, int slot, uint8_t * buf,
    511                             int * size, int write )
    512 {
    513     tr_torrent_t * tor = io->tor;
    514     tr_info_t    * inf = &tor->info;
    515 
    516     uint64_t offset = (uint64_t) slot * (uint64_t) inf->pieceSize;
    517    
    518     *size = 0;
    519     if( slot == inf->pieceCount - 1 )
    520     {
    521         *size = inf->totalSize % inf->pieceSize;
    522     }
    523     if( !*size )
    524     {
    525         *size = inf->pieceSize;
    526     }
    527 
    528     return readOrWriteBytes( io, offset, *size, buf, write );
    529 }
    530 
    531 static void invertSlots( tr_io_t * io, int slot1, int slot2 )
    532 {
    533     tr_torrent_t * tor = io->tor;
    534     tr_info_t    * inf = &tor->info;
    535 
    536     uint8_t * buf1, * buf2;
    537     int piece1, piece2, foo;
    538 
    539         io->writeCount += 2;
    540         io->readCount += 2;
    541 
    542     assert (inf->pieceCount > slot1);
    543         assert (inf->pieceCount > slot2);
    544         assert (slot1 != slot2);
    545 
    546     buf1 = malloc( inf->pieceSize );
    547     buf2 = malloc( inf->pieceSize );
    548 
    549     readSlot( io, slot1, buf1, &foo );
    550     readSlot( io, slot2, buf2, &foo );
    551 
    552     writeSlot( io, slot2, buf1, &foo );
    553     writeSlot( io, slot1, buf2, &foo );
    554 
    555     free( buf1 );
    556     free( buf2 );
    557 
    558     piece1                = io->slotPiece[slot1];
    559     piece2                = io->slotPiece[slot2];
    560     io->slotPiece[slot1]  = piece2;
    561     io->slotPiece[slot2]  = piece1;
    562     if( piece1 >= 0 )
    563     {
    564         io->pieceSlot[piece1] = slot2;
    565     }
    566     if( piece2 >= 0 )
    567     {
    568         io->pieceSlot[piece2] = slot1;
    569     }
    570 }
    571 
    572 static void moveSlot( tr_io_t * io, int slot1, int slot2 )
    573 {
    574         tr_torrent_t * tor = io->tor;
    575         tr_info_t    * inf = &tor->info;
    576 
    577         uint8_t * buf1;
    578         int piece, foo;
    579 
    580         io->writeCount += 1;
    581         io->readCount += 1;
    582 
    583         assert (inf->pieceCount > slot1);
    584         assert (inf->pieceCount > slot2);
    585         assert (slot1 != slot2);
    586 
    587         buf1 = malloc( inf->pieceSize );
    588 
    589         readSlot( io, slot1, buf1, &foo );      /* from slot1 */
    590         writeSlot( io, slot2, buf1, &foo );     /* to slot2 */
    591 
    592         free( buf1 );
    593 
    594         piece = io->slotPiece[slot1];
    595 
    596         io->slotPiece[slot2] = piece;
    597         io->slotPiece[slot1] = -1;      /* mark as free */
    598 
    599         if( piece >= 0 )
    600         {
    601                 io->pieceSlot[piece] = slot2;
    602         }
    603 }
    604 
    605 static int reorderPieces( tr_io_t * io )
    606 {
    607     tr_torrent_t * tor = io->tor;
    608     tr_info_t    * inf = &tor->info;
    609 
    610     int i, didInvert, didInvertReturn = 0;
    611 
    612         io->reorderCount += 1;
    613 
    614         /* Try to move pieces to their final places */
    615     do
    616     {
    617         didInvert = 0;
    618 
    619         for( i = 0; i < inf->pieceCount; i++ )
    620         {
    621             if( io->pieceSlot[i] < 0 )
    622             {
    623                 /* We haven't started this piece yet */
    624                 continue;
    625             }
    626             if( io->pieceSlot[i] == i )
    627             {
    628                 /* Already in place */
    629                 continue;
    630             }
    631             if( i > io->slotsUsed )
    632             {
    633                 /* File is not big enough yet */
    634                 continue;
    635             }
    636 
    637             /* Move/Invert piece i into slot i */
    638                        
    639                         if( io->slotPiece[i] >= 0 )
    640                         {
    641                                 tr_err( "reorder: invert slot %d and %d (piece %d)", io->pieceSlot[i], i, io->slotPiece[i] );
    642                                 invertSlots( io, i, io->pieceSlot[i] );
    643                         }
    644                         else
    645                         {
    646                                 tr_inf( "reorder: move slot %d to %d (piece %d)", io->pieceSlot[i], i, io->slotPiece[i] );
    647                                 moveSlot( io, io->pieceSlot[i], i );
    648                                 if (i == io->slotsUsed) (io->slotsUsed)++;
    649                         }
    650                        
    651             didInvert = 1;
    652         }
    653                 didInvertReturn += didInvert;
    654     } while( didInvert );
    655 
    656         return didInvertReturn;
    657 }
    658 
    659 static int nextFreeSlotToAppend(tr_io_t * io)
    660 {
    661         tr_torrent_t * tor = io->tor;
    662         tr_info_t    * inf = &tor->info;
    663 
    664         while( io->slotsUsed < ( (inf->pieceCount) - 1 ) && io->pieceSlot[io->slotsUsed] >= 0 )
    665         {
    666                 tr_inf( "slotUsed (%d) has piece in %d !", io->slotsUsed, io->pieceSlot[io->slotsUsed] );
    667                 (io->slotsUsed)++;
    668         }
    669 
    670         return (io->slotsUsed)++;
    671 }
    672 
    673 static void findSlotForPiece( tr_io_t * io, int piece )
    674 {
    675     int i;
    676         int freeSlot;
    677 
    678     tr_torrent_t * tor = io->tor;
    679     tr_info_t    * inf = &tor->info;
    680 
    681 #if 0
    682     tr_dbg( "Entering findSlotForPiece" );
    683 
    684     for( i = 0; i < inf->pieceCount; i++ )
    685         printf( "%02d ", io->slotPiece[i] );
    686     printf( "\n" );
    687     for( i = 0; i < inf->pieceCount; i++ )
    688         printf( "%02d ", io->pieceSlot[i] );
    689     printf( "\n" );
    690 #endif
    691 
    692         /* first we should look for the right slot! */
    693 
    694 #define PREFERSIZE 0
    695 
    696         if( piece <= io->slotsUsed )
    697         {
    698                 if( io->slotPiece[piece] >= 0 ) /* if used move it away! */
    699                 {
    700                         freeSlot = -1;
    701 
    702                         if( PREFERSIZE || io->slotsUsed >= ( inf->pieceCount - 1 ) )
    703                         {
    704                                 for( i = 0; i < io->slotsUsed; i++ )
    705                                 {
    706                                         if( io->slotPiece[i] < 0 )
    707                                         {
    708                                                 freeSlot = i;
    709                                                 break;
    710                                         }
    711                                 }
    712                         }
    713                        
    714                         if( freeSlot == -1 )
    715                                 freeSlot = nextFreeSlotToAppend(io);
    716 
    717                         tr_inf( "move slot %d (piece %d) to %d", piece, io->slotPiece[piece], freeSlot );
    718                         moveSlot( io, piece, freeSlot );
    719                 }
    720                 else
    721                 {
    722                         io->hitCount += 1;
    723                 }
    724 
    725                 io->pieceSlot[piece] = piece;
    726                 io->slotPiece[piece] = piece;
    727                 if (piece == io->slotsUsed) (io->slotsUsed)++;
    728                 goto reorder;
    729         }
    730 
    731     /* Look for an empty slot somewhere */
    732 
    733         if( PREFERSIZE || io->slotsUsed >= inf->pieceCount )
    734         {
    735                 for( i = 0; i < io->slotsUsed; i++ )
    736                 {
    737                         if( io->slotPiece[i] < 0 )
    738                         {
    739                                 io->pieceSlot[piece] = i;
    740                                 io->slotPiece[i]     = piece;
    741                                 goto reorder;
    742                         }
    743                 }
    744     }
    745        
    746         freeSlot = nextFreeSlotToAppend(io);
    747 
    748         /* No empty slot, extend the file */
    749         io->pieceSlot[piece]    = freeSlot;
    750         io->slotPiece[freeSlot] = piece;
    751 
    752 reorder:
    753                 return;
    754 
    755 #if 0
    756     for( i = 0; i < inf->pieceCount; i++ )
    757         printf( "%02d ", io->slotPiece[i] );
    758     printf( "\n" );
    759     for( i = 0; i < inf->pieceCount; i++ )
    760         printf( "%02d ", io->pieceSlot[i] );
    761     printf( "\n" );
    762 
    763     printf( "Leaving findSlotForPiece\n" );
    764 #endif
    765 }
     317        tr_err( "Piece %d hash FAILED", pieceIndex );
     318        tr_cpPieceRem( tor->completion, pieceIndex );
     319    }
     320
     321    /* Assign blame or credit to peers */
     322    for( i = 0; i < tor->peerCount; ++i )
     323        tr_peerBlame( tor->peers[i], pieceIndex, success );
     324
     325    return 0;
     326}
  • trunk/libtransmission/internal.h

    r1946 r2149  
    151151#include "xml.h"
    152152
     153#ifndef TRUE
     154#define TRUE 1
     155#endif
     156#ifndef FALSE
     157#define FALSE 0
     158#endif
     159
    153160int tr_torrentAddCompact( tr_torrent_t * tor, int from,
    154161                           uint8_t * buf, int count );
     
    169176    int               error;
    170177    char              errorString[128];
    171     int               finished;
     178    int               hasChangedState;
    172179
    173180    char            * id;
  • trunk/libtransmission/ipcparse.c

    r2038 r2149  
    706706
    707707int
    708 ipc_addstat( benc_val_t * list, int tor, tr_info_t * inf,
     708ipc_addstat( benc_val_t * list, int tor,
    709709             tr_stat_t * st, int types )
    710710{
     
    747747        {
    748748            case IPC_ST_COMPLETED:
    749                 tr_bencInitInt( item, st->progress * ( float )inf->totalSize );
     749                tr_bencInitInt( item, st->downloadedValid );
    750750                break;
    751751            case IPC_ST_DOWNSPEED:
  • trunk/libtransmission/ipcparse.h

    r2076 r2149  
    156156                            int, const int * );
    157157int          ipc_addinfo  ( benc_val_t *, int, tr_info_t *, int );
    158 int          ipc_addstat  ( benc_val_t *, int, tr_info_t *, tr_stat_t *, int );
     158int          ipc_addstat  ( benc_val_t *, int, tr_stat_t *, int );
    159159
    160160/* sets errno to EINVAL on parse error or
  • trunk/libtransmission/metainfo.c

    r1807 r2149  
    240240    }
    241241    inf->pieceCount = val->val.s.i / SHA_DIGEST_LENGTH;
    242     inf->pieces = (uint8_t *) tr_bencStealStr( val );
     242
     243    inf->pieces = calloc ( inf->pieceCount, sizeof(tr_piece_t) );
     244
     245    for ( i=0; i<inf->pieceCount; ++i )
     246    {
     247        memcpy (inf->pieces[i].hash, &val->val.s.s[i*SHA_DIGEST_LENGTH], SHA_DIGEST_LENGTH);
     248    }
    243249
    244250    /* TODO add more tests so we don't crash on weird files */
     
    364370    {
    365371        inf->trackerTiers = 0;
    366         inf->trackerList = calloc( sizeof( inf->trackerList[0] ),
    367                                    val->val.l.count );
     372        inf->trackerList = calloc( val->val.l.count,
     373                                   sizeof( inf->trackerList[0] ) );
    368374
    369375        /* iterate through the announce-list's tiers */
     
    376382            }
    377383            subcount = 0;
    378             sublist = calloc( sizeof( sublist[0] ), subval->val.l.count );
     384            sublist = calloc( subval->val.l.count, sizeof( sublist[0] ) );
    379385
    380386            /* iterate through the tier's items */
     
    412418            else if( 0 < subcount )
    413419            {
    414                 inf->trackerList[inf->trackerTiers].list = calloc( sizeof( sublist[0] ), subcount );
     420                inf->trackerList[inf->trackerTiers].list = calloc( subcount, sizeof( sublist[0] ) );
    415421                memcpy( inf->trackerList[inf->trackerTiers].list, sublist,
    416422                        sizeof( sublist[0] ) * subcount );
     
    437443        {
    438444            swapping = inf->trackerList;
    439             inf->trackerList = calloc( sizeof( inf->trackerList[0] ),
    440                                        inf->trackerTiers );
     445            inf->trackerList = calloc( inf->trackerTiers,
     446                                       sizeof( inf->trackerList[0] ) );
    441447            memcpy( inf->trackerList, swapping,
    442448                    sizeof( inf->trackerList[0] ) * inf->trackerTiers );
     
    462468        }
    463469
    464         sublist                   = calloc( sizeof( sublist[0] ), 1 );
     470        sublist                   = calloc( 1, sizeof( sublist[0] ) );
    465471        sublist[0].address        = address;
    466472        sublist[0].port           = port;
    467473        sublist[0].announce       = announce;
    468474        sublist[0].scrape         = announceToScrape( announce );
    469         inf->trackerList          = calloc( sizeof( inf->trackerList[0] ), 1 );
     475        inf->trackerList          = calloc( 1, sizeof( inf->trackerList[0] ) );
    470476        inf->trackerList[0].list  = sublist;
    471477        inf->trackerList[0].count = 1;
  • trunk/libtransmission/peer.c

    r2004 r2149  
    424424   
    425425    /* Disconnect if seeder and torrent is seeding */
    426     if( peer->tor->status == TR_STATUS_SEED && peer->progress >= 1.0 )
     426    if(   ( peer->progress >= 1.0 )
     427       && ( peer->tor->status & (TR_STATUS_SEED|TR_STATUS_DONE) ) )
    427428    {
    428429        return TR_ERROR;
     
    554555
    555556    /* Ask for a block whenever possible */
    556     if( !tr_cpIsSeeding( tor->completion ) &&
    557         !peer->amInterested && tor->peerCount > TR_MAX_PEER_COUNT - 2 )
     557    if( tr_cpGetStatus( tor->completion ) == TR_CP_INCOMPLETE
     558        && !peer->amInterested
     559        && tor->peerCount > TR_MAX_PEER_COUNT - 2 )
    558560    {
    559561        /* This peer is no use to us, and it seems there are
     
    563565    }
    564566
    565     if( peer->amInterested && !peer->peerChoking && !peer->banned )
    566     {
    567         int block;
    568         while( peer->inRequestCount < OUR_REQUEST_COUNT )
    569         {
    570             block = chooseBlock( tor, peer );</