1# -*- coding: utf-8 -*-
2#
3# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
4#
5# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
6# the additional special exception to link portions of this program with the OpenSSL library.
7# See LICENSE for more details.
8#
9
10from __future__ import unicode_literals
11
12import logging
13
14from deluge.common import is_ip
15from deluge.decorators import overrides
16from deluge.i18n import get_languages
17from deluge.ui.client import client
18from deluge.ui.common import DISK_CACHE_KEYS
19from deluge.ui.console.widgets import BaseInputPane, BaseWindow
20from deluge.ui.console.widgets.fields import FloatSpinInput, TextInput
21from deluge.ui.console.widgets.popup import PopupsHandler
22
23log = logging.getLogger(__name__)
24
25
26class BasePreferencePane(BaseInputPane, BaseWindow, PopupsHandler):
27    def __init__(self, name, preferences):
28        PopupsHandler.__init__(self)
29        self.preferences = preferences
30        BaseWindow.__init__(
31            self,
32            '%s' % name,
33            self.pane_width,
34            preferences.height,
35            posy=1,
36            posx=self.pane_x_pos,
37        )
38        BaseInputPane.__init__(self, preferences, border_off_east=1)
39        self.name = name
40
41        # have we scrolled down in the list
42        self.input_offset = 0
43
44    @overrides(BaseInputPane)
45    def handle_read(self, c):
46        if self.popup:
47            ret = self.popup.handle_read(c)
48            if self.popup and self.popup.closed():
49                self.pop_popup()
50            self.refresh()
51            return ret
52        return BaseInputPane.handle_read(self, c)
53
54    @property
55    def visible_content_pane_height(self):
56        y, x = self.visible_content_pane_size
57        return y
58
59    @property
60    def pane_x_pos(self):
61        return self.preferences.sidebar_width
62
63    @property
64    def pane_width(self):
65        return self.preferences.width
66
67    @property
68    def cols(self):
69        return self.pane_width
70
71    @property
72    def rows(self):
73        return self.preferences.height
74
75    def is_active_pane(self):
76        return self.preferences.is_active_pane(self)
77
78    def create_pane(self, core_conf, console_config):
79        pass
80
81    def add_config_values(self, conf_dict):
82        for ipt in self.inputs:
83            if ipt.has_input():
84                # Need special cases for in/out ports or proxy since they are tuples or dicts.
85                if ipt.name == 'listen_ports_to' or ipt.name == 'listen_ports_from':
86                    conf_dict['listen_ports'] = (
87                        self.infrom.get_value(),
88                        self.into.get_value(),
89                    )
90                elif ipt.name == 'out_ports_to' or ipt.name == 'out_ports_from':
91                    conf_dict['outgoing_ports'] = (
92                        self.outfrom.get_value(),
93                        self.outto.get_value(),
94                    )
95                elif ipt.name == 'listen_interface':
96                    listen_interface = ipt.get_value().strip()
97                    if is_ip(listen_interface) or not listen_interface:
98                        conf_dict['listen_interface'] = listen_interface
99                elif ipt.name == 'outgoing_interface':
100                    outgoing_interface = ipt.get_value().strip()
101                    conf_dict['outgoing_interface'] = outgoing_interface
102                elif ipt.name.startswith('proxy_'):
103                    if ipt.name == 'proxy_type':
104                        conf_dict.setdefault('proxy', {})['type'] = ipt.get_value()
105                    elif ipt.name == 'proxy_username':
106                        conf_dict.setdefault('proxy', {})['username'] = ipt.get_value()
107                    elif ipt.name == 'proxy_password':
108                        conf_dict.setdefault('proxy', {})['password'] = ipt.get_value()
109                    elif ipt.name == 'proxy_hostname':
110                        conf_dict.setdefault('proxy', {})['hostname'] = ipt.get_value()
111                    elif ipt.name == 'proxy_port':
112                        conf_dict.setdefault('proxy', {})['port'] = ipt.get_value()
113                    elif ipt.name == 'proxy_hostnames':
114                        conf_dict.setdefault('proxy', {})[
115                            'proxy_hostnames'
116                        ] = ipt.get_value()
117                    elif ipt.name == 'proxy_peer_connections':
118                        conf_dict.setdefault('proxy', {})[
119                            'proxy_peer_connections'
120                        ] = ipt.get_value()
121                    elif ipt.name == 'proxy_tracker_connections':
122                        conf_dict.setdefault('proxy', {})[
123                            'proxy_tracker_connections'
124                        ] = ipt.get_value()
125                elif ipt.name == 'force_proxy':
126                    conf_dict.setdefault('proxy', {})['force_proxy'] = ipt.get_value()
127                elif ipt.name == 'anonymous_mode':
128                    conf_dict.setdefault('proxy', {})[
129                        'anonymous_mode'
130                    ] = ipt.get_value()
131                else:
132                    conf_dict[ipt.name] = ipt.get_value()
133
134                if hasattr(ipt, 'get_child'):
135                    c = ipt.get_child()
136                    conf_dict[c.name] = c.get_value()
137
138    def update_values(self, conf_dict):
139        for ipt in self.inputs:
140            if ipt.has_input():
141                try:
142                    ipt.set_value(conf_dict[ipt.name])
143                except KeyError:  # just ignore if it's not in dict
144                    pass
145                if hasattr(ipt, 'get_child'):
146                    try:
147                        c = ipt.get_child()
148                        c.set_value(conf_dict[c.name])
149                    except KeyError:  # just ignore if it's not in dict
150                        pass
151
152    def render(self, mode, screen, width, focused):
153        height = self.get_content_height()
154        self.ensure_content_pane_height(height)
155        self.screen.erase()
156
157        if focused and self.active_input == -1:
158            self.move_active_down(1)
159
160        self.render_inputs(focused=focused)
161
162    @overrides(BaseWindow)
163    def refresh(self):
164        BaseWindow.refresh(self)
165        if self.popup:
166            self.popup.refresh()
167
168    def update(self, active):
169        pass
170
171
172class InterfacePane(BasePreferencePane):
173    def __init__(self, preferences):
174        BasePreferencePane.__init__(self, ' %s ' % _('Interface'), preferences)
175
176    @overrides(BasePreferencePane)
177    def create_pane(self, core_conf, console_config):
178        self.add_header(_('General options'))
179
180        self.add_checked_input(
181            'ring_bell',
182            _('Ring system bell when a download finishes'),
183            console_config['ring_bell'],
184        )
185        self.add_header('Console UI', space_above=True)
186        self.add_checked_input(
187            'separate_complete',
188            _('List complete torrents after incomplete regardless of sorting order'),
189            console_config['torrentview']['separate_complete'],
190        )
191        self.add_checked_input(
192            'move_selection',
193            _('Move selection when moving torrents in the queue'),
194            console_config['torrentview']['move_selection'],
195        )
196
197        langs = get_languages()
198        langs.insert(0, ('', 'System Default'))
199        self.add_combo_input(
200            'language', _('Language'), langs, default=console_config['language']
201        )
202        self.add_header(_('Command Line Mode'), space_above=True)
203        self.add_checked_input(
204            'ignore_duplicate_lines',
205            _('Do not store duplicate input in history'),
206            console_config['cmdline']['ignore_duplicate_lines'],
207        )
208        self.add_checked_input(
209            'save_command_history',
210            _('Store and load command line history in command line mode'),
211            console_config['cmdline']['save_command_history'],
212        )
213        self.add_header('')
214        self.add_checked_input(
215            'third_tab_lists_all',
216            _('Third tab lists all remaining torrents in command line mode'),
217            console_config['cmdline']['third_tab_lists_all'],
218        )
219        self.add_int_spin_input(
220            'torrents_per_tab_press',
221            _('Torrents per tab press'),
222            console_config['cmdline']['torrents_per_tab_press'],
223            min_val=5,
224            max_val=10000,
225        )
226
227
228class DownloadsPane(BasePreferencePane):
229    def __init__(self, preferences):
230        BasePreferencePane.__init__(self, ' %s ' % _('Downloads'), preferences)
231
232    @overrides(BasePreferencePane)
233    def create_pane(self, core_conf, console_config):
234        self.add_header(_('Folders'))
235        self.add_text_input(
236            'download_location',
237            '%s:' % _('Download To'),
238            core_conf['download_location'],
239            complete=True,
240            activate_input=True,
241            col='+1',
242        )
243        cmptxt = TextInput(
244            self.preferences,
245            'move_completed_path',
246            None,
247            self.move,
248            self.pane_width,
249            core_conf['move_completed_path'],
250            False,
251        )
252        self.add_checkedplus_input(
253            'move_completed',
254            '%s:' % _('Move completed to'),
255            cmptxt,
256            core_conf['move_completed'],
257        )
258        copytxt = TextInput(
259            self.preferences,
260            'torrentfiles_location',
261            None,
262            self.move,
263            self.pane_width,
264            core_conf['torrentfiles_location'],
265            False,
266        )
267        self.add_checkedplus_input(
268            'copy_torrent_file',
269            '%s:' % _('Copy of .torrent files to'),
270            copytxt,
271            core_conf['copy_torrent_file'],
272        )
273        self.add_checked_input(
274            'del_copy_torrent_file',
275            _('Delete copy of torrent file on remove'),
276            core_conf['del_copy_torrent_file'],
277        )
278
279        self.add_header(_('Options'), space_above=True)
280        self.add_checked_input(
281            'prioritize_first_last_pieces',
282            ('Prioritize first and last pieces of torrent'),
283            core_conf['prioritize_first_last_pieces'],
284        )
285        self.add_checked_input(
286            'sequential_download',
287            _('Sequential download'),
288            core_conf['sequential_download'],
289        )
290        self.add_checked_input('add_paused', _('Add Paused'), core_conf['add_paused'])
291        self.add_checked_input(
292            'pre_allocate_storage',
293            _('Pre-Allocate disk space'),
294            core_conf['pre_allocate_storage'],
295        )
296
297
298class NetworkPane(BasePreferencePane):
299    def __init__(self, preferences):
300        BasePreferencePane.__init__(self, ' %s ' % _('Network'), preferences)
301
302    @overrides(BasePreferencePane)
303    def create_pane(self, core_conf, console_config):
304        self.add_header(_('Incomming Ports'))
305        inrand = self.add_checked_input(
306            'random_port',
307            'Use Random Ports    Active Port: %d' % self.preferences.active_port,
308            core_conf['random_port'],
309        )
310        listen_ports = core_conf['listen_ports']
311        self.infrom = self.add_int_spin_input(
312            'listen_ports_from',
313            '    %s:' % _('From'),
314            value=listen_ports[0],
315            min_val=0,
316            max_val=65535,
317        )
318        self.infrom.set_depend(inrand, inverse=True)
319        self.into = self.add_int_spin_input(
320            'listen_ports_to',
321            '    %s:' % _('To'),
322            value=listen_ports[1],
323            min_val=0,
324            max_val=65535,
325        )
326        self.into.set_depend(inrand, inverse=True)
327
328        self.add_header(_('Outgoing Ports'), space_above=True)
329        outrand = self.add_checked_input(
330            'random_outgoing_ports',
331            _('Use Random Ports'),
332            core_conf['random_outgoing_ports'],
333        )
334        out_ports = core_conf['outgoing_ports']
335        self.outfrom = self.add_int_spin_input(
336            'out_ports_from',
337            '    %s:' % _('From'),
338            value=out_ports[0],
339            min_val=0,
340            max_val=65535,
341        )
342        self.outfrom.set_depend(outrand, inverse=True)
343        self.outto = self.add_int_spin_input(
344            'out_ports_to',
345            '    %s:' % _('To'),
346            value=out_ports[1],
347            min_val=0,
348            max_val=65535,
349        )
350        self.outto.set_depend(outrand, inverse=True)
351
352        self.add_header(_('Incoming Interface'), space_above=True)
353        self.add_text_input(
354            'listen_interface',
355            _('IP address of the interface to listen on (leave empty for default):'),
356            core_conf['listen_interface'],
357        )
358
359        self.add_header(_('Outgoing Interface'), space_above=True)
360        self.add_text_input(
361            'outgoing_interface',
362            _(
363                'The network interface name or IP address for outgoing '
364                'BitTorrent connections. (Leave empty for default.):'
365            ),
366            core_conf['outgoing_interface'],
367        )
368
369        self.add_header('TOS', space_above=True)
370        self.add_text_input('peer_tos', 'Peer TOS Byte:', core_conf['peer_tos'])
371
372        self.add_header(_('Network Extras'), space_above=True)
373        self.add_checked_input('upnp', 'UPnP', core_conf['upnp'])
374        self.add_checked_input('natpmp', 'NAT-PMP', core_conf['natpmp'])
375        self.add_checked_input('utpex', 'Peer Exchange', core_conf['utpex'])
376        self.add_checked_input('lsd', 'LSD', core_conf['lsd'])
377        self.add_checked_input('dht', 'DHT', core_conf['dht'])
378
379        self.add_header(_('Encryption'), space_above=True)
380        self.add_select_input(
381            'enc_in_policy',
382            '%s:' % _('Inbound'),
383            [_('Forced'), _('Enabled'), _('Disabled')],
384            [0, 1, 2],
385            core_conf['enc_in_policy'],
386            active_default=True,
387            col='+1',
388        )
389        self.add_select_input(
390            'enc_out_policy',
391            '%s:' % _('Outbound'),
392            [_('Forced'), _('Enabled'), _('Disabled')],
393            [0, 1, 2],
394            core_conf['enc_out_policy'],
395            active_default=True,
396        )
397        self.add_select_input(
398            'enc_level',
399            '%s:' % _('Level'),
400            [_('Handshake'), _('Full Stream'), _('Either')],
401            [0, 1, 2],
402            core_conf['enc_level'],
403            active_default=True,
404        )
405
406
407class BandwidthPane(BasePreferencePane):
408    def __init__(self, preferences):
409        BasePreferencePane.__init__(self, ' %s ' % _('Bandwidth'), preferences)
410
411    @overrides(BasePreferencePane)
412    def create_pane(self, core_conf, console_config):
413        self.add_header(_('Global Bandwidth Usage'))
414        self.add_int_spin_input(
415            'max_connections_global',
416            '%s:' % _('Maximum Connections'),
417            core_conf['max_connections_global'],
418            min_val=-1,
419            max_val=9000,
420        )
421        self.add_int_spin_input(
422            'max_upload_slots_global',
423            '%s:' % _('Maximum Upload Slots'),
424            core_conf['max_upload_slots_global'],
425            min_val=-1,
426            max_val=9000,
427        )
428        self.add_float_spin_input(
429            'max_download_speed',
430            '%s:' % _('Maximum Download Speed (KiB/s)'),
431            core_conf['max_download_speed'],
432            min_val=-1.0,
433            max_val=60000.0,
434        )
435        self.add_float_spin_input(
436            'max_upload_speed',
437            '%s:' % _('Maximum Upload Speed (KiB/s)'),
438            core_conf['max_upload_speed'],
439            min_val=-1.0,
440            max_val=60000.0,
441        )
442        self.add_int_spin_input(
443            'max_half_open_connections',
444            '%s:' % _('Maximum Half-Open Connections'),
445            core_conf['max_half_open_connections'],
446            min_val=-1,
447            max_val=9999,
448        )
449        self.add_int_spin_input(
450            'max_connections_per_second',
451            '%s:' % _('Maximum Connection Attempts per Second'),
452            core_conf['max_connections_per_second'],
453            min_val=-1,
454            max_val=9999,
455        )
456        self.add_checked_input(
457            'ignore_limits_on_local_network',
458            _('Ignore limits on local network'),
459            core_conf['ignore_limits_on_local_network'],
460        )
461        self.add_checked_input(
462            'rate_limit_ip_overhead',
463            _('Rate Limit IP Overhead'),
464            core_conf['rate_limit_ip_overhead'],
465        )
466        self.add_header(_('Per Torrent Bandwidth Usage'), space_above=True)
467        self.add_int_spin_input(
468            'max_connections_per_torrent',
469            '%s:' % _('Maximum Connections'),
470            core_conf['max_connections_per_torrent'],
471            min_val=-1,
472            max_val=9000,
473        )
474        self.add_int_spin_input(
475            'max_upload_slots_per_torrent',
476            '%s:' % _('Maximum Upload Slots'),
477            core_conf['max_upload_slots_per_torrent'],
478            min_val=-1,
479            max_val=9000,
480        )
481        self.add_float_spin_input(
482            'max_download_speed_per_torrent',
483            '%s:' % _('Maximum Download Speed (KiB/s)'),
484            core_conf['max_download_speed_per_torrent'],
485            min_val=-1.0,
486            max_val=60000.0,
487        )
488        self.add_float_spin_input(
489            'max_upload_speed_per_torrent',
490            '%s:' % _('Maximum Upload Speed (KiB/s)'),
491            core_conf['max_upload_speed_per_torrent'],
492            min_val=-1.0,
493            max_val=60000.0,
494        )
495
496
497class OtherPane(BasePreferencePane):
498    def __init__(self, preferences):
499        BasePreferencePane.__init__(self, ' %s ' % _('Other'), preferences)
500
501    @overrides(BasePreferencePane)
502    def create_pane(self, core_conf, console_config):
503        self.add_header(_('System Information'))
504        self.add_info_field('info1', ' Help us improve Deluge by sending us your', '')
505        self.add_info_field(
506            'info2', ' Python version, PyGTK version, OS and processor', ''
507        )
508        self.add_info_field(
509            'info3', ' types.  Absolutely no other information is sent.', ''
510        )
511        self.add_checked_input(
512            'send_info',
513            _('Yes, please send anonymous statistics.'),
514            core_conf['send_info'],
515        )
516        self.add_header(_('GeoIP Database'), space_above=True)
517        self.add_text_input(
518            'geoip_db_location', 'Location:', core_conf['geoip_db_location']
519        )
520
521
522class DaemonPane(BasePreferencePane):
523    def __init__(self, preferences):
524        BasePreferencePane.__init__(self, ' %s ' % _('Daemon'), preferences)
525
526    @overrides(BasePreferencePane)
527    def create_pane(self, core_conf, console_config):
528        self.add_header('Port')
529        self.add_int_spin_input(
530            'daemon_port',
531            '%s:' % _('Daemon Port'),
532            core_conf['daemon_port'],
533            min_val=0,
534            max_val=65535,
535        )
536        self.add_header('Connections', space_above=True)
537        self.add_checked_input(
538            'allow_remote', _('Allow remote connections'), core_conf['allow_remote']
539        )
540        self.add_header('Other', space_above=True)
541        self.add_checked_input(
542            'new_release_check',
543            _('Periodically check the website for new releases'),
544            core_conf['new_release_check'],
545        )
546
547
548class QueuePane(BasePreferencePane):
549    def __init__(self, preferences):
550        BasePreferencePane.__init__(self, ' %s ' % _('Queue'), preferences)
551
552    @overrides(BasePreferencePane)
553    def create_pane(self, core_conf, console_config):
554        self.add_header(_('New Torrents'))
555        self.add_checked_input(
556            'queue_new_to_top', _('Queue to top'), core_conf['queue_new_to_top']
557        )
558        self.add_header(_('Active Torrents'), True)
559        self.add_int_spin_input(
560            'max_active_limit',
561            '%s:' % _('Total'),
562            core_conf['max_active_limit'],
563            min_val=-1,
564            max_val=9999,
565        )
566        self.add_int_spin_input(
567            'max_active_downloading',
568            '%s:' % _('Downloading'),
569            core_conf['max_active_downloading'],
570            min_val=-1,
571            max_val=9999,
572        )
573        self.add_int_spin_input(
574            'max_active_seeding',
575            '%s:' % _('Seeding'),
576            core_conf['max_active_seeding'],
577            min_val=-1,
578            max_val=9999,
579        )
580        self.add_checked_input(
581            'dont_count_slow_torrents',
582            'Ignore slow torrents',
583            core_conf['dont_count_slow_torrents'],
584        )
585        self.add_checked_input(
586            'auto_manage_prefer_seeds',
587            'Prefer seeding torrents',
588            core_conf['auto_manage_prefer_seeds'],
589        )
590        self.add_header(_('Seeding Rotation'), space_above=True)
591        self.add_float_spin_input(
592            'share_ratio_limit',
593            '%s:' % _('Share Ratio'),
594            core_conf['share_ratio_limit'],
595            precision=2,
596            min_val=-1.0,
597            max_val=100.0,
598        )
599        self.add_float_spin_input(
600            'seed_time_ratio_limit',
601            '%s:' % _('Time Ratio'),
602            core_conf['seed_time_ratio_limit'],
603            precision=2,
604            min_val=-1.0,
605            max_val=100.0,
606        )
607        self.add_int_spin_input(
608            'seed_time_limit',
609            '%s:' % _('Time (m)'),
610            core_conf['seed_time_limit'],
611            min_val=1,
612            max_val=10000,
613        )
614        seedratio = FloatSpinInput(
615            self.mode,
616            'stop_seed_ratio',
617            '',
618            self.move,
619            core_conf['stop_seed_ratio'],
620            precision=2,
621            inc_amt=0.1,
622            min_val=0.5,
623            max_val=100.0,
624        )
625        self.add_checkedplus_input(
626            'stop_seed_at_ratio',
627            '%s:' % _('Share Ratio Reached'),
628            seedratio,
629            core_conf['stop_seed_at_ratio'],
630        )
631        self.add_checked_input(
632            'remove_seed_at_ratio',
633            _('Remove torrent (Unchecked pauses torrent)'),
634            core_conf['remove_seed_at_ratio'],
635        )
636
637
638class ProxyPane(BasePreferencePane):
639    def __init__(self, preferences):
640        BasePreferencePane.__init__(self, ' %s ' % _('Proxy'), preferences)
641
642    @overrides(BasePreferencePane)
643    def create_pane(self, core_conf, console_config):
644        proxy = core_conf['proxy']
645
646        self.add_header(_('Proxy Settings'))
647        self.add_header(_('Proxy'), space_above=True)
648        self.add_int_spin_input(
649            'proxy_type', '%s:' % _('Type'), proxy['type'], min_val=0, max_val=5
650        )
651        self.add_text_input('proxy_username', '%s:' % _('Username'), proxy['username'])
652        self.add_text_input('proxy_password', '%s:' % _('Password'), proxy['password'])
653        self.add_text_input('proxy_hostname', '%s:' % _('Hostname'), proxy['hostname'])
654        self.add_int_spin_input(
655            'proxy_port', '%s:' % _('Port'), proxy['port'], min_val=0, max_val=65535
656        )
657        self.add_checked_input(
658            'proxy_hostnames', _('Proxy Hostnames'), proxy['proxy_hostnames']
659        )
660        self.add_checked_input(
661            'proxy_peer_connections', _('Proxy Peers'), proxy['proxy_peer_connections']
662        )
663        self.add_checked_input(
664            'proxy_tracker_connections',
665            _('Proxy Trackers'),
666            proxy['proxy_tracker_connections'],
667        )
668        self.add_header('%s' % _('Force Proxy'), space_above=True)
669        self.add_checked_input('force_proxy', _('Force Proxy'), proxy['force_proxy'])
670        self.add_checked_input(
671            'anonymous_mode', _('Hide Client Identity'), proxy['anonymous_mode']
672        )
673        self.add_header('%s' % _('Proxy Type Help'), space_above=True)
674        self.add_text_area(
675            'proxy_text_area',
676            ' 0: None   1: Socks4\n'
677            ' 2: Socks5 3: Socks5 Auth\n'
678            ' 4: HTTP   5: HTTP Auth\n'
679            ' 6: I2P',
680        )
681
682
683class CachePane(BasePreferencePane):
684    def __init__(self, preferences):
685        BasePreferencePane.__init__(self, ' %s ' % _('Cache'), preferences)
686        self.created = False
687
688    @overrides(BasePreferencePane)
689    def create_pane(self, core_conf, console_config):
690        self.core_conf = core_conf
691
692    def build_pane(self, core_conf, status):
693        self.created = True
694        self.add_header(_('Settings'), space_below=True)
695        self.add_int_spin_input(
696            'cache_size',
697            '%s:' % _('Cache Size (16 KiB blocks)'),
698            core_conf['cache_size'],
699            min_val=0,
700            max_val=99999,
701        )
702        self.add_int_spin_input(
703            'cache_expiry',
704            '%s:' % _('Cache Expiry (seconds)'),
705            core_conf['cache_expiry'],
706            min_val=1,
707            max_val=32000,
708        )
709        self.add_header(' %s' % _('Write'), space_above=True)
710        self.add_info_field(
711            'blocks_written',
712            '  %s:' % _('Blocks Written'),
713            status['disk.num_blocks_written'],
714        )
715        self.add_info_field(
716            'writes', '  %s:' % _('Writes'), status['disk.num_write_ops']
717        )
718        self.add_info_field(
719            'write_hit_ratio',
720            '  %s:' % _('Write Cache Hit Ratio'),
721            '%.2f' % status['write_hit_ratio'],
722        )
723        self.add_header(' %s' % _('Read'))
724        self.add_info_field(
725            'blocks_read', '  %s:' % _('Blocks Read'), status['disk.num_blocks_read']
726        )
727        self.add_info_field(
728            'blocks_read_hit',
729            '  %s:' % _('Blocks Read hit'),
730            status['disk.num_blocks_cache_hits'],
731        )
732        self.add_info_field('reads', '  %s:' % _('Reads'), status['disk.num_read_ops'])
733        self.add_info_field(
734            'read_hit_ratio',
735            '  %s:' % _('Read Cache Hit Ratio'),
736            '%.2f' % status['read_hit_ratio'],
737        )
738        self.add_header(' %s' % _('Size'))
739        self.add_info_field(
740            'cache_size_info',
741            '  %s:' % _('Cache Size'),
742            status['disk.disk_blocks_in_use'],
743        )
744        self.add_info_field(
745            'read_cache_size',
746            '  %s:' % _('Read Cache Size'),
747            status['disk.read_cache_blocks'],
748        )
749
750    @overrides(BasePreferencePane)
751    def update(self, active):
752        if active:
753            client.core.get_session_status(DISK_CACHE_KEYS).addCallback(
754                self.update_cache_status_fields
755            )
756
757    def update_cache_status_fields(self, status):
758        if not self.created:
759            self.build_pane(self.core_conf, status)
760        else:
761            for ipt in self.inputs:
762                if not ipt.has_input() and ipt.name in status:
763                    ipt.set_value(status[ipt.name])
764        self.preferences.refresh()
765