      1 /*
      2 Script: Deluge.Add.js
      3     Contains the Add Torrent window.
      4
      5 Copyright:
      6 	(C) Damien Churchill 2009 <damoxc@gmail.com>
      7 	This program is free software; you can redistribute it and/or modify
      8 	it under the terms of the GNU General Public License as published by
      9 	the Free Software Foundation; either version 3, or (at your option)
     10 	any later version.
     11
     12 	This program is distributed in the hope that it will be useful,
     13 	but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 	GNU General Public License for more details.
     16
     17 	You should have received a copy of the GNU General Public License
     18 	along with this program.  If not, write to:
     19 		The Free Software Foundation, Inc.,
     20 		51 Franklin Street, Fifth Floor
     21 		Boston, MA  02110-1301, USA.
     22 #
     23 #    In addition, as a special exception, the copyright holders give
     24 #    permission to link the code of portions of this program with the OpenSSL
     25 #    library.
     26 #    You must obey the GNU General Public License in all respects for all of
     27 #    the code used other than OpenSSL. If you modify file(s) with this
     28 #    exception, you may extend this exception to your version of the file(s),
     29 #    but you are not obligated to do so. If you do not wish to do so, delete
     30 #    this exception statement from your version. If you delete this exception
     31 #    statement from all source files in the program, then also delete it here.
     32 #
     33 */
     34
     35 Ext.namespace('Ext.deluge.add');
     36 Ext.deluge.add.OptionsPanel = Ext.extend(Ext.TabPanel, {
     37
     38 	constructor: function(config) {
     39 		config = Ext.apply({
     40 			region: 'south',
     41 			margins: '5 5 5 5',
     42 			activeTab: 0,
     43 			height: 220
     44 		}, config);
     45 		Ext.deluge.add.OptionsPanel.superclass.constructor.call(this, config);
     46 	},
     47
     48 	initComponent: function() {
     49 		Ext.deluge.add.OptionsPanel.superclass.initComponent.call(this);
     50 		this.files = this.add(new Ext.tree.ColumnTree({
     51 			layout: 'fit',
     52 			title: _('Files'),
     53 			rootVisible: false,
     54 			autoScroll: true,
     55 			height: 170,
     56 			border: false,
     57 			animate: false,
     58
     59 			columns: [{
     60 				header: _('Filename'),
     61 				width: 275,
     62 				dataIndex: 'filename'
     63 			},{
     64 				header: _('Size'),
     65 				width: 80,
     66 				dataIndex: 'size'
     67 			}],
     68
     69 			root: new Ext.tree.AsyncTreeNode({
     70 				text: 'Files'
     71 			})
     72 		}));
     73 		new Ext.tree.TreeSorter(this.files, {
     74 			folderSort: true
     75 		});
     76
     77 		this.form = this.add({
     78 			xtype: 'form',
     79 			labelWidth: 1,
     80 			frame: false,
     81 			title: _('Options'),
     82 			bodyStyle: 'padding: 5px;',
     83 			border: false,
     84
     85
     86 			items: [{
     87 				xtype: 'fieldset',
     88 				title: _('Download Location'),
     89 				border: false,
     90 				defaultType: 'textfield',
     91 				labelWidth: 1,
     92 				items: [{
     93 					fieldLabel: '',
     94 					labelSeperator: '',
     95 					name: 'download_location',
     96 					width: 330
     97 				}]
     98 			}]
     99 		});
    100 	},
    101
    102 	clear: function() {
    103 		this.clearFiles();
    104 	},
    105
    106 	clearFiles: function() {
    107 		var root = this.files.getRootNode();
    108 		if (!root.hasChildNodes()) return;
    109 		root.cascade(function(node) {
    110 			if (!node.parentNode || !node.getOwnerTree()) return;
    111 			node.remove();
    112 		});
    113 	},
    114
    115 	getDefaults: function() {
    116 		var keys = [
    117             'add_paused',
    118             'compact_allocation',
    119             'download_location',
    120             'max_connections_per_torrent',
    121             'max_download_speed_per_torrent',
    122             'max_upload_slots_per_torrent',
    123             'max_upload_speed_per_torrent',
    124             'prioritize_first_last_pieces'
    125         ]
    126         Deluge.Client.core.get_config_values(keys, {
    127             success: function(config) {
    128 				this.defaults = config;
    129 				for (var key in config) {
    130 					var field = this.form.findField(key);
    131 					if (!field) return;
    132 					field.setValue(config[key]);
    133 				}
    134 				var field = this.form.findField('compact_allocation');
    135 				if (config['compact_allocation']) {
    136 					field.items.get('compact_allocation_true').setValue(true);
    137 					field.items.get('compact_allocation_false').setValue(false);
    138 				} else {
    139 					field.items.get('compact_allocation_false').setValue(true);
    140 					field.items.get('compact_allocation_true').setValue(false);
    141 				}
    142 			},
    143 			scope: this
    144         });
    145 	}
    146 });
    147
    148 Ext.deluge.add.Window = Ext.extend(Ext.Window, {
    149 	initComponent: function() {
    150 		Ext.deluge.add.Window.superclass.initComponent.call(this);
    151 		this.addEvents(
    152 			'beforeadd',
    153 			'add'
    154 		);
    155 	},
    156
    157 	createTorrentId: function() {
    158 		return new Date().getTime();
    159 	}
    160 });
    161
    162 Ext.deluge.add.AddWindow = Ext.extend(Ext.deluge.add.Window, {
    163
    164 	torrents: {},
    165
    166 	constructor: function(config) {
    167 		config = Ext.apply({
    168 			title: _('Add Torrents'),
    169 			layout: 'border',
    170 			width: 470,
    171 			height: 450,
    172 			bodyStyle: 'padding: 10px 5px;',
    173 			buttonAlign: 'right',
    174 			closeAction: 'hide',
    175 			closable: true,
    176 			plain: true,
    177 			iconCls: 'x-deluge-add-window-icon'
    178 		}, config);
    179 		Ext.deluge.add.AddWindow.superclass.constructor.call(this, config);
    180 	},
    181
    182 	initComponent: function() {
    183 		Ext.deluge.add.AddWindow.superclass.initComponent.call(this);
    184
    185 		this.addButton(_('Cancel'), this.onCancel, this);
    186 		this.addButton(_('Add'), this.onAdd, this);
    187
    188 		function torrentRenderer(value, p, r) {
    189 			if (r.data['info_hash']) {
    190 				return String.format('<div class="x-deluge-add-torrent-name">{0}</div>', value);
    191 			} else {
    192 				return String.format('<div class="x-deluge-add-torrent-name-loading">{0}</div>', value);
    193 			}
    194 		}
    195
    196 		this.grid = this.add({
    197 			xtype: 'grid',
    198 			region: 'center',
    199 			store: new Ext.data.SimpleStore({
    200 				fields: [
    201 					{name: 'info_hash', mapping: 1},
    202 					{name: 'text', mapping: 2}
    203 				],
    204 				id: 0
    205 			}),
    206 			columns: [{
    207 					id: 'torrent',
    208 					width: 150,
    209 					sortable: true,
    210 					renderer: torrentRenderer,
    211 					dataIndex: 'text'
    212 			}],
    213 			stripeRows: true,
    214 			selModel: new Ext.grid.RowSelectionModel({
    215 				singleSelect: true,
    216 				listeners: {
    217 					'rowselect': {
    218 						fn: this.onSelect,
    219 						scope: this
    220 					}
    221 				}
    222 			}),
    223 			hideHeaders: true,
    224 			autoExpandColumn: 'torrent',
    225 			deferredRender: false,
    226 			autoScroll: true,
    227 			margins: '5 5 5 5',
    228 			bbar: new Ext.Toolbar({
    229 				items: [{
    230 					id: 'file',
    231 					cls: 'x-btn-text-icon',
    232 					iconCls: 'x-deluge-add-file',
    233 					text: _('File'),
    234 					handler: this.onFile,
    235 					scope: this
    236 				}, {
    237 					id: 'url',
    238 					cls: 'x-btn-text-icon',
    239 					text: _('Url'),
    240 					icon: '/icons/add_url.png',
    241 					handler: this.onUrl,
    242 					scope: this
    243 				}, {
    244 					id: 'infohash',
    245 					cls: 'x-btn-text-icon',
    246 					text: _('Infohash'),
    247 					icon: '/icons/add_magnet.png',
    248 					disabled: true
    249 				}, '->', {
    250 					id: 'remove',
    251 					cls: 'x-btn-text-icon',
    252 					text: _('Remove'),
    253 					icon: '/icons/remove.png',
    254 					handler: this.onRemove,
    255 					scope: this
    256 				}]
    257 			})
    258 		});
    259
    260 		this.options = this.add(new Ext.deluge.add.OptionsPanel());
    261 		this.on('show', this.onShow, this);
    262 	},
    263
    264 	clear: function() {
    265 		this.torrents = {};
    266 		this.grid.getStore().removeAll();
    267 		this.options.clear();
    268 	},
    269
    270 	onAdd: function() {
    271 		torrents = [];
    272 		for (var id in this.torrents) {
    273 			var info = this.torrents[id];
    274 			torrents.push({
    275 				path: info['filename'],
    276 				options: {}
    277 			});
    278 		}
    279 		Deluge.Client.web.add_torrents(torrents, {
    280 			success: function(result) {
    281 			}
    282 		})
    283 		this.clear();
    284 		this.hide();
    285 	},
    286
    287 	onCancel: function() {
    288 		this.clear();
    289 		this.hide();
    290 	},
    291
    292 	onFile: function() {
    293 		this.file.show();
    294 	},
    295
    296 	onRemove: function() {
    297 		var selection = this.grid.getSelectionModel();
    298 		if (!selection.hasSelection()) return;
    299 		var torrent = selection.getSelected();
    300
    301 		delete this.torrents[torrent.id];
    302 		this.grid.getStore().remove(torrent);
    303 		this.options.clear();
    304 	},
    305
    306 	onSelect: function(selModel, rowIndex, record) {
    307 		var torrentInfo = this.torrents[record.get('info_hash')];
    308
    309 		function walk(files, parent) {
    310 			for (var file in files) {
    311 				var item = files[file];
    312
    313 				if (Ext.type(item) == 'object') {
    314 					var child = new Ext.tree.TreeNode({
    315 						text: file
    316 					});
    317 					walk(item, child);
    318 					parent.appendChild(child);
    319 				} else {
    320 					parent.appendChild(new Ext.tree.TreeNode({
    321 						filename: file,
    322 						text: file, // this needs to be here for sorting reasons
    323 						size: fsize(item[0]),
    324 						leaf: true,
    325 						checked: item[1],
    326 						iconCls: 'x-deluge-file',
    327 						uiProvider: Ext.tree.ColumnNodeUI
    328 					}));
    329 				}
    330 			}
    331 		}
    332
    333 		this.options.clearFiles();
    334 		var root = this.options.files.getRootNode();
    335 		walk(torrentInfo['files_tree'], root);
    336 		root.firstChild.expand();
    337 	},
    338
    339 	onShow: function() {
    340 		if (!this.url) {
    341 			this.url = new Ext.deluge.add.UrlWindow();
    342 			this.url.on('beforeadd', this.onTorrentBeforeAdd, this);
    343 			this.url.on('add', this.onTorrentAdd, this);
    344 		}
    345
    346 		if (!this.file) {
    347 			this.file = new Ext.deluge.add.FileWindow();
    348 			this.file.on('beforeadd', this.onTorrentBeforeAdd, this);
    349 			this.file.on('add', this.onTorrentAdd, this);
    350 		}
    351 	},
    352
    353 	onTorrentBeforeAdd: function(torrentId, text) {
    354 		var store = this.grid.getStore();
    355 		store.loadData([[torrentId, null, text]], true);
    356 	},
    357
    358 	onTorrentAdd: function(torrentId, info) {
    359 		if (!info) {
    360 			Ext.MessageBox.show({
    361 				title: _('Error'),
    362 				msg: _('Not a valid torrent'),
    363 				buttons: Ext.MessageBox.OK,
    364 				modal: false,
    365 				icon: Ext.MessageBox.ERROR,
    366 				iconCls: 'x-deluge-icon-error'
    367 			});
    368 			return;
    369 		}
    370
    371 		var r = this.grid.getStore().getById(torrentId);
    372 		r.set('info_hash', info['info_hash']);
    373 		r.set('text', info['name']);
    374 		this.grid.getStore().commitChanges();
    375 		this.torrents[info['info_hash']] = info;
    376 	},
    377
    378 	onUrl: function(button, event) {
    379 		this.url.show();
    380 	}
    381 });
    382 Deluge.Add = new Ext.deluge.add.AddWindow();