1 /*
  2 Script: deluge-bars.js
  3     Contains all objects and functions related to the statusbar, toolbar and
  4 	sidebar.
  5 
  6 Copyright:
  7 	(C) Damien Churchill 2009 <damoxc@gmail.com>
  8 	This program is free software; you can redistribute it and/or modify
  9 	it under the terms of the GNU General Public License as published by
 10 	the Free Software Foundation; either version 3, or (at your option)
 11 	any later version.
 12 
 13 	This program is distributed in the hope that it will be useful,
 14 	but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16 	GNU General Public License for more details.
 17 
 18 	You should have received a copy of the GNU General Public License
 19 	along with this program.  If not, write to:
 20 		The Free Software Foundation, Inc.,
 21 		51 Franklin Street, Fifth Floor
 22 		Boston, MA  02110-1301, USA.
 23 
 24     In addition, as a special exception, the copyright holders give
 25     permission to link the code of portions of this program with the OpenSSL
 26     library.
 27     You must obey the GNU General Public License in all respects for all of
 28     the code used other than OpenSSL. If you modify file(s) with this
 29     exception, you may extend this exception to your version of the file(s),
 30     but you are not obligated to do so. If you do not wish to do so, delete
 31     this exception statement from your version. If you delete this exception
 32     statement from all source files in the program, then also delete it here.
 33 
 34 */
 35 
 36 // These are just so gen_gettext.py will pick up the strings
 37 // _('State')
 38 // _('Tracker Host')
 39 
 40 (function() {
 41 	// Renderer for the items in the filter grids.
 42 	function filterRenderer(value, p, r) {
 43 		var lname = value.toLowerCase().replace('.', '_');
 44 		
 45 		var image = '';	
 46 		if (r.store.id == 'tracker_host') {
 47 			if (value != 'Error') {
 48 				image = String.format('url(/tracker/{0})', value);
 49 			} else {
 50 				lname = null;
 51 			}
 52 		}
 53 		if (image) {
 54 			return String.format('<div class="x-deluge-filter" style="background-image: {2};">{0} ({1})</div>', value, r.data['count'], image);
 55 		} else if (lname) {
 56 			return String.format('<div class="x-deluge-filter x-deluge-{2}">{0} ({1})</div>', value, r.data['count'], lname);
 57 		} else {
 58 			return String.format('<div class="x-deluge-filter">{0} ({1})</div>', value, r.data['count']);
 59 		}
 60 	}
 61 	
 62 	Ext.deluge.Sidebar = Ext.extend(Ext.Panel, {
 63 		
 64 		// private
 65 		panels: {},
 66 		
 67 		// private
 68 		selected: null,
 69 		
 70 		constructor: function(config) {
 71 			config = Ext.apply({
 72 				id: 'sidebar',
 73 				region: 'west',
 74 				cls: 'deluge-sidebar',
 75 				title: _('Filters'),
 76 				layout: 'accordion',
 77 				split: true,
 78 				width: 200,
 79 				minSize: 175,
 80 				collapsible: true,
 81 				margins: '5 0 0 5',
 82 				cmargins: '5 0 0 5'
 83 			}, config);
 84 			Ext.deluge.Sidebar.superclass.constructor.call(this, config);
 85 		},
 86 		
 87 		// private
 88 		initComponent: function() {
 89 			Ext.deluge.Sidebar.superclass.initComponent.call(this);
 90 			Deluge.Events.on("disconnect", this.onDisconnect, this);
 91 		},
 92 		
 93 		createFilter: function(filter, states) {
 94 			var store = new Ext.data.SimpleStore({
 95 				id: filter,
 96 				fields: [
 97 					{name: 'filter'},
 98 					{name: 'count'}
 99 				]
100 			});
101 			
102 			var title = filter.replace('_', ' ');
103 			var parts = title.split(' ');
104 			title = '';
105 			Ext.each(parts, function(part) {
106 				firstLetter = part.substring(0, 1);
107 				firstLetter = firstLetter.toUpperCase();
108 				part = firstLetter + part.substring(1);
109 				title += part + ' ';
110 			});
111 			
112 			var panel = new Ext.grid.GridPanel({
113 				id: filter + '-panel',
114 				store: store,
115 				title: _(title),
116 				columns: [
117 					{id: 'filter', sortable: false, renderer: filterRenderer, dataIndex: 'filter'}
118 				],	
119 				stripeRows: false,
120 				selModel: new Ext.grid.RowSelectionModel({
121 					singleSelect: true,
122 					listeners: {
123 						'rowselect': {fn: this.onFilterSelect, scope: this}
124 					}
125 				}),
126 				hideHeaders: true,
127 				autoExpandColumn: 'filter',
128 				deferredRender: false,
129 				autoScroll: true
130 			});
131 			
132 			if (Deluge.config['sidebar_show_zero'] == false) {
133 				states = this.removeZero(states);
134 			}
135 			
136 			store.loadData(states);
137 			this.add(panel);
138 			
139 			this.doLayout();
140 			this.panels[filter] = panel;
141 			
142 			if (!this.selected) {
143 				panel.getSelectionModel().selectFirstRow();
144 				this.selected = {
145 					row: 0,
146 					filter: states[0][0],
147 					panel: panel
148 				}
149 			}
150 		},
151 		
152 		getFilters: function() {
153 			var filters = {}
154 			if (!this.selected) {
155 				return filters;
156 			}
157 			if (!this.selected.filter || !this.selected.panel) {
158 				return filters;
159 			}
160 			var filterType = this.selected.panel.store.id;
161 			if (filterType == "state" && this.selected.filter == "All") {
162 				return filters;
163 			}
164 			
165 			filters[filterType] = this.selected.filter;
166 			return filters;
167 		},
168 		
169 		// private
170 		onDisconnect: function() {
171 			Ext.each(Ext.getKeys(this.panels), function(filter) {
172 				this.remove(filter + '-panel');
173 			}, this);
174 			this.panels = {};
175 			this.selected = null;
176 		},
177 		
178 		onFilterSelect: function(selModel, rowIndex, record) {
179 			if (!this.selected) needsUpdate = true;
180 			else if (this.selected.row != rowIndex) needsUpdate = true;
181 			else needsUpdate = false;
182 			this.selected = {
183 				row: rowIndex,
184 				filter: record.get('filter'),
185 				panel: this.panels[record.store.id]
186 			}
187 			
188 			if (needsUpdate) Deluge.UI.update();
189 		},
190 		
191 		/**
192 		 * Remove the states with zero torrents in them.
193 		 */
194 		removeZero: function(states) {
195 			var newStates = [];
196 			Ext.each(states, function(state) {
197 				if (state[1] > 0) {
198 					newStates.push(state);
199 				}
200 			});
201 			return newStates;
202 		},
203 		
204 		update: function(filters) {
205 			for (var filter in filters) {
206 				var states = filters[filter];
207 				if (Ext.getKeys(this.panels).indexOf(filter) > -1) {
208 					this.updateFilter(filter, states);
209 				} else {
210 					this.createFilter(filter, states);
211 				}
212 			}
213 			
214 			// Perform a cleanup of fitlers that aren't enabled any more
215 			Ext.each(Ext.keys(this.panels), function(filter) {
216 				if (Ext.keys(filters).indexOf(filter) == -1) {
217 					// We need to remove the panel
218 					this.panels[filter]
219 				}
220 			});
221 		},
222 		
223 		updateFilter: function(filter, states) {
224 			if (Deluge.config['sidebar_show_zero'] == false) {
225 				states = this.removeZero(states);
226 			}
227 			
228 			this.panels[filter].store.loadData(states);
229 			if (this.selected && this.selected.panel == this.panels[filter]) {
230 				this.panels[filter].getSelectionModel().selectRow(this.selected.row);
231 			}
232 		}
233 		
234 	});
235 	Deluge.Sidebar = new Ext.deluge.Sidebar();
236 })();