      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();