Guest User

Transmission GUI - main.pas

a guest
Sep 30th, 2016
158
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Pascal 206.94 KB | None | 0 0
  1. {*************************************************************************************
  2.   This file is part of Transmission Remote GUI.
  3.   Copyright (c) 2008-2014 by Yury Sidorov.
  4.  
  5.   Transmission Remote GUI is free software; you can redistribute it and/or modify
  6.   it under the terms of the GNU General Public License as published by
  7.   the Free Software Foundation; either version 2 of the License, or
  8.   (at your option) any later version.
  9.  
  10.   Transmission Remote GUI 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 Transmission Remote GUI; if not, write to the Free Software
  17.   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  18. *************************************************************************************}
  19.  
  20. unit Main;
  21.  
  22. {$mode objfpc}{$H+}
  23.  
  24. interface
  25.  
  26. uses
  27.   Classes, SysUtils, FileUtil, zstream, LResources, Forms, Controls, Graphics, Dialogs, ComCtrls, Menus, ActnList,
  28.   httpsend, StdCtrls, fpjson2, jsonparser2, ExtCtrls, rpc, syncobjs, variants, varlist, IpResolver,
  29.   zipper, ResTranslator, VarGrid, StrUtils, LCLProc, Grids, BaseForm, utils, AddTorrent, data;
  30.  
  31. const
  32.   AppName = 'Transmission Remote GUI';
  33.   AppVersion = '5.0.1';
  34.  
  35. resourcestring
  36.   sAll = 'All torrents';
  37.   sWaiting = 'Waiting';
  38.   sVerifying = 'Verifying';
  39.   sDownloading = 'Downloading';
  40.   sSeeding = 'Seeding';
  41.   sFinished = 'Finished';
  42.   sStopped = 'Stopped';
  43.   sUnknown = 'Unknown';
  44.   sCompleted = 'Completed';
  45.   sConnected = 'connected';
  46.   sActive = 'Active';
  47.   sInactive = 'Inactive';
  48.   sErrorState = 'Error';
  49.   sUpdating = 'Updating...';
  50.   sFinishedDownload = '''%s'' has finished downloading';
  51.   sDownloadComplete = 'Download complete';
  52.   sUpdateComplete = 'Update complete.';
  53.   sTorrentVerification = 'Torrent verification may take a long time.' + LineEnding + 'Are you sure to start verification of torrent ''%s''?';
  54.   sTorrentsVerification = 'Torrents verification may take a long time.' + LineEnding + 'Are you sure to start verification of %d torrents?';
  55.   sReconnect = 'Reconnect in %d seconds.';
  56.   sDisconnected = 'Disconnected';
  57.   sConnectingToDaemon = 'Connecting to daemon...';
  58.   sSecs = '%ds';
  59.   sMins = '%dm';
  60.   sHours = '%dh';
  61.   sDays = '%dd';
  62.   sDownloadingSeeding = '%s%s%d downloading, %d seeding%s%s, %s';
  63.   sDownSpeed = 'D: %s/s';
  64.   sUpSpeed = 'U: %s/s';
  65.   SFreeSpace = 'Free: %s';
  66.   sNoPathMapping = 'Unable to find path mapping.'+LineEnding+'Use the application''s options to setup path mappings.';
  67.   sGeoIPConfirm = 'Geo IP database is needed to resolve country by IP address.' + LineEnding + 'Download this database now?';
  68.   sFlagArchiveConfirm = 'Flag images archive is needed to display country flags.' + LineEnding + 'Download this archive now?';
  69.   sInSwarm = 'in swarm';
  70.   sHashfails = '%s (%d hashfails)';
  71.   sDone = '%s (%s done)';
  72.   sHave = '%d x %s (have %d)';
  73.   sUnableExtractFlag = 'Unable to extract flag image.';
  74.   sTrackerWorking = 'Working';
  75.   sTrackerUpdating = 'Updating';
  76.   sRestartRequired = 'You need to restart the application to apply changes.';
  77.   sRemoveTorrentData = 'Are you sure to remove torrent ''%s'' and all associated DATA?';
  78.   sRemoveTorrentDataMulti = 'Are you sure to remove %d selected torrents and all their associated DATA?';
  79.   sRemoveTorrent = 'Are you sure to remove torrent ''%s''?';
  80.   sRemoveTorrentMulti = 'Are you sure to remove %d selected torrents?';
  81.   sUnableGetFilesList = 'Unable to get files list';
  82.   sTrackerError = 'Tracker';
  83.   sSkip = 'skip';
  84.   sLow = 'low';
  85.   sNormal = 'normal';
  86.   sHigh = 'high';
  87.   sByte = 'b';
  88.   sKByte = 'KB';
  89.   sMByte = 'MB';
  90.   sGByte = 'GB';
  91.   sTByte = 'TB';
  92.   sPerSecond = '/s';
  93.   sOf = 'of';
  94.   sNoTracker = 'No tracker';
  95.   sTorrents = 'Torrents';
  96.   sBlocklistUpdateComplete = 'The block list has been updated successfully.' + LineEnding + 'The list entries count: %d.';
  97.   sSeveralTorrents = '%d torrents';
  98.   sUnableToExecute = 'Unable to execute "%s".';
  99.   sSSLLoadError = 'Unable to load OpenSSL library files: %s and %s';
  100.   SRemoveTracker = 'Are you sure to remove tracker ''%s''?';
  101.   SUnlimited = 'Unlimited';
  102.   SAverage = 'average';
  103.   SCheckNewVersion = 'Do you wish to enable automatic checking for a new version of %s?';
  104.   SDuplicateTorrent = 'Torrent already exists in the list';
  105.   SUpdateTrackers = 'Update trackers for the existing torrent?';
  106.   SDownloadingTorrent = 'Downloading torrent file...';
  107.   SConnectTo = 'Connect to %s';
  108.   SEnterPassword = 'Please enter a password to connect to %s:';
  109.  
  110.   SDownloaded = 'Downloaded';
  111.   SUploaded = 'Uploaded';
  112.   SFilesAdded = 'Files added';
  113.   SActiveTime = 'Active time';
  114.  
  115. type
  116.   { TProgressImage }
  117.  
  118.   TProgressImage = class(TGraphicControl)
  119.   private
  120.     FBmp: TBitmap;
  121.     FBorderColor: TColor;
  122.     FEndIndex: integer;
  123.     FImageIndex: integer;
  124.     FImages: TImageList;
  125.     FStartIndex: integer;
  126.     FTimer: TTimer;
  127.     function GetFrameDelay: integer;
  128.     procedure SetBorderColor(const AValue: TColor);
  129.     procedure SetEndIndex(const AValue: integer);
  130.     procedure SetFrameDelay(const AValue: integer);
  131.     procedure SetImageIndex(const AValue: integer);
  132.     procedure SetImages(const AValue: TImageList);
  133.     procedure SetStartIndex(const AValue: integer);
  134.     procedure UpdateIndex;
  135.     procedure DoTimer(Sender: TObject);
  136.   protected
  137.     procedure Paint; override;
  138.     procedure VisibleChanged; override;
  139.   public
  140.     constructor Create(AOwner: TComponent); override;
  141.     destructor Destroy; override;
  142.     property Images: TImageList read FImages write SetImages;
  143.     property ImageIndex: integer read FImageIndex write SetImageIndex;
  144.     property StartIndex: integer read FStartIndex write SetStartIndex;
  145.     property EndIndex: integer read FEndIndex write SetEndIndex;
  146.     property FrameDelay: integer read GetFrameDelay write SetFrameDelay;
  147.     property BorderColor: TColor read FBorderColor write SetBorderColor;
  148.   end;
  149.  
  150.   { TMainForm }
  151.  
  152.   TMainForm = class(TBaseForm)
  153.     acConnect: TAction;
  154.     acAddTorrent: TAction;
  155.     acStopTorrent: TAction;
  156.     acRemoveTorrent: TAction;
  157.     acStartTorrent: TAction;
  158.     acSetHighPriority: TAction;
  159.     acSetNormalPriority: TAction;
  160.     acSetLowPriority: TAction;
  161.     acSetNotDownload: TAction;
  162.     acOptions: TAction;
  163.     acDaemonOptions: TAction;
  164.     acStartAllTorrents: TAction;
  165.     acStopAllTorrents: TAction;
  166.     acExit: TAction;
  167.     acResolveHost: TAction;
  168.     acResolveCountry: TAction;
  169.     acShowCountryFlag: TAction;
  170.     acSetupColumns: TAction;
  171.     acRemoveTorrentAndData: TAction;
  172.     acOpenFile: TAction;
  173.     acOpenContainingFolder: TAction;
  174.     acAddLink: TAction;
  175.     acReannounceTorrent: TAction;
  176.     acMoveTorrent: TAction;
  177.     acSelectAll: TAction;
  178.     acShowApp: TAction;
  179.     acHideApp: TAction;
  180.     acAddTracker: TAction;
  181.     acEditTracker: TAction;
  182.     acDelTracker: TAction;
  183.     acConnOptions: TAction;
  184.     acNewConnection: TAction;
  185.     acDisconnect: TAction;
  186.     acAltSpeed: TAction;
  187.     acForceStartTorrent: TAction;
  188.     acQMoveTop: TAction;
  189.     acQMoveUp: TAction;
  190.     acQMoveDown: TAction;
  191.     acQMoveBottom: TAction;
  192.     acCheckNewVersion: TAction;
  193.     acFolderGrouping: TAction;
  194.     acAdvEditTrackers: TAction;
  195.     acFilterPane: TAction;
  196.     acInfoPane: TAction;
  197.     acStatusBar: TAction;
  198.     acCopyPath: TAction;
  199.     acRename: TAction;
  200.     acTrackerGrouping: TAction;
  201.     acUpdateBlocklist: TAction;
  202.     acUpdateGeoIP: TAction;
  203.     acTorrentProps: TAction;
  204.     acVerifyTorrent: TAction;
  205.     ActionList: TActionList;
  206.     ApplicationProperties: TApplicationProperties;
  207.     edSearch: TEdit;
  208.     imgSearch: TImage;
  209.     imgFlags: TImageList;
  210.     FilterTimer: TTimer;
  211.     MenuItem100: TMenuItem;
  212.     MenuItem68: TMenuItem;
  213.     MenuItem93: TMenuItem;
  214.     MenuItem94: TMenuItem;
  215.     MenuItem95: TMenuItem;
  216.     MenuItem96: TMenuItem;
  217.     MenuItem97: TMenuItem;
  218.     MenuItem98: TMenuItem;
  219.     MenuItem99: TMenuItem;
  220.     panDetailsWait: TPanel;
  221.     txGlobalStats: TLabel;
  222.     lvFilter: TVarGrid;
  223.     lvTrackers: TVarGrid;
  224.     MenuItem25: TMenuItem;
  225.     MenuItem34: TMenuItem;
  226.     MenuItem35: TMenuItem;
  227.     MenuItem40: TMenuItem;
  228.     MenuItem41: TMenuItem;
  229.     MenuItem43: TMenuItem;
  230.     MenuItem63: TMenuItem;
  231.     MenuItem64: TMenuItem;
  232.     MenuItem77: TMenuItem;
  233.     MenuItem78: TMenuItem;
  234.     MenuItem79: TMenuItem;
  235.     MenuItem80: TMenuItem;
  236.     MenuItem81: TMenuItem;
  237.     MenuItem82: TMenuItem;
  238.     MenuItem83: TMenuItem;
  239.     MenuItem84: TMenuItem;
  240.     MenuItem85: TMenuItem;
  241.     MenuItem86: TMenuItem;
  242.     MenuItem87: TMenuItem;
  243.     MenuItem88: TMenuItem;
  244.     MenuItem89: TMenuItem;
  245.     MenuItem90: TMenuItem;
  246.     MenuItem91: TMenuItem;
  247.     MenuItem92: TMenuItem;
  248.     miView: TMenuItem;
  249.     miDonate: TMenuItem;
  250.     miHomePage: TMenuItem;
  251.     pmiQueue: TMenuItem;
  252.     miQueue: TMenuItem;
  253.     pmFilter: TPopupMenu;
  254.     sepTrackers: TMenuItem;
  255.     MenuItem65: TMenuItem;
  256.     MenuItem66: TMenuItem;
  257.     MenuItem67: TMenuItem;
  258.     MenuItem69: TMenuItem;
  259.     MenuItem70: TMenuItem;
  260.     MenuItem72: TMenuItem;
  261.     MenuItem76: TMenuItem;
  262.     pmiUpSpeedLimit: TMenuItem;
  263.     pmiDownSpeedLimit: TMenuItem;
  264.     pmDownSpeeds: TPopupMenu;
  265.     pmUpSpeeds: TPopupMenu;
  266.     sepCon2: TMenuItem;
  267.     MenuItem71: TMenuItem;
  268.     sepCon1: TMenuItem;
  269.     MenuItem73: TMenuItem;
  270.     MenuItem74: TMenuItem;
  271.     MenuItem75: TMenuItem;
  272.     pmSepOpen2: TMenuItem;
  273.     MenuItem42: TMenuItem;
  274.     pmSepOpen1: TMenuItem;
  275.     MenuItem44: TMenuItem;
  276.     MenuItem45: TMenuItem;
  277.     MenuItem46: TMenuItem;
  278.     MenuItem47: TMenuItem;
  279.     MenuItem48: TMenuItem;
  280.     MenuItem49: TMenuItem;
  281.     MenuItem50: TMenuItem;
  282.     MenuItem51: TMenuItem;
  283.     MenuItem52: TMenuItem;
  284.     MenuItem53: TMenuItem;
  285.     MenuItem54: TMenuItem;
  286.     MenuItem55: TMenuItem;
  287.     MenuItem56: TMenuItem;
  288.     MenuItem58: TMenuItem;
  289.     MenuItem59: TMenuItem;
  290.     MenuItem60: TMenuItem;
  291.     MenuItem61: TMenuItem;
  292.     MenuItem62: TMenuItem;
  293.     miPriority: TMenuItem;
  294.     pmiPriority: TMenuItem;
  295.     MenuItem57: TMenuItem;
  296.     pbDownloaded: TPaintBox;
  297.     pmTrackers: TPopupMenu;
  298.     pmConnections: TPopupMenu;
  299.     tabStats: TTabSheet;
  300.     tabTrackers: TTabSheet;
  301.     tbConnect: TToolButton;
  302.     tbtAltSpeed: TToolButton;
  303.     sepAltSpeed: TToolButton;
  304.     sepQueue: TToolButton;
  305.     tbQMoveUp: TToolButton;
  306.     tbQMoveDown: TToolButton;
  307.     ToolButton9: TToolButton;
  308.     txConnErrorLabel: TLabel;
  309.     panSearch: TPanel;
  310.     panFilter: TPanel;
  311.     panReconnectFrame: TShape;
  312.     txReconnectSecs: TLabel;
  313.     txConnError: TLabel;
  314.     MenuItem38: TMenuItem;
  315.     MenuItem39: TMenuItem;
  316.     panReconnect: TPanel;
  317.     txLastActive: TLabel;
  318.     txLastActiveLabel: TLabel;
  319.     txTracker: TLabel;
  320.     txTrackerLabel: TLabel;
  321.     txCompletedOn: TLabel;
  322.     txCompletedOnLabel: TLabel;
  323.     txAddedOn: TLabel;
  324.     txAddedOnLabel: TLabel;
  325.     MenuItem19: TMenuItem;
  326.     MenuItem20: TMenuItem;
  327.     MenuItem21: TMenuItem;
  328.     MenuItem22: TMenuItem;
  329.     MenuItem23: TMenuItem;
  330.     MenuItem24: TMenuItem;
  331.     miTSep1: TMenuItem;
  332.     MenuItem26: TMenuItem;
  333.     MenuItem27: TMenuItem;
  334.     MenuItem28: TMenuItem;
  335.     MenuItem29: TMenuItem;
  336.     MenuItem30: TMenuItem;
  337.     MenuItem31: TMenuItem;
  338.     MenuItem32: TMenuItem;
  339.     MenuItem33: TMenuItem;
  340.     MenuItem36: TMenuItem;
  341.     MenuItem37: TMenuItem;
  342.     miAbout: TMenuItem;
  343.     miHelp: TMenuItem;
  344.     panTop: TPanel;
  345.     pmTray: TPopupMenu;
  346.     HSplitter: TSplitter;
  347.     pmPeers: TPopupMenu;
  348.     TrayIcon: TTrayIcon;
  349.     txCreated: TLabel;
  350.     txCreatedLabel: TLabel;
  351.     txTorrentHeader: TPanel;
  352.     txTorrentName: TLabel;
  353.     txTorrentNameLabel: TLabel;
  354.     txDownProgress: TLabel;
  355.     txDownProgressLabel: TLabel;
  356.     panProgress: TPanel;
  357.     txMaxPeers: TLabel;
  358.     txMaxPeersLabel: TLabel;
  359.     txPeers: TLabel;
  360.     txPeersLabel: TLabel;
  361.     txSeeds: TLabel;
  362.     txSeedsLabel: TLabel;
  363.     txDummy2: TLabel;
  364.     txTrackerUpdate: TLabel;
  365.     txDummy1: TLabel;
  366.     txRemaining: TLabel;
  367.     txRemainingLabel: TLabel;
  368.     txStatus: TLabel;
  369.     txStatusLabel: TLabel;
  370.     txRatio: TLabel;
  371.     txRatioLabel: TLabel;
  372.     txDownLimit: TLabel;
  373.     txDownLimitLabel: TLabel;
  374.     txTrackerUpdateLabel: TLabel;
  375.     txTransferHeader: TPanel;
  376.     txUpSpeed: TLabel;
  377.     txUpLimit: TLabel;
  378.     txUpSpeedLabel: TLabel;
  379.     txDownSpeed: TLabel;
  380.     txDownSpeedLabel: TLabel;
  381.     txUploaded: TLabel;
  382.     txUploadedLabel: TLabel;
  383.     txDownloaded: TLabel;
  384.     txDownloadedLabel: TLabel;
  385.     txUpLimitLabel: TLabel;
  386.     txWasted: TLabel;
  387.     txWastedLabel: TLabel;
  388.     miCopyLabel: TMenuItem;
  389.     pmLabels: TPopupMenu;
  390.     txError: TLabel;
  391.     txErrorLabel: TLabel;
  392.     MenuItem17: TMenuItem;
  393.     MenuItem18: TMenuItem;
  394.     miTools: TMenuItem;
  395.     TickTimer: TTimer;
  396.     MainToolBar: TToolBar;
  397.     panTransfer: TPanel;
  398.     ToolButton1: TToolButton;
  399.     ToolButton2: TToolButton;
  400.     ToolButton3: TToolButton;
  401.     ToolButton4: TToolButton;
  402.     tbStopTorrent: TToolButton;
  403.     ToolButton6: TToolButton;
  404.     ToolButton7: TToolButton;
  405.     ToolButton8: TToolButton;
  406.     txComment: TLabel;
  407.     txCommentLabel: TLabel;
  408.     txHash: TLabel;
  409.     txHashLabel: TLabel;
  410.     panGeneralInfo: TPanel;
  411.     lvFiles: TVarGrid;
  412.     lvPeers: TVarGrid;
  413.     MainMenu: TMainMenu;
  414.     MenuItem1: TMenuItem;
  415.     MenuItem10: TMenuItem;
  416.     MenuItem11: TMenuItem;
  417.     MenuItem12: TMenuItem;
  418.     MenuItem13: TMenuItem;
  419.     MenuItem14: TMenuItem;
  420.     MenuItem15: TMenuItem;
  421.     MenuItem16: TMenuItem;
  422.     MenuItem2: TMenuItem;
  423.     MenuItem3: TMenuItem;
  424.     MenuItem4: TMenuItem;
  425.     MenuItem5: TMenuItem;
  426.     MenuItem6: TMenuItem;
  427.     MenuItem7: TMenuItem;
  428.     MenuItem8: TMenuItem;
  429.     MenuItem9: TMenuItem;
  430.     miConnect: TMenuItem;
  431.     miExit: TMenuItem;
  432.     miTorrent: TMenuItem;
  433.     OpenTorrentDlg: TOpenDialog;
  434.     PageInfo: TPageControl;
  435.     pmTorrents: TPopupMenu;
  436.     pmFiles: TPopupMenu;
  437.     sbGenInfo: TScrollBox;
  438.     txPieces: TLabel;
  439.     txPiecesLabel: TLabel;
  440.     txTotalSize: TLabel;
  441.     txTotalSizeLabel: TLabel;
  442.     gTorrents: TVarGrid;
  443.     gStats: TVarGrid;
  444.     VSplitter: TSplitter;
  445.     StatusBar: TStatusBar;
  446.     tabPeers: TTabSheet;
  447.     tabGeneral: TTabSheet;
  448.     TorrentsListTimer: TTimer;
  449.     tabFiles: TTabSheet;
  450.     procedure acAddLinkExecute(Sender: TObject);
  451.     procedure acAddTorrentExecute(Sender: TObject);
  452.     procedure acAddTrackerExecute(Sender: TObject);
  453.     procedure acAdvEditTrackersExecute(Sender: TObject);
  454.     procedure acAltSpeedExecute(Sender: TObject);
  455.     procedure acCheckNewVersionExecute(Sender: TObject);
  456.     procedure acConnectExecute(Sender: TObject);
  457.     procedure acConnOptionsExecute(Sender: TObject);
  458.     procedure acCopyPathExecute(Sender: TObject);
  459.     procedure acDelTrackerExecute(Sender: TObject);
  460.     procedure acEditTrackerExecute(Sender: TObject);
  461.     procedure acFilterPaneExecute(Sender: TObject);
  462.     procedure acFolderGroupingExecute(Sender: TObject);
  463.     procedure acForceStartTorrentExecute(Sender: TObject);
  464.     procedure acHideAppExecute(Sender: TObject);
  465.     procedure acInfoPaneExecute(Sender: TObject);
  466.     procedure acMoveTorrentExecute(Sender: TObject);
  467.     procedure acNewConnectionExecute(Sender: TObject);
  468.     procedure acOpenContainingFolderExecute(Sender: TObject);
  469.     procedure acOpenFileExecute(Sender: TObject);
  470.     procedure acOptionsExecute(Sender: TObject);
  471.     procedure acDisconnectExecute(Sender: TObject);
  472.     procedure acExitExecute(Sender: TObject);
  473.     procedure acDaemonOptionsExecute(Sender: TObject);
  474.     procedure acQMoveBottomExecute(Sender: TObject);
  475.     procedure acQMoveDownExecute(Sender: TObject);
  476.     procedure acQMoveTopExecute(Sender: TObject);
  477.     procedure acQMoveUpExecute(Sender: TObject);
  478.     procedure acReannounceTorrentExecute(Sender: TObject);
  479.     procedure acRemoveTorrentAndDataExecute(Sender: TObject);
  480.     procedure acRemoveTorrentExecute(Sender: TObject);
  481.     procedure acRenameExecute(Sender: TObject);
  482.     procedure acResolveCountryExecute(Sender: TObject);
  483.     procedure acResolveHostExecute(Sender: TObject);
  484.     procedure acSelectAllExecute(Sender: TObject);
  485.     procedure acSetHighPriorityExecute(Sender: TObject);
  486.     procedure acSetLowPriorityExecute(Sender: TObject);
  487.     procedure acSetNormalPriorityExecute(Sender: TObject);
  488.     procedure acSetNotDownloadExecute(Sender: TObject);
  489.     procedure acSetupColumnsExecute(Sender: TObject);
  490.     procedure acShowAppExecute(Sender: TObject);
  491.     procedure acShowCountryFlagExecute(Sender: TObject);
  492.     procedure acStartAllTorrentsExecute(Sender: TObject);
  493.     procedure acStartTorrentExecute(Sender: TObject);
  494.     procedure acStatusBarExecute(Sender: TObject);
  495.     procedure acStopAllTorrentsExecute(Sender: TObject);
  496.     procedure acStopTorrentExecute(Sender: TObject);
  497.     procedure acTorrentPropsExecute(Sender: TObject);
  498.     procedure acTrackerGroupingExecute(Sender: TObject);
  499.     procedure acUpdateBlocklistExecute(Sender: TObject);
  500.     procedure acUpdateGeoIPExecute(Sender: TObject);
  501.     procedure acVerifyTorrentExecute(Sender: TObject);
  502.     procedure ApplicationPropertiesEndSession(Sender: TObject);
  503.     procedure ApplicationPropertiesException(Sender: TObject; E: Exception);
  504.     procedure ApplicationPropertiesIdle(Sender: TObject; var Done: Boolean);
  505.     procedure ApplicationPropertiesMinimize(Sender: TObject);
  506.     procedure ApplicationPropertiesRestore(Sender: TObject);
  507.     procedure edSearchChange(Sender: TObject);
  508.     procedure FormActivate(Sender: TObject);
  509.     procedure FormDropFiles(Sender: TObject; const FileNames: array of String);
  510.     procedure FormWindowStateChange(Sender: TObject);
  511.     procedure gTorrentsCellAttributes(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; var CellAttribs: TCellAttributes);
  512.     procedure gTorrentsClick(Sender: TObject);
  513.     procedure gTorrentsDblClick(Sender: TObject);
  514.     procedure gTorrentsDrawCell(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; const R: TRect; var ADefaultDrawing: boolean);
  515.     procedure gTorrentsEditorHide(Sender: TObject);
  516.     procedure gTorrentsEditorShow(Sender: TObject);
  517.     procedure gTorrentsQuickSearch(Sender: TVarGrid; var SearchText: string; var ARow: integer);
  518.     procedure gTorrentsResize(Sender: TObject);
  519.     procedure gTorrentsSetEditText(Sender: TObject; ACol, ARow: Integer; const Value: string);
  520.     procedure gTorrentsSortColumn(Sender: TVarGrid; var ASortCol: integer);
  521.     procedure HSplitterChangeBounds(Sender: TObject);
  522.     procedure lvFilesDblClick(Sender: TObject);
  523.     procedure lvFilesEditorHide(Sender: TObject);
  524.     procedure lvFilesEditorShow(Sender: TObject);
  525.     procedure lvFilesSetEditText(Sender: TObject; ACol, ARow: Integer; const Value: string);
  526.     procedure lvFilterCellAttributes(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; var CellAttribs: TCellAttributes);
  527.     procedure lvFilterClick(Sender: TObject);
  528.     procedure lvFilterDrawCell(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; const R: TRect; var ADefaultDrawing: boolean);
  529.     procedure lvPeersCellAttributes(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; var CellAttribs: TCellAttributes);
  530.     procedure lvTrackersCellAttributes(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; var CellAttribs: TCellAttributes);
  531.     procedure lvTrackersDblClick(Sender: TObject);
  532.     procedure lvTrackersKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  533.     procedure miDonateClick(Sender: TObject);
  534.     procedure miHomePageClick(Sender: TObject);
  535.     procedure PageInfoResize(Sender: TObject);
  536.     procedure panReconnectResize(Sender: TObject);
  537.     procedure pbDownloadedPaint(Sender: TObject);
  538.     procedure StatusBarMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  539.     procedure TickTimerTimer(Sender: TObject);
  540.     procedure FilterTimerTimer(Sender: TObject);
  541.     procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
  542.     procedure FormCreate(Sender: TObject);
  543.     procedure FormDestroy(Sender: TObject);
  544.     procedure FormResize(Sender: TObject);
  545.     procedure FormShow(Sender: TObject);
  546.     procedure lvFilterResize(Sender: TObject);
  547.     procedure miAboutClick(Sender: TObject);
  548.     procedure miCopyLabelClick(Sender: TObject);
  549.     procedure PageInfoChange(Sender: TObject);
  550.     procedure TorrentsListTimerTimer(Sender: TObject);
  551.     procedure pmFilesPopup(Sender: TObject);
  552.     procedure pmTorrentsPopup(Sender: TObject);
  553.     procedure TrayIconDblClick(Sender: TObject);
  554.     procedure VSplitterChangeBounds(Sender: TObject);
  555.   private
  556.     FStarted: boolean;
  557.     FTorrents: TVarList;
  558.     FFiles: TVarList;
  559.     FTrackers: TStringList;
  560.     FResolver: TIpResolver;
  561.     FUnZip: TUnZipper;
  562.     FReconnectWaitStart: TDateTime;
  563.     FReconnectTimeOut: integer;
  564.     FTorrentProgress: TBitmap;
  565.     FLastPieces: string;
  566.     FLastPieceCount: integer;
  567.     FLastDone: double;
  568.     FCurConn: string;
  569.     FPathMap: TStringList;
  570.     FLastFilerIndex: integer;
  571.     FFilterChanged: boolean;
  572.     FCurDownSpeedLimit: integer;
  573.     FCurUpSpeedLimit: integer;
  574.     FFlagsPath: string;
  575.     FAddingTorrent: integer;
  576.     FPendingTorrents: TStringList;
  577.     FLinksFromClipboard: boolean;
  578.     FLastClipboardLink: string;
  579. //Added by Antekgla
  580.     FFileManagerDefault: string;
  581.     FFileManagerDefaultParam: string;
  582. //End added by Antekgla
  583. {$ifdef LCLcarbon}
  584.     FFormActive: boolean;
  585. {$endif LCLcarbon}
  586.     FSlowResponse: TProgressImage;
  587.     FDetailsWait: TProgressImage;
  588.     FDetailsWaitStart: TDateTime;
  589.     FMainFormShown: boolean;
  590.     FFilesTree: TFilesTree;
  591.     FFilesCapt: string;
  592.     FCalcAvg: boolean;
  593.     FPasswords: TStringList;
  594.  
  595.     procedure UpdateUI;
  596.     procedure UpdateUIRpcVersion(RpcVersion: integer);
  597.     function DoConnect: boolean;
  598.     procedure DoCreateOutZipStream(Sender: TObject; var AStream: TStream; AItem: TFullZipFileEntry);
  599.     procedure DoDisconnect;
  600.     procedure DoOpenFlagsZip(Sender: TObject; var AStream: TStream);
  601.     procedure TorrentProps(PageNo: integer);
  602.     procedure ShowConnOptions(NewConnection: boolean);
  603.     procedure SaveColumns(LV: TVarGrid; const AName: string; FullInfo: boolean = True);
  604.     procedure LoadColumns(LV: TVarGrid; const AName: string; FullInfo: boolean = True);
  605.     function GetTorrentError(t: TJSONObject; Status: integer): string;
  606.     function SecondsToString(j: integer): string;
  607.     function DoAddTorrent(const FileName: Utf8String): boolean;
  608.     procedure UpdateTray;
  609.     procedure HideApp;
  610.     procedure ShowApp;
  611.     procedure DownloadFinished(const TorrentName: string);
  612.     function GetFlagImage(const CountryCode: string): integer;
  613.     procedure BeforeCloseApp;
  614.     function GetGeoIpDatabase: string;
  615.     function GetFlagsArchive: string;
  616.     function DownloadGeoIpDatabase(AUpdate: boolean): boolean;
  617.     procedure TorrentColumnsChanged;
  618.     function EtaToString(ETA: integer): string;
  619.     function GetTorrentStatus(TorrentIdx: integer): string;
  620.     function GetSeedsText(Seeds, SeedsTotal: integer): string;
  621.     function GetPeersText(Peers, PeersTotal, Leechers: integer): string;
  622.     function RatioToString(Ratio: double): string;
  623.     function TorrentDateTimeToString(d: Int64): string;
  624.     procedure DoRefresh(All: boolean = False);
  625.     function GetFilesCommonPath(files: TJSONArray): string;
  626.     procedure InternalRemoveTorrent(const Msg, MsgMulti: string; RemoveLocalData: boolean);
  627.     function IncludeProperTrailingPathDelimiter(const s: string): string;
  628.     procedure UrlLabelClick(Sender: TObject);
  629.     procedure CenterReconnectWindow;
  630.     procedure ProcessPieces(const Pieces: string; PieceCount: integer; const Done: double);
  631.     function ExecRemoteFile(const FileName: string; SelectFile: boolean): boolean;
  632.     function GetSelectedTorrents: variant;
  633.     procedure FillDownloadDirs(CB: TComboBox; const CurFolderParam: string);
  634.     procedure SaveDownloadDirs(CB: TComboBox; const CurFolderParam: string);
  635.     procedure SetRefreshInterval;
  636.     procedure AddTracker(EditMode: boolean);
  637.     procedure UpdateConnections;
  638.     procedure DoConnectToHost(Sender: TObject);
  639.     procedure FillSpeedsMenu;
  640.     procedure DoSetDownloadSpeed(Sender: TObject);
  641.     procedure DoSetUploadSpeed(Sender: TObject);
  642.     procedure SetSpeedLimit(const Dir: string; Speed: integer);
  643.     function FixSeparators(const p: string): string;
  644.     function MapRemoteToLocal(const RemotePath: string): string;
  645.     procedure CheckAddTorrents;
  646.     procedure CheckClipboardLink;
  647.     procedure CenterDetailsWait;
  648.     function GetPageInfoType(pg: TTabSheet): TAdvInfoType;
  649.     procedure DetailsUpdated;
  650.     function RenameTorrent(TorrentId: integer; const OldPath, NewName: string): boolean;
  651.     procedure FilesTreeStateChanged(Sender: TObject);
  652.     function SelectTorrent(TorrentId, TimeOut: integer): integer;
  653.     procedure OpenCurrentTorrent(OpenFolderOnly: boolean);
  654.   public
  655.     procedure FillTorrentsList(list: TJSONArray);
  656.     procedure FillPeersList(list: TJSONArray);
  657.     procedure FillFilesList(ATorrentId: integer; list, priorities, wanted: TJSONArray; const DownloadDir: WideString);
  658.     procedure FillGeneralInfo(t: TJSONObject);
  659.     procedure FillTrackersList(TrackersData: TJSONObject);
  660.     procedure FillSessionInfo(s: TJSONObject);
  661.     procedure FillStatistics(s: TJSONObject);
  662.     procedure CheckStatus(Fatal: boolean = True);
  663.     function TorrentAction(const TorrentIds: variant; const AAction: string; args: TJSONObject = nil): boolean;
  664.     function SetFilePriority(TorrentId: integer; const Files: array of integer; const APriority: string): boolean;
  665.     function SetCurrentFilePriority(const APriority: string): boolean;
  666.     procedure SetTorrentPriority(APriority: integer);
  667.     procedure ClearDetailsInfo(Skip: TAdvInfoType = aiNone);
  668.     function SelectRemoteFolder(const CurFolder, DialogTitle: string): string;
  669.     procedure ConnectionSettingsChanged(const ActiveConnection: string; ForceReconnect: boolean);
  670.   end;
  671.  
  672. function CheckAppParams: boolean;
  673. function GetHumanSize(sz: double; RoundTo: integer = 0; const EmptyStr: string = '-'): string;
  674. function PriorityToStr(p: integer; var ImageIndex: integer): string;
  675. procedure DrawProgressCell(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; const ACellRect: TRect);
  676.  
  677. var
  678.   MainForm: TMainForm;
  679.   RpcObj: TRpc;
  680.   FTranslationFileName: string;
  681.   FTranslationLanguage: string;
  682.   FAlterColor: TColor;
  683.   IsUnity: boolean;
  684.   Ini: TIniFileUtf8;
  685.  
  686. const
  687.   // Torrents list
  688.   idxName = 0;
  689.   idxSize = 1;
  690.   idxDone = 2;
  691.   idxStatus = 3;
  692.   idxSeeds = 4;
  693.   idxPeers = 5;
  694.   idxDownSpeed = 6;
  695.   idxUpSpeed = 7;
  696.   idxETA = 8;
  697.   idxRatio = 9;
  698.   idxDownloaded = 10;
  699.   idxUploaded = 11;
  700.   idxTracker = 12;
  701.   idxTrackerStatus = 13;
  702.   idxAddedOn = 14;
  703.   idxCompletedOn = 15;
  704.   idxLastActive = 16;
  705.   idxPath = 17;
  706.   idxPriority = 18;
  707.   idxSizeToDowload = 19;
  708.   idxTorrentId = 20;
  709.   idxQueuePos = 21;
  710.   idxSeedingTime = 22;
  711.  
  712.   idxTag = -1;
  713.   idxSeedsTotal = -2;
  714.   idxLeechersTotal = -3;
  715.   idxStateImg = -4;
  716.   idxDeleted = -5;
  717.   idxDownSpeedHistory = -6;
  718.   idxUpSpeedHistory = -7;
  719.   TorrentsExtraColumns = 7;
  720.  
  721.   // Peers list
  722.   idxPeerHost = 0;
  723.   idxPeerPort = 1;
  724.   idxPeerCountry = 2;
  725.   idxPeerClient = 3;
  726.   idxPeerFlags = 4;
  727.   idxPeerDone = 5;
  728.   idxPeerUpSpeed = 6;
  729.   idxPeerDownSpeed = 7;
  730.   idxPeerTag = -1;
  731.   idxPeerIP = -2;
  732.   idxPeerCountryImage = -3;
  733.   PeersExtraColumns = 3;
  734.  
  735.   // Trackers list
  736.   idxTrackersListName = 0;
  737.   idxTrackersListStatus = 1;
  738.   idxTrackersListUpdateIn = 2;
  739.   idxTrackersListSeeds = 3;
  740.   idxTrackerTag = -1;
  741.   idxTrackerID = -2;
  742.   TrackersExtraColumns = 2;
  743.  
  744.   // Filter idices
  745.   fltAll      = 0;
  746.   fltDown     = 1;
  747.   fltDone     = 2;
  748.   fltActive   = 3;
  749.   fltInactive = 4;
  750.   fltStopped  = 5;
  751.   fltError  = 6;
  752.  
  753.   // Status images
  754.   imgDown      = 9;
  755.   imgSeed      = 10;
  756.   imgDownError = 11;
  757.   imgSeedError = 12;
  758.   imgError     = 13;
  759.   imgDone      = 14;
  760.   imgStopped   = 29;
  761.   imgDownQueue = 16;
  762.   imgSeedQueue = 17;
  763.   imgAll       = 19;
  764.   imgActive    = 20;
  765.  
  766.   StatusFiltersCount = 7;
  767.  
  768.   TorrentFieldsMap: array[idxName..idxSeedingTime] of string =
  769.     ('', 'totalSize', '', 'status', 'peersSendingToUs,seeders',
  770.      'peersGettingFromUs,leechers', '', '', 'eta', 'uploadRatio',
  771.      'downloadedEver', 'uploadedEver', '', '', 'addedDate', 'doneDate', 'activityDate', '', 'bandwidthPriority',
  772.      '', '', 'queuePosition', 'secondsSeeding');
  773.  
  774.   FinishedQueue = 1000000;
  775.  
  776.   TR_PRI_SKIP   = -1000;  // psedudo priority
  777.   TR_PRI_LOW    = -1;
  778.   TR_PRI_NORMAL =  0;
  779.   TR_PRI_HIGH   =  1;
  780.  
  781. implementation
  782.  
  783. uses
  784. {$ifdef linux}
  785.   process, dynlibs,
  786. {$endif linux}
  787. {$ifdef darwin}
  788.   urllistenerosx,
  789. {$endif darwin}
  790.   synacode, ConnOptions, clipbrd, DateUtils, TorrProps, DaemonOptions, About,
  791.   ToolWin, download, ColSetup, types, AddLink, MoveTorrent, ssl_openssl_lib, AddTracker, lcltype,
  792.   Options, ButtonPanel, BEncode, synautil;
  793.  
  794. const
  795.   TR_STATUS_CHECK_WAIT_1   = ( 1 shl 0 ); // Waiting in queue to check files
  796.   TR_STATUS_CHECK_1        = ( 1 shl 1 ); // Checking files
  797.   TR_STATUS_DOWNLOAD_1     = ( 1 shl 2 ); // Downloading
  798.   TR_STATUS_SEED_1         = ( 1 shl 3 ); // Seeding
  799.   TR_STATUS_STOPPED_1      = ( 1 shl 4 ); // Torrent is stopped
  800.  
  801.   TR_STATUS_STOPPED_2       = 0;     // Torrent is stopped
  802.   TR_STATUS_CHECK_WAIT_2    = 1;     // Queued to check files
  803.   TR_STATUS_CHECK_2         = 2;     // Checking files
  804.   TR_STATUS_DOWNLOAD_WAIT_2 = 3;     // Queued to download
  805.   TR_STATUS_DOWNLOAD_2      = 4;     // Downloading
  806.   TR_STATUS_SEED_WAIT_2     = 5;     // Queued to seed
  807.   TR_STATUS_SEED_2          = 6;     // Seeding
  808.  
  809.   TR_STATUS_FINISHED        = $100; // Torrent is finished (pseudo status)
  810.  
  811.   TR_SPEEDLIMIT_GLOBAL    = 0;    // only follow the overall speed limit
  812.   TR_SPEEDLIMIT_SINGLE    = 1;    // only follow the per-torrent limit
  813.   TR_SPEEDLIMIT_UNLIMITED = 2;    // no limits at all
  814.  
  815.   SpeedHistorySize = 20;
  816.  
  817. const
  818.   SizeNames: array[1..5] of string = (sByte, sKByte, sMByte, sGByte, sTByte);
  819.  
  820. var
  821.   TR_STATUS_STOPPED, TR_STATUS_CHECK_WAIT, TR_STATUS_CHECK, TR_STATUS_DOWNLOAD_WAIT, TR_STATUS_DOWNLOAD, TR_STATUS_SEED_WAIT, TR_STATUS_SEED: integer;
  822.  
  823. function GetHumanSize(sz: double; RoundTo: integer; const EmptyStr: string): string;
  824. var
  825.   i: integer;
  826. begin
  827.   if sz < 0 then begin
  828.     Result:=EmptyStr;
  829.     exit;
  830.   end;
  831.   i:=Low(SizeNames);
  832.   if RoundTo > 0 then begin
  833.     Inc(i);
  834.     sz:=sz/1024;
  835.   end;
  836.   while i <= High(SizeNames) do begin
  837.     if sz < 1024 then
  838.       break;
  839.     sz:=sz/1024;
  840.     Inc(i);
  841.   end;
  842.   if (RoundTo = 0) and (i > 3) then
  843.     RoundTo:=i - 2;
  844.   Result:=Format('%.' + IntToStr(RoundTo) + 'f %s', [sz, SizeNames[i]]);
  845. end;
  846.  
  847. function AddToChannel(Clr: TColor; Value: integer; Position: byte): TColor;
  848. var i: integer;
  849.  
  850. begin
  851.      i:=(Clr shr (Position*8)) and $FF;
  852.      i:=i + Value;
  853.      if i < 0 then i:=0;
  854.      if i > $FF then i:=$FF;
  855.      Result:=Clr and (not (Cardinal($FF) shl (Position*8))) or (Cardinal(i) shl (Position*8));
  856. end;
  857.  
  858. function AddToColor(Color: TColor; R, G, B: integer): TColor;
  859. begin
  860.      Result:=ColorToRGB(Color);
  861.      Result:=AddToChannel(Result, R, 0);
  862.      Result:=AddToChannel(Result, G, 1);
  863.      Result:=AddToChannel(Result, B, 2);
  864. end;
  865.  
  866. function GetLikeColor(Color: TColor; Delta: integer): TColor;
  867. var i, j: integer;
  868.  
  869. begin
  870.     Result:=ColorToRGB(Color);
  871.     j:=Result and $FF;               //red
  872.     i:=(Result shr 8) and $FF;       // green
  873.     if i > j then
  874.       j:=i;
  875.     i:=((Result shr 16) and $FF) shr 1;      // blue
  876.     if i > j then
  877.       j:=i;
  878.     if j < $80 then
  879.       i:=(($80 - j) div $20 + 1)*Delta
  880.     else
  881.       i:=Delta;
  882.     if (i + j > 255) or (i + j < 0) then
  883.       i:=-Delta;
  884.  
  885.     Result:=AddToColor(Result, i, i, i);
  886. end;
  887.  
  888. function LocateFile(const FileName: string; const Paths: array of string): string;
  889. var
  890.   i: integer;
  891. begin
  892.   for i:=Low(Paths) to High(Paths) do begin
  893.     Result:=IncludeTrailingPathDelimiter(Paths[i]) + FileName;
  894.     if FileExistsUTF8(Result) then
  895.       exit;
  896.   end;
  897.   Result:='';
  898. end;
  899.  
  900. procedure OnTranslate(Sender: TResTranslator; const ResourceName: AnsiString; var Accept: boolean);
  901. const
  902.   IgnoreUnits: array[0..12] of string =
  903.       ('fpjson','jsonparser','jsonscanner','lclstrconsts','math',
  904.        'rtlconsts','sysconst','variants','zbase','zipper','zstream',
  905.        'xmlcfg', 'registry');
  906.  
  907.   IgnoreControls: array[0..3] of string =
  908.     ('AboutForm.txAuthor', 'MainForm.miLn', 'ConnOptionsForm.cbUseSocks5', 'ConnOptionsForm.tabConnection');
  909.  
  910. var
  911.   i: integer;
  912. begin
  913.   Accept := not AnsiMatchText(Copy2Symb(ResourceName, '.'), IgnoreUnits)
  914.              or AnsiStartsText('lclstrconsts.rsMb', ResourceName)  //<-- dialog buttons
  915.              or AnsiStartsText('lclstrconsts.rsMt', ResourceName); //<-- dialog message
  916.   if Accept then
  917.     for i:=Low(IgnoreControls) to High(IgnoreControls) do
  918.       if AnsiStartsText(IgnoreControls[i], ResourceName) then begin
  919.         Accept:=False;
  920.         exit;
  921.       end;
  922.   if Accept and (Copy(ResourceName, Length(ResourceName) - 8, MaxInt) = '.Category') then
  923.     Accept:=False;
  924. end;
  925.  
  926. var
  927.   FHomeDir: string;
  928.   FIPCFileName: string;
  929.   FRunFileName: string;
  930.  
  931. function IsProtocolSupported(const url: string): boolean;
  932. const
  933.   Protocols: array [1..3] of string =
  934.     ('http:', 'https:', 'magnet:');
  935. var
  936.   i: integer;
  937.   s: string;
  938. begin
  939.   s:=AnsiLowerCase(url);
  940.   for i:=Low(Protocols) to High(Protocols) do
  941.     if Copy(s, 1, Length(Protocols[i])) = Protocols[i] then begin
  942.       Result:=True;
  943.       exit;
  944.     end;
  945.   Result:=False;
  946. end;
  947.  
  948. procedure AddTorrentFile(const FileName: string);
  949. var
  950.   h: System.THandle;
  951.   t: TDateTime;
  952.   s: string;
  953. begin
  954.   if not IsProtocolSupported(FileName) and not FileExistsUTF8(FileName) then
  955.     exit;
  956.   t:=Now;
  957.   repeat
  958.     if FileExistsUTF8(FIPCFileName) then
  959.       h:=FileOpenUTF8(FIPCFileName, fmOpenWrite or fmShareDenyRead or fmShareDenyWrite)
  960.     else
  961.       h:=FileCreateUTF8(FIPCFileName);
  962.     if h <> System.THandle(-1) then begin
  963.       s:=FileName + LineEnding;
  964.       FileSeek(h, 0, soFromEnd);
  965.       FileWrite(h, s[1], Length(s));
  966.       FileClose(h);
  967.       break;
  968.     end;
  969.     Sleep(20);
  970.   until Now - t >= 3/SecsPerDay;
  971. end;
  972.  
  973. procedure LoadTranslation;
  974. begin
  975.   FTranslationFileName := Ini.ReadString('Interface', 'TranslationFile', '');
  976.   if FTranslationFileName <> '-' then
  977.     if (FTranslationFileName = '') or not IsTranslationFileValid(DefaultLangDir + FTranslationFileName) then
  978.       FTranslationLanguage := LoadDefaultTranslationFile(@OnTranslate)
  979.     else
  980.       FTranslationLanguage := LoadTranslationFile(DefaultLangDir + FTranslationFileName, @OnTranslate);
  981.   if FTranslationLanguage = '' then
  982.     FTranslationLanguage := 'English'
  983. end;
  984.  
  985. function CheckAppParams: boolean;
  986. var
  987.   i: integer;
  988.   s: string;
  989.   h: System.THandle;
  990.   pid: SizeUInt;
  991. {$ifdef linux}
  992.   proc: TProcess;
  993.   sr: TSearchRec;
  994.   hLib: TLibHandle;
  995. {$endif linux}
  996. begin
  997.   Application.Title:=AppName;
  998. {$ifdef linux}
  999.   IsUnity:=CompareText(GetEnvironmentVariable('XDG_CURRENT_DESKTOP'), 'unity') = 0;
  1000.   if GetEnvironmentVariable('LIBOVERLAY_SCROLLBAR') <> '0' then begin
  1001.     i:=FindFirstUTF8('/usr/lib/liboverlay-scrollbar*', faAnyFile, sr);
  1002.     FindClose(sr);
  1003.     hLib:=LoadLibrary('liboverlay-scrollbar.so');
  1004.     if hLib <> 0 then
  1005.       FreeLibrary(hLib);
  1006.     if (i = 0) or (hLib <> 0) then begin
  1007.       // Turn off overlay scrollbars, since they are not supported yet.
  1008.       // Restart the app with the LIBOVERLAY_SCROLLBAR=0 env var.
  1009.       proc:=TProcess.Create(nil);
  1010.       try
  1011.         s:='';
  1012.         for i:=0 to ParamCount do
  1013.           s:=s + '"' + ParamStrUTF8(i) + '" ';
  1014.         proc.CommandLine:=s;
  1015.         for i:=0 to GetEnvironmentVariableCount - 1 do
  1016.           proc.Environment.Add(GetEnvironmentString(i));
  1017.         proc.Environment.Values['LIBOVERLAY_SCROLLBAR']:='0';
  1018.         proc.Execute;
  1019.       finally
  1020.         proc.Free;
  1021.       end;
  1022.       Result:=False;
  1023.       exit;
  1024.     end;
  1025.   end;
  1026. {$endif linux}
  1027.   FHomeDir:=GetCmdSwitchValue('home');
  1028.   if FHomeDir = '' then begin
  1029.     if FileExistsUTF8(ChangeFileExt(ParamStrUTF8(0), '.ini')) then
  1030.       FHomeDir:=ExtractFilePath(ParamStrUTF8(0)) // Portable mode
  1031.     else
  1032.       FHomeDir:=IncludeTrailingPathDelimiter(GetAppConfigDirUTF8(False));
  1033.   end
  1034.   else
  1035.     FHomeDir:=IncludeTrailingPathDelimiter(FHomeDir);
  1036.   ForceDirectoriesUTF8(FHomeDir);
  1037.   FIPCFileName:=FHomeDir + 'ipc.txt';
  1038.   FRunFileName:=FHomeDir + 'run';
  1039.  
  1040.   Ini:=TIniFileUtf8.Create(FHomeDir+ChangeFileExt(ExtractFileName(ParamStrUTF8(0)), '.ini'));
  1041.   Ini.CacheUpdates:=True;
  1042.  
  1043.   // Check for outdated IPC file
  1044.   if FileExistsUTF8(FIPCFileName) then begin
  1045.     h:=FileOpenUTF8(FIPCFileName, fmOpenRead or fmShareDenyNone);
  1046.     if h <> INVALID_HANDLE_VALUE then begin
  1047.       i:=FileGetDate(h);
  1048.       FileClose(h);
  1049.       if (i > 0) and (Abs(Now - FileDateToDateTime(i)) > 1/MinsPerDay) then
  1050.         DeleteFileUTF8(FIPCFileName);
  1051.     end;
  1052.   end;
  1053.  
  1054.   for i:=1 to ParamCount do begin
  1055.     s:=ParamStrUTF8(i);
  1056.     if IsProtocolSupported(s) or FileExistsUTF8(s) then
  1057.       AddTorrentFile(s);
  1058.   end;
  1059.  
  1060.   if FileExistsUTF8(FRunFileName) then begin
  1061.     // Another process is running
  1062.     h:=FileOpenUTF8(FRunFileName, fmOpenRead or fmShareDenyNone);
  1063.     if FileRead(h, pid, SizeOf(pid)) = SizeOf(pid) then begin
  1064. {$ifdef mswindows}
  1065.       AllowSetForegroundWindow(pid);
  1066. {$endif mswindows}
  1067.     end;
  1068.     FileClose(h);
  1069.  
  1070.     if not FileExistsUTF8(FIPCFileName) then
  1071.       FileClose(FileCreateUTF8(FIPCFileName));
  1072.     for i:=1 to 50 do
  1073.       if not FileExistsUTF8(FIPCFileName) then begin
  1074.         // The running process works normally. Exit application.
  1075.         Result:=False;
  1076.         exit;
  1077.       end
  1078.       else
  1079.         Sleep(200);
  1080.     // The running process is not responding
  1081.     DeleteFileUTF8(FRunFileName);
  1082.     // Delete IPC file if it is empty
  1083.     h:=FileOpenUTF8(FIPCFileName, fmOpenRead or fmShareDenyNone);
  1084.     i:=FileSeek(h, 0, soFromEnd);
  1085.     FileClose(h);
  1086.     if i = 0 then
  1087.       DeleteFileUTF8(FIPCFileName);
  1088.   end;
  1089.  
  1090.   // Create a new run file
  1091.   h:=FileCreateUTF8(FRunFileName);
  1092.   pid:=GetProcessID;
  1093.   FileWrite(h, pid, SizeOf(pid));
  1094.   FileClose(h);
  1095.  
  1096.   LoadTranslation;
  1097.  
  1098.   SizeNames[1]:=sByte;
  1099.   SizeNames[2]:=sKByte;
  1100.   SizeNames[3]:=sMByte;
  1101.   SizeNames[4]:=sGByte;
  1102.   SizeNames[5]:=sTByte;
  1103.  
  1104.   IntfScale:=Ini.ReadInteger('Interface', 'Scaling', 100);
  1105.  
  1106.   Result:=True;
  1107. end;
  1108.  
  1109. function PriorityToStr(p: integer; var ImageIndex: integer): string;
  1110. begin
  1111.   case p of
  1112.     TR_PRI_SKIP:   begin Result:=sSkip; ImageIndex:=23; end;
  1113.     TR_PRI_LOW:    begin Result:=sLow; ImageIndex:=24; end;
  1114.     TR_PRI_NORMAL: begin Result:=sNormal; ImageIndex:=25; end;
  1115.     TR_PRI_HIGH:   begin Result:=sHigh; ImageIndex:=26; end;
  1116.     else           Result:='???';
  1117.   end;
  1118. end;
  1119.  
  1120. procedure DrawProgressCell(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; const ACellRect: TRect);
  1121. var
  1122.   R, RR: TRect;
  1123.   i, j, h: integer;
  1124.   s: string;
  1125.   cl: TColor;
  1126.   Progress: double;
  1127.   sz: TSize;
  1128.   ts: TTextStyle;
  1129. begin
  1130.   Progress:=double(Sender.Items[ADataCol, ARow]);
  1131.   with Sender.Canvas do begin
  1132.     R:=ACellRect;
  1133.     FrameRect(R);
  1134.     s:=Format('%.1f%%', [Progress]);
  1135.     sz:=TextExtent(s);
  1136.     InflateRect(R, -1, -1);
  1137.     Pen.Color:=clBtnFace;
  1138.     Rectangle(R);
  1139.     InflateRect(R, -1, -1);
  1140.  
  1141.     i:=R.Left + Round(Progress*(R.Right - R.Left)/100.0);
  1142.     j:=(R.Top + R.Bottom) div 2;
  1143.     h:=(R.Top + R.Bottom - sz.cy) div 2;
  1144.     cl:=GetLikeColor(clHighlight, 70);
  1145.     GradientFill(Rect(R.Left, R.Top, i, j), cl, clHighlight, gdVertical);
  1146.     GradientFill(Rect(R.Left, j, i, R.Bottom), clHighlight, cl, gdVertical);
  1147.  
  1148.     ts:=TextStyle;
  1149.     ts.Layout:=tlTop;
  1150.     ts.Alignment:=taLeftJustify;
  1151.     ts.Wordbreak:=False;
  1152.     TextStyle:=ts;
  1153.     j:=(R.Left + R.Right - sz.cx) div 2;
  1154.     if i > R.Left then begin
  1155.       RR:=Rect(R.Left, R.Top, i, R.Bottom);
  1156.       Font.Color:=clHighlightText;
  1157.       TextRect(RR, j, h, s);
  1158.     end;
  1159.     if i < R.Right then begin
  1160.       RR:=Rect(i, R.Top, R.Right, R.Bottom);
  1161.       Brush.Color:=Sender.Color;
  1162.       FillRect(RR);
  1163.       Font.Color:=clWindowText;
  1164.       TextRect(RR, j, h, s);
  1165.     end;
  1166.   end;
  1167. end;
  1168.  
  1169. { TProgressImage }
  1170.  
  1171. procedure TProgressImage.SetImages(const AValue: TImageList);
  1172. begin
  1173.   if FImages=AValue then exit;
  1174.   FImages:=AValue;
  1175.   Width:=FImages.Width;
  1176.   Height:=FImages.Height;
  1177. end;
  1178.  
  1179. procedure TProgressImage.SetStartIndex(const AValue: integer);
  1180. begin
  1181.   if FStartIndex=AValue then exit;
  1182.   FStartIndex:=AValue;
  1183.   UpdateIndex;
  1184. end;
  1185.  
  1186. procedure TProgressImage.UpdateIndex;
  1187. begin
  1188.   if (FImageIndex < FStartIndex) or (FImageIndex > FEndIndex) then
  1189.     FImageIndex:=FStartIndex;
  1190.   Invalidate;
  1191. end;
  1192.  
  1193. procedure TProgressImage.DoTimer(Sender: TObject);
  1194. begin
  1195.   ImageIndex:=ImageIndex + 1;
  1196. end;
  1197.  
  1198. procedure TProgressImage.SetImageIndex(const AValue: integer);
  1199. begin
  1200.   if FImageIndex=AValue then exit;
  1201.   FImageIndex:=AValue;
  1202.   UpdateIndex;
  1203. end;
  1204.  
  1205. procedure TProgressImage.SetEndIndex(const AValue: integer);
  1206. begin
  1207.   if FEndIndex=AValue then exit;
  1208.   FEndIndex:=AValue;
  1209.   UpdateIndex;
  1210. end;
  1211.  
  1212. function TProgressImage.GetFrameDelay: integer;
  1213. begin
  1214.   Result:=FTimer.Interval;
  1215. end;
  1216.  
  1217. procedure TProgressImage.SetBorderColor(const AValue: TColor);
  1218. begin
  1219.   if FBorderColor=AValue then exit;
  1220.   FBorderColor:=AValue;
  1221. end;
  1222.  
  1223. procedure TProgressImage.SetFrameDelay(const AValue: integer);
  1224. begin
  1225.   FTimer.Interval:=AValue;
  1226. end;
  1227.  
  1228. procedure TProgressImage.Paint;
  1229. begin
  1230.   if FBmp = nil then begin
  1231.     FBmp:=TBitmap.Create;
  1232.     FBmp.Width:=Width;
  1233.     FBmp.Height:=Height;
  1234.   end;
  1235.   with FBmp.Canvas do begin
  1236.     Brush.Color:=clBtnFace;
  1237.     if FBorderColor <> clNone then begin
  1238.       Pen.Color:=FBorderColor;
  1239.       Rectangle(0, 0, FBmp.Width, FBmp.Height);
  1240.     end
  1241.     else
  1242.       FillRect(0, 0, FBmp.Width, FBmp.Height);
  1243.     if FImages <> nil then
  1244.       FImages.Draw(FBmp.Canvas, (Self.Width - FImages.Width) div 2, (Self.Height - FImages.Height) div 2, ImageIndex);
  1245.   end;
  1246.   Canvas.Draw(0, 0, FBmp);
  1247. end;
  1248.  
  1249. procedure TProgressImage.VisibleChanged;
  1250. begin
  1251.   inherited VisibleChanged;
  1252.   if Visible then begin
  1253.     ImageIndex:=StartIndex;
  1254.     FTimer.Enabled:=True;
  1255.   end
  1256.   else
  1257.     FTimer.Enabled:=False;
  1258. end;
  1259.  
  1260. constructor TProgressImage.Create(AOwner: TComponent);
  1261. begin
  1262.   inherited Create(AOwner);
  1263.   FTimer:=TTimer.Create(Self);
  1264.   FTimer.Enabled:=False;
  1265.   FTimer.Interval:=100;
  1266.   FTimer.OnTimer:=@DoTimer;
  1267.   FBorderColor:=clNone;
  1268.   Visible:=False;
  1269. end;
  1270.  
  1271. destructor TProgressImage.Destroy;
  1272. begin
  1273.   FBmp.Free;
  1274.   inherited Destroy;
  1275. end;
  1276.  
  1277. { TMainForm }
  1278.  
  1279. procedure TMainForm.FormCreate(Sender: TObject);
  1280. var
  1281.   ws: TWindowState;
  1282.   i, j: integer;
  1283.   R: TRect;
  1284. {$ifdef darwin}
  1285.   s: string;
  1286.   pic: TPicture;
  1287. {$endif darwin}
  1288. begin
  1289. {$ifdef darwin}
  1290.   // Load better icon if possible
  1291.   s:=ExtractFilePath(ParamStrUTF8(0)) + '..' + DirectorySeparator + 'Resources'
  1292.      + DirectorySeparator + ChangeFileExt(ExtractFileName(ParamStrUTF8(0)), '.icns');
  1293.   if FileExistsUTF8(s) then begin
  1294.     pic:=TPicture.Create;
  1295.     try
  1296.       pic.LoadFromFile(s);
  1297.       try
  1298.         Application.Icon.Assign(pic.Graphic);
  1299.       except
  1300.       end;
  1301.     finally
  1302.       pic.Free;
  1303.     end;
  1304.   end;
  1305.  
  1306.   RegisterURLHandler(@AddTorrentFile);
  1307. {$endif darwin}
  1308.   Application.Title:=AppName;
  1309.   Caption:=Application.Title;
  1310.   txTransferHeader.Font.Size:=Font.Size + 2;
  1311.   txTorrentHeader.Font.Size:=txTransferHeader.Font.Size;
  1312.   TrayIcon.Icon.Assign(Application.Icon);
  1313.   RpcObj:=TRpc.Create;
  1314.   FTorrents:=TVarList.Create(gTorrents.Columns.Count, 0);
  1315.   FTorrents.ExtraColumns:=TorrentsExtraColumns;
  1316.   gTorrents.Items.ExtraColumns:=TorrentsExtraColumns;
  1317.   lvFiles.Items.ExtraColumns:=FilesExtraColumns;
  1318.   FFiles:=lvFiles.Items;
  1319.   FFilesTree:=TFilesTree.Create(lvFiles);
  1320.   FFilesTree.Checkboxes:=True;
  1321.   FFilesTree.OnStateChange:=@FilesTreeStateChanged;
  1322.   lvPeers.Items.ExtraColumns:=PeersExtraColumns;
  1323.   lvTrackers.Items.ExtraColumns:=TrackersExtraColumns;
  1324.   FTrackers:=TStringList.Create;
  1325.   FTrackers.Sorted:=True;
  1326.   FReconnectTimeOut:=-1;
  1327.   FAlterColor:=GetLikeColor(gTorrents.Color, -$10);
  1328.   lvFilter.Items.ExtraColumns:=1;
  1329.   gTorrents.AlternateColor:=FAlterColor;
  1330.   lvPeers.AlternateColor:=FAlterColor;
  1331.   lvTrackers.AlternateColor:=FAlterColor;
  1332.   gStats.AlternateColor:=FAlterColor;
  1333.   FPendingTorrents:=TStringList.Create;
  1334.   FFilesCapt:=tabFiles.Caption;
  1335.   FPasswords:=TStringList.Create;
  1336.  
  1337.   MainToolBar.ButtonWidth:=DM.ImageList16.Width + ScaleInt(8);
  1338.   MainToolBar.ButtonHeight:=MainToolBar.ButtonWidth;
  1339.   FSlowResponse:=TProgressImage.Create(MainToolBar);
  1340.   with FSlowResponse do begin
  1341.     Align:=alRight;
  1342.     Images:=DM.ImageList16;
  1343.     StartIndex:=30;
  1344.     EndIndex:=37;
  1345.     Width:=MainToolBar.ButtonWidth;
  1346.     Left:=MainToolBar.ClientWidth;
  1347.     Parent:=MainToolBar;
  1348.   end;
  1349.   FDetailsWait:=TProgressImage.Create(panDetailsWait);
  1350.   with FDetailsWait do begin
  1351.     Images:=DM.ImageList16;
  1352.     StartIndex:=FSlowResponse.StartIndex;
  1353.     EndIndex:=FSlowResponse.EndIndex;
  1354.     Width:=Images.Width*2;
  1355.     Height:=Width;
  1356.     BorderColor:=clBtnShadow;
  1357.     panDetailsWait.Width:=Width;
  1358.     panDetailsWait.Height:=Height;
  1359.     Parent:=panDetailsWait;
  1360.   end;
  1361.  
  1362.   DoDisconnect;
  1363.   PageInfo.ActivePageIndex:=0;
  1364.   PageInfoChange(nil);
  1365. {$ifdef LCLgtk2}
  1366.   with MainToolBar do begin
  1367.     EdgeBorders:=[ebLeft, ebTop, ebRight, ebBottom];
  1368.     EdgeInner:=esNone;
  1369.     EdgeOuter:=esRaised;
  1370.     Flat:=True;
  1371.   end;
  1372.   i:=acAltSpeed.ImageIndex;
  1373.   acAltSpeed.ImageIndex:=-1;
  1374.   tbtAltSpeed.ImageIndex:=i;
  1375. {$endif}
  1376.   txTransferHeader.Color:=GetLikeColor(clBtnFace, -15);
  1377.   txTorrentHeader.Color:=txTransferHeader.Color;
  1378.   txTransferHeader.Caption:=' ' + txTransferHeader.Caption;
  1379.   txTorrentHeader.Caption:=' ' + txTorrentHeader.Caption;
  1380.   txTransferHeader.Height:=txTransferHeader.Canvas.TextHeight(txTransferHeader.Caption) + 2;
  1381.   txTorrentHeader.Height:=txTorrentHeader.Canvas.TextHeight(txTorrentHeader.Caption) + 2;
  1382.  
  1383.   with gStats do begin
  1384.     BeginUpdate;
  1385.     try
  1386.       Items[0, 0]:=UTF8Decode(SDownloaded);
  1387.       Items[0, 1]:=UTF8Decode(SUploaded);
  1388.       Items[0, 2]:=UTF8Decode(SFilesAdded);
  1389.       Items[0, 3]:=UTF8Decode(SActiveTime);
  1390.     finally
  1391.       EndUpdate;
  1392.     end;
  1393.   end;
  1394.  
  1395.   if Ini.ReadInteger('MainForm', 'State', -1) = -1 then begin
  1396.     R:=Screen.MonitorFromRect(BoundsRect).WorkareaRect;
  1397.     if R.Right - R.Left < 300 then
  1398.       R:=Rect(0, 0, Screen.Width, Screen.Height);
  1399.     j:=R.Right - R.Left;
  1400.     i:=j*3 div 4;
  1401.     j:=j*95 div 100;
  1402.     if i > Width then
  1403.       Width:=i;
  1404.     if Width > j then
  1405.       Width:=j;
  1406.     Left:=(R.Right - R.Left - Width) div 2;
  1407.     j:=R.Bottom - R.Top;
  1408.     i:=j*3 div 4;
  1409.     j:=j*8 div 10;
  1410.     if i > Height then
  1411.       Height:=i;
  1412.     if Height > j then
  1413.       Height:=j;
  1414.     Top:=(R.Bottom - R.Top - Height) div 2;
  1415.   end
  1416.   else begin
  1417.     ws:=TWindowState(Ini.ReadInteger('MainForm', 'State', integer(WindowState)));
  1418.     Left:=Ini.ReadInteger('MainForm', 'Left', Left);
  1419.     Top:=Ini.ReadInteger('MainForm', 'Top', Top);
  1420.     Width:=Ini.ReadInteger('MainForm', 'Width', Width);
  1421.     Height:=Ini.ReadInteger('MainForm', 'Height', Height);
  1422.     if ws = wsMaximized then
  1423.       WindowState:=wsMaximized;
  1424.   end;
  1425.  
  1426.   if Ini.ReadBool('MainForm', 'FilterPane', acFilterPane.Checked) <> acFilterPane.Checked then
  1427.     acFilterPane.Execute;
  1428.   if Ini.ReadBool('MainForm', 'InfoPane', acInfoPane.Checked) <> acInfoPane.Checked then
  1429.     acInfoPane.Execute;
  1430.   if Ini.ReadBool('MainForm', 'StatusBar', acStatusBar.Checked) <> acStatusBar.Checked then
  1431.     acStatusBar.Execute;
  1432.  
  1433.   LoadColumns(gTorrents, 'TorrentsList');
  1434.   TorrentColumnsChanged;
  1435.   LoadColumns(lvFiles, 'FilesList');
  1436.   LoadColumns(lvPeers, 'PeerList');
  1437.   LoadColumns(lvTrackers, 'TrackersList');
  1438.  
  1439.   acResolveHost.Checked:=Ini.ReadBool('PeersList', 'ResolveHost', True);
  1440.   acResolveCountry.Checked:=Ini.ReadBool('PeersList', 'ResolveCountry', True) and (GetGeoIpDatabase <> '');
  1441.   acShowCountryFlag.Checked:=Ini.ReadBool('PeersList', 'ShowCountryFlag', True) and (GetFlagsArchive <> '');
  1442.   acShowCountryFlag.Enabled:=acResolveCountry.Checked;
  1443.   FCurConn:=Ini.ReadString('Hosts', 'CurHost', '');
  1444.   if FCurConn = '' then
  1445.     FCurConn:=Ini.ReadString('Connection', 'Host', '');
  1446.   FPathMap:=TStringList.Create;
  1447.   if Application.HasOption('hidden') then begin
  1448.     ApplicationProperties.ShowMainForm:=False;
  1449.     TickTimer.Enabled:=True;
  1450.     UpdateTray;
  1451.   end;
  1452.   UpdateConnections;
  1453.  
  1454.   i:=Ini.ReadInteger('Interface', 'LastRpcVersion', -1);
  1455.   if i >= 0 then
  1456.     UpdateUIRpcVersion(i);
  1457.  
  1458.   acFolderGrouping.Checked:=Ini.ReadBool('Interface', 'FolderGrouping', True);
  1459.   acTrackerGrouping.Checked:=Ini.ReadBool('Interface', 'TrackerGrouping', True);
  1460.   FLinksFromClipboard:=Ini.ReadBool('Interface', 'LinksFromClipboard', True);
  1461. // Added by Antekgla
  1462.   FFileManagerDefault:=Ini.ReadString('Interface','FileManagerDefault','explorer.exe');
  1463.   FFileManagerDefaultParam:=Ini.ReadString('Interface', 'FileManagerDefaultParam', '/select,"%s"');
  1464. // End Added by Antekgla
  1465.   Application.OnActivate:=@FormActivate;
  1466.   Application.OnException:=@ApplicationPropertiesException;
  1467. end;
  1468.  
  1469. procedure TMainForm.FormDestroy(Sender: TObject);
  1470.  
  1471.   procedure _CreateAllForms;
  1472.   begin
  1473.     // Create all application forms to properly update language files
  1474.     TAboutForm.Create(Self).Free;
  1475.     TAddLinkForm.Create(Self).Free;
  1476.     TAddTorrentForm.Create(Self).Free;
  1477.     TAddTrackerForm.Create(Self).Free;
  1478.     TColSetupForm.Create(Self).Free;
  1479.     TConnOptionsForm.Create(Self).Free;
  1480.     TDaemonOptionsForm.Create(Self).Free;
  1481.     TDownloadForm.Create(Self).Free;
  1482.     TMoveTorrentForm.Create(Self).Free;
  1483.     TOptionsForm.Create(Self).Free;
  1484.     TTorrPropsForm.Create(Self).Free;
  1485.   end;
  1486.  
  1487. begin
  1488.   if Application.HasOption('updatelang') then begin
  1489.     _CreateAllForms;
  1490.     SupplementTranslationFiles;
  1491.   end;
  1492.   if Application.HasOption('makelang') then begin
  1493.     _CreateAllForms;
  1494.     MakeTranslationFile;
  1495.   end;
  1496.  
  1497.   DeleteFileUTF8(FRunFileName);
  1498.   FPasswords.Free;
  1499.   FResolver.Free;
  1500.   FTrackers.Free;
  1501.   FUnZip.Free;
  1502.   RpcObj.Free;
  1503.   FTorrentProgress.Free;
  1504.   FPathMap.Free;
  1505.   FTorrents.Free;
  1506.   FPendingTorrents.Free;
  1507.   try
  1508.     Ini.UpdateFile;
  1509.   except
  1510.   end;
  1511. end;
  1512.  
  1513. procedure TMainForm.FormResize(Sender: TObject);
  1514. begin
  1515.   if panReconnect.Visible then
  1516.     CenterReconnectWindow;
  1517. end;
  1518.  
  1519. procedure TMainForm.FormShow(Sender: TObject);
  1520. begin
  1521.   if not FMainFormShown then begin
  1522.     FMainFormShown:=True;
  1523.     VSplitter.SetSplitterPosition(Ini.ReadInteger('MainForm', 'VSplitter', VSplitter.GetSplitterPosition));
  1524.     HSplitter.SetSplitterPosition(Ini.ReadInteger('MainForm', 'HSplitter', HSplitter.GetSplitterPosition));
  1525.     MakeFullyVisible;
  1526.   end;
  1527.   if not FStarted then
  1528.     TickTimer.Enabled:=True;
  1529.   UpdateTray;
  1530. end;
  1531.  
  1532. procedure TMainForm.lvFilterResize(Sender: TObject);
  1533. begin
  1534.   lvFilter.Columns[0].Width:=lvFilter.ClientWidth;
  1535. end;
  1536.  
  1537. procedure TMainForm.miAboutClick(Sender: TObject);
  1538. begin
  1539.   with TAboutForm.Create(Self) do
  1540.   try
  1541.     ShowModal;
  1542.   finally
  1543.     Free;
  1544.   end;
  1545. end;
  1546.  
  1547. procedure TMainForm.miCopyLabelClick(Sender: TObject);
  1548. begin
  1549.   with TLabel(pmLabels.PopupComponent) do
  1550.     if (Length(Name) > 5) and (Copy(Name, Length(Name) - 4, 5) = 'Label') then
  1551.       Clipboard.AsText:=TLabel(Parent.FindChildControl(Copy(Name, 1, Length(Name) - 5))).Caption
  1552.     else
  1553.       Clipboard.AsText:=Caption;
  1554. end;
  1555.  
  1556. procedure TMainForm.acConnectExecute(Sender: TObject);
  1557. begin
  1558.   if RpcObj.Connected then begin
  1559.     tbConnect.CheckMenuDropdown;
  1560.     exit;
  1561.   end;
  1562.   if FCurConn = '' then
  1563.     ShowConnOptions(True)
  1564.   else
  1565.     DoConnect;
  1566. end;
  1567.  
  1568. procedure TMainForm.acConnOptionsExecute(Sender: TObject);
  1569. begin
  1570.   ShowConnOptions(False);
  1571. end;
  1572.  
  1573. procedure TMainForm.acCopyPathExecute(Sender: TObject);
  1574. begin
  1575.   if lvFiles.Items.Count > 0 then
  1576.     Clipboard.AsText:='"' + FFilesTree.GetFullPath(lvFiles.Row) + '"';
  1577. end;
  1578.  
  1579. procedure TMainForm.acDelTrackerExecute(Sender: TObject);
  1580. var
  1581.   req, args: TJSONObject;
  1582.   id, torid: integer;
  1583. begin
  1584.   id:=lvTrackers.Items[idxTrackerID, lvTrackers.Row];
  1585.   torid:=RpcObj.CurTorrentId;
  1586.   if MessageDlg('', Format(SRemoveTracker, [UTF8Encode(widestring(lvTrackers.Items[idxTrackersListName, lvTrackers.Row]))]), mtConfirmation, mbYesNo, 0, mbNo) <> mrYes then exit;
  1587.   AppBusy;
  1588.   Self.Update;
  1589.   req:=TJSONObject.Create;
  1590.   try
  1591.     req.Add('method', 'torrent-set');
  1592.     args:=TJSONObject.Create;
  1593.     args.Add('ids', TJSONArray.Create([torid]));
  1594.     args.Add('trackerRemove', TJSONArray.Create([id]));
  1595.     req.Add('arguments', args);
  1596.     args:=nil;
  1597.     args:=RpcObj.SendRequest(req, False);
  1598.     if args = nil then begin
  1599.       CheckStatus(False);
  1600.       exit;
  1601.     end;
  1602.     args.Free;
  1603.   finally
  1604.     req.Free;
  1605.   end;
  1606.   DoRefresh;
  1607.   AppNormal;
  1608. end;
  1609.  
  1610. procedure TMainForm.acEditTrackerExecute(Sender: TObject);
  1611. begin
  1612.   AddTracker(True);
  1613. end;
  1614.  
  1615. procedure TMainForm.acFilterPaneExecute(Sender: TObject);
  1616. begin
  1617.   acFilterPane.Checked:=not acFilterPane.Checked;
  1618.   panFilter.Visible:=acFilterPane.Checked;
  1619.   HSplitter.Visible:=acFilterPane.Checked;
  1620.   HSplitter.Left:=panFilter.Width;
  1621.   if lvFilter.Items.Count > 0 then
  1622.     lvFilter.Row:=0;
  1623. end;
  1624.  
  1625. procedure TMainForm.acFolderGroupingExecute(Sender: TObject);
  1626. begin
  1627.   acFolderGrouping.Checked:=not acFolderGrouping.Checked;
  1628.   Ini.WriteBool('Interface', 'FolderGrouping', acFolderGrouping.Checked);
  1629.   RpcObj.RefreshNow:=RpcObj.RefreshNow + [rtTorrents];
  1630. end;
  1631.  
  1632. procedure TMainForm.acForceStartTorrentExecute(Sender: TObject);
  1633. begin
  1634.   TorrentAction(GetSelectedTorrents, 'torrent-start-now');
  1635. end;
  1636.  
  1637. procedure TMainForm.acHideAppExecute(Sender: TObject);
  1638. begin
  1639.   HideApp;
  1640. end;
  1641.  
  1642. procedure TMainForm.acInfoPaneExecute(Sender: TObject);
  1643. begin
  1644.   acInfoPane.Checked:=not acInfoPane.Checked;
  1645.   PageInfo.Visible:=acInfoPane.Checked;
  1646.   VSplitter.Top:=PageInfo.Top - VSplitter.Height;
  1647.   VSplitter.Visible:=acInfoPane.Checked;
  1648.   if VSplitter.Visible then
  1649.     PageInfoChange(nil)
  1650.   else
  1651.     RpcObj.AdvInfo:=aiNone;
  1652. end;
  1653.  
  1654. procedure TMainForm.acMoveTorrentExecute(Sender: TObject);
  1655. var
  1656.   ids: variant;
  1657.   i: integer;
  1658.   s: string;
  1659.   req: TJSONObject;
  1660.   aids: TJSONArray;
  1661.   args: TJSONObject;
  1662.   ok: boolean;
  1663.   t: TDateTime;
  1664. begin
  1665.   if gTorrents.Items.Count = 0 then
  1666.     exit;
  1667.   AppBusy;
  1668.   with TMoveTorrentForm.Create(Self) do
  1669.   try
  1670.     gTorrents.Tag:=1;
  1671.     gTorrents.EnsureSelectionVisible;
  1672.     FillDownloadDirs(edTorrentDir, 'LastMoveDir');
  1673.     if gTorrents.SelCount = 0 then
  1674.       gTorrents.RowSelected[gTorrents.Row]:=True;
  1675.     ids:=GetSelectedTorrents;
  1676.     i:=gTorrents.Items.IndexOf(idxTorrentId, ids[0]);
  1677.     if VarIsEmpty(gTorrents.Items[idxPath, i]) then
  1678.       exit;
  1679.     edTorrentDir.Text:=UTF8Encode(widestring(gTorrents.Items[idxPath, i]));
  1680.     if gTorrents.SelCount > 1 then
  1681.       s:=Format(sSeveralTorrents, [gTorrents.SelCount])
  1682.     else
  1683.       s:=UTF8Encode(widestring(gTorrents.Items[idxName, i]));
  1684.     Caption:=Caption + ' - ' + s;
  1685.     AppNormal;
  1686.     if ShowModal = mrOk then begin
  1687.       Application.ProcessMessages;
  1688.       AppBusy;
  1689.       req:=TJSONObject.Create;
  1690.       try
  1691.         req.Add('method', 'torrent-set-location');
  1692.         args:=TJSONObject.Create;
  1693.         aids:=TJSONArray.Create;
  1694.         for i:=VarArrayLowBound(ids, 1) to VarArrayHighBound(ids, 1) do
  1695.           aids.Add(integer(ids[i]));
  1696.         args.Add('ids', aids);
  1697.         args.Add('location', TJSONString.Create(UTF8Decode(edTorrentDir.Text)));
  1698.         args.Add('move', TJSONIntegerNumber.Create(integer(cbMoveData.Checked) and 1));
  1699.         req.Add('arguments', args);
  1700.         args:=RpcObj.SendRequest(req, False);
  1701.         args.Free;
  1702.       finally
  1703.         req.Free;
  1704.       end;
  1705.       gTorrents.Tag:=0;
  1706.       AppNormal;
  1707.       if args = nil then
  1708.         CheckStatus(False)
  1709.       else begin
  1710.         SaveDownloadDirs(edTorrentDir, 'LastMoveDir');
  1711.         ok:=False;
  1712.         t:=Now;
  1713.         with gTorrents do
  1714.           while not ok and not Application.Terminated and (Now - t < 20/SecsPerDay) do begin
  1715.             RpcObj.RequestFullInfo:=True;
  1716.             DoRefresh(True);
  1717.             Sleep(200);
  1718.             Application.ProcessMessages;
  1719.             ok:=True;
  1720.             for i:=0 to Items.Count - 1 do
  1721.               if RowSelected[i] then begin
  1722.                 if VarIsEmpty(Items[idxPath, i]) or (AnsiCompareText(UTF8Encode(widestring(Items[idxPath, i])), edTorrentDir.Text) <> 0) then begin
  1723.                   ok:=False;
  1724.                   break;
  1725.                 end;
  1726.               end;
  1727.           end;
  1728.       end;
  1729.     end;
  1730.   finally
  1731.     gTorrents.Tag:=0;
  1732.     Free;
  1733.   end;
  1734. end;
  1735.  
  1736. procedure TMainForm.acNewConnectionExecute(Sender: TObject);
  1737. begin
  1738.   ShowConnOptions(True);
  1739. end;
  1740.  
  1741. procedure TMainForm.acOpenContainingFolderExecute(Sender: TObject);
  1742. begin
  1743.   if gTorrents.Items.Count = 0 then
  1744.     exit;
  1745.   Application.ProcessMessages;
  1746.   if lvFiles.Focused and (lvFiles.Items.Count > 0) then begin
  1747.     AppBusy;
  1748.     ExecRemoteFile(FFilesTree.GetFullPath(lvFiles.Row), not FFilesTree.IsFolder(lvFiles.Row));
  1749.     AppNormal;
  1750.   end
  1751.   else
  1752.     OpenCurrentTorrent(True);
  1753. end;
  1754.  
  1755. procedure TMainForm.acOpenFileExecute(Sender: TObject);
  1756. begin
  1757.   if gTorrents.Items.Count = 0 then
  1758.     exit;
  1759.   Application.ProcessMessages;
  1760.   if lvFiles.Focused then begin
  1761.     if lvFiles.Items.Count = 0 then exit;
  1762.     ExecRemoteFile(FFilesTree.GetFullPath(lvFiles.Row), False);
  1763.   end
  1764.   else
  1765.     OpenCurrentTorrent(False);
  1766. end;
  1767.  
  1768. procedure TMainForm.acOptionsExecute(Sender: TObject);
  1769. var
  1770.   OldCheckVer: boolean;
  1771. begin
  1772.   AppBusy;
  1773.   with TOptionsForm.Create(Self) do
  1774.   try
  1775.     ConnForm.ActiveConnection:=FCurConn;
  1776.     edRefreshInterval.Value:=Ini.ReadInteger('Interface', 'RefreshInterval', 5);
  1777.     edRefreshIntervalMin.Value:=Ini.ReadInteger('Interface', 'RefreshIntervalMin', 20);
  1778.     cbCalcAvg.Checked:=FCalcAvg;
  1779. {$ifndef darwin}
  1780.     cbTrayMinimize.Checked:=Ini.ReadBool('Interface', 'TrayMinimize', True);
  1781. {$else}
  1782.     cbTrayMinimize.Enabled:=False;
  1783. {$endif}
  1784.     cbTrayClose.Checked:=Ini.ReadBool('Interface', 'TrayClose', False);
  1785.     cbTrayIconAlways.Checked:=Ini.ReadBool('Interface', 'TrayIconAlways', True);
  1786.     cbTrayNotify.Checked:=Ini.ReadBool('Interface', 'TrayNotify', True);
  1787.  
  1788.     cbShowAddTorrentWindow.Checked:=Ini.ReadBool('Interface', 'ShowAddTorrentWindow', True);
  1789.     cbDeleteTorrentFile.Checked:=Ini.ReadBool('Interface', 'DeleteTorrentFile', False);
  1790.     cbLinksFromClipboard.Checked:=Ini.ReadBool('Interface', 'LinksFromClipboard', True);
  1791.     edIntfScale.Value:=Ini.ReadInteger('Interface', 'Scaling', 100);
  1792.     cbCheckNewVersion.Checked:=Ini.ReadBool('Interface', 'CheckNewVersion', False);
  1793.     edCheckVersionDays.Value:=Ini.ReadInteger('Interface', 'CheckNewVersionDays', 5);
  1794.     cbCheckNewVersionClick(nil);
  1795.     OldCheckVer:=cbCheckNewVersion.Checked;
  1796. {$ifdef linux}
  1797.     if IsUnity then begin
  1798.       cbTrayIconAlways.Enabled:=False;
  1799.       cbTrayIconAlways.Checked:=False;
  1800.       cbTrayMinimize.Enabled:=False;
  1801.       cbTrayMinimize.Checked:=False;
  1802.       cbTrayNotify.Enabled:=False;
  1803.       cbTrayNotify.Checked:=False;
  1804.     end;
  1805. {$endif linux}
  1806.     AppNormal;
  1807.     if ShowModal = mrOk then begin
  1808.       AppBusy;
  1809.       Ini.WriteInteger('Interface', 'RefreshInterval', edRefreshInterval.Value);
  1810.       Ini.WriteInteger('Interface', 'RefreshIntervalMin', edRefreshIntervalMin.Value);
  1811.       Ini.WriteBool('Interface', 'CalcAvg', cbCalcAvg.Checked);
  1812. {$ifndef darwin}
  1813.       Ini.WriteBool('Interface', 'TrayMinimize', cbTrayMinimize.Checked);
  1814. {$endif}
  1815.       Ini.WriteBool('Interface', 'TrayClose', cbTrayClose.Checked);
  1816.       Ini.WriteBool('Interface', 'TrayIconAlways', cbTrayIconAlways.Checked);
  1817.       Ini.WriteBool('Interface', 'TrayNotify', cbTrayNotify.Checked);
  1818.  
  1819.       Ini.WriteBool('Interface', 'ShowAddTorrentWindow', cbShowAddTorrentWindow.Checked);
  1820.       Ini.WriteBool('Interface', 'DeleteTorrentFile', cbDeleteTorrentFile.Checked);
  1821.       Ini.WriteBool('Interface', 'LinksFromClipboard', cbLinksFromClipboard.Checked);
  1822.       FLinksFromClipboard:=cbLinksFromClipboard.Checked;
  1823.  
  1824.       Ini.WriteInteger('Interface', 'Scaling', edIntfScale.Value);
  1825.  
  1826.       Ini.WriteBool('Interface', 'CheckNewVersion', cbCheckNewVersion.Checked);
  1827.       Ini.WriteInteger('Interface', 'CheckNewVersionDays', edCheckVersionDays.Value);
  1828.  
  1829.       if cbCheckNewVersion.Checked and not OldCheckVer then
  1830.         CheckNewVersion;
  1831.       Ini.UpdateFile;
  1832.       UpdateTray;
  1833.       AppNormal;
  1834.       with ConnForm do
  1835.         ConnectionSettingsChanged(ActiveConnection, ActiveSettingChanged);
  1836.     end;
  1837.   finally
  1838.     Free;
  1839.   end;
  1840. end;
  1841.  
  1842. procedure TMainForm.acAddTorrentExecute(Sender: TObject);
  1843. begin
  1844.   if not OpenTorrentDlg.Execute then exit;
  1845.   FPendingTorrents.AddStrings(OpenTorrentDlg.Files);
  1846.   TickTimerTimer(nil);
  1847. end;
  1848.  
  1849. procedure TMainForm.acAddTrackerExecute(Sender: TObject);
  1850. begin
  1851.   AddTracker(False);
  1852. end;
  1853.  
  1854. procedure TMainForm.acAdvEditTrackersExecute(Sender: TObject);
  1855. begin
  1856.   gTorrents.RemoveSelection;
  1857.   TorrentProps(1);
  1858. end;
  1859.  
  1860. procedure TMainForm.acAltSpeedExecute(Sender: TObject);
  1861. var
  1862.   req, args: TJSONObject;
  1863. begin
  1864.   AppBusy;
  1865.   req:=TJSONObject.Create;
  1866.   try
  1867.     req.Add('method', 'session-set');
  1868.     args:=TJSONObject.Create;
  1869.     args.Add('alt-speed-enabled', integer(not acAltSpeed.Checked) and 1);
  1870.     req.Add('arguments', args);
  1871.     args:=RpcObj.SendRequest(req, False);
  1872.     if args = nil then begin
  1873.       CheckStatus(False);
  1874.       exit;
  1875.     end;
  1876.     args.Free;
  1877.   finally
  1878.     req.Free;
  1879.   end;
  1880.   RpcObj.RefreshNow:=RpcObj.RefreshNow + [rtSession];
  1881.   AppNormal;
  1882. end;
  1883.  
  1884. procedure TMainForm.acCheckNewVersionExecute(Sender: TObject);
  1885. begin
  1886.   Application.ProcessMessages;
  1887.   AppBusy;
  1888.   CheckNewVersion(False);
  1889.   AppNormal;
  1890. end;
  1891.  
  1892. procedure TMainForm.acAddLinkExecute(Sender: TObject);
  1893. begin
  1894.   AppBusy;
  1895.   with TAddLinkForm.Create(Self) do
  1896.   try
  1897.     AppNormal;
  1898.     if ShowModal = mrOk then
  1899.       DoAddTorrent(edLink.Text);
  1900.   finally
  1901.     Free;
  1902.   end;
  1903. end;
  1904.  
  1905. function TMainForm.DoAddTorrent(const FileName: Utf8String): boolean;
  1906. var
  1907.   torrent: string;
  1908.   WaitForm: TBaseForm;
  1909.   IsAppHidden: boolean;
  1910.  
  1911.   Procedure _AddTrackers(TorrentId: integer);
  1912.   var
  1913.     req, args: TJSONObject;
  1914.     fs: TFileStreamUTF8;
  1915.     TorData, AnnData, LData, LLData: TBEncoded;
  1916.     t: TJSONArray;
  1917.     tt: TJSONObject;
  1918.     trackers: TJSONArray;
  1919.     i, j: Integer;
  1920.     s, tfn, TorrentHash: string;
  1921.     TrackersList: TStringList;
  1922.   begin
  1923.     RpcObj.Status:='';
  1924.     s:='';
  1925.     if TorrentId <> 0 then begin
  1926.       i:=SelectTorrent(TorrentId, 2000);
  1927.       if i >= 0 then
  1928.         s:=Format(': %s', [UTF8Encode(widestring(gTorrents.Items[idxName, i]))]);
  1929.     end;
  1930.     ForceAppNormal;
  1931.     s:=SDuplicateTorrent + s + '.';
  1932.     if RpcObj.RPCVersion < 10 then begin
  1933.       MessageDlg(s, mtError, [mbOK], 0);
  1934.       exit;
  1935.     end;
  1936.     if MessageDlg(s + LineEnding + LineEnding + SUpdateTrackers, mtConfirmation, [mbYes, mbNo], 0) <> mrYes then
  1937.       exit;
  1938.     Application.ProcessMessages;
  1939.     TrackersList:=TStringList.Create;
  1940.     try
  1941.       TorrentHash:='';
  1942.       if AnsiCompareText('magnet:?', Copy(FileName, 1, 8)) = 0 then begin
  1943.         // Get trackers from the magnet link
  1944.         TrackersList.Delimiter:='&';
  1945.         TrackersList.DelimitedText:=Copy(FileName, 9, MaxInt);
  1946.         i:=0;
  1947.         while i < TrackersList.Count do begin
  1948.           s:=TrackersList.Names[i];
  1949.           if (TorrentId = 0) and (CompareText(s, 'xt') = 0) then begin
  1950.             s:=LowerCase(TrackersList.ValueFromIndex[i]);
  1951.             if (TorrentHash = '') and (Pos('urn:btih:', s) = 1) then begin
  1952.               s:=Copy(s, 10, MaxInt);
  1953.               if Length(s) = 32 then
  1954.                 // base32 encoded hash
  1955.                 TorrentHash:=StrToHex(Base32Decode(UpperCase(s)))
  1956.               else
  1957.                 TorrentHash:=s;
  1958.             end;
  1959.           end
  1960.           else
  1961.             if CompareText(s, 'tr') = 0 then begin
  1962.               TrackersList[i]:=DecodeURL(TrackersList.ValueFromIndex[i]);
  1963.               Inc(i);
  1964.               continue;
  1965.             end;
  1966.           TrackersList.Delete(i);
  1967.         end;
  1968.       end
  1969.       else
  1970.       try
  1971.         if IsProtocolSupported(FileName) then begin
  1972.           // Downloading torrent file
  1973.           tfn:=SysToUTF8(GetTempDir(True)) + 'remote-torrent.torrent';
  1974.           if not DownloadFile(FileName, ExtractFilePath(tfn), ExtractFileName(tfn), SDownloadingTorrent) then
  1975.             exit;
  1976.         end
  1977.         else
  1978.           tfn:=FileName;
  1979.         // Read trackers from the torrent file...
  1980.         AppBusy;
  1981.         TorData:=nil;
  1982.         fs:=TFileStreamUTF8.Create(tfn, fmOpenRead or fmShareDenyNone);
  1983.         try
  1984.           TorData:=TBEncoded.Create(fs);
  1985.           if TorrentId = 0 then begin
  1986.             // Calculate torrent hash
  1987.             LData:=(TorData.ListData.FindElement('info') as TBEncoded);
  1988.             s:='';
  1989.             LData.Encode(LData, s);
  1990.             TorrentHash:=StrToHex(SHA1(s));
  1991.           end;
  1992.           AnnData:=(TorData.ListData.FindElement('announce-list', False) as TBEncoded);
  1993.           if AnnData <> nil then
  1994.             for i:=0 to AnnData.ListData.Count - 1 do begin
  1995.               LData:=AnnData.ListData.Items[i].Data as TBEncoded;
  1996.               for j:=0 to LData.ListData.Count - 1 do begin
  1997.                 LLData:=LData.ListData.Items[j].Data as TBEncoded;
  1998.                 TrackersList.Add(LLData.StringData);
  1999.               end;
  2000.             end
  2001.           else begin
  2002.             AnnData:=(TorData.ListData.FindElement('announce', False) as TBEncoded);
  2003.             if AnnData <> nil then
  2004.               TrackersList.Add(AnnData.StringData);
  2005.           end;
  2006.         finally
  2007.           TorData.Free;
  2008.           fs.Free;
  2009.         end;
  2010.       finally
  2011.         // Delete temp file
  2012.         if (tfn <> '') and (tfn <> FileName) then
  2013.           DeleteFileUTF8(tfn);
  2014.       end;
  2015.  
  2016.       // Request trackers from the existing torrent
  2017.       req:=TJSONObject.Create;
  2018.       try
  2019.         req.Add('method', 'torrent-get');
  2020.         args:=TJSONObject.Create;
  2021.         if TorrentId = 0 then
  2022.           args.Add('ids', TJSONArray.Create([TorrentHash]))
  2023.         else
  2024.           args.Add('ids', TJSONArray.Create([TorrentId]));
  2025.         args.Add('fields', TJSONArray.Create(['id', 'trackers']));
  2026.         req.Add('arguments', args);
  2027.         args:=RpcObj.SendRequest(req);
  2028.         if args = nil then begin
  2029.           CheckStatus(False);
  2030.           exit;
  2031.         end;
  2032.         try
  2033.           t:=args.Arrays['torrents'];
  2034.           if t.Count = 0 then
  2035.             raise Exception.Create('Torrent not found.');
  2036.           tt:=t.Objects[0] as TJSONObject;
  2037.           i:=tt.Integers['id'];
  2038.           if TorrentId = 0 then
  2039.             SelectTorrent(i, 2000);
  2040.           TorrentId:=i;
  2041.           trackers:=tt.Arrays['trackers'];
  2042.           // Deleting existing trackers from the list
  2043.           for i:=0 to trackers.Count - 1 do begin
  2044.             s:=UTF8Encode((Trackers.Items[i] as TJSONObject).Strings['announce']);
  2045.             j:=TrackersList.IndexOf(s);
  2046.             if j >= 0 then
  2047.               TrackersList.Delete(j);
  2048.           end;
  2049.         finally
  2050.           args.Free;
  2051.         end;
  2052.       finally
  2053.         req.Free;
  2054.       end;
  2055.  
  2056.       if TrackersList.Count > 0 then begin
  2057.         trackers:=TJSONArray.Create;
  2058.         for i:=0 to TrackersList.Count - 1 do
  2059.           trackers.Add(TrackersList[i]);
  2060.         args:=TJSONObject.Create;
  2061.         args.Add('ids', TJSONArray.Create([TorrentId]));
  2062.         args.Add('trackerAdd', trackers);
  2063.         req:=TJSONObject.Create;
  2064.         try
  2065.           req.Add('method', 'torrent-set');
  2066.           req.Add('arguments', args);
  2067.           args:=RpcObj.SendRequest(req, False);
  2068.           if args = nil then begin
  2069.             CheckStatus(False);
  2070.             exit;
  2071.           end;
  2072.           args.Free;
  2073.         finally
  2074.           req.Free;
  2075.         end;
  2076.         DoRefresh;
  2077.       end;
  2078.     finally
  2079.       TrackersList.Free;
  2080.       AppNormal;
  2081.     end;
  2082.   end;
  2083.  
  2084.   function _AddTorrent(args: TJSONObject): integer;
  2085.   var
  2086.     req: TJSONObject;
  2087.   begin
  2088.     Result:=0;
  2089.     req:=TJSONObject.Create;
  2090.     try
  2091.       req.Add('method', 'torrent-add');
  2092.       if torrent = '-' then
  2093.         args.Add('filename', TJSONString.Create(FileName))
  2094.       else
  2095.         args.Add('metainfo', TJSONString.Create(torrent));
  2096.       req.Add('arguments', args);
  2097.       args:=RpcObj.SendRequest(req);
  2098.       if args <> nil then
  2099.       try
  2100.         if args.IndexOfName('torrent-duplicate') >= 0 then begin
  2101.           _AddTrackers(args.Objects['torrent-duplicate'].Integers['id']);
  2102.           exit;
  2103.         end;
  2104.         Result:=args.Objects['torrent-added'].Integers['id'];
  2105.       finally
  2106.         args.Free;
  2107.       end
  2108.       else
  2109.         if RpcObj.Status='duplicate torrent' then begin
  2110.           _AddTrackers(0);
  2111.           exit;
  2112.         end;
  2113.     finally
  2114.       req.Free;
  2115.     end;
  2116.     if Result = 0 then
  2117.       CheckStatus(False);
  2118.   end;
  2119.  
  2120.   procedure ShowWaitMsg(const AText: string);
  2121.   begin
  2122.     if WaitForm = nil then begin
  2123.       WaitForm:=TBaseForm.CreateNew(Self);
  2124.       with WaitForm do begin
  2125. {$ifndef windows}
  2126.         if IsAppHidden then
  2127.           ShowInTaskBar:=stAlways;
  2128. {$endif windows}
  2129.         Caption:=AppName;
  2130.         BorderStyle:=bsToolWindow;
  2131.         BorderIcons:=[];
  2132.         Position:=poScreenCenter;
  2133.         Constraints.MinWidth:=300;
  2134.         AutoSize:=True;
  2135.         BorderWidth:=ScaleInt(16);
  2136.         with TLabel.Create(WaitForm) do begin
  2137.           Alignment:=taCenter;
  2138.           Align:=alClient;
  2139.           Parent:=WaitForm;
  2140.         end;
  2141.       end;
  2142.     end;
  2143.     with WaitForm do begin
  2144.       TLabel(Controls[0]).Caption:=AText + '...';
  2145.       Show;
  2146.       BringToFront;
  2147. {$ifdef lclgtk2}
  2148.       Application.ProcessMessages;
  2149. {$endif lclgtk2}
  2150.       Update;
  2151. {$ifdef lclgtk2}
  2152.       sleep(100);
  2153.       Application.ProcessMessages;
  2154. {$endif lclgtk2}
  2155.     end;
  2156.   end;
  2157.  
  2158.   procedure HideWaitMsg;
  2159.   begin
  2160.     if WaitForm <> nil then
  2161.       FreeAndNil(WaitForm);
  2162.   end;
  2163.  
  2164. var
  2165.   req, args: TJSONObject;
  2166.   id: integer;
  2167.   t, files: TJSONArray;
  2168.   i: integer;
  2169.   fs: TFileStreamUTF8;
  2170.   s, OldDownloadDir, IniSec, OldName: string;
  2171.   ok: boolean;
  2172. begin
  2173.   Result:=False;
  2174.   if not RpcObj.Connected and not RpcObj.Connecting then
  2175.     if not DoConnect then
  2176.       exit;
  2177.   WaitForm:=nil;
  2178.   id:=0;
  2179.   Inc(FAddingTorrent);
  2180.   try
  2181.     AppBusy;
  2182.     try
  2183.       IsAppHidden:=not Self.Visible or (Self.WindowState = wsMinimized);
  2184.       with TAddTorrentForm.Create(Self) do
  2185.       try
  2186.         if IsAppHidden then begin
  2187.           ShowWaitMsg(Caption);
  2188. {$ifndef windows}
  2189.           ShowInTaskBar:=stAlways;
  2190. {$endif windows}
  2191.         end;
  2192.  
  2193.         Application.ProcessMessages;
  2194.         if IsProtocolSupported(FileName) then
  2195.           torrent:='-'
  2196.         else begin
  2197.           fs:=TFileStreamUTF8.Create(FileName, fmOpenRead or fmShareDenyNone);
  2198.           try
  2199.             SetLength(torrent, fs.Size);
  2200.             fs.ReadBuffer(PChar(torrent)^, Length(torrent));
  2201.           finally
  2202.             fs.Free;
  2203.           end;
  2204.           torrent:=EncodeBase64(torrent);
  2205.         end;
  2206.  
  2207.         IniSec:='AddTorrent.' + FCurConn;
  2208.         FillDownloadDirs(cbDestFolder, 'LastDownloadDir');
  2209.  
  2210.         req:=TJSONObject.Create;
  2211.         try
  2212.           req.Add('method', 'session-get');
  2213.           args:=RpcObj.SendRequest(req);
  2214.           if args = nil then begin
  2215.             CheckStatus(False);
  2216.             exit;
  2217.           end;
  2218.           s:=UTF8Encode(args.Strings['download-dir']);
  2219.           if cbDestFolder.Items.IndexOf(s) < 0 then begin
  2220.             cbDestFolder.Items.Insert(0, s);
  2221.             cbDestFolder.ItemIndex:=0;
  2222.           end;
  2223.  
  2224.           if RpcObj.RPCVersion < 15 then
  2225.             if args.IndexOfName('download-dir-free-space') >= 0 then
  2226.               txDiskSpace.Caption:=txDiskSpace.Caption + ' ' + GetHumanSize(args.Floats['download-dir-free-space'])
  2227.             else begin
  2228.               txDiskSpace.Hide;
  2229.               txSize.Top:=(txSize.Top + txDiskSpace.Top) div 2;
  2230.             end;
  2231.           args.Free;
  2232.         finally
  2233.           req.Free;
  2234.         end;
  2235.  
  2236.         lvFilter.Row:=0;
  2237.         edSearch.Text:='';
  2238.  
  2239.         args:=TJSONObject.Create;
  2240.         args.Add('paused', TJSONIntegerNumber.Create(1));
  2241.         i:=Ini.ReadInteger(IniSec, 'PeerLimit', 0);
  2242.         if i <> 0 then
  2243.           args.Add('peer-limit', TJSONIntegerNumber.Create(i));
  2244.         args.Add('download-dir', TJSONString.Create(UTF8Decode(cbDestFolder.Text)));
  2245.         id:=_AddTorrent(args);
  2246.         if id = 0 then
  2247.           exit;
  2248.  
  2249.         DoRefresh(True);
  2250.  
  2251.         args:=RpcObj.RequestInfo(id, ['files','maxConnectedPeers','name','metadataPercentComplete']);
  2252.         if args = nil then begin
  2253.           CheckStatus(False);
  2254.           exit;
  2255.         end;
  2256.         try
  2257.           t:=args.Arrays['torrents'];
  2258.           if t.Count = 0 then
  2259.             raise Exception.Create(sUnableGetFilesList);
  2260.           OldName:=UTF8Encode(t.Objects[0].Strings['name']);
  2261.           edSaveAs.Caption:=OldName;
  2262.           if RpcObj.RPCVersion < 15 then begin
  2263.             edSaveAs.Enabled:=False;
  2264.             edSaveAs.ParentColor:=True;
  2265.           end;
  2266.           edPeerLimit.Value:=t.Objects[0].Integers['maxConnectedPeers'];
  2267.           FilesTree.FillTree(id, t.Objects[0].Arrays['files'], nil, nil);
  2268.           Width:=Ini.ReadInteger('AddTorrent', 'Width', Width);
  2269.           if (RpcObj.RPCVersion >= 7) and (lvFiles.Items.Count = 0) and (t.Objects[0].Floats['metadataPercentComplete'] <> 1.0) then begin
  2270.             // Magnet link
  2271.             gbContents.Hide;
  2272.             gbSaveAs.BorderSpacing.Bottom:=gbSaveAs.BorderSpacing.Top;
  2273.             BorderStyle:=bsDialog;
  2274.             AutoSizeForm(TCustomForm(gbContents.Parent));
  2275.             edSaveAs.Enabled:=False;
  2276.             edSaveAs.ParentColor:=True;
  2277.           end
  2278.           else
  2279.             // Torrent file
  2280.             Height:=Ini.ReadInteger('AddTorrent', 'Height', Height);
  2281.         finally
  2282.           args.Free;
  2283.         end;
  2284.         OldDownloadDir:=cbDestFolder.Text;
  2285.         AppNormal;
  2286.  
  2287.         ok:=not Ini.ReadBool('Interface', 'ShowAddTorrentWindow', True);
  2288.         if ok then
  2289.           btSelectAllClick(nil)
  2290.         else begin
  2291.           HideWaitMsg;
  2292.           ok:=ShowModal = mrOk;
  2293.           if BorderStyle = bsSizeable then begin
  2294.             Ini.WriteInteger('AddTorrent', 'Width', Width);
  2295.             Ini.WriteInteger('AddTorrent', 'Height', Height);
  2296.           end;
  2297.         end;
  2298.  
  2299.         if ok then begin
  2300.           if IsAppHidden then
  2301.             ShowWaitMsg(Caption);
  2302.           AppBusy;
  2303.           Self.Update;
  2304.  
  2305.           if OldDownloadDir <> cbDestFolder.Text then begin
  2306.             TorrentAction(id, 'torrent-remove');
  2307.             id:=0;
  2308.             args:=TJSONObject.Create;
  2309.             args.Add('paused', TJSONIntegerNumber.Create(1));
  2310.             args.Add('peer-limit', TJSONIntegerNumber.Create(edPeerLimit.Value));
  2311.             args.Add('download-dir', TJSONString.Create(UTF8Decode(cbDestFolder.Text)));
  2312.             id:=_AddTorrent(args);
  2313.             if id = 0 then
  2314.               exit;
  2315.             DoRefresh(True);
  2316.             Application.ProcessMessages;
  2317.           end;
  2318.  
  2319.           req:=TJSONObject.Create;
  2320.           try
  2321.             req.Add('method', 'torrent-set');
  2322.             args:=TJSONObject.Create;
  2323.             args.Add('ids', TJSONArray.Create([id]));
  2324.             args.Add('peer-limit', TJSONIntegerNumber.Create(edPeerLimit.Value));
  2325.  
  2326.             files:=TJSONArray.Create;
  2327.             for i:=0 to lvFiles.Items.Count - 1 do
  2328.               if not FilesTree.IsFolder(i) and (FilesTree.Checked[i] = cbChecked) then
  2329.                 files.Add(integer(lvFiles.Items[idxFileId, i]));
  2330.             if files.Count > 0 then
  2331.               args.Add('files-wanted', files)
  2332.             else
  2333.               files.Free;
  2334.  
  2335.             files:=TJSONArray.Create;
  2336.             for i:=0 to lvFiles.Items.Count - 1 do
  2337.               if not FilesTree.IsFolder(i) and (FilesTree.Checked[i] <> cbChecked) then
  2338.                 files.Add(integer(lvFiles.Items[idxFileId, i]));
  2339.             if files.Count > 0 then
  2340.               args.Add('files-unwanted', files)
  2341.             else
  2342.               files.Free;
  2343.  
  2344.             req.Add('arguments', args);
  2345.             args:=nil;
  2346.             args:=RpcObj.SendRequest(req, False);
  2347.             if args = nil then begin
  2348.               CheckStatus(False);
  2349.               exit;
  2350.             end;
  2351.             args.Free;
  2352.  
  2353.             edSaveAs.Text:=Trim(edSaveAs.Text);
  2354.             if OldName <> edSaveAs.Text then begin
  2355.               // Changing torrent name
  2356.               req.Free;
  2357.               req:=TJSONObject.Create;
  2358.               req.Add('method', 'torrent-rename-path');
  2359.               args:=TJSONObject.Create;
  2360.               args.Add('ids', TJSONArray.Create([id]));
  2361.               args.Add('path', UTF8Decode(OldName));
  2362.               args.Add('name', UTF8Decode(edSaveAs.Text));
  2363.               req.Add('arguments', args);
  2364.               args:=nil;
  2365.               args:=RpcObj.SendRequest(req, False);
  2366.               if args = nil then begin
  2367.                 CheckStatus(False);
  2368.                 exit;
  2369.               end;
  2370.               args.Free;
  2371.             end;
  2372.           finally
  2373.             req.Free;
  2374.           end;
  2375.  
  2376.           if cbStartTorrent.Checked then
  2377.             TorrentAction(id, 'torrent-start');
  2378.  
  2379.           SelectTorrent(id, 2000);
  2380.  
  2381.           id:=0;
  2382.           if Ini.ReadBool('Interface', 'DeleteTorrentFile', False) and not IsProtocolSupported(FileName) then
  2383.             DeleteFileUTF8(FileName);
  2384.  
  2385.           Ini.WriteInteger(IniSec, 'PeerLimit', edPeerLimit.Value);
  2386.           SaveDownloadDirs(cbDestFolder, 'LastDownloadDir');
  2387.           Result:=True;
  2388.           AppNormal;
  2389.         end;
  2390.       finally
  2391.         Free;
  2392.       end;
  2393.     finally
  2394.       if id <> 0 then
  2395.         TorrentAction(id, 'torrent-remove');
  2396.     end;
  2397.   finally
  2398.     HideWaitMsg;
  2399.     Dec(FAddingTorrent);
  2400.   end;
  2401. end;
  2402.  
  2403. procedure TMainForm.UpdateTray;
  2404. begin
  2405.   TrayIcon.Visible:=not IsUnity and
  2406.     ( Ini.ReadBool('Interface', 'TrayIconAlways', True)
  2407.       or ( (WindowState = wsMinimized) and Ini.ReadBool('Interface', 'TrayMinimize', True) )
  2408.       or ( not Self.Visible and Ini.ReadBool('Interface', 'TrayClose', False) )
  2409.     );
  2410. {$ifdef darwin}
  2411.   acShowApp.Visible:=False;
  2412.   acHideApp.Visible:=False;
  2413.   miTSep1.Visible:=False;
  2414. {$else}
  2415.   acHideApp.Visible:=Visible and (WindowState <> wsMinimized);
  2416. {$endif darwin}
  2417.   SetRefreshInterval;
  2418.   FCalcAvg:=Ini.ReadBool('Interface', 'CalcAvg', True);
  2419. end;
  2420.  
  2421. procedure TMainForm.HideApp;
  2422. begin
  2423.   if WindowState <> wsMinimized then
  2424.     Hide;
  2425.    HideTaskbarButton;
  2426.   UpdateTray;
  2427. end;
  2428.  
  2429. procedure TMainForm.ShowApp;
  2430. var
  2431.   i: integer;
  2432. begin
  2433.   ShowTaskbarButton;
  2434.   if WindowState = wsMinimized then
  2435.     Application.Restore;
  2436.   Application.ProcessMessages;
  2437.   Show;
  2438.   Application.BringToFront;
  2439.   BringToFront;
  2440.   for i:=0 to Screen.FormCount - 1 do
  2441.     with Screen.Forms[i] do
  2442.       if fsModal in FormState then
  2443.         BringToFront;
  2444.   UpdateTray;
  2445. end;
  2446.  
  2447. procedure TMainForm.DownloadFinished(const TorrentName: string);
  2448. begin
  2449.   if not TrayIcon.Visible or not Ini.ReadBool('Interface', 'TrayNotify', True) then exit;
  2450.   TrayIcon.BalloonHint:=Format(sFinishedDownload, [TorrentName]);
  2451.   TrayIcon.BalloonTitle:=sDownloadComplete;
  2452.   TrayIcon.ShowBalloonHint;
  2453. end;
  2454.  
  2455. Procedure TMainForm.DoOpenFlagsZip(Sender: TObject; var AStream: TStream);
  2456. begin
  2457.   AStream:=TFileStreamUTF8.Create(TUnZipper(Sender).FileName, fmOpenRead or fmShareDenyWrite);
  2458. end;
  2459.  
  2460. Procedure TMainForm.DoCreateOutZipStream(Sender : TObject; var AStream : TStream; AItem : TFullZipFileEntry);
  2461. begin
  2462.   ForceDirectoriesUTF8(FFlagsPath);
  2463.   AStream:=TFileStreamUTF8.Create(FFlagsPath + AItem.DiskFileName, fmCreate);
  2464. end;
  2465.  
  2466. function TMainForm.GetFlagImage(const CountryCode: string): integer;
  2467. var
  2468.   s, ImageName: string;
  2469.   pic: TPicture;
  2470.   fs: TFileStreamUTF8;
  2471. begin
  2472.   Result:=0;
  2473.   if CountryCode = '' then exit;
  2474.   try
  2475.     ImageName:=CountryCode + '.png';
  2476.     if FFlagsPath = '' then
  2477.       FFlagsPath:=FHomeDir + 'flags' + DirectorySeparator;
  2478.     if not FileExistsUTF8(FFlagsPath + ImageName) then begin
  2479.       // Unzipping flag image
  2480.       if FUnZip = nil then begin
  2481.         s:=GetFlagsArchive;
  2482.         if s <> '' then begin
  2483.           FUnZip:=TUnZipper.Create;
  2484.           FUnZip.FileName:=s;
  2485.           FUnZip.OnOpenInputStream:=@DoOpenFlagsZip;
  2486.           FUnZip.OnCreateStream:=@DoCreateOutZipStream;
  2487.         end
  2488.         else
  2489.           exit;
  2490.       end;
  2491.  
  2492.       FUnZip.Files.Clear;
  2493.       FUnZip.Files.Add(ImageName);
  2494.       try
  2495.         FUnZip.UnZipAllFiles;
  2496.       except
  2497.         FreeAndNil(FUnZip);
  2498.         DeleteFileUTF8(GetFlagsArchive);
  2499.         acShowCountryFlag.Checked:=False;
  2500.         MessageDlg(sUnableExtractFlag + LineEnding + Exception(ExceptObject).Message, mtError, [mbOK], 0);
  2501.         exit;
  2502.       end;
  2503.       if not FileExistsUTF8(FFlagsPath + ImageName) then exit;
  2504.     end;
  2505.  
  2506.     fs:=nil;
  2507.     pic:=TPicture.Create;
  2508.     try
  2509.       fs:=TFileStreamUTF8.Create(FFlagsPath + ImageName, fmOpenRead or fmShareDenyWrite);
  2510.       pic.LoadFromStream(fs);
  2511.       if imgFlags.Count = 1 then begin
  2512.         imgFlags.Width:=pic.Width;
  2513.         imgFlags.Height:=pic.Height;
  2514.       end;
  2515.       Result:=imgFlags.AddMasked(pic.Bitmap, clNone);
  2516.     finally
  2517.       pic.Free;
  2518.       fs.Free;
  2519.     end;
  2520.   except
  2521.   end;
  2522. end;
  2523.  
  2524. procedure TMainForm.BeforeCloseApp;
  2525. begin
  2526.   if WindowState = wsNormal then begin
  2527.     Ini.WriteInteger('MainForm', 'Left', Left);
  2528.     Ini.WriteInteger('MainForm', 'Top', Top);
  2529.     Ini.WriteInteger('MainForm', 'Width', Width);
  2530.     Ini.WriteInteger('MainForm', 'Height', Height);
  2531.   end;
  2532.   if WindowState <> wsMinimized then
  2533.     Ini.WriteInteger('MainForm', 'State', integer(WindowState));
  2534.  
  2535.   if VSplitter.Visible then
  2536.     Ini.WriteInteger('MainForm', 'VSplitter', VSplitter.GetSplitterPosition);
  2537.   if HSplitter.Visible then
  2538.     Ini.WriteInteger('MainForm', 'HSplitter', HSplitter.GetSplitterPosition);
  2539.  
  2540.   Ini.WriteBool('MainForm', 'FilterPane', acFilterPane.Checked);
  2541.   Ini.WriteBool('MainForm', 'InfoPane', acInfoPane.Checked);
  2542.   Ini.WriteBool('MainForm', 'StatusBar', acStatusBar.Checked);
  2543.  
  2544.   SaveColumns(gTorrents, 'TorrentsList');
  2545.   SaveColumns(lvFiles, 'FilesList');
  2546.   SaveColumns(lvPeers, 'PeerList');
  2547.   SaveColumns(lvTrackers, 'TrackersList');
  2548.  
  2549.   Ini.WriteBool('PeersList', 'ResolveHost', acResolveHost.Checked);
  2550.   Ini.WriteBool('PeersList', 'ResolveCountry', acResolveCountry.Checked);
  2551.   Ini.WriteBool('PeersList', 'ShowCountryFlag', acShowCountryFlag.Checked);
  2552.  
  2553.   if RpcObj.Connected then
  2554.     Ini.WriteInteger('Interface', 'LastRpcVersion', RpcObj.RPCVersion);
  2555.  
  2556.   try
  2557.     Ini.UpdateFile;
  2558.   except
  2559.     Application.HandleException(nil);
  2560.   end;
  2561.  
  2562.   DoDisconnect;
  2563.   Application.ProcessMessages;
  2564. end;
  2565.  
  2566. function TMainForm.GetGeoIpDatabase: string;
  2567. begin
  2568.   Result:=LocateFile('GeoIP.dat', [FHomeDir, ExtractFilePath(ParamStrUTF8(0))]);
  2569. end;
  2570.  
  2571. function TMainForm.GetFlagsArchive: string;
  2572. begin
  2573.   Result:=LocateFile('flags.zip', [FHomeDir, ExtractFilePath(ParamStrUTF8(0))]);
  2574. end;
  2575.  
  2576. function TMainForm.DownloadGeoIpDatabase(AUpdate: boolean): boolean;
  2577. const
  2578.   GeoLiteURL = 'http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz';
  2579. var
  2580.   tmp: string;
  2581.   gz: TGZFileStream;
  2582.   fs: TFileStreamUTF8;
  2583.   buf: array[0..65535] of byte;
  2584.   i: integer;
  2585. begin
  2586.   Result:=False;
  2587.   tmp:=SysToUTF8(GetTempDir(True)) + 'GeoIP.dat.gz';
  2588.   if not FileExistsUTF8(tmp) or AUpdate then begin
  2589.     if MessageDlg('', sGeoIPConfirm, mtConfirmation, mbYesNo, 0, mbYes) <> mrYes then
  2590.       exit;
  2591.     if not DownloadFile(GeoLiteURL, ExtractFilePath(tmp), ExtractFileName(tmp)) then
  2592.       exit;
  2593.   end;
  2594.   try
  2595.     FreeAndNil(FResolver);
  2596.     gz:=TGZFileStream.Create(tmp, gzopenread);
  2597.     try
  2598.       fs:=TFileStreamUTF8.Create(FHomeDir + 'GeoIP.dat', fmCreate);
  2599.       try
  2600.         repeat
  2601.           i:=gz.read(buf, SizeOf(buf));
  2602.           fs.WriteBuffer(buf, i);
  2603.         until i < SizeOf(buf);
  2604.       finally
  2605.         fs.Free;
  2606.       end;
  2607.     finally
  2608.       gz.Free;
  2609.     end;
  2610.     DeleteFileUTF8(tmp);
  2611.   except
  2612.     DeleteFileUTF8(FHomeDir + 'GeoIP.dat');
  2613.     DeleteFileUTF8(tmp);
  2614.     raise;
  2615.   end;
  2616.   Result:=True;
  2617. end;
  2618.  
  2619. procedure TMainForm.TorrentColumnsChanged;
  2620. var
  2621.   i: integer;
  2622.   s: string;
  2623. begin
  2624.   s:='';
  2625.   for i:=0 to gTorrents.Columns.Count - 1 do
  2626.     with gTorrents.Columns[i] do
  2627.       if Visible and (Width > 0) then begin
  2628.         if TorrentFieldsMap[ID - 1] <> '' then begin
  2629.           if s <> '' then
  2630.             s:=s + ',';
  2631.           s:=s + TorrentFieldsMap[ID - 1];
  2632.         end;
  2633.       end;
  2634.   RpcObj.TorrentFields:=s;
  2635.   DoRefresh(True);
  2636. end;
  2637.  
  2638. function TMainForm.EtaToString(ETA: integer): string;
  2639. const
  2640.   r1 = 60;
  2641.   r2 = 5*60;
  2642.   r3 = 30*60;
  2643.   r4 = 60*60;
  2644.  
  2645. begin
  2646.   if (ETA < 0) or (ETA = MaxInt) then
  2647.     Result:=''
  2648.   else begin
  2649.     if ETA > 2*60*60 then  // > 5 hours - round to 1 hour
  2650.       ETA:=(ETA + r4 div 2) div r4 * r4
  2651.     else
  2652.     if ETA > 2*60*60 then  // > 2 hours - round to 30 mins
  2653.       ETA:=(ETA + r3 div 2) div r3 * r3
  2654.     else
  2655.     if ETA > 30*60 then  // > 30 mins - round to 5 mins
  2656.       ETA:=(ETA + r2 div 2) div r2 * r2
  2657.     else
  2658.     if ETA > 2*60 then   // > 2 mins - round to 1 min
  2659.     ETA:=(ETA + r1 div 2) div r1 * r1;
  2660.     Result:=SecondsToString(ETA);
  2661.   end;
  2662. end;
  2663.  
  2664. function TMainForm.GetTorrentStatus(TorrentIdx: integer): string;
  2665. var
  2666.   i: integer;
  2667. begin
  2668.   i:=gTorrents.Items[idxStatus, TorrentIdx];
  2669.   if i = TR_STATUS_CHECK_WAIT then
  2670.     Result:=sWaiting
  2671.   else
  2672.   if i = TR_STATUS_CHECK then
  2673.     Result:=sVerifying
  2674.   else
  2675.   if i = TR_STATUS_DOWNLOAD_WAIT then
  2676.     Result:=sWaiting
  2677.   else
  2678.   if i = TR_STATUS_DOWNLOAD then
  2679.     Result:=sDownloading
  2680.   else
  2681.   if i = TR_STATUS_SEED_WAIT then
  2682.     Result:=sWaiting
  2683.   else
  2684.   if i = TR_STATUS_SEED then
  2685.     Result:=sSeeding
  2686.   else
  2687.   if i = TR_STATUS_STOPPED then
  2688.     Result:=sStopped
  2689.   else
  2690.   if i = TR_STATUS_FINISHED then
  2691.     Result:=sFinished
  2692.   else
  2693.     Result:=sUnknown;
  2694. end;
  2695.  
  2696. function TMainForm.GetSeedsText(Seeds, SeedsTotal: integer): string;
  2697. begin
  2698.   if SeedsTotal <> -1 then
  2699.     Result:=Format('%d/%d', [Seeds, SeedsTotal])
  2700.   else
  2701.     Result:=Format('%d', [Seeds]);
  2702. end;
  2703.  
  2704. function TMainForm.GetPeersText(Peers, PeersTotal, Leechers: integer): string;
  2705. begin
  2706.   Result:=Format('%d', [Peers]);
  2707.   if Leechers <> -1 then
  2708.     Result:=Format('%s/%d', [Result, Leechers]);
  2709.   Dec(PeersTotal);
  2710.   if PeersTotal >= 0 then
  2711.     Result:=Format('%s (%d)', [Result, PeersTotal]);
  2712. end;
  2713.  
  2714. function TMainForm.RatioToString(Ratio: double): string;
  2715. begin
  2716.   if (Ratio = MaxInt) or (Ratio = -2) then
  2717.     Result:=Utf8Encode(WideString(WideChar($221E)))
  2718.   else
  2719.     if Ratio = -1 then
  2720.       Result:=''
  2721.     else
  2722.       Result:=Format('%.3f', [Ratio]);
  2723. end;
  2724.  
  2725. function TMainForm.TorrentDateTimeToString(d: Int64): string;
  2726. begin
  2727.   if d = 0 then
  2728.     Result:=''
  2729.   else
  2730.     Result:=DateTimeToStr(UnixToDateTime(d) + GetTimeZoneDelta);
  2731. end;
  2732.  
  2733. procedure TMainForm.DoRefresh(All: boolean);
  2734. begin
  2735.   if All then
  2736.     RpcObj.RefreshNow:=RpcObj.RefreshNow + [rtTorrents, rtDetails]
  2737.   else
  2738.     RpcObj.RefreshNow:=RpcObj.RefreshNow + [rtDetails];
  2739. end;
  2740.  
  2741. procedure TMainForm.acDisconnectExecute(Sender: TObject);
  2742. begin
  2743.   DoDisconnect;
  2744. end;
  2745.  
  2746. procedure TMainForm.acExitExecute(Sender: TObject);
  2747. begin
  2748.   BeforeCloseApp;
  2749.   Application.Terminate;
  2750. end;
  2751.  
  2752. procedure TMainForm.acDaemonOptionsExecute(Sender: TObject);
  2753. var
  2754.   req, args: TJSONObject;
  2755.   s: string;
  2756.   i, j: integer;
  2757. begin
  2758.   with TDaemonOptionsForm.Create(Self) do
  2759.   try
  2760.     AppBusy;
  2761.     req:=TJSONObject.Create;
  2762.     try
  2763.       req.Add('method', 'session-get');
  2764.       args:=RpcObj.SendRequest(req);
  2765.       if args <> nil then
  2766.         try
  2767.           edDownloadDir.Text:=UTF8Encode(args.Strings['download-dir']);
  2768.           if RpcObj.RPCVersion >= 5 then begin
  2769.             // RPC version 5
  2770.             edPort.Value:=args.Integers['peer-port'];
  2771.             cbPEX.Checked:=args.Integers['pex-enabled'] <> 0;
  2772.             edMaxPeers.Value:=args.Integers['peer-limit-global'];
  2773.             cbRandomPort.Checked:=args.Integers['peer-port-random-on-start'] <> 0;
  2774.             cbDHT.Checked:=args.Integers['dht-enabled'] <> 0;
  2775.             cbSeedRatio.Checked:=args.Integers['seedRatioLimited'] <> 0;
  2776.             edSeedRatio.Value:=args.Floats['seedRatioLimit'];
  2777.             cbBlocklist.Checked:=args.Integers['blocklist-enabled'] <> 0;
  2778.  
  2779.             cbAltEnabled.Checked:=args.Integers['alt-speed-enabled'] <> 0;
  2780.             edAltDown.Value:=args.Integers['alt-speed-down'];
  2781.             edAltUp.Value:=args.Integers['alt-speed-up'];
  2782.             cbAutoAlt.Checked:=args.Integers['alt-speed-time-enabled'] <> 0;
  2783.             edAltTimeBegin.Text:=FormatDateTime('hh:nn', args.Integers['alt-speed-time-begin']/MinsPerDay);
  2784.             edAltTimeEnd.Text:=FormatDateTime('hh:nn', args.Integers['alt-speed-time-end']/MinsPerDay);
  2785.             j:=args.Integers['alt-speed-time-day'];
  2786.             for i:=1 to 7 do begin
  2787.               TCheckBox(gbAltSpeed.FindChildControl(Format('cbDay%d', [i]))).Checked:=LongBool(j and 1);
  2788.               j:=j shr 1;
  2789.             end;
  2790.             cbAutoAltClick(nil);
  2791.           end
  2792.           else begin
  2793.             // RPC versions prior to v5
  2794.             cbPortForwarding.Top:=cbRandomPort.Top;
  2795.             edPort.Value:=args.Integers['port'];
  2796.             cbPEX.Checked:=args.Integers['pex-allowed'] <> 0;
  2797.             edMaxPeers.Value:=args.Integers['peer-limit'];
  2798.             cbRandomPort.Visible:=False;
  2799.             cbDHT.Visible:=False;
  2800.             cbSeedRatio.Visible:=False;
  2801.             edSeedRatio.Visible:=False;
  2802.             btTestPort.Visible:=False;
  2803.             cbBlocklist.Visible:=False;
  2804.             gbAltSpeed.Visible:=False;
  2805.           end;
  2806.  
  2807.           if RpcObj.RPCVersion >= 7 then begin
  2808.             cbIncompleteDir.Checked:=args.Integers['incomplete-dir-enabled'] <> 0;
  2809.             edIncompleteDir.Text:=UTF8Encode(args.Strings['incomplete-dir']);
  2810.             cbIncompleteDirClick(nil);
  2811.           end
  2812.           else begin
  2813.             cbIncompleteDir.Visible:=False;
  2814.             edIncompleteDir.Visible:=False;
  2815.           end;
  2816.  
  2817.           if RpcObj.RPCVersion >= 8 then
  2818.             cbPartExt.Checked:=args.Integers['rename-partial-files'] <> 0
  2819.           else
  2820.             cbPartExt.Visible:=False;
  2821.  
  2822.           if RpcObj.RPCVersion >= 9 then
  2823.             cbLPD.Checked:=args.Integers['lpd-enabled'] <> 0
  2824.           else
  2825.             cbLPD.Visible:=False;
  2826.  
  2827.           if RpcObj.RPCVersion >= 10 then begin
  2828.             edCacheSize.Value:=args.Integers['cache-size-mb'];
  2829.             cbIdleSeedLimit.Checked:=args.Integers['idle-seeding-limit-enabled'] <> 0;
  2830.             edIdleSeedLimit.Value:=args.Integers['idle-seeding-limit'];
  2831.             cbIdleSeedLimitClick(nil);
  2832.           end
  2833.           else begin
  2834.             edCacheSize.Visible:=False;
  2835.             txCacheSize.Visible:=False;
  2836.             txMB.Visible:=False;
  2837.             cbIdleSeedLimit.Visible:=False;
  2838.             edIdleSeedLimit.Visible:=False;
  2839.             txMinutes.Visible:=False;
  2840.           end;
  2841.  
  2842.           if args.IndexOfName('blocklist-url') >= 0 then
  2843.             edBlocklistURL.Text:=UTF8Encode(args.Strings['blocklist-url'])
  2844.           else begin
  2845.             edBlocklistURL.Visible:=False;
  2846.             cbBlocklist.Left:=cbPEX.Left;
  2847.             cbBlocklist.Caption:=StringReplace(cbBlocklist.Caption, ':', '', [rfReplaceAll]);
  2848.           end;
  2849.           cbBlocklistClick(nil);
  2850.  
  2851.           if RpcObj.RPCVersion >= 13 then
  2852.             cbUTP.Checked:=args.Integers['utp-enabled'] <> 0
  2853.           else
  2854.             cbUTP.Visible:=False;
  2855.  
  2856.           if RpcObj.RPCVersion >= 14 then begin
  2857.             tabQueue.TabVisible:=True;
  2858.             cbDownQueue.Checked:=args.Integers['download-queue-enabled'] <> 0;
  2859.             edDownQueue.Value:=args.Integers['download-queue-size'];
  2860.             cbUpQueue.Checked:=args.Integers['seed-queue-enabled'] <> 0;
  2861.             edUpQueue.Value:=args.Integers['seed-queue-size'];
  2862.             cbStalled.Checked:=args.Integers['queue-stalled-enabled'] <> 0;
  2863.             edStalledTime.Value:=args.Integers['queue-stalled-minutes'];
  2864.           end
  2865.           else
  2866.             tabQueue.TabVisible:=False;
  2867.  
  2868.           cbPortForwarding.Checked:=args.Integers['port-forwarding-enabled'] <> 0;
  2869.           s:=args.Strings['encryption'];
  2870.           if s = 'preferred' then
  2871.             cbEncryption.ItemIndex:=1
  2872.           else
  2873.           if s = 'required' then
  2874.             cbEncryption.ItemIndex:=2
  2875.           else
  2876.             cbEncryption.ItemIndex:=0;
  2877.           cbMaxDown.Checked:=args.Integers['speed-limit-down-enabled'] <> 0;
  2878.           edMaxDown.Value:=args.Integers['speed-limit-down'];
  2879.           cbMaxUp.Checked:=args.Integers['speed-limit-up-enabled'] <> 0;
  2880.           edMaxUp.Value:=args.Integers['speed-limit-up'];
  2881.         finally
  2882.           args.Free;
  2883.         end
  2884.       else begin
  2885.         CheckStatus(False);
  2886.         exit;
  2887.       end;
  2888.     finally
  2889.       req.Free;
  2890.     end;
  2891.     cbMaxDownClick(nil);
  2892.     cbMaxUpClick(nil);
  2893.     cbRandomPortClick(nil);
  2894.     cbSeedRatioClick(nil);
  2895.     AppNormal;
  2896.  
  2897.     if ShowModal = mrOK then begin
  2898.       AppBusy;
  2899.       Self.Update;
  2900.       req:=TJSONObject.Create;
  2901.       try
  2902.         req.Add('method', 'session-set');
  2903.         args:=TJSONObject.Create;
  2904.         args.Add('download-dir', UTF8Decode(edDownloadDir.Text));
  2905.         args.Add('port-forwarding-enabled', integer(cbPortForwarding.Checked) and 1);
  2906.         case cbEncryption.ItemIndex of
  2907.           1: s:='preferred';
  2908.           2: s:='required';
  2909.           else s:='tolerated';
  2910.         end;
  2911.         args.Add('encryption', s);
  2912.         args.Add('speed-limit-down-enabled', integer(cbMaxDown.Checked) and 1);
  2913.         if cbMaxDown.Checked then
  2914.           args.Add('speed-limit-down', edMaxDown.Value);
  2915.         args.Add('speed-limit-up-enabled', integer(cbMaxUp.Checked) and 1);
  2916.         if cbMaxUp.Checked then
  2917.           args.Add('speed-limit-up', edMaxUp.Value);
  2918.         if RpcObj.RPCVersion >= 5 then begin
  2919.           args.Add('peer-limit-global', edMaxPeers.Value);
  2920.           args.Add('peer-port', edPort.Value);
  2921.           args.Add('pex-enabled', integer(cbPEX.Checked) and 1);
  2922.           args.Add('peer-port-random-on-start', integer(cbRandomPort.Checked) and 1);
  2923.           args.Add('dht-enabled', integer(cbDHT.Checked) and 1);
  2924.           args.Add('seedRatioLimited', integer(cbSeedRatio.Checked) and 1);
  2925.           if cbSeedRatio.Checked then
  2926.             args.Add('seedRatioLimit', edSeedRatio.Value);
  2927.           args.Add('blocklist-enabled', integer(cbBlocklist.Checked) and 1);
  2928.  
  2929.           args.Add('alt-speed-enabled', integer(cbAltEnabled.Checked) and 1);
  2930.           args.Add('alt-speed-down', edAltDown.Value);
  2931.           args.Add('alt-speed-up', edAltUp.Value);
  2932.           args.Add('alt-speed-time-enabled', integer(cbAutoAlt.Checked) and 1);
  2933.           if cbAutoAlt.Checked then begin
  2934.             args.Add('alt-speed-time-begin', Round(Frac(StrToTime(edAltTimeBegin.Text))*MinsPerDay));
  2935.             args.Add('alt-speed-time-end', Round(Frac(StrToTime(edAltTimeEnd.Text))*MinsPerDay));
  2936.             j:=0;
  2937.             for i:=7 downto 1 do begin
  2938.               j:=j shl 1;
  2939.               j:=j or (integer(TCheckBox(gbAltSpeed.FindChildControl(Format('cbDay%d', [i]))).Checked) and 1);
  2940.             end;
  2941.             args.Add('alt-speed-time-day', j);
  2942.           end;
  2943.         end
  2944.         else begin
  2945.           args.Add('peer-limit', edMaxPeers.Value);
  2946.           args.Add('port', edPort.Value);
  2947.           args.Add('pex-allowed', integer(cbPEX.Checked) and 1);
  2948.         end;
  2949.         if RpcObj.RPCVersion >= 7 then begin
  2950.           args.Add('incomplete-dir-enabled', integer(cbIncompleteDir.Checked) and 1);
  2951.           if cbIncompleteDir.Checked then
  2952.             args.Add('incomplete-dir', UTF8Decode(edIncompleteDir.Text));
  2953.         end;
  2954.         if RpcObj.RPCVersion >= 8 then
  2955.           args.Add('rename-partial-files', integer(cbPartExt.Checked) and 1);
  2956.         if RpcObj.RPCVersion >= 9 then
  2957.           args.Add('lpd-enabled', integer(cbLPD.Checked) and 1);
  2958.         if RpcObj.RPCVersion >= 10 then begin
  2959.           args.Add('cache-size-mb', edCacheSize.Value);
  2960.           args.Add('idle-seeding-limit-enabled', integer(cbIdleSeedLimit.Checked) and 1);
  2961.           args.Add('idle-seeding-limit', edIdleSeedLimit.Value);
  2962.         end;
  2963.         if edBlocklistURL.Visible then
  2964.           if cbBlocklist.Checked then
  2965.             args.Add('blocklist-url', UTF8Decode(edBlocklistURL.Text));
  2966.         if RpcObj.RPCVersion >= 13 then
  2967.           args.Add('utp-enabled', integer(cbUTP.Checked) and 1);
  2968.         if RpcObj.RPCVersion >= 14 then begin
  2969.           args.Add('download-queue-enabled', integer(cbDownQueue.Checked) and 1);
  2970.           args.Add('download-queue-size', edDownQueue.Value);
  2971.           args.Add('seed-queue-enabled', integer(cbUpQueue.Checked) and 1);
  2972.           args.Add('seed-queue-size', edUpQueue.Value);
  2973.           args.Add('queue-stalled-enabled', integer(cbStalled.Checked) and 1);
  2974.           args.Add('queue-stalled-minutes', edStalledTime.Value);
  2975.         end;
  2976.  
  2977.         req.Add('arguments', args);
  2978.         args:=RpcObj.SendRequest(req, False);
  2979.         if args = nil then begin
  2980.           CheckStatus(False);
  2981.           exit;
  2982.         end;
  2983.         args.Free;
  2984.       finally
  2985.         req.Free;
  2986.       end;
  2987.       RpcObj.RefreshNow:=RpcObj.RefreshNow + [rtSession];
  2988.       AppNormal;
  2989.     end;
  2990.   finally
  2991.     Free;
  2992.   end;
  2993. end;
  2994.  
  2995. procedure TMainForm.acQMoveBottomExecute(Sender: TObject);
  2996. begin
  2997.   TorrentAction(GetSelectedTorrents, 'queue-move-bottom');
  2998. end;
  2999.  
  3000. procedure TMainForm.acQMoveDownExecute(Sender: TObject);
  3001. begin
  3002.   TorrentAction(GetSelectedTorrents, 'queue-move-down');
  3003. end;
  3004.  
  3005. procedure TMainForm.acQMoveTopExecute(Sender: TObject);
  3006. begin
  3007.   TorrentAction(GetSelectedTorrents, 'queue-move-top');
  3008. end;
  3009.  
  3010. procedure TMainForm.acQMoveUpExecute(Sender: TObject);
  3011. begin
  3012.   TorrentAction(GetSelectedTorrents, 'queue-move-up');
  3013. end;
  3014.  
  3015. procedure TMainForm.acReannounceTorrentExecute(Sender: TObject);
  3016. begin
  3017.   TorrentAction(GetSelectedTorrents, 'torrent-reannounce');
  3018. end;
  3019.  
  3020. procedure TMainForm.acRemoveTorrentAndDataExecute(Sender: TObject);
  3021. begin
  3022.   InternalRemoveTorrent(sRemoveTorrentData, sRemoveTorrentDataMulti, True);
  3023. end;
  3024.  
  3025. procedure TMainForm.acRemoveTorrentExecute(Sender: TObject);
  3026. begin
  3027.   InternalRemoveTorrent(sRemoveTorrent, sRemoveTorrentMulti, False);
  3028. end;
  3029.  
  3030. procedure TMainForm.acRenameExecute(Sender: TObject);
  3031. begin
  3032.   if lvFiles.Focused then
  3033.     lvFiles.EditCell(idxFileName, lvFiles.Row)
  3034.   else
  3035.     gTorrents.EditCell(idxName, gTorrents.Row);
  3036. end;
  3037.  
  3038. procedure TMainForm.acResolveCountryExecute(Sender: TObject);
  3039. begin
  3040.   if not acResolveCountry.Checked then
  3041.     if GetGeoIpDatabase = '' then
  3042.       if not DownloadGeoIpDatabase(False) then
  3043.         exit;
  3044.  
  3045.   acResolveCountry.Checked:=not acResolveCountry.Checked;
  3046.   FreeAndNil(FResolver);
  3047.   DoRefresh;
  3048.   acShowCountryFlag.Enabled:=acResolveCountry.Checked;
  3049. end;
  3050.  
  3051. procedure TMainForm.acResolveHostExecute(Sender: TObject);
  3052. begin
  3053.   acResolveHost.Checked:=not acResolveHost.Checked;
  3054.   FreeAndNil(FResolver);
  3055.   DoRefresh;
  3056. end;
  3057.  
  3058. procedure TMainForm.acSelectAllExecute(Sender: TObject);
  3059. begin
  3060.   Application.ProcessMessages;
  3061.   if lvFiles.Focused then
  3062.     lvFiles.SelectAll
  3063.   else
  3064.     gTorrents.SelectAll;
  3065. end;
  3066.  
  3067. procedure TMainForm.acSetHighPriorityExecute(Sender: TObject);
  3068. begin
  3069.   Application.ProcessMessages;
  3070.   if lvFiles.Focused then
  3071.     SetCurrentFilePriority('high')
  3072.   else
  3073.     SetTorrentPriority(TR_PRI_HIGH);
  3074. end;
  3075.  
  3076. procedure TMainForm.acSetLowPriorityExecute(Sender: TObject);
  3077. begin
  3078.   Application.ProcessMessages;
  3079.   if lvFiles.Focused then
  3080.     SetCurrentFilePriority('low')
  3081.   else
  3082.     SetTorrentPriority(TR_PRI_LOW);
  3083. end;
  3084.  
  3085. procedure TMainForm.acSetNormalPriorityExecute(Sender: TObject);
  3086. begin
  3087.   Application.ProcessMessages;
  3088.   if lvFiles.Focused then
  3089.     SetCurrentFilePriority('normal')
  3090.   else
  3091.     SetTorrentPriority(TR_PRI_NORMAL);
  3092. end;
  3093.  
  3094. procedure TMainForm.acSetNotDownloadExecute(Sender: TObject);
  3095. begin
  3096.   SetCurrentFilePriority('skip');
  3097. end;
  3098.  
  3099. procedure TMainForm.acSetupColumnsExecute(Sender: TObject);
  3100. var
  3101.   g: TVarGrid;
  3102.   s: string;
  3103. begin
  3104.   Application.ProcessMessages;
  3105.   if lvTrackers.Focused then
  3106.     g:=lvTrackers
  3107.   else
  3108.   if lvPeers.Focused then
  3109.     g:=lvPeers
  3110.   else
  3111.   if lvFiles.Focused then
  3112.     g:=lvFiles
  3113.   else
  3114.     g:=gTorrents;
  3115.   if g = gTorrents then
  3116.     s:=sTorrents
  3117.   else
  3118.     if PageInfo.ActivePage = tabFiles then
  3119.       s:=FFilesCapt
  3120.     else
  3121.       s:=PageInfo.ActivePage.Caption;
  3122.   if not SetupColumns(g, 0, s) then exit;
  3123.   if g = gTorrents then
  3124.     TorrentColumnsChanged;
  3125. end;
  3126.  
  3127. procedure TMainForm.acShowAppExecute(Sender: TObject);
  3128. begin
  3129.   ShowApp;
  3130. end;
  3131.  
  3132. procedure TMainForm.acShowCountryFlagExecute(Sender: TObject);
  3133. const
  3134.   FlagsURL = 'http://transmisson-remote-gui.googlecode.com/files/flags.zip';
  3135. begin
  3136.   if not acShowCountryFlag.Checked then
  3137.     if GetFlagsArchive = '' then begin
  3138.       if MessageDlg('', sFlagArchiveConfirm, mtConfirmation, mbYesNo, 0, mbYes) <> mrYes then
  3139.         exit;
  3140.       if not DownloadFile(FlagsURL, FHomeDir) then
  3141.         exit;
  3142.     end;
  3143.   acShowCountryFlag.Checked:=not acShowCountryFlag.Checked;
  3144.   DoRefresh;
  3145. end;
  3146.  
  3147. procedure TMainForm.acStartAllTorrentsExecute(Sender: TObject);
  3148. begin
  3149.   TorrentAction(NULL, 'torrent-start');
  3150. end;
  3151.  
  3152. procedure TMainForm.acStartTorrentExecute(Sender: TObject);
  3153. begin
  3154.   TorrentAction(GetSelectedTorrents, 'torrent-start');
  3155. end;
  3156.  
  3157. procedure TMainForm.acStatusBarExecute(Sender: TObject);
  3158. begin
  3159.   acStatusBar.Checked:=not acStatusBar.Checked;
  3160.   StatusBar.Visible:=acStatusBar.Checked;
  3161.   if StatusBar.Visible then
  3162.     StatusBar.Top:=ClientHeight;
  3163. end;
  3164.  
  3165. procedure TMainForm.acStopAllTorrentsExecute(Sender: TObject);
  3166. begin
  3167.   TorrentAction(NULL, 'torrent-stop');
  3168. end;
  3169.  
  3170. procedure TMainForm.acStopTorrentExecute(Sender: TObject);
  3171. begin
  3172.   TorrentAction(GetSelectedTorrents, 'torrent-stop');
  3173. end;
  3174.  
  3175. procedure TMainForm.acTorrentPropsExecute(Sender: TObject);
  3176. begin
  3177.   TorrentProps(0);
  3178. end;
  3179.  
  3180. procedure TMainForm.TorrentProps(PageNo: integer);
  3181. const
  3182.   TR_RATIOLIMIT_GLOBAL    = 0; // follow the global settings
  3183.   TR_RATIOLIMIT_SINGLE    = 1; // override the global settings, seeding until a certain ratio
  3184.   TR_RATIOLIMIT_UNLIMITED = 2; // override the global settings, seeding regardless of ratio
  3185.  
  3186.   TR_IDLELIMIT_GLOBAL     = 0; // follow the global settings
  3187.   TR_IDLELIMIT_SINGLE     = 1; // override the global settings, seeding until a certain idle time
  3188.   TR_IDLELIMIT_UNLIMITED  = 2; // override the global settings, seeding regardless of activity
  3189.  
  3190. var
  3191.   req, args, t, tr: TJSONObject;
  3192.   i, j, id: integer;
  3193.   ids, Trackers, AddT, EditT, DelT: TJSONArray;
  3194.   TorrentIds: variant;
  3195.   s: string;
  3196.   trlist, sl: TStringList;
  3197. begin
  3198.   gTorrentsClick(nil);
  3199.   id:=RpcObj.CurTorrentId;
  3200.   if id = 0 then exit;
  3201.   AppBusy;
  3202.   trlist:=nil;
  3203.   with TTorrPropsForm.Create(Self) do
  3204.   try
  3205.     Page.ActivePageIndex:=PageNo;
  3206.     gTorrents.Tag:=1;
  3207.     gTorrents.EnsureSelectionVisible;
  3208.     TorrentIds:=GetSelectedTorrents;
  3209.     args:=RpcObj.RequestInfo(id, ['downloadLimit', 'downloadLimitMode', 'downloadLimited', 'uploadLimit', 'uploadLimitMode', 'uploadLimited',
  3210.                                   'name', 'maxConnectedPeers', 'seedRatioMode', 'seedRatioLimit', 'seedIdleLimit', 'seedIdleMode', 'trackers']);
  3211.     if args = nil then begin
  3212.       CheckStatus(False);
  3213.       exit;
  3214.     end;
  3215.     try
  3216.       t:=args.Arrays['torrents'].Objects[0];
  3217.  
  3218.       if gTorrents.SelCount > 1 then
  3219.         s:=Format(sSeveralTorrents, [gTorrents.SelCount])
  3220.       else
  3221.         s:=UTF8Encode(t.Strings['name']);
  3222.  
  3223.       txName.Caption:=txName.Caption + ' ' + s;
  3224.       Caption:=Caption + ' - ' + s;
  3225.       if RpcObj.RPCVersion < 5 then begin
  3226.         // RPC versions prior to v5
  3227.         j:=t.Integers['downloadLimitMode'];
  3228.         cbMaxDown.Checked:=j = TR_SPEEDLIMIT_SINGLE;
  3229.         i:=t.Integers['downloadLimit'];
  3230.         if (i < 0) or (j = TR_SPEEDLIMIT_UNLIMITED) then
  3231.           edMaxDown.ValueEmpty:=True
  3232.         else
  3233.           edMaxDown.Value:=i;
  3234.  
  3235.         j:=t.Integers['uploadLimitMode'];
  3236.         cbMaxUp.Checked:=j = TR_SPEEDLIMIT_SINGLE;
  3237.         i:=t.Integers['uploadLimit'];
  3238.         if (i < 0) or (j = TR_SPEEDLIMIT_UNLIMITED) then
  3239.           edMaxUp.ValueEmpty:=True
  3240.         else
  3241.           edMaxUp.Value:=i;
  3242.         cbSeedRatio.Visible:=False;
  3243.         edSeedRatio.Visible:=False;
  3244.       end else begin
  3245.         // RPC version 5
  3246.         cbMaxDown.Checked:=t.Booleans['downloadLimited'];
  3247.         i:=t.Integers['downloadLimit'];
  3248.         if i < 0 then
  3249.           edMaxDown.ValueEmpty:=True
  3250.         else
  3251.           edMaxDown.Value:=i;
  3252.  
  3253.         cbMaxUp.Checked:=t.Booleans['uploadLimited'];
  3254.         i:=t.Integers['uploadLimit'];
  3255.         if i < 0 then
  3256.           edMaxUp.ValueEmpty:=True
  3257.         else
  3258.           edMaxUp.Value:=i;
  3259.  
  3260.         case t.Integers['seedRatioMode'] of
  3261.           TR_RATIOLIMIT_SINGLE:
  3262.             cbSeedRatio.State:=cbChecked;
  3263.           TR_RATIOLIMIT_UNLIMITED:
  3264.             cbSeedRatio.State:=cbUnchecked;
  3265.           else
  3266.             cbSeedRatio.State:=cbGrayed;
  3267.         end;
  3268.         edSeedRatio.Value:=t.Floats['seedRatioLimit'];
  3269.       end;
  3270.  
  3271.       if RpcObj.RPCVersion >= 10 then begin
  3272.         case t.Integers['seedIdleMode'] of
  3273.           TR_IDLELIMIT_SINGLE:
  3274.             cbIdleSeedLimit.State:=cbChecked;
  3275.           TR_IDLELIMIT_UNLIMITED:
  3276.             cbIdleSeedLimit.State:=cbUnchecked;
  3277.           else
  3278.             cbIdleSeedLimit.State:=cbGrayed;
  3279.         end;
  3280.         edIdleSeedLimit.Value:=t.Integers['seedIdleLimit'];
  3281.         cbIdleSeedLimitClick(nil);
  3282.  
  3283.         trlist:=TStringList.Create;
  3284.         Trackers:=t.Arrays['trackers'];
  3285.         for i:=0 to Trackers.Count - 1 do begin
  3286.           tr:=Trackers[i] as TJSONObject;
  3287.           trlist.AddObject(UTF8Decode(tr.Strings['announce']), TObject(PtrUInt(tr.Integers['id'])));
  3288.         end;
  3289.         edTrackers.Lines.Assign(trlist);
  3290.       end
  3291.       else begin
  3292.         cbIdleSeedLimit.Visible:=False;
  3293.         edIdleSeedLimit.Visible:=False;
  3294.         txMinutes.Visible:=False;
  3295.         tabAdvanced.TabVisible:=False;
  3296.       end;
  3297.       edPeerLimit.Value:=t.Integers['maxConnectedPeers'];
  3298.     finally
  3299.       args.Free;
  3300.     end;
  3301.     cbMaxDownClick(nil);
  3302.     cbMaxUpClick(nil);
  3303.     cbSeedRatioClick(nil);
  3304.     AppNormal;
  3305.     if ShowModal = mrOk then begin
  3306.       AppBusy;
  3307.       Self.Update;
  3308.       req:=TJSONObject.Create;
  3309.       try
  3310.         req.Add('method', 'torrent-set');
  3311.         args:=TJSONObject.Create;
  3312.         ids:=TJSONArray.Create;
  3313.         for i:=VarArrayLowBound(TorrentIds, 1) to VarArrayHighBound(TorrentIds, 1) do
  3314.           ids.Add(integer(TorrentIds[i]));
  3315.         args.Add('ids', ids);
  3316.  
  3317.         if RpcObj.RPCVersion < 5 then
  3318.         begin
  3319.           // RPC versions prior to v5
  3320.           args.Add('speed-limit-down-enabled', integer(cbMaxDown.Checked) and 1);
  3321.           args.Add('speed-limit-up-enabled', integer(cbMaxUp.Checked) and 1);
  3322.           if cbMaxDown.Checked then
  3323.             args.Add('speed-limit-down', edMaxDown.Value);
  3324.           if cbMaxUp.Checked then
  3325.             args.Add('speed-limit-up', edMaxUp.Value);
  3326.         end else begin
  3327.           // RPC version 5
  3328.           args.Add('downloadLimited', integer(cbMaxDown.Checked) and 1);
  3329.           args.Add('uploadLimited', integer(cbMaxUp.Checked) and 1);
  3330.           if cbMaxDown.Checked then
  3331.             args.Add('downloadLimit', edMaxDown.Value);
  3332.           if cbMaxUp.Checked then
  3333.             args.Add('uploadLimit', edMaxUp.Value);
  3334.           case cbSeedRatio.State of
  3335.             cbChecked:
  3336.               i:=TR_RATIOLIMIT_SINGLE;
  3337.             cbUnchecked:
  3338.               i:=TR_RATIOLIMIT_UNLIMITED;
  3339.             else
  3340.               i:=TR_RATIOLIMIT_GLOBAL;
  3341.           end;
  3342.           args.Add('seedRatioMode', i);
  3343.           if cbSeedRatio.State = cbChecked then
  3344.             args.Add('seedRatioLimit', edSeedRatio.Value);
  3345.         end;
  3346.  
  3347.         if RpcObj.RPCVersion >= 10 then begin
  3348.           case cbIdleSeedLimit.State of
  3349.             cbChecked:
  3350.               i:=TR_IDLELIMIT_SINGLE;
  3351.             cbUnchecked:
  3352.               i:=TR_IDLELIMIT_UNLIMITED;
  3353.             else
  3354.               i:=TR_IDLELIMIT_GLOBAL;
  3355.           end;
  3356.           args.Add('seedIdleMode', i);
  3357.           if cbIdleSeedLimit.State = cbChecked then
  3358.             args.Add('seedIdleLimit', edIdleSeedLimit.Value);
  3359.  
  3360.           sl:=TStringList.Create;
  3361.           try
  3362.             sl.Assign(edTrackers.Lines);
  3363.             // Removing unchanged trackers
  3364.             i:=0;
  3365.             while i < sl.Count do begin
  3366.               s:=Trim(sl[i]);
  3367.               if s = '' then begin
  3368.                 sl.Delete(i);
  3369.                 continue;
  3370.               end;
  3371.               j:=trlist.IndexOf(s);
  3372.               if j >= 0 then begin
  3373.                 trlist.Delete(j);
  3374.                 sl.Delete(i);
  3375.                 continue;
  3376.               end;
  3377.               Inc(i);
  3378.             end;
  3379.  
  3380.             AddT:=TJSONArray.Create;
  3381.             EditT:=TJSONArray.Create;
  3382.             DelT:=TJSONArray.Create;
  3383.             try
  3384.               for i:=0 to sl.Count - 1 do begin
  3385.                 s:=Trim(sl[i]);
  3386.                 if trlist.Count > 0 then begin
  3387.                   EditT.Add(PtrUInt(trlist.Objects[0]));
  3388.                   EditT.Add(UTF8Decode(s));
  3389.                   trlist.Delete(0);
  3390.                 end
  3391.                 else
  3392.                   AddT.Add(UTF8Decode(s));
  3393.               end;
  3394.  
  3395.               for i:=0 to trlist.Count - 1 do
  3396.                 DelT.Add(PtrUInt(trlist.Objects[i]));
  3397.  
  3398.               if AddT.Count > 0 then begin
  3399.                 args.Add('trackerAdd', AddT);
  3400.                 AddT:=nil;
  3401.               end;
  3402.               if EditT.Count > 0 then begin
  3403.                 args.Add('trackerReplace', EditT);
  3404.                 EditT:=nil;
  3405.               end;
  3406.               if DelT.Count > 0 then begin
  3407.                 args.Add('trackerRemove', DelT);
  3408.                 DelT:=nil;
  3409.               end;
  3410.             finally
  3411.               DelT.Free;
  3412.               EditT.Free;
  3413.               AddT.Free;
  3414.             end;
  3415.           finally
  3416.             sl.Free;
  3417.           end;
  3418.         end;
  3419.  
  3420.         args.Add('peer-limit', edPeerLimit.Value);
  3421.         req.Add('arguments', args);
  3422.         args:=nil;
  3423.         args:=RpcObj.SendRequest(req, False);
  3424.         if args = nil then begin
  3425.           CheckStatus(False);
  3426.           exit;
  3427.         end;
  3428.         args.Free;
  3429.       finally
  3430.         req.Free;
  3431.       end;
  3432.       DoRefresh;
  3433.       AppNormal;
  3434.     end;
  3435.   finally
  3436.     gTorrents.Tag:=0;
  3437.     Free;
  3438.     trlist.Free;
  3439.   end;
  3440. end;
  3441.  
  3442. procedure TMainForm.acTrackerGroupingExecute(Sender: TObject);
  3443. begin
  3444.   acTrackerGrouping.Checked:=not acTrackerGrouping.Checked;
  3445.   Ini.WriteBool('Interface', 'TrackerGrouping', acTrackerGrouping.Checked);
  3446.   RpcObj.RefreshNow:=RpcObj.RefreshNow + [rtTorrents];
  3447. end;
  3448.  
  3449. procedure TMainForm.acUpdateBlocklistExecute(Sender: TObject);
  3450. var
  3451.   req: TJSONObject;
  3452.   res: TJSONObject;
  3453. begin
  3454.   Application.ProcessMessages;
  3455.   AppBusy;
  3456.   req:=TJSONObject.Create;
  3457.   try
  3458.     req.Add('method', 'blocklist-update');
  3459.     res:=RpcObj.SendRequest(req, True, 3*60000);
  3460.     AppNormal;
  3461.     if res = nil then begin
  3462.       CheckStatus(False);
  3463.       exit;
  3464.     end;
  3465.     MessageDlg(Format(sBlocklistUpdateComplete, [res.Integers[('blocklist-size')]]), mtInformation, [mbOK], 0);
  3466.     res.Free;
  3467.   finally
  3468.     req.Free;
  3469.   end;
  3470. end;
  3471.  
  3472. procedure TMainForm.acUpdateGeoIPExecute(Sender: TObject);
  3473. begin
  3474.   if DownloadGeoIpDatabase(True) then
  3475.     MessageDlg(sUpdateComplete, mtInformation, [mbOK], 0);
  3476. end;
  3477.  
  3478. procedure TMainForm.acVerifyTorrentExecute(Sender: TObject);
  3479. var
  3480.   ids: variant;
  3481.   s: string;
  3482. begin
  3483.   if gTorrents.Items.Count = 0 then exit;
  3484.   gTorrents.Tag:=1;
  3485.   try
  3486.     gTorrents.EnsureSelectionVisible;
  3487.     ids:=GetSelectedTorrents;
  3488.     if gTorrents.SelCount < 2 then
  3489.       s:=Format(sTorrentVerification, [UTF8Encode(widestring(gTorrents.Items[idxName, gTorrents.Items.IndexOf(idxTorrentId, ids[0])]))])
  3490.     else
  3491.       s:=Format(sTorrentsVerification, [gTorrents.SelCount]);
  3492.     if MessageDlg('', s, mtConfirmation, mbYesNo, 0, mbNo) <> mrYes then
  3493.       exit;
  3494.   finally
  3495.     gTorrents.Tag:=0;
  3496.   end;
  3497.   TorrentAction(ids, 'torrent-verify');
  3498. end;
  3499.  
  3500. procedure TMainForm.ApplicationPropertiesEndSession(Sender: TObject);
  3501. begin
  3502.   DeleteFileUTF8(FRunFileName);
  3503.   BeforeCloseApp;
  3504. end;
  3505.  
  3506. procedure TMainForm.ApplicationPropertiesException(Sender: TObject; E: Exception);
  3507. var
  3508.   msg: string;
  3509. {$ifdef CALLSTACK}
  3510.   sl: TStringList;
  3511. {$endif CALLSTACK}
  3512. begin
  3513.   ForceAppNormal;
  3514.   msg:=E.Message;
  3515. {$ifdef CALLSTACK}
  3516.   try
  3517.     sl:=TStringList.Create;
  3518.     try
  3519.       sl.Text:=GetLastExceptionCallStack;
  3520.       Clipboard.AsText:=msg + LineEnding + sl.Text;
  3521.       DebugLn(msg + LineEnding + sl.Text);
  3522.       if sl.Count > 20 then begin
  3523.         while sl.Count > 20 do
  3524.           sl.Delete(20);
  3525.         sl.Add('...');
  3526.       end;
  3527.       msg:=msg + LineEnding + '---' + LineEnding + 'The error details has been copied to the clipboard.' + LineEnding + '---';
  3528.       msg:=msg + LineEnding + sl.Text;
  3529.     finally
  3530.       sl.Free;
  3531.     end;
  3532.   except
  3533.     ; // suppress exception
  3534.   end;
  3535. {$endif CALLSTACK}
  3536.   MessageDlg(msg, mtError, [mbOK], 0);
  3537. end;
  3538.  
  3539. procedure TMainForm.ApplicationPropertiesIdle(Sender: TObject; var Done: Boolean);
  3540. begin
  3541.   UpdateUI;
  3542. {$ifdef LCLcarbon}
  3543.   CheckSynchronize;
  3544. {$endif LCLcarbon}
  3545.   Done:=True;
  3546. end;
  3547.  
  3548. procedure TMainForm.ApplicationPropertiesMinimize(Sender: TObject);
  3549. begin
  3550. {$ifndef darwin}
  3551.   if not IsUnity and Ini.ReadBool('Interface', 'TrayMinimize', True) then
  3552.     HideApp;
  3553. {$endif darwin}
  3554.   UpdateTray;
  3555. end;
  3556.  
  3557. procedure TMainForm.ApplicationPropertiesRestore(Sender: TObject);
  3558. begin
  3559.   UpdateTray;
  3560.   CheckClipboardLink;
  3561. end;
  3562.  
  3563. procedure TMainForm.edSearchChange(Sender: TObject);
  3564. begin
  3565.   DoRefresh(True);
  3566. end;
  3567.  
  3568. procedure TMainForm.FormActivate(Sender: TObject);
  3569. begin
  3570.   CheckClipboardLink;
  3571. end;
  3572.  
  3573. procedure TMainForm.FormDropFiles(Sender: TObject; const FileNames: array of String);
  3574. var
  3575.   i: integer;
  3576. begin
  3577.   for i:=Low(FileNames) to High(FileNames) do
  3578.     AddTorrentFile(FileNames[i]);
  3579. end;
  3580.  
  3581. procedure TMainForm.FormWindowStateChange(Sender: TObject);
  3582. begin
  3583. {$ifdef lclgtk2}
  3584.   if WindowState = wsMinimized then
  3585.     ApplicationPropertiesMinimize(nil)
  3586.   else
  3587.     ApplicationPropertiesRestore(nil);
  3588. {$endif lclgtk2}
  3589. end;
  3590.  
  3591. procedure TMainForm.gTorrentsCellAttributes(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState;
  3592.                                             var CellAttribs: TCellAttributes);
  3593. var
  3594.   j: integer;
  3595. begin
  3596.   if ARow < 0 then exit;
  3597.   with CellAttribs do begin
  3598.     if ACol = gTorrents.FirstVisibleColumn then
  3599.       ImageIndex:=integer(Sender.Items[idxStateImg, ARow]);
  3600.     if Text = '' then exit;
  3601.     if not VarIsEmpty(Sender.Items[idxDeleted, ARow]) then
  3602.       with Sender.Canvas.Font do
  3603.         Style:=Style + [fsStrikeOut];
  3604.     case ADataCol of
  3605.       idxStatus:
  3606.         Text:=GetTorrentStatus(ARow);
  3607.       idxSize, idxDownloaded, idxUploaded, idxSizeToDowload:
  3608.         Text:=GetHumanSize(Sender.Items[ADataCol, ARow], 0, '?');
  3609.       idxDone:
  3610.         Text:=Format('%.1f%%', [double(Sender.Items[idxDone, ARow])]);
  3611.       idxSeeds:
  3612.         if not VarIsNull(Sender.Items[idxSeedsTotal, ARow]) then
  3613.           Text:=GetSeedsText(Sender.Items[idxSeeds, ARow], Sender.Items[idxSeedsTotal, ARow]);
  3614.       idxPeers:
  3615.         Text:=GetPeersText(Sender.Items[idxPeers, ARow], -1, Sender.Items[idxLeechersTotal, ARow]);
  3616.       idxDownSpeed, idxUpSpeed:
  3617.         begin
  3618.           j:=Sender.Items[ADataCol, ARow];
  3619.           if j > 0 then
  3620.             Text:=GetHumanSize(j, 1) + sPerSecond
  3621.           else
  3622.             Text:='';
  3623.         end;
  3624.       idxETA:
  3625.         Text:=EtaToString(Sender.Items[idxETA, ARow]);
  3626.       idxRatio:
  3627.         Text:=RatioToString(Sender.Items[idxRatio, ARow]);
  3628.       idxAddedOn, idxCompletedOn, idxLastActive:
  3629.         Text:=TorrentDateTimeToString(Sender.Items[ADataCol, ARow]);
  3630.       idxPriority:
  3631.         Text:=PriorityToStr(Sender.Items[ADataCol, ARow], ImageIndex);
  3632.       idxQueuePos:
  3633.         begin
  3634.           j:=Sender.Items[ADataCol, ARow];
  3635.           if j >= FinishedQueue then
  3636.             Dec(j, FinishedQueue);
  3637.           Text:=IntToStr(j);
  3638.         end;
  3639.       idxSeedingTime:
  3640.         begin
  3641.           j:=Sender.Items[idxSeedingTime, ARow];
  3642.           if j > 0 then
  3643.             Text:=EtaToString(j)
  3644.           else
  3645.             Text:='';
  3646.         end;
  3647.     end;
  3648.   end;
  3649. end;
  3650.  
  3651. procedure TMainForm.gTorrentsClick(Sender: TObject);
  3652. var
  3653.   i: integer;
  3654. begin
  3655.   if gTorrents.Tag <> 0 then exit;
  3656.   RpcObj.Lock;
  3657.   try
  3658.     if gTorrents.Items.Count > 0 then
  3659.       i:=gTorrents.Items[idxTorrentId, gTorrents.Row]
  3660.     else
  3661.       i:=0;
  3662.     if RpcObj.CurTorrentId = i then
  3663.       exit;
  3664.     RpcObj.CurTorrentId:=i;
  3665.   finally
  3666.     RpcObj.Unlock;
  3667.   end;
  3668.  
  3669.   ClearDetailsInfo(GetPageInfoType(PageInfo.ActivePage));
  3670.  
  3671.   TorrentsListTimer.Enabled:=False;
  3672.   TorrentsListTimer.Enabled:=True;
  3673. end;
  3674.  
  3675. procedure TMainForm.gTorrentsDblClick(Sender: TObject);
  3676. var
  3677.   res: TJSONObject;
  3678.   s, n: string;
  3679. begin
  3680.   if gTorrents.Items.Count = 0 then
  3681.     exit;
  3682.   if gTorrents.Items[idxDone, gTorrents.Row] = 100.0 then begin
  3683.     // The torrent is finished. Check if it is possible to open its file/folder
  3684.     AppBusy;
  3685.     try
  3686.       res:=RpcObj.RequestInfo(gTorrents.Items[idxTorrentId, gTorrents.Row], ['downloadDir']);
  3687.       if res = nil then
  3688.         CheckStatus(False);
  3689.       with res.Arrays['torrents'].Objects[0] do
  3690.         n:=IncludeProperTrailingPathDelimiter(UTF8Encode(Strings['downloadDir'])) + UTF8Encode(widestring(gTorrents.Items[idxName, gTorrents.Row]));
  3691.       s:=MapRemoteToLocal(n);
  3692.       if s = '' then
  3693.         s:=n;
  3694.       if FileExistsUTF8(s) or DirectoryExistsUTF8(s) then begin
  3695.         // File/folder exists - open it
  3696.         OpenCurrentTorrent(False);
  3697.         exit;
  3698.       end;
  3699.     finally
  3700.       AppNormal;
  3701.     end;
  3702.   end;
  3703.   acTorrentProps.Execute;
  3704. end;
  3705.  
  3706. procedure TMainForm.gTorrentsDrawCell(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; const R: TRect; var ADefaultDrawing: boolean);
  3707. begin
  3708.   if ARow < 0 then exit;
  3709.   if ADataCol = idxDone then begin
  3710.     ADefaultDrawing:=False;
  3711.     DrawProgressCell(Sender, ACol, ARow, ADataCol, AState, R);
  3712.   end;
  3713. end;
  3714.  
  3715. procedure TMainForm.gTorrentsEditorHide(Sender: TObject);
  3716. begin
  3717.   gTorrents.Tag:=0;
  3718. end;
  3719.  
  3720. procedure TMainForm.gTorrentsEditorShow(Sender: TObject);
  3721. begin
  3722.   gTorrents.Tag:=1;
  3723.   gTorrents.RemoveSelection;
  3724. end;
  3725.  
  3726. procedure TMainForm.gTorrentsQuickSearch(Sender: TVarGrid; var SearchText: string; var ARow: integer);
  3727. var
  3728.   i: integer;
  3729.   s: string;
  3730.   v: variant;
  3731. begin
  3732.   s:=UTF8UpperCase(SearchText);
  3733.   for i:=ARow to gTorrents.Items.Count - 1 do begin
  3734.     v:=gTorrents.Items[idxName, i];
  3735.     if VarIsEmpty(v) or VarIsNull(v) then
  3736.       continue;
  3737.     if Pos(s, Trim(UTF8UpperCase(UTF8Encode(widestring(v))))) > 0 then begin
  3738.       ARow:=i;
  3739.       break;
  3740.     end;
  3741.   end;
  3742. end;
  3743.  
  3744. procedure TMainForm.gTorrentsResize(Sender: TObject);
  3745. begin
  3746.   if not FStarted then begin
  3747.     VSplitter.SetSplitterPosition(Ini.ReadInteger('MainForm', 'VSplitter', VSplitter.GetSplitterPosition));
  3748.     HSplitter.SetSplitterPosition(Ini.ReadInteger('MainForm', 'HSplitter', HSplitter.GetSplitterPosition));
  3749.   end;
  3750. end;
  3751.  
  3752. procedure TMainForm.gTorrentsSetEditText(Sender: TObject; ACol, ARow: Integer; const Value: string);
  3753. begin
  3754.   if RenameTorrent(gTorrents.Items[idxTorrentId, ARow], UTF8Encode(widestring(gTorrents.Items[idxName, ARow])), Trim(Value)) then begin
  3755.     gTorrents.Items[idxName, ARow]:=UTF8Decode(Trim(Value));
  3756.     FFilesTree.Clear;
  3757.   end;
  3758. end;
  3759.  
  3760. procedure TMainForm.gTorrentsSortColumn(Sender: TVarGrid; var ASortCol: integer);
  3761. begin
  3762.   if ASortCol = idxSeeds then
  3763.     ASortCol:=idxSeedsTotal;
  3764.   if ASortCol = idxPeers then
  3765.     ASortCol:=idxLeechersTotal;
  3766. end;
  3767.  
  3768. procedure TMainForm.HSplitterChangeBounds(Sender: TObject);
  3769. begin
  3770. {$ifdef windows}
  3771.   Update;
  3772. {$endif windows}
  3773. end;
  3774.  
  3775. procedure TMainForm.lvFilesDblClick(Sender: TObject);
  3776. begin
  3777.   acOpenFile.Execute;
  3778. end;
  3779.  
  3780. procedure TMainForm.lvFilesEditorHide(Sender: TObject);
  3781. begin
  3782.   gTorrents.Tag:=0;
  3783.   lvFiles.Tag:=0;
  3784.   lvFiles.HideSelection:=True;
  3785. end;
  3786.  
  3787. procedure TMainForm.lvFilesEditorShow(Sender: TObject);
  3788. begin
  3789.   gTorrents.Tag:=1;
  3790.   lvFiles.Tag:=1;
  3791.   lvFiles.RemoveSelection;
  3792.   lvFiles.HideSelection:=False;
  3793. end;
  3794.  
  3795. procedure TMainForm.lvFilesSetEditText(Sender: TObject; ACol, ARow: Integer; const Value: string);
  3796. var
  3797.   p: string;
  3798.   i, lvl, len: integer;
  3799. begin
  3800.   p:=FFilesTree.GetFullPath(ARow, False);
  3801.   if RenameTorrent(gTorrents.Items[idxTorrentId, gTorrents.Row], p, Trim(Value)) then begin
  3802.     FFiles[idxFileName, ARow]:=UTF8Decode(Trim(Value));
  3803.     if FFilesTree.IsFolder(ARow) then begin
  3804.       // Updating path for child elements
  3805.       len:=Length(p);
  3806.       p:=ExtractFilePath(p) + Trim(Value);
  3807.       lvl:=FFilesTree.RowLevel[ARow];
  3808.       FFiles.BeginUpdate;
  3809.       try
  3810.         FFiles[idxFileFullPath, ARow]:=UTF8Decode(p + RemotePathDelimiter);
  3811.         for i:=ARow + 1 to FFiles.Count - 1 do
  3812.           if FFilesTree.RowLevel[i] > lvl then
  3813.             FFiles[idxFileFullPath, i]:=UTF8Decode(p + Copy(UTF8Encode(widestring(FFiles[idxFileFullPath, i])), len + 1, MaxInt))
  3814.           else
  3815.             break;
  3816.       finally
  3817.         FFiles.EndUpdate;
  3818.       end;
  3819.     end;
  3820.   end;
  3821. end;
  3822.  
  3823. procedure TMainForm.lvFilterCellAttributes(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; var CellAttribs: TCellAttributes);
  3824. begin
  3825.   if ARow < 0 then exit;
  3826.   with CellAttribs do begin
  3827.     case ARow of
  3828.       0: ImageIndex:=imgAll;
  3829.       1: ImageIndex:=imgDown;
  3830.       2: ImageIndex:=imgSeed;
  3831.       3: ImageIndex:=imgActive;
  3832.       4: ImageIndex:=15;
  3833.       5: ImageIndex:=imgStopped;
  3834.       6: ImageIndex:=imgError;
  3835.       else
  3836.         if Text <> '' then
  3837.           if VarIsNull(Sender.Items[-1, ARow]) then
  3838.             ImageIndex:=5
  3839.           else
  3840.             ImageIndex:=22;
  3841.     end;
  3842.   end;
  3843. end;
  3844.  
  3845. procedure TMainForm.lvFilterClick(Sender: TObject);
  3846. begin
  3847.   if VarIsNull(lvFilter.Items[0, lvFilter.Row]) then
  3848.     if (FLastFilerIndex > lvFilter.Row) or (lvFilter.Row = lvFilter.Items.Count - 1) then
  3849.       lvFilter.Row:=lvFilter.Row - 1
  3850.     else
  3851.       lvFilter.Row:=lvFilter.Row + 1;
  3852.   FLastFilerIndex:=lvFilter.Row;
  3853.   FilterTimer.Enabled:=False;
  3854.   FilterTimer.Enabled:=True;
  3855. end;
  3856.  
  3857. procedure TMainForm.lvFilterDrawCell(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; const R: TRect;
  3858.   var ADefaultDrawing: boolean);
  3859. var
  3860.   i: integer;
  3861.   RR: TRect;
  3862. begin
  3863.   ADefaultDrawing:=not VarIsNull(Sender.Items[0, ARow]);
  3864.   if ADefaultDrawing then exit;
  3865.  
  3866.   with lvFilter.Canvas do begin
  3867.     Brush.Color:=lvFilter.Color;
  3868.     FillRect(R);
  3869.     i:=(R.Bottom + R.Top) div 2;
  3870.     Brush.Color:=clBtnFace;
  3871.     RR:=R;
  3872.     InflateRect(RR, -4, 0);
  3873.     RR.Top:=i - 1;
  3874.     RR.Bottom:=i + 1;
  3875.     FillRect(RR);
  3876.   end;
  3877. end;
  3878.  
  3879. procedure TMainForm.lvPeersCellAttributes(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; var CellAttribs: TCellAttributes);
  3880. var
  3881.   i: integer;
  3882. begin
  3883.   if ARow < 0 then exit;
  3884.   with CellAttribs do begin
  3885.     if Text = '' then exit;
  3886.     if ACol = 0 then begin
  3887.       ImageIndex:=Sender.Items[idxPeerCountryImage, ARow];
  3888.       if ImageIndex = 0 then
  3889.         ImageIndex:=-1;
  3890.     end;
  3891.     case ADataCol of
  3892.       idxPeerDone:
  3893.         Text:=Format('%.1f%%', [double(Sender.Items[ADataCol, ARow])*100.0]);
  3894.       idxPeerDownSpeed, idxPeerUpSpeed:
  3895.         begin
  3896.           i:=Sender.Items[ADataCol, ARow];
  3897.           if i > 0 then
  3898.             Text:=GetHumanSize(i, 1) + sPerSecond
  3899.           else
  3900.             Text:='';
  3901.         end;
  3902.     end;
  3903.   end;
  3904. end;
  3905.  
  3906. procedure TMainForm.lvTrackersCellAttributes(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; var CellAttribs: TCellAttributes);
  3907. var
  3908.   f: double;
  3909. begin
  3910.   if ARow < 0 then exit;
  3911.   with CellAttribs do begin
  3912.     if Text = '' then exit;
  3913.     case ADataCol of
  3914.       idxTrackersListSeeds:
  3915.         if lvTrackers.Items[ADataCol, ARow] < 0 then
  3916.           Text:='';
  3917.       idxTrackersListUpdateIn:
  3918.         begin
  3919.           f:=double(lvTrackers.Items[ADataCol, ARow]);
  3920.           if f = 0 then
  3921.             Text:='-'
  3922.           else
  3923.           if f = 1 then
  3924.             Text:=sUpdating
  3925.           else
  3926.             Text:=SecondsToString(Trunc(f));
  3927.         end;
  3928.     end;
  3929.   end;
  3930. end;
  3931.  
  3932. procedure TMainForm.lvTrackersDblClick(Sender: TObject);
  3933. begin
  3934.   acEditTracker.Execute;
  3935. end;
  3936.  
  3937. procedure TMainForm.lvTrackersKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  3938. begin
  3939.   if Key = VK_DELETE then begin
  3940.     Key:=0;
  3941.     acDelTracker.Execute;
  3942.   end;
  3943. end;
  3944.  
  3945. procedure TMainForm.miDonateClick(Sender: TObject);
  3946. begin
  3947.   GoDonate;
  3948. end;
  3949.  
  3950. procedure TMainForm.miHomePageClick(Sender: TObject);
  3951. begin
  3952.   GoHomePage;
  3953. end;
  3954.  
  3955. procedure TMainForm.PageInfoResize(Sender: TObject);
  3956. begin
  3957.   if FDetailsWait.Visible then
  3958.     CenterDetailsWait;
  3959. end;
  3960.  
  3961. procedure TMainForm.panReconnectResize(Sender: TObject);
  3962. begin
  3963.   panReconnectFrame.BoundsRect:=panReconnect.ClientRect;
  3964. end;
  3965.  
  3966. procedure TMainForm.pbDownloadedPaint(Sender: TObject);
  3967. begin
  3968.   if FTorrentProgress <> nil then
  3969.     pbDownloaded.Canvas.StretchDraw(pbDownloaded.ClientRect, FTorrentProgress);
  3970. end;
  3971.  
  3972. procedure TMainForm.StatusBarMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  3973. var
  3974.   pt: TPoint;
  3975.   rb: boolean;
  3976. begin
  3977.   rb:=(Button = mbRight) and RpcObj.Connected;
  3978.   pt:=StatusBar.ClientToScreen(Point(X, Y));
  3979.   case StatusBar.GetPanelIndexAt(X, Y) of
  3980.     0: if Button = mbLeft then
  3981.          acConnOptions.Execute;
  3982.     1: if rb then
  3983.          pmDownSpeeds.PopUp(pt.X, pt.Y);
  3984.     2: if rb then
  3985.          pmUpSpeeds.PopUp(pt.X, pt.Y);
  3986.   end;
  3987. end;
  3988.  
  3989. {$ifdef LCLcarbon}
  3990. type
  3991.   THackApplication = class(TApplication)
  3992.   end;
  3993. {$endif LCLcarbon}
  3994.  
  3995. procedure TMainForm.TickTimerTimer(Sender: TObject);
  3996. var
  3997.   i: integer;
  3998. begin
  3999.   TickTimer.Enabled:=False;
  4000.   try
  4001.     if not FStarted then begin
  4002.       Application.ProcessMessages;
  4003.       FStarted:=True;
  4004.       acConnect.Execute;
  4005.       Application.ProcessMessages;
  4006.       panTransfer.ChildSizing.Layout:=cclLeftToRightThenTopToBottom;
  4007.       panGeneralInfo.ChildSizing.Layout:=cclLeftToRightThenTopToBottom;
  4008.       panTransfer.ChildSizing.Layout:=cclNone;
  4009.       panGeneralInfo.ChildSizing.Layout:=cclNone;
  4010.       with panTransfer do
  4011.         ClientHeight:=Controls[ControlCount - 1].BoundsRect.Bottom + ChildSizing.TopBottomSpacing;
  4012.       with panGeneralInfo do
  4013.         ClientHeight:=Controls[ControlCount - 1].BoundsRect.Bottom + ChildSizing.TopBottomSpacing;
  4014.       panSearch.AutoSize:=False;
  4015.  
  4016.       if Ini.ReadBool('MainForm', 'FirstRun', True) then begin
  4017.         if not acResolveCountry.Checked then
  4018.           acResolveCountry.Execute;
  4019.         if acResolveCountry.Checked and not acShowCountryFlag.Checked then
  4020.           acShowCountryFlag.Execute;
  4021.         Ini.WriteBool('MainForm', 'FirstRun', False);
  4022.       end;
  4023.  
  4024.       i:=Ini.ReadInteger('Interface', 'LastNewVersionCheck', Trunc(Now));
  4025.       if i + Ini.ReadInteger('Interface', 'CheckNewVersionDays', 5) <= Trunc(Now) then begin
  4026.         if Ini.ReadBool('Interface', 'AskCheckNewVersion', True) then begin
  4027.           Ini.WriteBool('Interface', 'AskCheckNewVersion', False);
  4028.           if not Ini.ReadBool('Interface', 'CheckNewVersion', False) then
  4029.             if MessageDlg(Format(SCheckNewVersion, [AppName]), mtConfirmation, mbYesNo, 0) = mrYes then
  4030.               Ini.WriteBool('Interface', 'CheckNewVersion', True);
  4031.         end;
  4032.         if Ini.ReadBool('Interface', 'CheckNewVersion', False) then
  4033.           CheckNewVersion;
  4034.       end;
  4035.     end;
  4036.  
  4037.     CheckAddTorrents;
  4038.  
  4039.     if RpcObj.Connected then
  4040.       FReconnectTimeOut:=0
  4041.     else
  4042.       if panReconnect.Visible then
  4043.         if Now - FReconnectWaitStart >= FReconnectTimeOut/SecsPerDay then
  4044.           DoConnect
  4045.         else
  4046.           txReconnectSecs.Caption:=Format(sReconnect, [FReconnectTimeOut - Round(SecsPerDay*(Now - FReconnectWaitStart))]);
  4047.  
  4048.     if FSlowResponse.Visible then begin
  4049.       if RpcObj.RequestStartTime = 0 then
  4050.         FSlowResponse.Visible:=False;
  4051.     end
  4052.     else
  4053.       if (RpcObj.RequestStartTime <> 0) and (Now - RpcObj.RequestStartTime >= 1/SecsPerDay) then
  4054.         FSlowResponse.Visible:=True;
  4055.  
  4056.     if FDetailsWait.Visible then begin
  4057.       if (FDetailsWaitStart = 0) or not (rtDetails in RpcObj.RefreshNow) then begin
  4058.         FDetailsWaitStart:=0;
  4059.         FDetailsWait.Visible:=False;
  4060.         panDetailsWait.Visible:=False;
  4061.       end;
  4062.     end
  4063.     else
  4064.       if (FDetailsWaitStart <> 0) and (Now - FDetailsWaitStart >= 300/MSecsPerDay) then begin
  4065.         CenterDetailsWait;
  4066.         FDetailsWait.Visible:=True;
  4067.         panDetailsWait.Visible:=True;
  4068.         panDetailsWait.BringToFront;
  4069.       end;
  4070.  
  4071. {$ifdef LCLcarbon}
  4072.      THackApplication(Application).ProcessAsyncCallQueue;
  4073.      if Active and (WindowState <> wsMinimized) then begin
  4074.        if not FFormActive then begin
  4075.          FFormActive:=True;
  4076.          CheckClipboardLink;
  4077.        end;
  4078.      end
  4079.      else
  4080.        FFormActive:=False;
  4081. {$endif LCLcarbon}
  4082.   finally
  4083.     TickTimer.Enabled:=True;
  4084.   end;
  4085. end;
  4086.  
  4087. procedure TMainForm.FilterTimerTimer(Sender: TObject);
  4088. begin
  4089.   FilterTimer.Enabled:=False;
  4090.   FFilterChanged:=True;
  4091.   DoRefresh(True);
  4092. end;
  4093.  
  4094. procedure TMainForm.FormClose(Sender: TObject; var CloseAction: TCloseAction);
  4095. begin
  4096.   if Ini.ReadBool('Interface', 'TrayClose', False) then begin
  4097. {$ifdef darwin}
  4098.     CloseAction:=caMinimize;
  4099. {$else}
  4100. {$ifdef linux}
  4101.     if IsUnity then
  4102.       CloseAction:=caMinimize
  4103.     else
  4104. {$endif linux}
  4105.     begin
  4106.       CloseAction:=caHide;
  4107.       HideApp;
  4108.       UpdateTray;
  4109.     end;
  4110. {$endif darwin}
  4111.     exit;
  4112.   end;
  4113.   BeforeCloseApp;
  4114. end;
  4115.  
  4116. procedure TMainForm.PageInfoChange(Sender: TObject);
  4117. begin
  4118.   if PageInfo.ActivePage.Tag <> 0 then
  4119.     FDetailsWaitStart:=Now;
  4120.   RpcObj.Lock;
  4121.   try
  4122.     RpcObj.AdvInfo:=GetPageInfoType(PageInfo.ActivePage);
  4123.     DoRefresh;
  4124.   finally
  4125.     RpcObj.Unlock;
  4126.   end;
  4127. end;
  4128.  
  4129. procedure TMainForm.TorrentsListTimerTimer(Sender: TObject);
  4130. begin
  4131.   TorrentsListTimer.Enabled:=False;
  4132.   if RpcObj.CurTorrentId <> 0 then
  4133.     FDetailsWaitStart:=Now;
  4134.   DoRefresh;
  4135. end;
  4136.  
  4137. procedure TMainForm.pmFilesPopup(Sender: TObject);
  4138. begin
  4139.   UpdateUI;
  4140. end;
  4141.  
  4142. procedure TMainForm.pmTorrentsPopup(Sender: TObject);
  4143. begin
  4144.   UpdateUI;
  4145. end;
  4146.  
  4147. procedure TMainForm.TrayIconDblClick(Sender: TObject);
  4148. begin
  4149. {$ifndef darwin}
  4150.   acShowApp.Execute;
  4151. {$endif darwin}
  4152. end;
  4153.  
  4154. procedure TMainForm.VSplitterChangeBounds(Sender: TObject);
  4155. begin
  4156. {$ifdef windows}
  4157.   Update;
  4158. {$endif windows}
  4159. end;
  4160.  
  4161. procedure TMainForm.UrlLabelClick(Sender: TObject);
  4162. begin
  4163.   AppBusy;
  4164.   OpenURL((Sender as TLabel).Caption);
  4165.   AppNormal;
  4166. end;
  4167.  
  4168. procedure TMainForm.CenterReconnectWindow;
  4169. begin
  4170.   CenterOnParent(panReconnect);
  4171. end;
  4172.  
  4173. function TMainForm.DoConnect: boolean;
  4174. var
  4175.   Sec, pwd: string;
  4176.   i, j: integer;
  4177. begin
  4178.   Result:=True;
  4179.   panReconnect.Hide;
  4180.   DoDisconnect;
  4181.   Sec:='Connection.' + FCurConn;
  4182.   if not Ini.SectionExists(Sec) then
  4183.     Sec:='Connection';
  4184.  
  4185.   i:=FPasswords.IndexOfName(FCurConn);
  4186.   pwd:=Ini.ReadString(Sec, 'Password', '');
  4187.   if pwd = '-' then begin
  4188.     if i >= 0 then
  4189.       pwd:=FPasswords.ValueFromIndex[i]
  4190.     else begin
  4191.       pwd:='';
  4192.       if not InputQuery(Format(SConnectTo, [FCurConn]), Format(SEnterPassword, [FCurConn]), pwd) then begin
  4193.         RpcObj.Url:='-';
  4194.         Result:=False;
  4195.         exit;
  4196.       end;
  4197.     end;
  4198.   end
  4199.   else
  4200.     pwd:=DecodeBase64(pwd);
  4201.   if i >= 0 then
  4202.     FPasswords.Delete(i);
  4203.  
  4204.   if Ini.ReadBool(Sec, 'UseSSL', False) then begin
  4205.     RpcObj.InitSSL;
  4206.     if not IsSSLloaded then begin
  4207.       MessageDlg(Format(sSSLLoadError, [DLLSSLName, DLLUtilName]), mtError, [mbOK], 0);
  4208.       exit;
  4209.     end;
  4210.     RpcObj.Url:='https';
  4211.   end
  4212.   else
  4213.     RpcObj.Url:='http';
  4214.   RpcObj.Http.UserName:=Ini.ReadString(Sec, 'UserName', '');
  4215.   RpcObj.Http.Password:=pwd;
  4216.   RpcObj.Http.ProxyHost:='';
  4217.   RpcObj.Http.ProxyPort:='';
  4218.   RpcObj.Http.ProxyUser:='';
  4219.   RpcObj.Http.ProxyPass:='';
  4220.   RpcObj.Http.Sock.SocksIP:='';
  4221.   RpcObj.Http.Sock.SocksPort:='';
  4222.   RpcObj.Http.Sock.SocksUsername:='';
  4223.   RpcObj.Http.Sock.SocksPassword:='';
  4224.   if Ini.ReadBool(Sec, 'UseProxy', False) then begin
  4225.     if Ini.ReadBool(Sec, 'UseSockProxy', False) then begin
  4226.       RpcObj.Http.Sock.SocksIP := Ini.ReadString(Sec, 'ProxyHost', '');
  4227.       RpcObj.Http.Sock.SocksPort := IntToStr(Ini.ReadInteger(Sec, 'ProxyPort', 8080));
  4228.       RpcObj.Http.Sock.SocksUsername := Ini.ReadString(Sec, 'ProxyUser', '');
  4229.       RpcObj.Http.Sock.SocksPassword := DecodeBase64(Ini.ReadString(Sec, 'ProxyPass', ''));
  4230.     end
  4231.     else begin
  4232.       RpcObj.Http.ProxyHost:=Ini.ReadString(Sec, 'ProxyHost', '');
  4233.       RpcObj.Http.ProxyPort:=IntToStr(Ini.ReadInteger(Sec, 'ProxyPort', 8080));
  4234.       RpcObj.Http.ProxyUser:=Ini.ReadString(Sec, 'ProxyUser', '');
  4235.       RpcObj.Http.ProxyPass:=DecodeBase64(Ini.ReadString(Sec, 'ProxyPass', ''));
  4236.     end;
  4237.   end;
  4238.   RpcObj.RpcPath:=Ini.ReadString(Sec, 'RpcPath', '');
  4239.   RpcObj.Url:=Format('%s://%s:%d', [RpcObj.Url, Ini.ReadString(Sec, 'Host', ''), Ini.ReadInteger(Sec, 'Port', 9091)]);
  4240.   SetRefreshInterval;
  4241.   RpcObj.InfoStatus:=sConnectingToDaemon;
  4242.   CheckStatus;
  4243.   TrayIcon.Hint:=RpcObj.InfoStatus;
  4244.   RpcObj.Connect;
  4245.   FPathMap.Text:=StringReplace(Ini.ReadString(Sec, 'PathMap', ''), '|', LineEnding, [rfReplaceAll]);
  4246.   i:=0;
  4247.   while i < FPathMap.Count do
  4248.     if Trim(FPathMap.ValueFromIndex[i]) = '' then
  4249.       FPathMap.Delete(i)
  4250.     else
  4251.       Inc(i);
  4252.  
  4253.   Ini.WriteString('Hosts', 'CurHost', FCurConn);
  4254.   if FCurConn <> Ini.ReadString('Hosts', 'Host1', '') then begin
  4255.     Ini.WriteString('Hosts', 'Host1', FCurConn);
  4256.     j:=2;
  4257.     for i:=0 to pmConnections.Items.Count - 1 do
  4258.       with pmConnections.Items[i] do
  4259.         if (Tag = 0) and (Caption <> FCurConn) then begin
  4260.           Ini.WriteString('Hosts', Format('Host%d', [j]), Caption);
  4261.           Inc(j);
  4262.         end;
  4263.     Ini.UpdateFile;
  4264.     UpdateConnections;
  4265.   end
  4266.   else
  4267.     if pmConnections.Items[0].Tag = 0 then begin
  4268.       pmConnections.Items[0].Checked:=True;
  4269.       miConnect.Items[0].Checked:=True;
  4270.     end;
  4271. end;
  4272.  
  4273. procedure TMainForm.DoDisconnect;
  4274. var
  4275.   i: integer;
  4276. begin
  4277.   TorrentsListTimer.Enabled:=False;
  4278.   FilterTimer.Enabled:=False;
  4279.   ClearDetailsInfo;
  4280.   gTorrents.Items.Clear;
  4281.   gTorrents.Enabled:=False;
  4282.   gTorrents.Color:=clBtnFace;
  4283.   lvPeers.Enabled:=False;
  4284.   lvPeers.Color:=gTorrents.Color;
  4285.   lvFiles.Enabled:=False;
  4286.   lvFiles.Color:=gTorrents.Color;
  4287.   lvTrackers.Enabled:=False;
  4288.   lvTrackers.Color:=gTorrents.Color;
  4289.  
  4290.   lvFilter.Enabled:=False;
  4291.   lvFilter.Color:=gTorrents.Color;
  4292.   with lvFilter do begin
  4293.     Items[0, 0]:=UTF8Decode(SAll);
  4294.     Items[0, 1]:=UTF8Decode(SDownloading);
  4295.     Items[0, 2]:=UTF8Decode(SCompleted);
  4296.     Items[0, 3]:=UTF8Decode(SActive);
  4297.     Items[0, 4]:=UTF8Decode(SInactive);
  4298.     Items[0, 5]:=UTF8Decode(sStopped);
  4299.     Items[0, 6]:=UTF8Decode(sErrorState);
  4300.   end;
  4301.   edSearch.Enabled:=False;
  4302.   edSearch.Color:=gTorrents.Color;
  4303.   edSearch.Text:='';
  4304.  
  4305.   with gStats do begin
  4306.     BeginUpdate;
  4307.     try
  4308.       for i:=0 to Items.Count - 1 do begin
  4309.         Items[1, i]:=NULL;
  4310.         Items[2, i]:=NULL;
  4311.       end;
  4312.     finally
  4313.       EndUpdate;
  4314.     end;
  4315.     Enabled:=False;
  4316.     Color:=gTorrents.Color;
  4317.   end;
  4318.  
  4319.   RpcObj.Disconnect;
  4320.  
  4321.   RpcObj.InfoStatus:=sDisconnected;
  4322.   CheckStatus;
  4323.   UpdateUI;
  4324.   TrayIcon.Hint:=RpcObj.InfoStatus;
  4325.   gTorrents.Items.RowCnt:=0;
  4326.   FTorrents.RowCnt:=0;
  4327.   lvFilter.Row:=0;
  4328.   lvFilter.Items.RowCnt:=StatusFiltersCount;
  4329.   TorrentsListTimer.Enabled:=False;
  4330.   FilterTimer.Enabled:=False;
  4331.   pmConnections.Items[0].Checked:=False;
  4332.   miConnect.Items[0].Checked:=False;
  4333.   FCurDownSpeedLimit:=-2;
  4334.   FCurUpSpeedLimit:=-2;
  4335.   FillSpeedsMenu;
  4336. end;
  4337.  
  4338. procedure TMainForm.ClearDetailsInfo(Skip: TAdvInfoType);
  4339.  
  4340.   procedure ClearChildren(AParent: TPanel);
  4341.   var
  4342.     i: integer;
  4343.   begin
  4344.     AParent.AutoSize:=False;
  4345.     AParent.ChildSizing.Layout:=cclNone;
  4346.     for i:=0 to AParent.ControlCount - 1 do begin
  4347.       if AParent.Controls[i] is TLabel then
  4348.         with AParent.Controls[i] as TLabel do begin
  4349.           if (Length(Name) < 5) or (Copy(Name, Length(Name) - 4, 5) <> 'Label') then
  4350.             Caption:='';
  4351.           PopupMenu:=pmLabels;
  4352.         end;
  4353.     end;
  4354.   end;
  4355.  
  4356. var
  4357.   i, t: integer;
  4358. begin
  4359.   if RpcObj.CurTorrentId = 0 then begin
  4360.     Skip:=aiNone;
  4361.     t:=0;
  4362.   end
  4363.   else
  4364.     t:=1;
  4365.   FDetailsWaitStart:=0;
  4366.   if Skip <> aiFiles then begin
  4367.     FFiles.Clear;
  4368.     tabFiles.Caption:=FFilesCapt;
  4369.   end;
  4370.   if Skip <> aiPeers then
  4371.     lvPeers.Items.Clear;
  4372.   if Skip <> aiTrackers then
  4373.     lvTrackers.Items.Clear;
  4374.   if Skip <> aiGeneral then begin
  4375.     ClearChildren(panGeneralInfo);
  4376.     ClearChildren(panTransfer);
  4377.     ProcessPieces('', 0, 0);
  4378.     txDownProgress.AutoSize:=False;
  4379.     txDownProgress.Caption:='';
  4380.   end;
  4381.   for i:=0 to PageInfo.PageCount - 1 do
  4382.     PageInfo.Pages[i].Tag:=t;
  4383. end;
  4384.  
  4385. function TMainForm.SelectRemoteFolder(const CurFolder, DialogTitle: string): string;
  4386. var
  4387.   i, j: integer;
  4388.   s, ss, sss, fn: string;
  4389.   dlg: TSelectDirectoryDialog;
  4390.   d: char;
  4391. begin
  4392.   Result:='';
  4393.   if Trim(FPathMap.Text) = '' then begin
  4394.     MessageDlg(sNoPathMapping, mtInformation, [mbOK], 0);
  4395.     exit;
  4396.   end;
  4397.   s:=MapRemoteToLocal(CurFolder);
  4398.   if (s = '') or not DirectoryExistsUTF8(s) then
  4399.     s:=FPathMap.ValueFromIndex[0];
  4400.  
  4401.   if not DirectoryExistsUTF8(s) then begin
  4402.     MessageDlg(sNoPathMapping, mtInformation, [mbOK], 0);
  4403.     exit;
  4404.   end;
  4405.  
  4406.   dlg:=TSelectDirectoryDialog.Create(nil);
  4407.   try
  4408.     dlg.Title:=DialogTitle;
  4409.     dlg.InitialDir:=s;
  4410.     if not dlg.Execute then
  4411.       exit;
  4412.  
  4413.     fn:=dlg.FileName;
  4414.     for i:=0 to FPathMap.Count - 1 do begin
  4415.       s:=FPathMap[i];
  4416.       j:=Pos('=', s);
  4417.       if j > 0 then begin
  4418.         ss:=FixSeparators(Copy(s, j + 1, MaxInt));
  4419.         sss:=IncludeTrailingPathDelimiter(ss);
  4420.         if (CompareFilePath(ss, fn) = 0) or (CompareFilePath(sss, Copy(fn, 1, Length(sss))) = 0) then begin
  4421.           Result:=Copy(s, 1, j - 1);
  4422.           d:='/';
  4423.           for j:=1 to Length(Result) do
  4424.             if Result[j] in ['/','\'] then begin
  4425.               d:=Result[j];
  4426.               break;
  4427.             end;
  4428.           if CompareFilePath(ss, fn) <> 0 then begin
  4429.             if (Result <> '') and (Copy(Result, Length(Result), 1) <> d) then
  4430.               Result:=Result + d;
  4431.             ss:=IncludeProperTrailingPathDelimiter(ss);
  4432.             Result:=Result + Copy(fn, Length(ss) + 1, MaxInt);
  4433.           end;
  4434.  
  4435.           Result:=StringReplace(Result, DirectorySeparator, d, [rfReplaceAll]);
  4436.           if Copy(Result, Length(Result), 1) = d then
  4437.             SetLength(Result, Length(Result) - 1);
  4438.         end;
  4439.       end;
  4440.     end;
  4441.   finally
  4442.     dlg.Free;
  4443.   end;
  4444.   if Result = '' then
  4445.     MessageDlg(sNoPathMapping, mtError, [mbOK], 0);
  4446. end;
  4447.  
  4448. procedure TMainForm.ConnectionSettingsChanged(const ActiveConnection: string; ForceReconnect: boolean);
  4449. begin
  4450.   UpdateConnections;
  4451.   if (FCurConn <> ActiveConnection) or ForceReconnect then begin
  4452.     DoDisconnect;
  4453.     FReconnectTimeOut:=-1;
  4454.     FCurConn:=ActiveConnection;
  4455.     if FCurConn <> '' then
  4456.       DoConnect;
  4457.   end;
  4458. end;
  4459.  
  4460. procedure TMainForm.UpdateUI;
  4461. var
  4462.   e: boolean;
  4463. begin
  4464.   e:=((Screen.ActiveForm = Self) or not Visible or (WindowState = wsMinimized))
  4465.      and not gTorrents.EditorMode and not lvFiles.EditorMode;
  4466.   acConnect.Enabled:=e;
  4467.   acOptions.Enabled:=e;
  4468.   acConnOptions.Enabled:=e;
  4469.   e:=RpcObj.Connected and e;
  4470.   acDisconnect.Enabled:=e;
  4471.   acSelectAll.Enabled:=e;
  4472.   acAddTorrent.Enabled:=e;
  4473.   acAddLink.Enabled:=e;
  4474.   acDaemonOptions.Enabled:=e;
  4475.   acStartAllTorrents.Enabled:=e and RpcObj.Connected;
  4476.   acStopAllTorrents.Enabled:=acStartAllTorrents.Enabled;
  4477.   acStartTorrent.Enabled:=e and (gTorrents.Items.Count > 0);
  4478.   acForceStartTorrent.Enabled:=acStartTorrent.Enabled and (RpcObj.RPCVersion >= 14);
  4479.   acStopTorrent.Enabled:=e and (gTorrents.Items.Count > 0);
  4480.   acVerifyTorrent.Enabled:=e and (gTorrents.Items.Count > 0);
  4481.   acRemoveTorrent.Enabled:=e and (gTorrents.Items.Count > 0) and not edSearch.Focused;
  4482.   acRemoveTorrentAndData.Enabled:=acRemoveTorrent.Enabled and (RpcObj.RPCVersion >= 4);
  4483.   acReannounceTorrent.Enabled:=acVerifyTorrent.Enabled and (RpcObj.RPCVersion >= 5);
  4484.   acMoveTorrent.Enabled:=acVerifyTorrent.Enabled and (RpcObj.RPCVersion >= 6);
  4485.   acTorrentProps.Enabled:=acVerifyTorrent.Enabled;
  4486.   acOpenContainingFolder.Enabled:=acTorrentProps.Enabled and (RpcObj.RPCVersion >= 4);
  4487.   pmiPriority.Enabled:=e and (gTorrents.Items.Count > 0);
  4488.   miPriority.Enabled:=pmiPriority.Enabled;
  4489.   acSetHighPriority.Enabled:=e and (gTorrents.Items.Count > 0) and
  4490.                       ( ( not lvFiles.Focused and (RpcObj.RPCVersion >= 5) ) or
  4491.                         ((lvFiles.Items.Count > 0) and (PageInfo.ActivePage = tabFiles)) );
  4492.   acSetNormalPriority.Enabled:=acSetHighPriority.Enabled;
  4493.   acSetLowPriority.Enabled:=acSetHighPriority.Enabled;
  4494.   miQueue.Enabled:=e and (gTorrents.Items.Count > 0) and (RpcObj.RPCVersion >= 14);
  4495.   pmiQueue.Enabled:=miQueue.Enabled;
  4496.   acQMoveTop.Enabled:=miQueue.Enabled;
  4497.   acQMoveUp.Enabled:=miQueue.Enabled;
  4498.   acQMoveDown.Enabled:=miQueue.Enabled;
  4499.   acQMoveBottom.Enabled:=miQueue.Enabled;
  4500.   acOpenFile.Enabled:=acSetHighPriority.Enabled and (lvFiles.SelCount < 2) and (RpcObj.RPCVersion >= 4);
  4501.   acCopyPath.Enabled:=acOpenFile.Enabled;
  4502.   acSetNotDownload.Enabled:=acSetHighPriority.Enabled;
  4503.   acRename.Enabled:=(RpcObj.RPCVersion >= 15) and acSetHighPriority.Enabled;
  4504.   acSetupColumns.Enabled:=e;
  4505.   acUpdateBlocklist.Enabled:=(acUpdateBlocklist.Tag <> 0) and e and (RpcObj.RPCVersion >= 5);
  4506.   acAddTracker.Enabled:=acTorrentProps.Enabled and (RpcObj.RPCVersion >= 10);
  4507.   acAdvEditTrackers.Enabled:=acAddTracker.Enabled;
  4508.   acEditTracker.Enabled:=acAddTracker.Enabled and (lvTrackers.Items.Count > 0);
  4509.   acDelTracker.Enabled:=acEditTracker.Enabled;
  4510.   acAltSpeed.Enabled:=e and (RpcObj.RPCVersion >= 5);
  4511.   pmiDownSpeedLimit.Enabled:=RpcObj.Connected;
  4512.   pmiUpSpeedLimit.Enabled:=RpcObj.Connected;
  4513. end;
  4514.  
  4515. procedure TMainForm.ShowConnOptions(NewConnection: boolean);
  4516. var
  4517.   frm: TConnOptionsForm;
  4518. begin
  4519.   AppBusy;
  4520.   frm:=TConnOptionsForm.Create(Self);
  4521.   with frm do
  4522.   try
  4523.     ActiveConnection:=FCurConn;
  4524.     if NewConnection then begin
  4525.       Caption:=SNewConnection;
  4526.       btNewClick(nil);
  4527.       if Ini.ReadInteger('Hosts', 'Count', 0) = 0 then begin
  4528.         panTop.Visible:=False;
  4529. {$ifdef LCLgtk2}
  4530.         panTop.Height:=0;
  4531. {$endif LCLgtk2}
  4532.         with Page.BorderSpacing do
  4533.           Top:=Left;
  4534.         tabPaths.TabVisible:=False;
  4535.         tabMisc.TabVisible:=False;
  4536.       end
  4537.       else begin
  4538.         btNew.Hide;
  4539.         btRename.Hide;
  4540.         btDel.Hide;
  4541.         panTop.ClientHeight:=btNew.Top;
  4542.       end;
  4543.       cbShowAdvancedClick(nil);
  4544.       AutoSizeForm(frm);
  4545.     end;
  4546.     AppNormal;
  4547.     ShowModal;
  4548.     ConnectionSettingsChanged(ActiveConnection, ActiveSettingChanged);
  4549.   finally
  4550.     Free;
  4551.   end;
  4552. end;
  4553.  
  4554. procedure TMainForm.SaveColumns(LV: TVarGrid; const AName: string; FullInfo: boolean);
  4555. var
  4556.   i: integer;
  4557. begin
  4558.   for i:=0 to LV.Columns.Count - 1 do
  4559.     with LV.Columns[i] do begin
  4560.       Ini.WriteInteger(AName, Format('Id%d', [i]), ID - 1);
  4561.       Ini.WriteInteger(AName, Format('Width%d', [i]), Width);
  4562.       if FullInfo then begin
  4563.         Ini.WriteInteger(AName, Format('Index%d', [i]), Index);
  4564.         Ini.WriteBool(AName, Format('Visible%d', [i]), Visible);
  4565.       end;
  4566.     end;
  4567.   if LV.SortColumn >= 0 then begin
  4568.     Ini.WriteInteger(AName, 'SortColumn', LV.SortColumn);
  4569.     Ini.WriteInteger(AName, 'SortOrder', integer(LV.SortOrder));
  4570.   end;
  4571. end;
  4572.  
  4573. procedure TMainForm.LoadColumns(LV: TVarGrid; const AName: string; FullInfo: boolean);
  4574. var
  4575.   i, j, ColId: integer;
  4576. begin
  4577.   LV.Columns.BeginUpdate;
  4578.   try
  4579.     for i:=0 to LV.Columns.Count - 1 do begin
  4580.       ColId:=Ini.ReadInteger(AName, Format('Id%d', [i]), -1);
  4581.       if ColId = -1 then continue;
  4582.       for j:=0 to LV.Columns.Count - 1 do
  4583.         with LV.Columns[j] do
  4584.           if ID - 1 = ColId then begin
  4585.             if FullInfo then begin
  4586.               Index:=Ini.ReadInteger(AName, Format('Index%d', [i]), Index);
  4587.               Visible:=Ini.ReadBool(AName, Format('Visible%d', [i]), Visible);
  4588.             end;
  4589.             Width:=Ini.ReadInteger(AName, Format('Width%d', [i]), Width);
  4590.             break;
  4591.           end;
  4592.     end;
  4593.   finally
  4594.     LV.Columns.EndUpdate;
  4595.   end;
  4596.   LV.SortColumn:=Ini.ReadInteger(AName, 'SortColumn', LV.SortColumn);
  4597.   LV.SortOrder:=TSortOrder(Ini.ReadInteger(AName, 'SortOrder', integer(LV.SortOrder)));
  4598. end;
  4599.  
  4600. function TMainForm.GetTorrentError(t: TJSONObject; Status: integer): string;
  4601. var
  4602.   i: integer;
  4603.   stats: TJSONArray;
  4604.   err, gerr: widestring;
  4605.   NoTrackerError: boolean;
  4606. begin
  4607.   Result:='';
  4608.   gerr:=t.Strings['errorString'];
  4609.   if RpcObj.RPCVersion >= 7 then begin
  4610.     NoTrackerError:=False;
  4611.     stats:=t.Arrays['trackerStats'];
  4612.     for i:=0 to stats.Count - 1 do
  4613.       with stats.Objects[i] do begin
  4614.         err:='';
  4615.         if Booleans['hasAnnounced'] and not Booleans['lastAnnounceSucceeded'] then
  4616.           err:=Strings['lastAnnounceResult'];
  4617.         if err = 'Success' then
  4618.           err:='';
  4619.         if err = '' then begin
  4620.           // If at least one tracker is working, then report no error
  4621.           NoTrackerError:=True;
  4622.           Result:='';
  4623.         end
  4624.         else begin
  4625.           if not NoTrackerError and (Result = '') then
  4626.             Result:=sTrackerError + ': ' + UTF8Encode(err);
  4627.           // Workaround for transmission bug
  4628.           // If the global error string is equal to some tracker error string,
  4629.           // then igonore the global error string
  4630.           if gerr = err then
  4631.             gerr:='';
  4632.         end;
  4633.       end;
  4634.   end
  4635.   else begin
  4636.     Result:=UTF8Encode(t.Strings['announceResponse']);
  4637.     if Result = 'Success' then
  4638.       Result:=''
  4639.     else
  4640.       if Result <> '' then begin
  4641.         i:=Pos('(', Result);
  4642.         if i <> 0 then
  4643.           if Copy(Result, i, 5) = '(200)' then
  4644.             Result:=''
  4645.           else
  4646.             Result:=sTrackerError + ': ' + Copy(Result, 1, i - 1);
  4647.       end;
  4648.   end;
  4649.  
  4650.   if (Result = '') or (Status = TR_STATUS_STOPPED) or (Status = TR_STATUS_FINISHED) then
  4651.     Result:=UTF8Encode(gerr);
  4652. end;
  4653.  
  4654. function TMainForm.SecondsToString(j: integer): string;
  4655. begin
  4656.   if j < 60 then
  4657.     Result:=Format(sSecs, [j])
  4658.   else
  4659.   if j < 60*60 then begin
  4660.     Result:=Format(sMins, [j div 60]);
  4661.     j:=j mod 60;
  4662.     if j > 0 then
  4663.       Result:=Format('%s, %s', [Result, Format(sSecs, [j])]);
  4664.   end
  4665.   else begin
  4666.     j:=(j + 30) div 60;
  4667.     if j < 60*24 then begin
  4668.       Result:=Format(sHours, [j div 60]);
  4669.       j:=j mod 60;
  4670.       if j > 0 then
  4671.         Result:=Format('%s, %s', [Result, Format(sMins, [j])]);
  4672.     end
  4673.     else begin
  4674.       j:=(j + 30) div 60;
  4675.       Result:=Format(sDays, [j div 24]);
  4676.       j:=j mod 24;
  4677.       if j > 0 then
  4678.         Result:=Format('%s, %s', [Result, Format(sHours, [j])]);
  4679.     end;
  4680.   end;
  4681. end;
  4682.  
  4683. procedure TMainForm.FillTorrentsList(list: TJSONArray);
  4684. var
  4685.   i, j, row, crow, id, StateImg: integer;
  4686.   t: TJSONObject;
  4687.   f: double;
  4688.   ExistingRow: boolean;
  4689.   s, ss: string;
  4690.  
  4691.   function GetTorrentValue(AIndex: integer; const AName: string; AType: integer): boolean;
  4692.   var
  4693.     res: variant;
  4694.     i: integer;
  4695.   begin
  4696.     i:=t.IndexOfName(AName);
  4697.     Result:=i >= 0;
  4698.     if Result then
  4699.       case AType of
  4700.         vtInteger:
  4701.           res:=t.Items[i].AsInteger;
  4702.         vtExtended:
  4703.           res:=t.Items[i].AsFloat;
  4704.         else
  4705.           res:=t.Items[i].AsString;
  4706.       end
  4707.     else
  4708.       res:=NULL;
  4709.  
  4710.     FTorrents[AIndex, row]:=res;
  4711.   end;
  4712.  
  4713.   function StoreSpeed(var History: variant; Speed: integer): integer;
  4714.   var
  4715.     j, cnt: integer;
  4716.     p: PInteger;
  4717.     IsNew: boolean;
  4718.     res: Int64;
  4719.   begin
  4720.     IsNew:=VarIsEmpty(History);
  4721.     if IsNew then begin
  4722.       if Speed = 0 then begin
  4723.         Result:=0;
  4724.         exit;
  4725.       end;
  4726.       History:=VarArrayCreate([0, SpeedHistorySize], varInteger);
  4727.     end;
  4728.     p:=VarArrayLock(History);
  4729.     try
  4730.       if IsNew then begin
  4731.         for j:=1 to SpeedHistorySize do
  4732.           p[j]:=-1;
  4733.         j:=1;
  4734.       end
  4735.       else begin
  4736.         j:=Round((Now - cardinal(p[0])/SecsPerDay)/RpcObj.RefreshInterval);
  4737.         if j = 0 then
  4738.           j:=1;
  4739.       end;
  4740.       p[0]:=integer(cardinal(Round(Now*SecsPerDay)));
  4741.       // Shift speed array
  4742.       if j < SpeedHistorySize then
  4743.         Move(p[1], p[j + 1], (SpeedHistorySize - j)*SizeOf(integer))
  4744.       else
  4745.         j:=SpeedHistorySize;
  4746.  
  4747.       while j > 0 do begin
  4748.         p[j]:=Speed;
  4749.         Dec(j);
  4750.       end;
  4751.       // Calc average speed
  4752.       res:=Speed;
  4753.       cnt:=1;
  4754.       for j:=2 to SpeedHistorySize do
  4755.         if p[j] < 0 then
  4756.           break
  4757.         else begin
  4758.           Inc(res, p[j]);
  4759.           Inc(cnt);
  4760.         end;
  4761.  
  4762.       Result:=res div cnt;
  4763.     finally
  4764.       VarArrayUnlock(History);
  4765.     end;
  4766.     if Result = 0 then
  4767.       VarClear(History);
  4768.   end;
  4769.  
  4770. var
  4771.   FilterIdx, OldId: integer;
  4772.   TrackerFilter, PathFilter: string;
  4773.   UpSpeed, DownSpeed: double;
  4774.   DownCnt, SeedCnt, CompletedCnt, ActiveCnt, StoppedCnt, ErrorCnt: integer;
  4775.   IsActive: boolean;
  4776.   Paths: TStringList;
  4777.   v: variant;
  4778.   FieldExists: array of boolean;
  4779. begin
  4780.   if gTorrents.Tag <> 0 then exit;
  4781.   if list = nil then begin
  4782.     ClearDetailsInfo;
  4783.     exit;
  4784.   end;
  4785. {
  4786.   for i:=1 to 1000 do begin
  4787.     t:=TJSONObject.Create;
  4788.     t.Integers['id']:=i + 10000;
  4789.     t.Strings['name']:=Format('ZName %d', [i]);
  4790.     t.Integers['status']:=TR_STATUS_STOPPED;
  4791.     t.Arrays['trackerStats']:=TJSONArray.Create;
  4792.     t.Floats['sizeWhenDone']:=0;
  4793.     t.Floats['leftUntilDone']:=0;
  4794.     t.Integers['rateDownload']:=0;
  4795.     t.Integers['rateUpload']:=0;
  4796.     list.Add(t);
  4797.   end;
  4798. }
  4799.   Paths:=TStringList.Create;
  4800.   try
  4801.   Paths.Sorted:=True;
  4802.   OldId:=RpcObj.CurTorrentId;
  4803.   IsActive:=gTorrents.Enabled;
  4804.   gTorrents.Enabled:=True;
  4805.   lvFilter.Enabled:=True;
  4806.   gTorrents.Color:=clWindow;
  4807.   lvFilter.Color:=clWindow;
  4808.   edSearch.Enabled:=True;
  4809.   edSearch.Color:=clWindow;
  4810.   if not IsActive then
  4811.     ActiveControl:=gTorrents;
  4812.  
  4813.   for i:=0 to FTrackers.Count - 1 do
  4814.     FTrackers.Objects[i]:=nil;
  4815.  
  4816.   // Check fields' existence
  4817.   SetLength(FieldExists, FTorrents.ColCnt);
  4818.   if list.Count > 0 then begin
  4819.     t:=list[0] as TJSONObject;
  4820.     FieldExists[idxName]:=t.IndexOfName('name') >= 0;
  4821.     FieldExists[idxRatio]:=t.IndexOfName('uploadRatio') >= 0;
  4822.     FieldExists[idxTracker]:=t.IndexOfName('trackers') >= 0;
  4823.     FieldExists[idxPath]:=t.IndexOfName('downloadDir') >= 0;
  4824.     FieldExists[idxPriority]:=t.IndexOfName('bandwidthPriority') >= 0;
  4825.     FieldExists[idxQueuePos]:=t.IndexOfName('queuePosition') >= 0;
  4826.     FieldExists[idxSeedingTime]:=t.IndexOfName('secondsSeeding') >= 0;
  4827.   end;
  4828.  
  4829.   UpSpeed:=0;
  4830.   DownSpeed:=0;
  4831.   DownCnt:=0;
  4832.   SeedCnt:=0;
  4833.   CompletedCnt:=0;
  4834.   ActiveCnt:=0;
  4835.   StoppedCnt:=0;
  4836.   ErrorCnt:=0;
  4837.  
  4838.   FilterIdx:=lvFilter.Row;
  4839.   if VarIsNull(lvFilter.Items[0, FilterIdx]) then
  4840.     Dec(FilterIdx);
  4841.   if FilterIdx >= StatusFiltersCount then
  4842.     if not VarIsNull(lvFilter.Items[-1, FilterIdx]) then begin
  4843.       PathFilter:=UTF8Encode(widestring(lvFilter.Items[-1, FilterIdx]));
  4844.       FilterIdx:=fltAll;
  4845.     end
  4846.     else begin
  4847.       TrackerFilter:=UTF8Encode(widestring(lvFilter.Items[0, FilterIdx]));
  4848.       FilterIdx:=fltAll;
  4849.       i:=RPos('(', TrackerFilter);
  4850.       if i > 0 then
  4851.         TrackerFilter:=Trim(Copy(TrackerFilter, 1, i - 1));
  4852.     end;
  4853.  
  4854.   for i:=0 to FTorrents.Count - 1 do
  4855.     FTorrents[idxTag, i]:=0;
  4856.  
  4857.   for i:=0 to list.Count - 1 do begin
  4858.     StateImg:=-1;
  4859.  
  4860.     t:=list[i] as TJSONObject;
  4861.     id:=t.Integers['id'];
  4862.     ExistingRow:=FTorrents.Find(idxTorrentId, id, row);
  4863.     if not ExistingRow then
  4864.       FTorrents.InsertRow(row);
  4865.  
  4866.     FTorrents[idxTorrentId, row]:=t.Integers['id'];
  4867.  
  4868.     if FieldExists[idxName] then
  4869.       FTorrents[idxName, row]:=t.Strings['name'];
  4870.  
  4871.     j:=t.Integers['status'];
  4872.     if ExistingRow and (j = TR_STATUS_SEED) and (FTorrents[idxStatus, row] = TR_STATUS_DOWNLOAD) then
  4873.       DownloadFinished(UTF8Encode(widestring(FTorrents[idxName, row])));
  4874.     FTorrents[idxStatus, row]:=j;
  4875.     if j = TR_STATUS_CHECK_WAIT  then StateImg:=imgDownQueue else
  4876.     if j = TR_STATUS_CHECK  then StateImg:=imgDownQueue else
  4877.     if j = TR_STATUS_DOWNLOAD_WAIT  then StateImg:=imgDownQueue else
  4878.     if j = TR_STATUS_DOWNLOAD  then StateImg:=imgDown else
  4879.     if j = TR_STATUS_SEED_WAIT  then StateImg:=imgSeedQueue else
  4880.     if j = TR_STATUS_SEED  then StateImg:=imgSeed else
  4881.     if j = TR_STATUS_STOPPED  then StateImg:=imgDone;
  4882.  
  4883.     if GetTorrentError(t, j) <> '' then
  4884.       if t.Strings['errorString'] <> '' then
  4885.         StateImg:=imgError
  4886.       else
  4887.         if StateImg in [imgDown,imgSeed] then
  4888.           Inc(StateImg, 2);
  4889.  
  4890.     if j <> TR_STATUS_STOPPED then begin
  4891.       s:=GetTorrentError(t, j);
  4892.       if s <> '' then
  4893.         if t.Strings['errorString'] <> '' then
  4894.           StateImg:=imgError
  4895.         else
  4896.           if StateImg in [imgDown,imgSeed] then
  4897.             Inc(StateImg, 2);
  4898.  
  4899.       if RpcObj.RPCVersion >= 7 then begin
  4900.         s:='';
  4901.         if t.Arrays['trackerStats'].Count > 0 then
  4902.           with t.Arrays['trackerStats'].Objects[0] do begin
  4903.             if integer(Integers['announceState']) in [2, 3] then
  4904.               s:=sTrackerUpdating
  4905.             else
  4906.               if Booleans['hasAnnounced'] then
  4907.                 if Booleans['lastAnnounceSucceeded'] then
  4908.                   s:=sTrackerWorking
  4909.                 else
  4910.                   s:=TranslateString(UTF8Encode(Strings['lastAnnounceResult']), True);
  4911.  
  4912.             if s = 'Success' then
  4913.               s:=sTrackerWorking;
  4914.           end;
  4915.       end
  4916.       else
  4917.         s:=t.Strings['announceResponse'];
  4918.     end
  4919.     else
  4920.       s:='';
  4921.     FTorrents[idxTrackerStatus, row]:=UTF8Decode(s);
  4922.  
  4923.     if FTorrents[idxStatus, row] = TR_STATUS_CHECK then
  4924.       f:=t.Floats['recheckProgress']*100.0
  4925.     else begin
  4926.       f:=t.Floats['sizeWhenDone'];
  4927.       if f <> 0 then
  4928.         f:=(f - t.Floats['leftUntilDone'])*100.0/f;
  4929.       if StateImg = imgDone then
  4930.         if (t.Floats['leftUntilDone'] <> 0) or (t.Floats['sizeWhenDone'] = 0) then
  4931.           StateImg:=imgStopped
  4932.         else
  4933.           FTorrents[idxStatus, row]:=TR_STATUS_FINISHED;
  4934.     end;
  4935.     if f < 0 then
  4936.       f:=0;
  4937.     FTorrents[idxDone, row]:=Int(f*10.0)/10.0;
  4938.     FTorrents[idxStateImg, row]:=StateImg;
  4939.     GetTorrentValue(idxDownSpeed, 'rateDownload', vtInteger);
  4940.     j:=StoreSpeed(FTorrents.ItemPtrs[idxDownSpeedHistory, row]^, FTorrents[idxDownSpeed, row]);
  4941.     if FCalcAvg and (StateImg in [imgDown, imgDownError]) then
  4942.       FTorrents[idxDownSpeed, row]:=j;
  4943.     GetTorrentValue(idxUpSpeed, 'rateUpload', vtInteger);
  4944.     j:=StoreSpeed(FTorrents.ItemPtrs[idxUpSpeedHistory, row]^, FTorrents[idxUpSpeed, row]);
  4945.     if FCalcAvg and (StateImg in [imgSeed, imgSeedError]) then
  4946.       FTorrents[idxUpSpeed, row]:=j;
  4947.  
  4948.     GetTorrentValue(idxSize, 'totalSize', vtExtended);
  4949.     GetTorrentValue(idxSizeToDowload, 'sizeWhenDone', vtExtended);
  4950.     GetTorrentValue(idxSeeds, 'peersSendingToUs', vtInteger);
  4951.     GetTorrentValue(idxPeers, 'peersGettingFromUs', vtInteger);
  4952.     GetTorrentValue(idxETA, 'eta', vtInteger);
  4953.     v:=FTorrents[idxETA, row];
  4954.     if not VarIsNull(v) then
  4955.       if v < 0 then
  4956.         FTorrents[idxETA, row]:=MaxInt
  4957.       else begin
  4958.         f:=FTorrents[idxDownSpeed, row];
  4959.         if f > 0 then
  4960.           FTorrents[idxETA, row]:=Round(t.Floats['leftUntilDone']/f);
  4961.       end;
  4962.     GetTorrentValue(idxDownloaded, 'downloadedEver', vtExtended);
  4963.     GetTorrentValue(idxUploaded, 'uploadedEver', vtExtended);
  4964.     GetTorrentValue(idxAddedOn, 'addedDate', vtExtended);
  4965.     GetTorrentValue(idxCompletedOn, 'doneDate', vtExtended);
  4966.     GetTorrentValue(idxLastActive, 'activityDate', vtExtended);
  4967.  
  4968.     if RpcObj.RPCVersion >= 7 then begin
  4969.       if t.Arrays['trackerStats'].Count > 0 then
  4970.         with t.Arrays['trackerStats'].Objects[0] do begin
  4971.           FTorrents[idxSeedsTotal, row]:=Integers['seederCount'];
  4972.           FTorrents[idxLeechersTotal, row]:=Integers['leecherCount'];
  4973.         end
  4974.       else begin
  4975.         FTorrents[idxSeedsTotal, row]:=-1;
  4976.         FTorrents[idxLeechersTotal, row]:=-1;
  4977.       end;
  4978.       if t.Floats['metadataPercentComplete'] <> 1.0 then begin
  4979.         FTorrents[idxSize, row]:=-1;
  4980.         FTorrents[idxSizeToDowload, row]:=-1;
  4981.       end;
  4982.     end
  4983.     else begin
  4984.       GetTorrentValue(idxSeedsTotal, 'seeders', vtInteger);
  4985.       GetTorrentValue(idxLeechersTotal, 'leechers', vtInteger);
  4986.     end;
  4987.     if FieldExists[idxRatio] then begin
  4988.       f:=t.Floats['uploadRatio'];
  4989.       if f = -2 then
  4990.         f:=MaxInt;
  4991.       FTorrents[idxRatio, row]:=f;
  4992.     end
  4993.     else
  4994.       FTorrents[idxRatio, row]:=NULL;
  4995.     if FieldExists[idxSeedingTime] then
  4996.       FTorrents[idxSeedingTime, row]:=t.Integers['secondsSeeding']
  4997.     else
  4998.       FTorrents[idxSeedingTime, row]:=NULL;
  4999.  
  5000.     if RpcObj.RPCVersion >= 7 then begin
  5001.       if t.Arrays['trackerStats'].Count > 0 then
  5002.         s:=t.Arrays['trackerStats'].Objects[0].Strings['announce']
  5003.       else
  5004.         s:=sNoTracker;
  5005.     end
  5006.     else
  5007.       if FieldExists[idxTracker] then
  5008.         s:=UTF8Encode(t.Arrays['trackers'].Objects[0].Strings['announce'])
  5009.       else begin
  5010.         s:='';
  5011.         if VarIsEmpty(FTorrents[idxTracker, row]) then
  5012.           RpcObj.RequestFullInfo:=True;
  5013.       end;
  5014.  
  5015.     if s <> '' then begin
  5016.       j:=Pos('://', s);
  5017.       if j > 0 then
  5018.         s:=Copy(s, j + 3, MaxInt);
  5019.       j:=Pos('/', s);
  5020.       if j > 0 then
  5021.         s:=Copy(s, 1, j - 1);
  5022.       j:=Pos('.', s);
  5023.       if j > 0 then begin
  5024.         ss:=Copy(s, 1, j - 1);
  5025.         if AnsiCompareText(ss, 'bt') = 0 then
  5026.           System.Delete(s, 1, 3)
  5027.         else
  5028.           if (Length(ss) = 3) and (AnsiCompareText(Copy(ss, 1, 2), 'bt') = 0) and (ss[3] in ['1'..'9']) then
  5029.             System.Delete(s, 1, 4);
  5030.       end;
  5031.       j:=Pos(':', s);
  5032.       if j > 0 then
  5033.         System.Delete(s, j, MaxInt);
  5034.       FTorrents[idxTracker, row]:=UTF8Decode(s);
  5035.     end;
  5036.  
  5037.     if FieldExists[idxPath] then
  5038.       FTorrents[idxPath, row]:=UTF8Decode(ExcludeTrailingPathDelimiter(UTF8Encode(t.Strings['downloadDir'])))
  5039.     else
  5040.       if VarIsEmpty(FTorrents[idxPath, row]) then
  5041.         RpcObj.RequestFullInfo:=True;
  5042.  
  5043.     if not VarIsEmpty(FTorrents[idxPath, row]) then begin
  5044.       s:=UTF8Encode(widestring(FTorrents[idxPath, row]));
  5045.       j:=Paths.IndexOf(s);
  5046.       if j < 0 then
  5047.         Paths.AddObject(s, TObject(1))
  5048.       else
  5049.         Paths.Objects[j]:=TObject(PtrInt(Paths.Objects[j]) + 1);
  5050.     end;
  5051.  
  5052.     if FieldExists[idxPriority] then
  5053.       FTorrents[idxPriority, row]:=t.Integers['bandwidthPriority'];
  5054.  
  5055.     if FieldExists[idxQueuePos] then begin
  5056.       j:=t.Integers['queuePosition'];
  5057.       if FTorrents[idxStatus, row] = TR_STATUS_FINISHED then
  5058.         Inc(j, FinishedQueue);
  5059.       FTorrents[idxQueuePos, row]:=j;
  5060.     end;
  5061.  
  5062.     DownSpeed:=DownSpeed + FTorrents[idxDownSpeed, row];
  5063.     UpSpeed:=UpSpeed + FTorrents[idxUpSpeed, row];
  5064.  
  5065.     FTorrents[idxTag, row]:=1;
  5066.   end;
  5067.  
  5068.   i:=0;
  5069.   while i < FTorrents.Count do
  5070.     if FTorrents[idxTag, i] = 0 then
  5071.       FTorrents.Delete(i)
  5072.     else
  5073.       Inc(i);
  5074.  
  5075.   gTorrents.Items.BeginUpdate;
  5076.   try
  5077.     for i:=0 to gTorrents.Items.Count - 1 do
  5078.       gTorrents.Items[idxTag, i]:=0;
  5079.  
  5080.     gTorrents.Items.Sort(idxTorrentId);
  5081.  
  5082.     for i:=0 to FTorrents.Count - 1 do begin
  5083.       IsActive:=(FTorrents[idxDownSpeed, i] <> 0) or (FTorrents[idxUpSpeed, i] <> 0);
  5084.       if IsActive then
  5085.         Inc(ActiveCnt);
  5086.  
  5087.       j:=FTorrents[idxStatus, i];
  5088.       if j = TR_STATUS_DOWNLOAD then
  5089.         Inc(DownCnt)
  5090.       else
  5091.       if j = TR_STATUS_SEED then begin
  5092.         Inc(SeedCnt);
  5093.         Inc(CompletedCnt);
  5094.       end
  5095.       else
  5096.       if j = TR_STATUS_FINISHED then
  5097.         Inc(CompletedCnt);
  5098.  
  5099.       StateImg:=FTorrents[idxStateImg, i];
  5100.       if StateImg in [imgStopped, imgDone] then
  5101.         Inc(StoppedCnt)
  5102.       else
  5103.         if StateImg in [imgDownError, imgSeedError, imgError] then
  5104.           Inc(ErrorCnt);
  5105.  
  5106.       if not VarIsEmpty(FTorrents[idxTracker, i]) then begin
  5107.         s:=UTF8Encode(widestring(FTorrents[idxTracker, i]));
  5108.         j:=FTrackers.IndexOf(s);
  5109.         if j < 0 then
  5110.           j:=FTrackers.Add(s);
  5111.         FTrackers.Objects[j]:=TObject(ptruint(FTrackers.Objects[j]) + 1);
  5112.         if (TrackerFilter <> '') and (TrackerFilter <> s) then
  5113.           continue;
  5114.       end;
  5115.  
  5116.       if (PathFilter <> '') and not VarIsEmpty(FTorrents[idxPath, i]) and (UTF8Decode(PathFilter) <> FTorrents[idxPath, i]) then
  5117.         continue;
  5118.  
  5119.       case FilterIdx of
  5120.         fltActive:
  5121.           if not IsActive then
  5122.             continue;
  5123.         fltInactive:
  5124.           if IsActive then
  5125.             continue;
  5126.         fltDown:
  5127.           if FTorrents[idxStatus, i] <> TR_STATUS_DOWNLOAD then
  5128.             continue;
  5129.         fltDone:
  5130.           if (StateImg <> imgDone) and (FTorrents[idxStatus, i] <> TR_STATUS_SEED) then
  5131.             continue;
  5132.         fltStopped:
  5133.           if not (StateImg in [imgStopped, imgDone]) then
  5134.             continue;
  5135.         fltError:
  5136.           if not (StateImg in [imgDownError, imgSeedError, imgError]) then
  5137.             continue;
  5138.       end;
  5139.  
  5140.       if edSearch.Text <> '' then
  5141.         if UTF8Pos(UTF8UpperCase(edSearch.Text), UTF8UpperCase(UTF8Encode(widestring(FTorrents[idxName, i])))) = 0 then
  5142.           continue;
  5143.  
  5144.       if not gTorrents.Items.Find(idxTorrentId, FTorrents[idxTorrentId, i], row) then
  5145.         gTorrents.Items.InsertRow(row);
  5146.       for j:=-TorrentsExtraColumns to FTorrents.ColCnt - 1 do
  5147.         if (j <> idxDownSpeedHistory) and (j <> idxUpSpeedHistory) then
  5148.           gTorrents.Items[j, row]:=FTorrents[j, i];
  5149.       gTorrents.Items[idxTag, row]:=1;
  5150.     end;
  5151.  
  5152.     i:=0;
  5153.     while i < gTorrents.Items.Count do
  5154.       if gTorrents.Items[idxTag, i] = 0 then
  5155.         gTorrents.Items.Delete(i)
  5156.       else
  5157.         Inc(i);
  5158.  
  5159.     gTorrents.Sort;
  5160.     if gTorrents.Items.Count > 0 then begin
  5161.       if OldId <> 0 then begin
  5162.         i:=gTorrents.Items.IndexOf(idxTorrentId, OldId);
  5163.         if i >= 0 then
  5164.           gTorrents.Row:=i
  5165.         else
  5166.           if FFilterChanged then
  5167.             OldId:=0;
  5168.       end;
  5169.       if OldId = 0 then
  5170.         gTorrents.Row:=0;
  5171.     end;
  5172.     FFilterChanged:=False;
  5173.   finally
  5174.     gTorrents.Items.EndUpdate;
  5175.   end;
  5176.   gTorrentsClick(nil);
  5177.  
  5178.   crow:=-1;
  5179.   lvFilter.Items.BeginUpdate;
  5180.   try
  5181.     lvFilter.Items[0, 0]:=UTF8Decode(Format('%s (%d)', [SAll, list.Count]));
  5182.     lvFilter.Items[0, 1]:=UTF8Decode(Format('%s (%d)', [SDownloading, DownCnt]));
  5183.     lvFilter.Items[0, 2]:=UTF8Decode(Format('%s (%d)', [SCompleted, CompletedCnt]));
  5184.     lvFilter.Items[0, 3]:=UTF8Decode(Format('%s (%d)', [SActive, ActiveCnt]));
  5185.     lvFilter.Items[0, 4]:=UTF8Decode(Format('%s (%d)', [SInactive, FTorrents.Count - ActiveCnt]));
  5186.     lvFilter.Items[0, 5]:=UTF8Decode(Format('%s (%d)', [sStopped, StoppedCnt]));
  5187.     lvFilter.Items[0, 6]:=UTF8Decode(Format('%s (%d)', [sErrorState, ErrorCnt]));
  5188.  
  5189.     j:=StatusFiltersCount;
  5190.  
  5191.     if acFolderGrouping.Checked then begin
  5192.       lvFilter.Items[0, j]:=NULL;
  5193.       Inc(j);
  5194.  
  5195.       for i:=0 to Paths.Count - 1 do begin
  5196.         s:=ExtractFileName(Paths[i]);
  5197.         for row:=StatusFiltersCount + 1 to j - 1 do
  5198.           if ExtractFileName(UTF8Encode(widestring(lvFilter.Items[-1, row]))) = s then begin
  5199.             s:=Paths[i];
  5200.             lvFilter.Items[0, row]:=UTF8Decode(Format('%s (%d)', [UTF8Encode(widestring(lvFilter.Items[-1, row])), ptruint(Paths.Objects[row - StatusFiltersCount - 1])]));
  5201.           end;
  5202.         lvFilter.Items[0, j]:=UTF8Decode(Format('%s (%d)', [s, ptruint(Paths.Objects[i])]));
  5203.         lvFilter.Items[-1, j]:=UTF8Decode(Paths[i]);
  5204.         if Paths[i] = PathFilter then
  5205.           crow:=j;
  5206.         Inc(j);
  5207.       end;
  5208.     end;
  5209.  
  5210.     row:=j;
  5211.  
  5212.     if acTrackerGrouping.Checked then begin
  5213.       if not VarIsNull(lvFilter.Items[0, row - 1]) then begin
  5214.         lvFilter.Items[0, row]:=NULL;
  5215.         Inc(row);
  5216.       end;
  5217.  
  5218.       i:=0;
  5219.       while i < FTrackers.Count do begin
  5220.         j:=ptruint(FTrackers.Objects[i]);
  5221.         if j > 0 then begin
  5222.           lvFilter.Items[0, row]:=UTF8Decode(Format('%s (%d)', [FTrackers[i], j]));
  5223.           lvFilter.Items[-1, row]:=NULL;
  5224.           if FTrackers[i] = TrackerFilter then
  5225.             crow:=row;
  5226.           Inc(i);
  5227.           Inc(row);
  5228.         end
  5229.         else
  5230.           FTrackers.Delete(i);
  5231.       end;
  5232.     end;
  5233.  
  5234.     lvFilter.Items.RowCnt:=row;
  5235.   finally
  5236.     lvFilter.Items.EndUpdate;
  5237.   end;
  5238.   if crow >= 0 then
  5239.     lvFilter.Row:=crow
  5240.   else
  5241.     if lvFilter.Row >= StatusFiltersCount then
  5242.       lvFilterClick(nil);
  5243.  
  5244.   CheckStatus;
  5245.  
  5246.   StatusBar.Panels[1].Text:=Format(sDownSpeed, [GetHumanSize(DownSpeed, 1)]);
  5247.   StatusBar.Panels[2].Text:=Format(sUpSpeed, [GetHumanSize(UpSpeed, 1)]);
  5248.  
  5249. {$ifndef LCLcarbon}
  5250.   // There is memory leak in TTrayIcon implementation for Mac.
  5251.   // Disable tray icon update for Mac.
  5252.   TrayIcon.Hint:=Format(sDownloadingSeeding,
  5253.         [RpcObj.InfoStatus, LineEnding, DownCnt, SeedCnt, LineEnding, StatusBar.Panels[1].Text, StatusBar.Panels[2].Text]);
  5254. {$endif LCLcarbon}
  5255.   finally
  5256.     Paths.Free;
  5257.   end;
  5258.   DetailsUpdated;
  5259. end;
  5260.  
  5261. procedure TMainForm.FillPeersList(list: TJSONArray);
  5262. var
  5263.   i, j, row: integer;
  5264.   port: integer;
  5265.   d: TJSONData;
  5266.   p: TJSONObject;
  5267.   ip, s: string;
  5268.   hostinfo: PHostEntry;
  5269.   opt: TResolverOptions;
  5270.   WasEmpty: boolean;
  5271. begin
  5272.   if list = nil then begin
  5273.     ClearDetailsInfo;
  5274.     exit;
  5275.   end;
  5276.   WasEmpty:=lvPeers.Items.Count = 0;
  5277.   lvPeers.Items.BeginUpdate;
  5278.   try
  5279.     lvPeers.Enabled:=True;
  5280.     lvPeers.Color:=clWindow;
  5281.     if FResolver = nil then begin
  5282.       opt:=[];
  5283.       if acResolveHost.Checked then
  5284.         Include(opt, roResolveIP);
  5285.       if acResolveCountry.Checked then
  5286.         Include(opt, roResolveCountry);
  5287.       if opt <> [] then
  5288.         FResolver:=TIpResolver.Create(GetGeoIpDatabase, opt);
  5289.     end;
  5290.  
  5291.     for i:=0 to lvPeers.Items.Count - 1 do
  5292.       lvPeers.Items[idxPeerTag, i]:=0;
  5293.  
  5294.     lvPeers.Items.Sort(idxPeerIP);
  5295.     for i:=0 to list.Count - 1 do begin
  5296.       d:=list[i];
  5297.       if not (d is TJSONObject) then continue;
  5298.       p:=d as TJSONObject;
  5299.       ip:=p.Strings['address'];
  5300.       if p.IndexOfName('port') >= 0 then
  5301.         port:=p.Integers['port']
  5302.       else
  5303.         port:=0;
  5304.  
  5305.       s:=ip + ':' + IntToStr(port);
  5306.       if not lvPeers.Items.Find(idxPeerIP, s, row) then
  5307.         lvPeers.Items.InsertRow(row);
  5308.       lvPeers.Items[idxPeerIP, row]:=s;
  5309.       lvPeers.Items[idxPeerPort, row]:=port;
  5310.  
  5311.       if FResolver <> nil then
  5312.         hostinfo:=FResolver.Resolve(ip)
  5313.       else
  5314.         hostinfo:=nil;
  5315.       if hostinfo <> nil then
  5316.         lvPeers.Items[idxPeerHost, row]:=hostinfo^.HostName
  5317.       else
  5318.         lvPeers.Items[idxPeerHost, row]:=ip;
  5319.  
  5320.       if hostinfo <> nil then
  5321.         lvPeers.Items[idxPeerCountry, row]:=hostinfo^.CountryName
  5322.       else
  5323.         lvPeers.Items[idxPeerCountry, row]:='';
  5324.  
  5325.       if acShowCountryFlag.Checked and (hostinfo <> nil) then begin
  5326.         if hostinfo^.ImageIndex = 0 then
  5327.           hostinfo^.ImageIndex:=GetFlagImage(hostinfo^.CountryCode);
  5328.         j:=hostinfo^.ImageIndex
  5329.       end
  5330.       else
  5331.         j:=0;
  5332.       lvPeers.Items[idxPeerCountryImage, row]:=j;
  5333.       lvPeers.Items[idxPeerClient, row]:=p.Strings['clientName'];
  5334.       lvPeers.Items[idxPeerFlags, row]:=p.Strings['flagStr'];
  5335.       lvPeers.Items[idxPeerDone, row]:=p.Floats['progress'];
  5336.  
  5337.       if p.IndexOfName('rateToClient') >= 0 then
  5338.         lvPeers.Items[idxPeerDownSpeed, row]:=p.Integers['rateToClient'];
  5339.       if p.IndexOfName('rateToPeer') >= 0 then
  5340.         lvPeers.Items[idxPeerUpSpeed, row]:=p.Integers['rateToPeer'];
  5341.  
  5342.       lvPeers.Items[idxPeerTag, row]:=1;
  5343.     end;
  5344.  
  5345.     i:=0;
  5346.     while i < lvPeers.Items.Count do
  5347.       if lvPeers.Items[idxPeerTag, i] = 0 then
  5348.         lvPeers.Items.Delete(i)
  5349.       else
  5350.         Inc(i);
  5351.     lvPeers.Sort;
  5352.     if WasEmpty and (lvPeers.Items.Count > 0) then
  5353.       lvPeers.Row:=0;
  5354.   finally
  5355.     lvPeers.Items.EndUpdate;
  5356.   end;
  5357.   DetailsUpdated;
  5358. end;
  5359.  
  5360. function TMainForm.GetFilesCommonPath(files: TJSONArray): string;
  5361. var
  5362.   i: integer;
  5363.   d: TJSONData;
  5364.   f: TJSONObject;
  5365.   s: string;
  5366. begin
  5367.   Result:='';
  5368.   for i:=0 to files.Count - 1 do begin
  5369.     d:=files[i];
  5370.     if not (d is TJSONObject) then continue;
  5371.     f:=d as TJSONObject;
  5372.     s:=UTF8Encode(f.Strings['name']);
  5373.     if i = 0 then
  5374.       Result:=ExtractFilePath(s)
  5375.     else begin
  5376.       while True do begin
  5377.         if Result = '' then
  5378.           exit;
  5379.         if Copy(s, 1, Length(Result)) <> Result then begin
  5380.           SetLength(Result, Length(Result) - 1);
  5381.           Result:=ExtractFilePath(Result);
  5382.         end
  5383.         else
  5384.           break;
  5385.       end;
  5386.     end;
  5387.   end;
  5388. end;
  5389.  
  5390. procedure TMainForm.InternalRemoveTorrent(const Msg, MsgMulti: string; RemoveLocalData: boolean);
  5391. var
  5392.   args: TJSONObject;
  5393.   ids: variant;
  5394.   s: string;
  5395.   i, j, id: integer;
  5396. begin
  5397.   if gTorrents.Items.Count = 0 then exit;
  5398.   gTorrents.Tag:=1;
  5399.   try
  5400.     gTorrents.EnsureSelectionVisible;
  5401.     ids:=GetSelectedTorrents;
  5402.     if gTorrents.SelCount < 2 then
  5403.       s:=Format(Msg, [UTF8Encode(widestring(gTorrents.Items[idxName, gTorrents.Items.IndexOf(idxTorrentId, ids[0])]))])
  5404.     else
  5405.       s:=Format(MsgMulti, [gTorrents.SelCount]);
  5406.     if MessageDlg('', s, mtConfirmation, mbYesNo, 0, mbNo) <> mrYes then exit;
  5407.   finally
  5408.     gTorrents.Tag:=0;
  5409.   end;
  5410.   args:=TJSONObject.Create;
  5411.   if RemoveLocalData then
  5412.     args.Add('delete-local-data', TJSONIntegerNumber.Create(1));
  5413.  
  5414.   if TorrentAction(ids, 'torrent-remove', args) then begin
  5415.     with gTorrents do begin
  5416.       BeginUpdate;
  5417.       try
  5418.         i:=0;
  5419.         while i < Items.Count do begin
  5420.           id:=Items[idxTorrentId, i];
  5421.           for j:=0 to VarArrayHighBound(ids, 1) do
  5422.             if id = ids[j] then begin
  5423.               Items.Items[idxDeleted, i]:=1;
  5424.               break;
  5425.             end;
  5426.           Inc(i);
  5427.         end;
  5428.       finally
  5429.         EndUpdate;
  5430.       end;
  5431.     end;
  5432.  
  5433.     if RemoveLocalData then
  5434.       RpcObj.RefreshNow:=RpcObj.RefreshNow + [rtSession];
  5435.   end;
  5436. end;
  5437.  
  5438. function TMainForm.IncludeProperTrailingPathDelimiter(const s: string): string;
  5439. var
  5440.   i: integer;
  5441.   d: char;
  5442. begin
  5443.   Result:=s;
  5444.   if Result = '' then exit;
  5445.   d:='/';
  5446.   for i:=1 to Length(Result) do
  5447.     if Result[i] in ['/','\'] then begin
  5448.       d:=Result[i];
  5449.       break;
  5450.     end;
  5451.  
  5452.   if Result[Length(Result)] <> d then
  5453.     Result:=Result + d;
  5454. end;
  5455.  
  5456. procedure TMainForm.FillFilesList(ATorrentId: integer; list, priorities, wanted: TJSONArray; const DownloadDir: WideString);
  5457. begin
  5458.   if lvFiles.Tag <> 0 then exit;
  5459.   if (list = nil) or (priorities = nil) or (wanted = nil) then begin
  5460.     ClearDetailsInfo;
  5461.     exit;
  5462.   end;
  5463.  
  5464.   lvFiles.Enabled:=True;
  5465.   lvFiles.Color:=clWindow;
  5466.   FFilesTree.DownloadDir:=UTF8Encode(DownloadDir);
  5467.   FFilesTree.FillTree(ATorrentId, list, priorities, wanted);
  5468.   tabFiles.Caption:=Format('%s (%d)', [FFilesCapt, list.Count]);
  5469.   DetailsUpdated;
  5470. end;
  5471.  
  5472. procedure TMainForm.FillGeneralInfo(t: TJSONObject);
  5473. var
  5474.   i, j, idx: integer;
  5475.   s: string;
  5476.   f: double;
  5477. begin
  5478.   if (gTorrents.Items.Count = 0) or (t = nil) then begin
  5479.     ClearDetailsInfo;
  5480.     exit;
  5481.   end;
  5482.   idx:=gTorrents.Items.IndexOf(idxTorrentId, t.Integers['id']);
  5483.   if idx = -1 then begin
  5484.     ClearDetailsInfo;
  5485.     exit;
  5486.   end;
  5487.  
  5488.   txDownProgress.Caption:=Format('%.1f%%', [double(gTorrents.Items[idxDone, idx])]);
  5489.   txDownProgress.AutoSize:=True;
  5490.   if RpcObj.RPCVersion >= 5 then
  5491.     s:=t.Strings['pieces']
  5492.   else
  5493.     s:='';
  5494.   ProcessPieces(s, t.Integers['pieceCount'], gTorrents.Items[idxDone, idx]);
  5495.  
  5496.   panTransfer.ChildSizing.Layout:=cclNone;
  5497.   txStatus.Caption:=GetTorrentStatus(idx);
  5498.   txError.Caption:=GetTorrentError(t, gTorrents.Items[idxStatus, idx]);
  5499.   i:=t.Integers['eta'];
  5500.   f:=gTorrents.Items[idxDownSpeed, idx];
  5501.   if f > 0 then
  5502.     i:=Round(t.Floats['leftUntilDone']/f);
  5503.   txRemaining.Caption:=EtaToString(i);
  5504.   txDownloaded.Caption:=GetHumanSize(t.Floats['downloadedEver']);
  5505.   txUploaded.Caption:=GetHumanSize(t.Floats['uploadedEver']);
  5506.   f:=t.Floats['pieceSize'];
  5507.   if f > 0 then
  5508.     i:=Round(t.Floats['corruptEver']/f)
  5509.   else
  5510.     i:=0;
  5511.   txWasted.Caption:=Format(sHashfails, [GetHumanSize(t.Floats['corruptEver']), i]);
  5512.   s:=GetHumanSize(gTorrents.Items[idxDownSpeed, idx], 1)+sPerSecond;
  5513.   if t.IndexOfName('secondsDownloading') >= 0 then begin
  5514.     f:=t.Integers['secondsDownloading'];
  5515.     if f > 0 then
  5516.       s:=Format('%s (%s: %s)', [s, SAverage, GetHumanSize(t.Floats['downloadedEver']/f, 1) + sPerSecond]);
  5517.   end;
  5518.   txDownSpeed.Caption:=s;
  5519.   txUpSpeed.Caption:=GetHumanSize(gTorrents.Items[idxUpSpeed, idx], 1)+sPerSecond;
  5520.   s:=RatioToString(t.Floats['uploadRatio']);
  5521.   if t.IndexOfName('secondsSeeding') >= 0 then begin
  5522.     i:=t.Integers['secondsSeeding'];
  5523.     if i > 0 then
  5524.       s:=Format('%s (%s)', [s, EtaToString(i)]);
  5525.   end;
  5526.   txRatio.Caption:=s;
  5527.  
  5528.   if RpcObj.RPCVersion < 5 then
  5529.   begin
  5530.     // RPC versions prior to v5
  5531.     j:=t.Integers['downloadLimitMode'];
  5532.     if j = TR_SPEEDLIMIT_GLOBAL then
  5533.       s:='-'
  5534.     else begin
  5535.       i:=t.Integers['downloadLimit'];
  5536.       if (i < 0) or (j = TR_SPEEDLIMIT_UNLIMITED) then
  5537.         s:=Utf8Encode(WideString(WideChar($221E)))
  5538.       else
  5539.         s:=GetHumanSize(i*1024)+sPerSecond;
  5540.     end;
  5541.     txDownLimit.Caption:=s;
  5542.     j:=t.Integers['uploadLimitMode'];
  5543.     if j = TR_SPEEDLIMIT_GLOBAL then
  5544.       s:='-'
  5545.     else begin
  5546.       i:=t.Integers['uploadLimit'];
  5547.       if (i < 0) or (j = TR_SPEEDLIMIT_UNLIMITED) then
  5548.         s:=Utf8Encode(WideString(WideChar($221E)))
  5549.       else
  5550.         s:=GetHumanSize(i*1024)+sPerSecond;
  5551.     end;
  5552.     txUpLimit.Caption:=s;
  5553.   end else begin
  5554.     // RPC version 5
  5555.     if t.Booleans['downloadLimited'] then
  5556.     begin
  5557.       i:=t.Integers['downloadLimit'];
  5558.       if i < 0 then
  5559.         s:=Utf8Encode(WideString(WideChar($221E)))
  5560.       else
  5561.         s:=GetHumanSize(i*1024)+sPerSecond;
  5562.     end else s:='-';
  5563.     txDownLimit.Caption:=s;
  5564.  
  5565.     if t.Booleans['uploadLimited'] then
  5566.     begin
  5567.       i:=t.Integers['uploadLimit'];
  5568.       if i < 0 then
  5569.         s:=Utf8Encode(WideString(WideChar($221E)))
  5570.       else
  5571.         s:=GetHumanSize(i*1024)+sPerSecond;
  5572.     end else s:='-';
  5573.     txUpLimit.Caption:=s;
  5574.   end;
  5575.  
  5576.   if RpcObj.RPCVersion >= 7 then
  5577.     with t.Arrays['trackerStats'] do begin
  5578.       if Count > 0 then begin
  5579.         if integer(Objects[0].Integers['announceState']) in [2, 3] then
  5580.           f:=1
  5581.         else
  5582.           f:=Objects[0].Floats['nextAnnounceTime'];
  5583.       end
  5584.       else
  5585.         f:=0;
  5586.     end
  5587.   else
  5588.     f:=t.Floats['nextAnnounceTime'];
  5589.   if f = 0 then
  5590.     s:='-'
  5591.   else
  5592.   if f = 1 then
  5593.     s:=sUpdating
  5594.   else
  5595.     s:=DateTimeToStr(UnixToDateTime(Trunc(f)) + GetTimeZoneDelta);
  5596.   txTrackerUpdate.Caption:=s;
  5597.   txTracker.Caption:=UTF8Encode(widestring(gTorrents.Items[idxTracker, idx]));
  5598.   if RpcObj.RPCVersion >= 7 then
  5599.     if t.Arrays['trackerStats'].Count > 0 then
  5600.       i:=t.Arrays['trackerStats'].Objects[0].Integers['seederCount']
  5601.     else
  5602.       i:=-1
  5603.   else
  5604.     i:=t.Integers['seeders'];
  5605.   s:=GetSeedsText(t.Integers['peersSendingToUs'], i);
  5606.   txSeeds.Caption:=StringReplace(s, '/', ' ' + sOf + ' ', []) + ' '+ sConnected;
  5607.   if RpcObj.RPCVersion >= 7 then
  5608.     if t.Arrays['trackerStats'].Count > 0 then
  5609.       i:=t.Arrays['trackerStats'].Objects[0].Integers['leecherCount']
  5610.     else
  5611.       i:=-1
  5612.   else
  5613.     i:=t.Integers['leechers'];
  5614.   s:=GetPeersText(t.Integers['peersGettingFromUs'], -1, i);
  5615.   s:=StringReplace(s, ' ', ' '+ sConnected +' ', []);
  5616.   s:=StringReplace(s, '/', ' ' + sOf + ' ', []);
  5617.   txPeers.Caption:=StringReplace(s, ')', ' '+ sInSwarm+ ')', []);
  5618.   txMaxPeers.Caption:=t.Strings['maxConnectedPeers'];
  5619.   txLastActive.Caption:=TorrentDateTimeToString(Trunc(t.Floats['activityDate']));
  5620.   panTransfer.ChildSizing.Layout:=cclLeftToRightThenTopToBottom;
  5621.  
  5622.   panGeneralInfo.ChildSizing.Layout:=cclNone;
  5623.  
  5624.   s:=UTF8Encode(widestring(gTorrents.Items[idxName, idx]));
  5625.   if RpcObj.RPCVersion >= 4 then
  5626.     s:=IncludeProperTrailingPathDelimiter(UTF8Encode(t.Strings['downloadDir'])) + s;
  5627.   txTorrentName.Caption:=s;
  5628.   s:=Trim(UTF8Encode(t.Strings['creator']));
  5629.   if s <> '' then
  5630.     s:=' by ' + s;
  5631.   txCreated.Caption:=TorrentDateTimeToString(Trunc(t.Floats['dateCreated'])) + s;
  5632.   if gTorrents.Items[idxSize, idx] >= 0 then begin
  5633.     txTotalSize.Caption:=Format(sDone, [GetHumanSize(t.Floats['totalSize']), GetHumanSize(t.Floats['sizeWhenDone'] - t.Floats['leftUntilDone'])]);
  5634.     if t.Floats['totalSize'] = t.Floats['haveValid'] then
  5635.       i:=t.Integers['pieceCount']
  5636.     else
  5637.       i:=Trunc(t.Floats['haveValid']/t.Floats['pieceSize']);
  5638.     txPieces.Caption:=Format(sHave, [t.Integers['pieceCount'], GetHumanSize(t.Floats['pieceSize']), i]);
  5639.   end
  5640.   else begin
  5641.     txTotalSize.Caption:='?';
  5642.     txPieces.Caption:='?';
  5643.   end;
  5644.  
  5645.   txHash.Caption:=t.Strings['hashString'];
  5646.   txComment.Caption:=UTF8Encode(t.Strings['comment']);
  5647.   if (AnsiCompareText(Copy(txComment.Caption, 1, 7), 'http://') = 0)
  5648.      or (AnsiCompareText(Copy(txComment.Caption, 1, 8), 'https://') = 0)
  5649.   then begin
  5650.     if not Assigned(txComment.OnClick) then begin
  5651.       txComment.OnClick:=@UrlLabelClick;
  5652.       txComment.Cursor:=crHandPoint;
  5653.       txComment.Font.Color:=clBlue;
  5654.       txComment.Font.Style:=[fsUnderline];
  5655.     end;
  5656.   end
  5657.   else begin
  5658.     if Assigned(txComment.OnClick) then begin
  5659.       txComment.OnClick:=nil;
  5660.       txComment.Cursor:=crDefault;
  5661.       txComment.ParentFont:=True;
  5662.     end;
  5663.   end;
  5664.   txAddedOn.Caption:=TorrentDateTimeToString(Trunc(t.Floats['addedDate']));
  5665.   txCompletedOn.Caption:=TorrentDateTimeToString(Trunc(t.Floats['doneDate']));
  5666.   panGeneralInfo.ChildSizing.Layout:=cclLeftToRightThenTopToBottom;
  5667.   DetailsUpdated;
  5668. end;
  5669.  
  5670. procedure TMainForm.FillTrackersList(TrackersData: TJSONObject);
  5671. var
  5672.   i, tidx, row: integer;
  5673.   id: integer;
  5674.   d: TJSONData;
  5675.   t: TJSONObject;
  5676.   f: double;
  5677.   s: string;
  5678.   Trackers, TrackerStats: TJSONArray;
  5679.   WasEmpty, NoInfo: boolean;
  5680. begin
  5681.   if TrackersData = nil then begin
  5682.     ClearDetailsInfo;
  5683.     exit;
  5684.   end;
  5685.   Trackers:=TrackersData.Arrays['trackers'];
  5686.   if RpcObj.RPCVersion >= 7 then
  5687.     TrackerStats:=TrackersData.Arrays['trackerStats']
  5688.   else
  5689.     TrackerStats:=nil;
  5690.   tidx:=gTorrents.Items.IndexOf(idxTorrentId, TrackersData.Integers['id']);
  5691.   if tidx = -1 then begin
  5692.     ClearDetailsInfo;
  5693.     exit;
  5694.   end;
  5695.   i:=gTorrents.Items[idxStatus, tidx];
  5696.   NoInfo:=(i = TR_STATUS_STOPPED) or (i = TR_STATUS_FINISHED);
  5697.   WasEmpty:=lvTrackers.Items.Count = 0;
  5698.   lvTrackers.Items.BeginUpdate;
  5699.   try
  5700.     lvTrackers.Enabled:=True;
  5701.     lvTrackers.Color:=clWindow;
  5702.     for i:=0 to lvTrackers.Items.Count - 1 do
  5703.       lvTrackers.Items[idxTrackerTag, i]:=0;
  5704.  
  5705.     lvTrackers.Items.Sort(idxTrackerID);
  5706.     for i:=0 to Trackers.Count - 1 do begin
  5707.       d:=Trackers[i];
  5708.       if not (d is TJSONObject) then continue;
  5709.       t:=d as TJSONObject;
  5710.       if t.IndexOfName('id') >= 0 then
  5711.         id:=t.Integers['id']
  5712.       else
  5713.         id:=i;
  5714.       if not lvTrackers.Items.Find(idxTrackerID, id, row) then
  5715.         lvTrackers.Items.InsertRow(row);
  5716.       lvTrackers.Items[idxTrackerID, row]:=id;
  5717.       lvTrackers.Items[idxTrackersListName, row]:=t.Strings['announce'];
  5718.       if NoInfo then begin
  5719.         lvTrackers.Items[idxTrackersListStatus, row]:=NULL;
  5720.         lvTrackers.Items[idxTrackersListSeeds, row]:=NULL;
  5721.         f:=0;
  5722.       end
  5723.       else
  5724.         if TrackerStats <> nil then begin
  5725.           f:=0;
  5726.           if i < TrackerStats.Count then
  5727.             with TrackerStats.Objects[i] do begin
  5728.               s:='';
  5729.               if integer(Integers['announceState']) in [2, 3] then
  5730.                 s:=sTrackerUpdating
  5731.               else
  5732.                 if Booleans['hasAnnounced'] then
  5733.                   if Booleans['lastAnnounceSucceeded'] then
  5734.                     s:=sTrackerWorking
  5735.                   else
  5736.                     s:=TranslateString(UTF8Encode(Strings['lastAnnounceResult']), True);
  5737.  
  5738.               if s = 'Success' then
  5739.                 s:=sTrackerWorking;
  5740.  
  5741.               lvTrackers.Items[idxTrackersListStatus, row]:=UTF8Decode(s);
  5742.               lvTrackers.Items[idxTrackersListSeeds, row]:=Integers['seederCount'];
  5743.  
  5744.               if integer(Integers['announceState']) in [2, 3] then
  5745.                 f:=1
  5746.               else
  5747.                 f:=Floats['nextAnnounceTime'];
  5748.             end;
  5749.         end
  5750.         else begin
  5751.           if i = 0 then begin
  5752.             lvTrackers.Items[idxTrackersListStatus, row]:=gTorrents.Items[idxTrackerStatus, tidx];
  5753.             lvTrackers.Items[idxTrackersListSeeds, row]:=gTorrents.Items[idxSeedsTotal, tidx];
  5754.           end;
  5755.           f:=TrackersData.Floats['nextAnnounceTime'];
  5756.         end;
  5757.  
  5758.       if f > 1 then begin
  5759.         f:=(UnixToDateTime(Trunc(f)) + GetTimeZoneDelta - Now)*SecsPerDay;
  5760.         if f < 0 then
  5761.           f:=0;
  5762.       end;
  5763.       if (TrackerStats <> nil) or (i = 0) then
  5764.         lvTrackers.Items[idxTrackersListUpdateIn, row]:=f;
  5765.  
  5766.       lvTrackers.Items[idxTrackerTag, row]:=1;
  5767.     end;
  5768.  
  5769.     i:=0;
  5770.     while i < lvTrackers.Items.Count do
  5771.       if lvTrackers.Items[idxTrackerTag, i] = 0 then
  5772.         lvTrackers.Items.Delete(i)
  5773.       else
  5774.         Inc(i);
  5775.  
  5776.     lvTrackers.Sort;
  5777.     if WasEmpty and (lvTrackers.Items.Count > 0) then
  5778.       lvTrackers.Row:=0;
  5779.   finally
  5780.     lvTrackers.Items.EndUpdate;
  5781.   end;
  5782.   DetailsUpdated;
  5783. end;
  5784.  
  5785. procedure TMainForm.FillSessionInfo(s: TJSONObject);
  5786. var
  5787.   d, u: integer;
  5788. begin
  5789. {$ifdef LCLcarbon}
  5790.   TrayIcon.Tag:=0;
  5791. {$endif LCLcarbon}
  5792.   if RpcObj.RPCVersion < 14 then begin
  5793.     TR_STATUS_STOPPED:=TR_STATUS_STOPPED_1;
  5794.     TR_STATUS_CHECK_WAIT:=TR_STATUS_CHECK_WAIT_1;
  5795.     TR_STATUS_CHECK:=TR_STATUS_CHECK_1;
  5796.     TR_STATUS_DOWNLOAD_WAIT:=-1;
  5797.     TR_STATUS_DOWNLOAD:=TR_STATUS_DOWNLOAD_1;
  5798.     TR_STATUS_SEED_WAIT:=-1;
  5799.     TR_STATUS_SEED:=TR_STATUS_SEED_1;
  5800.   end
  5801.   else begin
  5802.     TR_STATUS_STOPPED:=TR_STATUS_STOPPED_2;
  5803.     TR_STATUS_CHECK_WAIT:=TR_STATUS_CHECK_WAIT_2;
  5804.     TR_STATUS_CHECK:=TR_STATUS_CHECK_2;
  5805.     TR_STATUS_DOWNLOAD_WAIT:=TR_STATUS_DOWNLOAD_WAIT_2;
  5806.     TR_STATUS_DOWNLOAD:=TR_STATUS_DOWNLOAD_2;
  5807.     TR_STATUS_SEED_WAIT:=TR_STATUS_SEED_WAIT_2;
  5808.     TR_STATUS_SEED:=TR_STATUS_SEED_2;
  5809.   end;
  5810.  
  5811.   UpdateUIRpcVersion(RpcObj.RPCVersion);
  5812.  
  5813.   if RpcObj.RPCVersion >= 5 then begin
  5814. {$ifdef LCLcarbon}
  5815.     if acAltSpeed.Checked <> (s.Integers['alt-speed-enabled'] <> 0) then
  5816.       TrayIcon.Tag:=1;
  5817. {$endif LCLcarbon}
  5818.     acAltSpeed.Checked:=s.Integers['alt-speed-enabled'] <> 0;
  5819.     acUpdateBlocklist.Tag:=s.Integers['blocklist-enabled'];
  5820.     acUpdateBlocklist.Enabled:=acUpdateBlocklist.Tag <> 0;
  5821.   end;
  5822.   if s.IndexOfName('download-dir-free-space') >= 0 then
  5823.     StatusBar.Panels[3].Text:=Format(SFreeSpace, [GetHumanSize(s.Floats['download-dir-free-space'])]);
  5824.  
  5825.   if (RpcObj.RPCVersion >= 5) and acAltSpeed.Checked then begin
  5826.     d:=s.Integers['alt-speed-down'];
  5827.     u:=s.Integers['alt-speed-up']
  5828.   end
  5829.   else begin
  5830.     if s.Integers['speed-limit-down-enabled'] <> 0 then
  5831.       d:=s.Integers['speed-limit-down']
  5832.     else
  5833.       d:=-1;
  5834.     if s.Integers['speed-limit-up-enabled'] <> 0 then
  5835.       u:=s.Integers['speed-limit-up']
  5836.     else
  5837.       u:=-1;
  5838.   end;
  5839. {$ifdef LCLcarbon}
  5840.   UpdateUI;
  5841. {$endif LCLcarbon}
  5842.   if (FCurDownSpeedLimit <> d) or (FCurUpSpeedLimit <> u) then begin
  5843.     FCurDownSpeedLimit:=d;
  5844.     FCurUpSpeedLimit:=u;
  5845.     FillSpeedsMenu;
  5846.   end;
  5847. {$ifdef LCLcarbon}
  5848.   if TrayIcon.Tag <> 0 then
  5849.     TrayIcon.InternalUpdate;
  5850. {$endif LCLcarbon}
  5851. end;
  5852.  
  5853. procedure TMainForm.FillStatistics(s: TJSONObject);
  5854.  
  5855.   procedure _Fill(idx: integer; s: TJSONObject);
  5856.   begin
  5857.     with gStats do begin
  5858.       Items[idx, 0]:=UTF8Decode(GetHumanSize(s.Floats['downloadedBytes']));
  5859.       Items[idx, 1]:=UTF8Decode(GetHumanSize(s.Floats['uploadedBytes']));
  5860.       Items[idx, 2]:=s.Integers['filesAdded'];
  5861.       Items[idx, 3]:=UTF8Decode(SecondsToString(s.Integers['secondsActive']));
  5862.     end;
  5863.   end;
  5864.  
  5865. begin
  5866.   if RpcObj.RPCVersion < 4 then
  5867.     exit;
  5868.   if s = nil then begin
  5869.     ClearDetailsInfo;
  5870.     exit;
  5871.   end;
  5872.   gStats.BeginUpdate;
  5873.   try
  5874.     gStats.Enabled:=True;
  5875.     gStats.Color:=clWindow;
  5876.     _Fill(1, s.Objects['current-stats']);
  5877.     _Fill(2, s.Objects['cumulative-stats']);
  5878.   finally
  5879.     gStats.EndUpdate;
  5880.   end;
  5881.   DetailsUpdated;
  5882. end;
  5883.  
  5884. procedure TMainForm.CheckStatus(Fatal: boolean);
  5885. var
  5886.   s: string;
  5887.   i: integer;
  5888. begin
  5889.   with MainForm do begin
  5890.     s:=RpcObj.Status;
  5891.     if s <> '' then begin
  5892.       RpcObj.Status:='';
  5893.       if Fatal then
  5894.         DoDisconnect;
  5895.       ForceAppNormal;
  5896.       if Fatal and not RpcObj.Connected and RpcObj.ReconnectAllowed and (FReconnectTimeOut <> -1) then begin
  5897.         FReconnectWaitStart:=Now;
  5898.         if FReconnectTimeOut < 60 then
  5899.           if FReconnectTimeOut < 10 then
  5900.             Inc(FReconnectTimeOut, 5)
  5901.           else
  5902.             Inc(FReconnectTimeOut, 10);
  5903.         txConnError.Caption:=s;
  5904.         panReconnectFrame.Hide;
  5905.         panReconnect.AutoSize:=True;
  5906.         CenterReconnectWindow;
  5907.         panReconnect.Show;
  5908.         panReconnect.BringToFront;
  5909.         TickTimerTimer(nil);
  5910.         panReconnect.AutoSize:=False;
  5911.         panReconnectFrame.Show;
  5912.         CenterReconnectWindow;
  5913.       end
  5914.       else
  5915.         MessageDlg(s, mtError, [mbOK], 0);
  5916.     end;
  5917.  
  5918.     if StatusBar.Panels[0].Text <> RpcObj.InfoStatus then begin
  5919.       StatusBar.Panels[0].Text:=RpcObj.InfoStatus;
  5920.       TrayIcon.Hint:=RpcObj.InfoStatus;
  5921.       if (RpcObj.Connected) and (RpcObj.Http.UserName <> '') then
  5922.         FPasswords.Values[FCurConn]:=RpcObj.Http.Password;  // Save password to cache
  5923.     end;
  5924.     if not RpcObj.Connected then
  5925.       for i:=1 to StatusBar.Panels.Count - 1 do
  5926.         StatusBar.Panels[i].Text:='';
  5927.   end;
  5928. end;
  5929.  
  5930. function TMainForm.TorrentAction(const TorrentIds: variant; const AAction: string; args: TJSONObject): boolean;
  5931. var
  5932.   req: TJSONObject;
  5933.   ids: TJSONArray;
  5934.   i: integer;
  5935. begin
  5936.   if VarIsEmpty(TorrentIds) then
  5937.     exit;
  5938.   Application.ProcessMessages;
  5939.   AppBusy;
  5940.   req:=TJSONObject.Create;
  5941.   try
  5942.     req.Add('method', AAction);
  5943.     if args = nil then
  5944.       args:=TJSONObject.Create;
  5945.     if not VarIsNull(TorrentIds) then begin
  5946.       ids:=TJSONArray.Create;
  5947.       if VarIsArray(TorrentIds) then begin
  5948.         for i:=VarArrayLowBound(TorrentIds, 1) to VarArrayHighBound(TorrentIds, 1) do
  5949.           ids.Add(integer(TorrentIds[i]));
  5950.       end
  5951.       else
  5952.         ids.Add(integer(TorrentIds));
  5953.       args.Add('ids', ids);
  5954.     end;
  5955.     req.Add('arguments', args);
  5956.     args:=RpcObj.SendRequest(req, False, 30000);
  5957.     Result:=args <> nil;
  5958.     args.Free;
  5959.   finally
  5960.     req.Free;
  5961.   end;
  5962.   if not Result then
  5963.     CheckStatus(False)
  5964.   else
  5965.     DoRefresh(True);
  5966.   AppNormal;
  5967. end;
  5968.  
  5969. function TMainForm.SetFilePriority(TorrentId: integer; const Files: array of integer; const APriority: string): boolean;
  5970.  
  5971.   function CreateFilesArray: TJSONArray;
  5972.   var
  5973.     i: integer;
  5974.   begin
  5975.     Result:=TJSONArray.Create;
  5976.     for i:=Low(Files) to High(Files) do
  5977.       Result.Add(Files[i]);
  5978.   end;
  5979.  
  5980. var
  5981.   req, args: TJSONObject;
  5982. begin
  5983.   AppBusy;
  5984.   req:=TJSONObject.Create;
  5985.   try
  5986.     req.Add('method', 'torrent-set');
  5987.     args:=TJSONObject.Create;
  5988.     if TorrentId <> 0 then
  5989.       args.Add('ids', TJSONArray.Create([TorrentId]));
  5990.     if APriority = 'skip' then
  5991.       args.Add('files-unwanted', CreateFilesArray)
  5992.     else begin
  5993.       args.Add('files-wanted', CreateFilesArray);
  5994.       args.Add('priority-' + APriority, CreateFilesArray);
  5995.     end;
  5996.     req.Add('arguments', args);
  5997.     args:=RpcObj.SendRequest(req, False);
  5998.     Result:=args<> nil;
  5999.     args.Free;
  6000.   finally
  6001.     req.Free;
  6002.   end;
  6003.   if not Result then
  6004.     CheckStatus(False)
  6005.   else
  6006.     DoRefresh;
  6007.   AppNormal;
  6008. end;
  6009.  
  6010. function TMainForm.SetCurrentFilePriority(const APriority: string): boolean;
  6011. var
  6012.   Files: array of integer;
  6013.   i, j, k, level: integer;
  6014.   pri: string;
  6015. begin
  6016.   if (gTorrents.Items.Count = 0) or (PageInfo.ActivePage <> tabFiles) then exit;
  6017.   SetLength(Files, lvFiles.Items.Count);
  6018.   pri:=APriority;
  6019.   j:=0;
  6020.   if APriority <> '' then begin
  6021.     // Priority for currently selected rows
  6022.     if lvFiles.SelCount = 0 then
  6023.       lvFiles.RowSelected[lvFiles.Row]:=True;
  6024.     level:=-1;
  6025.     for i:=0 to lvFiles.Items.Count - 1 do begin
  6026.       k:=FFilesTree.RowLevel[i];
  6027.       if k <= level then
  6028.         level:=-1;
  6029.       if lvFiles.RowSelected[i] or ( (level <> -1) and (k > level) ) then begin
  6030.         if FFilesTree.IsFolder(i) then begin
  6031.           if level = -1 then
  6032.             level:=k;
  6033.         end
  6034.         else begin
  6035.           Files[j]:=FFiles[idxFileId, i];
  6036.           Inc(j);
  6037.         end;
  6038.       end;
  6039.     end;
  6040.   end
  6041.   else begin
  6042.     // Priority based on checkbox state
  6043.     for i:=0 to FFiles.Count - 1 do
  6044.       if not FFilesTree.IsFolder(i) then begin
  6045.         k:=FFiles[idxFilePriority, i];
  6046.         if (k <> TR_PRI_SKIP) <> (FFilesTree.Checked[i] = cbChecked) then begin
  6047.           if pri = '' then
  6048.             if FFilesTree.Checked[i] = cbChecked then
  6049.               pri:='normal'
  6050.             else
  6051.               pri:='skip';
  6052.           Files[j]:=FFiles[idxFileId, i];
  6053.           Inc(j);
  6054.         end;
  6055.       end;
  6056.   end;
  6057.  
  6058.   if j = 0 then exit;
  6059.   SetLength(Files, j);
  6060.   Result:=SetFilePriority(RpcObj.CurTorrentId, Files, pri);
  6061. end;
  6062.  
  6063. procedure TMainForm.SetTorrentPriority(APriority: integer);
  6064. var
  6065.   args: TJSONObject;
  6066. begin
  6067.   if gTorrents.Items.Count = 0 then exit;
  6068.   args:=TJSONObject.Create;
  6069.   args.Add('bandwidthPriority', TJSONIntegerNumber.Create(APriority));
  6070.   TorrentAction(GetSelectedTorrents, 'torrent-set', args);
  6071. end;
  6072.  
  6073. procedure TMainForm.ProcessPieces(const Pieces: string; PieceCount: integer; const Done: double);
  6074. const
  6075.   MaxPieces = 4000;
  6076. var
  6077.   i, j, k, x, xx: integer;
  6078.   s: string;
  6079.   R: TRect;
  6080.   bmp: TBitmap;
  6081.   c: double;
  6082. begin
  6083.   FLastPieces:=Pieces;
  6084.   FLastPieceCount:=PieceCount;
  6085.   FLastDone:=Done;
  6086.   bmp:=nil;
  6087.   try
  6088.     if FTorrentProgress = nil then
  6089.       FTorrentProgress:=TBitmap.Create;
  6090.     if RpcObj.RPCVersion >= 5 then begin
  6091.       bmp:=TBitmap.Create;
  6092.       if PieceCount > MaxPieces then begin
  6093.         bmp.Width:=MaxPieces;
  6094.         c:=MaxPieces/PieceCount;
  6095.       end
  6096.       else begin
  6097.         bmp.Width:=PieceCount;
  6098.         c:=1;
  6099.       end;
  6100.       bmp.Height:=12;
  6101.       bmp.Canvas.Brush.Color:=clWindow;
  6102.       bmp.Canvas.FillRect(0, 0, bmp.Width, bmp.Height);
  6103.       bmp.Canvas.Brush.Color:=clHighlight;
  6104.       x:=0;
  6105.       s:=DecodeBase64(Pieces);
  6106.       for i:=1 to Length(s) do begin
  6107.         j:=byte(s[i]);
  6108.         for k:=1 to 8 do begin
  6109.           if PieceCount = 0 then
  6110.             break;
  6111.           if j and $80 <> 0 then begin
  6112.             xx:=Trunc(x*c);
  6113.             bmp.Canvas.FillRect(xx, 0, xx + 1, bmp.Height);
  6114.           end;
  6115.           Inc(x);
  6116.           j:=j shl 1;
  6117.           Dec(PieceCount);
  6118.         end;
  6119.       end;
  6120.     end;
  6121.  
  6122.     with FTorrentProgress.Canvas do begin
  6123.       FTorrentProgress.Width:=pbDownloaded.ClientWidth;
  6124.       if bmp <> nil then begin
  6125.         i:=bmp.Height div 3;
  6126.         FTorrentProgress.Height:=bmp.Height + 5 + i;
  6127.         Brush.Color:=clWindow;
  6128.         FillRect(0, 0, FTorrentProgress.Width, FTorrentProgress.Height);
  6129.         Brush.Color:=clBtnShadow;
  6130.         R:=Rect(0, i + 3, FTorrentProgress.Width, FTorrentProgress.Height);
  6131.         FillRect(R);
  6132.         InflateRect(R, -1, -1);
  6133.         if bmp.Width > 0 then
  6134.           StretchDraw(R, bmp)
  6135.         else begin
  6136.           Brush.Color:=clWindow;
  6137.           FillRect(R);
  6138.         end;
  6139.         R:=Rect(0, 0, FTorrentProgress.Width, i + 2);
  6140.       end
  6141.       else begin
  6142.         FTorrentProgress.Height:=14;
  6143.         R:=Rect(0, 0, FTorrentProgress.Width, FTorrentProgress.Height);
  6144.       end;
  6145.       Brush.Color:=clBtnShadow;
  6146.       FillRect(R);
  6147.       InflateRect(R, -1, -1);
  6148.       x:=R.Left + Round((R.Right - R.Left)*Done/100.0);
  6149.       Brush.Color:=clHighlight;
  6150.       FillRect(R.Left, R.Top, x, R.Bottom);
  6151.       Brush.Color:=clWindow;
  6152.       FillRect(x, R.Top, R.Right, R.Bottom);
  6153.     end;
  6154.     if pbDownloaded.Height <> FTorrentProgress.Height then begin
  6155.       pbDownloaded.Constraints.MaxHeight:=FTorrentProgress.Height;
  6156.       pbDownloaded.Height:=FTorrentProgress.Height;
  6157.       panProgress.AutoSize:=True;
  6158.       panProgress.AutoSize:=False;
  6159.     end;
  6160.     pbDownloaded.Invalidate;
  6161.   finally
  6162.     bmp.Free;
  6163.   end;
  6164. end;
  6165.  
  6166. function TMainForm.ExecRemoteFile(const FileName: string; SelectFile: boolean): boolean;
  6167.  
  6168.   procedure _Exec(s: string);
  6169.   var
  6170.     p: string;
  6171.   begin
  6172.     AppBusy;
  6173.     if SelectFile then
  6174.       if FileExistsUTF8(s) then begin
  6175. {$ifdef mswindows}
  6176.                            { Commented by Antekgla
  6177.         p:=Format('/select,"%s"', [s]);
  6178.         s:='explorer.exe';
  6179.                             End Commented by Antekgla }
  6180. // Added by Antekgla
  6181.            p:=Format(FFileManagerDefaultParam, [s]);
  6182.            s:=FFileManagerDefault;
  6183. //End Added by Antekgla
  6184. {$else}
  6185.         p:='';
  6186.         s:=ExtractFilePath(s);
  6187. {$endif mswindows}
  6188.       end
  6189.       else begin
  6190.         p:='';
  6191.         s:=ExtractFilePath(s);
  6192.       end;
  6193.     Result:=OpenURL(s, p);
  6194.     AppNormal;
  6195.     if not Result then begin
  6196.       ForceAppNormal;
  6197.       MessageDlg(Format(sUnableToExecute, [s]), mtError, [mbOK], 0);
  6198.     end;
  6199.   end;
  6200.  
  6201. var
  6202.   s: string;
  6203. begin
  6204.   s:=MapRemoteToLocal(FileName);
  6205.   if s <> '' then begin
  6206.     _Exec(s);
  6207.     exit;
  6208.   end;
  6209.   if FileExistsUTF8(FileName) or DirectoryExistsUTF8(FileName) then begin
  6210.     _Exec(FileName);
  6211.     exit;
  6212.   end;
  6213.  
  6214.   ForceAppNormal;
  6215.   MessageDlg(sNoPathMapping, mtInformation, [mbOK], 0);
  6216. end;
  6217.  
  6218. function TMainForm.GetSelectedTorrents: variant;
  6219. var
  6220.   i, j: integer;
  6221. begin
  6222.   with gTorrents do begin
  6223.     if Items.Count = 0 then begin
  6224.       Result:=Unassigned;
  6225.       exit;
  6226.     end;
  6227.     if SelCount = 0 then
  6228.       Result:=VarArrayOf([Items[idxTorrentId, Row]])
  6229.     else begin
  6230.       Result:=VarArrayCreate([0, SelCount - 1], varinteger);
  6231.       j:=0;
  6232.       for i:=0 to gTorrents.Items.Count - 1 do
  6233.         if gTorrents.RowSelected[i] then begin
  6234.           Result[j]:=Items[idxTorrentId, i];
  6235.           Inc(j);
  6236.         end;
  6237.     end;
  6238.   end;
  6239. end;
  6240.  
  6241. procedure TMainForm.FillDownloadDirs(CB: TComboBox; const CurFolderParam: string);
  6242. var
  6243.   i, j: integer;
  6244.   s, IniSec: string;
  6245. begin
  6246.   CB.Items.Clear;
  6247.   IniSec:='AddTorrent.' + FCurConn;
  6248.   j:=Ini.ReadInteger(IniSec, 'FolderCount', 0);
  6249.   for i:=0 to j - 1 do begin
  6250.     s:=Ini.ReadString(IniSec, Format('Folder%d', [i]), '');
  6251.     if s <> '' then
  6252.       CB.Items.Add(s);
  6253.   end;
  6254.  
  6255.   s:=Ini.ReadString(IniSec, CurFolderParam, '');
  6256.   if s <> '' then begin
  6257.     i:=CB.Items.IndexOf(s);
  6258.     if i > 0 then
  6259.       CB.Items.Move(i, 0);
  6260.   end;
  6261.  
  6262.   if CB.Items.Count > 0 then
  6263.     CB.ItemIndex:=0;
  6264. end;
  6265.  
  6266. procedure TMainForm.SaveDownloadDirs(CB: TComboBox; const CurFolderParam: string);
  6267. var
  6268.   i: integer;
  6269.   IniSec: string;
  6270. begin
  6271.   IniSec:='AddTorrent.' + FCurConn;
  6272.   i:=CB.Items.IndexOf(CB.Text);
  6273.   if i >= 0 then
  6274.     CB.Items.Move(i, 0)
  6275.   else
  6276.     CB.Items.Insert(0, CB.Text);
  6277.   i:=Ini.ReadInteger('Interface', 'MaxFoldersHistory', 10);
  6278.   while CB.Items.Count > i do
  6279.     CB.Items.Delete(CB.Items.Count - 1);
  6280.   Ini.WriteInteger(IniSec, 'FolderCount', CB.Items.Count);
  6281.   for i:=0 to CB.Items.Count - 1 do
  6282.     Ini.WriteString(IniSec, Format('Folder%d', [i]), CB.Items[i]);
  6283.   Ini.WriteString(IniSec, CurFolderParam, CB.Items[0]);
  6284.   Ini.UpdateFile;
  6285. end;
  6286.  
  6287. procedure TMainForm.SetRefreshInterval;
  6288. var
  6289.   i: TDateTime;
  6290. begin
  6291.   if Visible and (WindowState <> wsMinimized) then
  6292.     i:=Ini.ReadInteger('Interface', 'RefreshInterval', 5)
  6293.   else
  6294.     i:=Ini.ReadInteger('Interface', 'RefreshIntervalMin', 20);
  6295.   if i < 1 then
  6296.     i:=1;
  6297.   RpcObj.RefreshInterval:=i/SecsPerDay;
  6298. end;
  6299.  
  6300. procedure TMainForm.AddTracker(EditMode: boolean);
  6301. var
  6302.   req, args: TJSONObject;
  6303.   id, torid: integer;
  6304. begin
  6305.   AppBusy;
  6306.   with TAddTrackerForm.Create(Self) do
  6307.   try
  6308.     id:=0;
  6309.     torid:=RpcObj.CurTorrentId;
  6310.     if EditMode then begin
  6311.       Caption:=STrackerProps;
  6312.       edTracker.Text:=UTF8Encode(widestring(lvTrackers.Items[idxTrackersListName, lvTrackers.Row]));
  6313.       id:=lvTrackers.Items[idxTrackerID, lvTrackers.Row];
  6314.     end;
  6315.     AppNormal;
  6316.     if ShowModal = mrOk then begin
  6317.       AppBusy;
  6318.       Self.Update;
  6319.       req:=TJSONObject.Create;
  6320.       try
  6321.         req.Add('method', 'torrent-set');
  6322.         args:=TJSONObject.Create;
  6323.         args.Add('ids', TJSONArray.Create([torid]));
  6324.         if EditMode then
  6325.           args.Add('trackerReplace', TJSONArray.Create([id, UTF8Decode(edTracker.Text)]))
  6326.         else
  6327.           args.Add('trackerAdd', TJSONArray.Create([UTF8Decode(edTracker.Text)]));
  6328.         req.Add('arguments', args);
  6329.         args:=nil;
  6330.         args:=RpcObj.SendRequest(req, False);
  6331.         if args = nil then begin
  6332.           CheckStatus(False);
  6333.           exit;
  6334.         end;
  6335.         args.Free;
  6336.       finally
  6337.         req.Free;
  6338.       end;
  6339.       DoRefresh;
  6340.       AppNormal;
  6341.     end;
  6342.   finally
  6343.     Free;
  6344.   end;
  6345. end;
  6346.  
  6347. procedure TMainForm.UpdateConnections;
  6348. var
  6349.   i, j, cnt: integer;
  6350.   s, cur: string;
  6351.   mi: TMenuItem;
  6352. begin
  6353.   while (pmConnections.Items.Count > 0) and (pmConnections.Items[0].Tag = 0) do
  6354.     pmConnections.Items[0].Free;
  6355.   while (miConnect.Count > 0) and (miConnect.Items[0].Tag = 0) do
  6356.     miConnect.Items[0].Free;
  6357.   cur:=Ini.ReadString('Hosts', 'CurHost', '');
  6358.   cnt:=Ini.ReadInteger('Hosts', 'Count', 0);
  6359.   j:=0;
  6360.   for i:=1 to cnt do begin
  6361.     s:=Ini.ReadString('Hosts', Format('Host%d', [i]), '');
  6362.     if s <> '' then begin
  6363.       mi:=TMenuItem.Create(pmConnections);
  6364.       mi.Caption:=s;
  6365.       if s = cur then
  6366.         mi.Checked:=True;
  6367.       mi.OnClick:=@DoConnectToHost;
  6368.       pmConnections.Items.Insert(j, mi);
  6369.  
  6370.       mi:=TMenuItem.Create(miConnect);
  6371.       mi.Caption:=s;
  6372.       if s = cur then
  6373.         mi.Checked:=True;
  6374.       mi.OnClick:=@DoConnectToHost;
  6375.       miConnect.Insert(j, mi);
  6376.       Inc(j);
  6377.     end;
  6378.   end;
  6379.   sepCon1.Visible:=j > 0;
  6380.   sepCon2.Visible:=j > 0;
  6381. end;
  6382.  
  6383. procedure TMainForm.DoConnectToHost(Sender: TObject);
  6384. var
  6385.   mi: TMenuItem;
  6386. begin
  6387.   mi:=TMenuItem(Sender);
  6388.   if RpcObj.Connected and (FCurConn = mi.Caption) then
  6389.     exit;
  6390.   DoDisconnect;
  6391.   FReconnectTimeOut:=-1;
  6392.   FCurConn:=mi.Caption;
  6393.   DoConnect;
  6394. end;
  6395.  
  6396. procedure TMainForm.DoSetDownloadSpeed(Sender: TObject);
  6397. begin
  6398.   SetSpeedLimit('down', TMenuItem(Sender).Tag);
  6399. end;
  6400.  
  6401. procedure TMainForm.DoSetUploadSpeed(Sender: TObject);
  6402. begin
  6403.   SetSpeedLimit('up', TMenuItem(Sender).Tag);
  6404. end;
  6405.  
  6406. procedure TMainForm.SetSpeedLimit(const Dir: string; Speed: integer);
  6407. var
  6408.   req, args: TJSONObject;
  6409. begin
  6410.   AppBusy;
  6411.   req:=TJSONObject.Create;
  6412.   try
  6413.     req.Add('method', 'session-set');
  6414.     args:=TJSONObject.Create;
  6415.     args.Add(Format('speed-limit-%s-enabled', [Dir]), integer(Speed >= 0) and 1);
  6416.     if Speed >= 0 then
  6417.       args.Add(Format('speed-limit-%s', [Dir]), Speed);
  6418.     args.Add('alt-speed-enabled', 0);
  6419.     req.Add('arguments', args);
  6420.     args:=RpcObj.SendRequest(req, False);
  6421.     if args = nil then begin
  6422.       CheckStatus(False);
  6423.       exit;
  6424.     end;
  6425.     args.Free;
  6426.   finally
  6427.     req.Free;
  6428.   end;
  6429.   RpcObj.RefreshNow:=RpcObj.RefreshNow + [rtSession];
  6430.   AppNormal;
  6431. end;
  6432.  
  6433. function TMainForm.FixSeparators(const p: string): string;
  6434. begin
  6435.   Result:=StringReplace(p, '/', DirectorySeparator, [rfReplaceAll]);
  6436.   Result:=StringReplace(Result, '\', DirectorySeparator, [rfReplaceAll]);
  6437. end;
  6438.  
  6439. function TMainForm.MapRemoteToLocal(const RemotePath: string): string;
  6440. var
  6441.   i, j: integer;
  6442.   s, ss, fn: string;
  6443. begin
  6444.   Result:='';
  6445.   fn:=FixSeparators(Trim(RemotePath));
  6446.   for i:=0 to FPathMap.Count - 1 do begin
  6447.     s:=FPathMap[i];
  6448.     j:=Pos('=', s);
  6449.     if j > 0 then begin
  6450.       ss:=FixSeparators(Copy(s, 1, j - 1));
  6451.       if (ss = fn) or (Pos(IncludeProperTrailingPathDelimiter(ss), fn) = 1) then begin
  6452.         if ss = fn then
  6453.           ss:=Copy(s, j + 1, MaxInt)
  6454.         else begin
  6455.           ss:=IncludeProperTrailingPathDelimiter(ss);
  6456.           ss:=IncludeTrailingPathDelimiter(Copy(s, j + 1, MaxInt)) + Copy(fn, Length(ss) + 1, MaxInt);
  6457.         end;
  6458.         Result:=FixSeparators(ss);
  6459.         exit;
  6460.       end;
  6461.     end;
  6462.   end;
  6463. end;
  6464.  
  6465. procedure TMainForm.UpdateUIRpcVersion(RpcVersion: integer);
  6466. var
  6467.   vc: boolean;
  6468. begin
  6469.   acRemoveTorrentAndData.Visible:=RPCVersion >= 4;
  6470.   acReannounceTorrent.Visible:=RPCVersion >= 5;
  6471.   acUpdateBlocklist.Visible:=RPCVersion >= 5;
  6472.   acMoveTorrent.Visible:=RPCVersion >= 6;
  6473.   pmiPriority.Visible:=RPCVersion >= 5;
  6474.   miPriority.Visible:=pmiPriority.Visible;
  6475.   acOpenContainingFolder.Visible:=RPCVersion >= 4;
  6476.   acOpenFile.Visible:=acOpenContainingFolder.Visible;
  6477.   pmSepOpen1.Visible:=acOpenContainingFolder.Visible;
  6478.   pmSepOpen2.Visible:=acOpenContainingFolder.Visible;
  6479.  
  6480.   vc:=not sepAltSpeed.Visible and (RPCVersion >= 5);
  6481.   sepAltSpeed.Visible:=RPCVersion >= 5;
  6482.   acAltSpeed.Visible:=RPCVersion >= 5;
  6483.   if vc then begin
  6484.     sepAltSpeed.Left:=tbStopTorrent.Left + 1;
  6485.     tbtAltSpeed.Left:=sepAltSpeed.Left + 1;
  6486.   end;
  6487.  
  6488.   acAddTracker.Visible:=RPCVersion >= 10;
  6489.   acEditTracker.Visible:=acAddTracker.Visible;
  6490.   acDelTracker.Visible:=acAddTracker.Visible;
  6491.   acAdvEditTrackers.Visible:=acAddTracker.Visible;
  6492.   sepTrackers.Visible:=acAddTracker.Visible;
  6493.  
  6494.   vc:=not sepQueue.Visible and (RPCVersion >= 14);
  6495.   sepQueue.Visible:=RPCVersion >= 14;
  6496.   acQMoveUp.Visible:=RPCVersion >= 14;
  6497.   acQMoveDown.Visible:=RPCVersion >= 14;
  6498.   miQueue.Visible:=RPCVersion >= 14;
  6499.   pmiQueue.Visible:=RPCVersion >= 14;
  6500.   if vc then begin
  6501.     sepQueue.Left:=tbStopTorrent.Left + 1;
  6502.     tbQMoveUp.Left:=sepQueue.Left + 1;
  6503.     tbQMoveDown.Left:=tbQMoveUp.Left + 1;
  6504.   end;
  6505.   acForceStartTorrent.Visible:=RPCVersion >= 14;
  6506.   tabStats.Visible:=RpcVersion >= 4;
  6507.   acRename.Visible:=RpcVersion >= 15;
  6508. end;
  6509.  
  6510. procedure TMainForm.CheckAddTorrents;
  6511. var
  6512.   i: integer;
  6513.   h: System.THandle;
  6514.   s: string;
  6515.   WasHidden: boolean;
  6516. begin
  6517.   h:=FileOpenUTF8(FIPCFileName, fmOpenRead or fmShareDenyWrite);
  6518.   if h <> System.THandle(-1) then begin
  6519.     i:=FileSeek(h, 0, soFromEnd);
  6520.     SetLength(s, i);
  6521.     if i > 0 then begin
  6522.       FileSeek(h, 0, soFromBeginning);
  6523.       SetLength(s, FileRead(h, s[1], i));
  6524.     end;
  6525.     FileTruncate(h, 0);
  6526.     FileClose(h);
  6527.     DeleteFileUTF8(FIPCFileName);
  6528.  
  6529.     if s = '' then begin
  6530.       ShowApp;
  6531.       exit;
  6532.     end;
  6533.  
  6534.     FPendingTorrents.Text:=FPendingTorrents.Text + s;
  6535.   end;
  6536.  
  6537.   if FAddingTorrent <> 0 then
  6538.     exit;
  6539.  
  6540.   Inc(FAddingTorrent);
  6541.   try
  6542.     if FPendingTorrents.Count > 0 then begin
  6543.       Application.ProcessMessages;
  6544.       TickTimer.Enabled:=True;
  6545.       WasHidden:=not IsTaskbarButtonVisible;
  6546.       if WasHidden then
  6547.         Application.BringToFront
  6548.       else
  6549.         ShowApp;
  6550.       try
  6551.         while FPendingTorrents.Count > 0 do begin
  6552.           s:=FPendingTorrents[0];
  6553.           FPendingTorrents.Delete(0);
  6554.           if s <> '' then
  6555.             DoAddTorrent(s);
  6556.         end;
  6557.       finally
  6558.         if WasHidden then
  6559.           HideTaskbarButton;
  6560.       end;
  6561.     end;
  6562.   finally
  6563.     Dec(FAddingTorrent);
  6564.   end;
  6565. end;
  6566.  
  6567. procedure TMainForm.CheckClipboardLink;
  6568. const
  6569.   strTorrentExt = '.torrent';
  6570. var
  6571.   s: string;
  6572. begin
  6573.   try
  6574.     if not FLinksFromClipboard then
  6575.       exit;
  6576.     s:=Clipboard.AsText;
  6577.     if s = FLastClipboardLink then
  6578.       exit;
  6579.     FLastClipboardLink:=s;
  6580.     if not IsProtocolSupported(s) then
  6581.       exit;
  6582.     if (Pos('magnet:', UTF8LowerCase(s)) <> 1) and (UTF8LowerCase(Copy(s, Length(s) - Length(strTorrentExt) + 1, MaxInt)) <> strTorrentExt) then
  6583.       exit;
  6584.  
  6585.     AddTorrentFile(s);
  6586.     Clipboard.AsText:='';
  6587.   except
  6588.     // Turn off this function if an error occurs
  6589.     FLinksFromClipboard:=False;
  6590.   end;
  6591. end;
  6592.  
  6593. procedure TMainForm.CenterDetailsWait;
  6594. begin
  6595.   panDetailsWait.Left:=PageInfo.Left + (PageInfo.Width - panDetailsWait.Width) div 2;
  6596.   panDetailsWait.Top:=PageInfo.Top + (PageInfo.Height - panDetailsWait.Height) div 2;
  6597. end;
  6598.  
  6599. function TMainForm.GetPageInfoType(pg: TTabSheet): TAdvInfoType;
  6600. begin
  6601.   if pg = tabGeneral then
  6602.     Result:=aiGeneral
  6603.   else
  6604.   if pg = tabPeers then
  6605.     Result:=aiPeers
  6606.   else
  6607.   if pg = tabFiles then
  6608.     Result:=aiFiles
  6609.   else
  6610.   if pg = tabTrackers then
  6611.     Result:=aiTrackers
  6612.   else
  6613.   if pg = tabStats then
  6614.     Result:=aiStats
  6615.   else
  6616.     Result:=aiNone;
  6617. end;
  6618.  
  6619. procedure TMainForm.DetailsUpdated;
  6620. begin
  6621.   FDetailsWaitStart:=0;
  6622.   PageInfo.ActivePage.Tag:=0;
  6623. end;
  6624.  
  6625. function TMainForm.RenameTorrent(TorrentId: integer; const OldPath, NewName: string): boolean;
  6626. var
  6627.   args: TJSONObject;
  6628. begin
  6629.   Result:=False;
  6630.   if ExtractFileName(OldPath) = NewName then
  6631.     exit;
  6632.   args:=TJSONObject.Create;
  6633.   args.Add('path', UTF8Decode(OldPath));
  6634.   args.Add('name', UTF8Decode(NewName));
  6635.   Result:=TorrentAction(TorrentId, 'torrent-rename-path', args);
  6636. end;
  6637.  
  6638. procedure TMainForm.FilesTreeStateChanged(Sender: TObject);
  6639. begin
  6640.   SetCurrentFilePriority('');
  6641. end;
  6642.  
  6643. function TMainForm.SelectTorrent(TorrentId, TimeOut: integer): integer;
  6644. var
  6645.   tt: TDateTime;
  6646.   br: boolean;
  6647. begin
  6648.   Result:=-1;
  6649.   if TorrentId = 0 then
  6650.     exit;
  6651.   br:=False;
  6652.   tt:=Now;
  6653.   while True do begin
  6654.     Application.ProcessMessages;
  6655.     Result:=gTorrents.Items.IndexOf(idxTorrentId, TorrentId);
  6656.     if Result >= 0 then begin
  6657.       gTorrents.RemoveSelection;
  6658.       gTorrents.Row:=Result;
  6659.       RpcObj.CurTorrentId:=TorrentId;
  6660.       if Self.Visible and (Self.WindowState <> wsMinimized) and gTorrents.Enabled then
  6661.         Self.ActiveControl:=gTorrents;
  6662.       break;
  6663.     end;
  6664.     if br then
  6665.       break;
  6666.     Sleep(100);
  6667.     if Now - tt >= TimeOut/MSecsPerDay then
  6668.       br:=True;
  6669.   end;
  6670. end;
  6671.  
  6672. procedure TMainForm.OpenCurrentTorrent(OpenFolderOnly: boolean);
  6673. var
  6674.   res: TJSONObject;
  6675.   p, s: string;
  6676.   sel: boolean;
  6677.   files: TJSONArray;
  6678. begin
  6679.   if gTorrents.Items.Count = 0 then
  6680.     exit;
  6681.   Application.ProcessMessages;
  6682.   AppBusy;
  6683.   try
  6684.     sel:=False;
  6685.     gTorrents.RemoveSelection;
  6686.     res:=RpcObj.RequestInfo(gTorrents.Items[idxTorrentId, gTorrents.Row], ['files', 'downloadDir']);
  6687.     if res = nil then
  6688.       CheckStatus(False)
  6689.     else
  6690.       try
  6691.         with res.Arrays['torrents'].Objects[0] do begin
  6692.           files:=Arrays['files'];
  6693.           if files.Count = 0 then exit;
  6694.           if files.Count = 1 then begin
  6695.             p:=UTF8Encode((files[0] as TJSONObject).Strings['name']);
  6696.             sel:=OpenFolderOnly;
  6697.           end
  6698.           else begin
  6699.             s:=GetFilesCommonPath(files);
  6700.             repeat
  6701.               p:=s;
  6702.               s:=ExtractFilePath(p);
  6703.             until (s = '') or (s = p);
  6704.           end;
  6705.           p:=IncludeTrailingPathDelimiter(UTF8Encode(Strings['downloadDir'])) + p;
  6706.         end;
  6707.       finally
  6708.         res.Free;
  6709.       end;
  6710.     ExecRemoteFile(p, sel);
  6711.   finally
  6712.     AppNormal;
  6713.   end;
  6714. end;
  6715.  
  6716. procedure TMainForm.FillSpeedsMenu;
  6717.  
  6718.   procedure _FillMenu(Items: TMenuItem; const Speeds: string; OnClickHandler: TNotifyEvent; CurSpeed: integer);
  6719.   var
  6720.     sl: TStringList;
  6721.     i, j: integer;
  6722.     mi: TMenuItem;
  6723.   begin
  6724.     Items.Clear;
  6725.     if not RpcObj.Connected then
  6726.       exit;
  6727.     sl:=TStringList.Create;
  6728.     try
  6729.       sl.Delimiter:=',';
  6730.       sl.DelimitedText:=Speeds;
  6731.       i:=0;
  6732.       while i < sl.Count do begin
  6733.         j:=StrToIntDef(Trim(sl[i]), -1);
  6734.         if j >= 0 then begin
  6735.           sl[i]:=Format('%.08d', [j]);
  6736.           Inc(i);
  6737.         end
  6738.         else
  6739.           sl.Delete(i);
  6740.       end;
  6741.       sl.Duplicates:=dupIgnore;
  6742.       sl.Sorted:=True;
  6743.       sl.Add(Format('%.08d', [CurSpeed]));
  6744.  
  6745.       for i:=0 to sl.Count - 1 do begin
  6746.         j:=StrToIntDef(Trim(sl[i]), -1);
  6747.         if j >= 0 then begin
  6748.           mi:=TMenuItem.Create(Items);
  6749.           mi.Caption:=Format('%d %s%s', [j, sKByte, sPerSecond]);
  6750.           mi.Tag:=j;
  6751.           mi.OnClick:=OnClickHandler;
  6752.           if j = CurSpeed then
  6753.             mi.Checked:=True;
  6754.           Items.Insert(0, mi);
  6755.         end;
  6756.       end;
  6757.     finally
  6758.       sl.Free;
  6759.     end;
  6760.     if Items.Count > 0 then begin
  6761.       mi:=TMenuItem.Create(Items);
  6762.       mi.Caption:='-';
  6763.       Items.Insert(0, mi);
  6764.     end;
  6765.     mi:=TMenuItem.Create(Items);
  6766.     mi.Caption:=SUnlimited;
  6767.     mi.Tag:=-1;
  6768.     mi.OnClick:=OnClickHandler;
  6769.     if CurSpeed = -1 then
  6770.       mi.Checked:=True;
  6771.     Items.Insert(0, mi);
  6772.   end;
  6773.  
  6774. var
  6775.   s: string;
  6776. begin
  6777.   s:=Ini.ReadString('Connection.' + FCurConn, 'DownSpeeds', DefSpeeds);
  6778.   _FillMenu(pmDownSpeeds.Items, s, @DoSetDownloadSpeed, FCurDownSpeedLimit);
  6779.   _FillMenu(pmiDownSpeedLimit, s, @DoSetDownloadSpeed, FCurDownSpeedLimit);
  6780.  
  6781.   s:=Ini.ReadString('Connection.' + FCurConn, 'UpSpeeds', DefSpeeds);
  6782.   _FillMenu(pmUpSpeeds.Items, s, @DoSetUploadSpeed, FCurUpSpeedLimit);
  6783.   _FillMenu(pmiUpSpeedLimit, s, @DoSetUploadSpeed, FCurUpSpeedLimit);
  6784. {$ifdef LCLcarbon}
  6785.   TrayIcon.InternalUpdate;
  6786. {$endif LCLcarbon}
  6787. end;
  6788.  
  6789. initialization
  6790.   {$I main.lrs}
  6791.  
  6792. finalization
  6793.   try
  6794.     FreeAndNil(Ini);
  6795.   except
  6796.   end;
  6797. end.
Add Comment
Please, Sign In to add comment