Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

# -*- coding: utf-8 -*- 

# 

# Copyright (C) 2011 Nick Lanham <nick@afternight.org> 

# 

# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with 

# the additional special exception to link portions of this program with the OpenSSL library. 

# See LICENSE for more details. 

# 

 

import logging 

from collections import deque 

 

import deluge.component as component 

from deluge.ui.client import client 

from deluge.ui.console.modes.basemode import BaseMode 

from deluge.ui.console.modes.input_popup import Popup, SelectInput 

from deluge.ui.console.modes.popup import MessagePopup 

from deluge.ui.console.modes.preference_panes import (BandwidthPane, CachePane, ColumnsPane, DaemonPane, DownloadsPane, 

                                                      InterfacePane, NetworkPane, OtherPane, ProxyPane, QueuePane) 

 

try: 

    import curses 

except ImportError: 

    pass 

 

 

log = logging.getLogger(__name__) 

 

 

# Big help string that gets displayed when the user hits 'h' 

HELP_STR = """This screen lets you view and configure various options in deluge. 

 

There are three main sections to this screen.  Only one 

section is active at a time.  You can switch the active 

section by hitting TAB (or Shift-TAB to go back one) 

 

The section on the left displays the various categories 

that the settings fall in.  You can navigate the list 

using the up/down arrows 

 

The section on the right shows the settings for the 

selected category.  When this section is active 

you can navigate the various settings with the up/down 

arrows.  Special keys for each input type are described 

below. 

 

The final section is at the bottom right, the: 

[Cancel] [Apply] [OK] buttons.  When this section 

is active, simply select the option you want using 

the arrow keys and press Enter to confim. 

 

 

Special keys for various input types are as follows: 

- For text inputs you can simply type in the value. 

 

- For numeric inputs (indicated by the value being 

  in []s), you can type a value, or use PageUp and 

  PageDown to increment/decrement the value. 

 

- For checkbox inputs use the spacebar to toggle 

 

- For checkbox plus something else inputs (the 

  something else being only visible when you 

  check the box) you can toggle the check with 

  space, use the right arrow to edit the other 

  value, and escape to get back to the check box. 

 

 

""" 

HELP_LINES = HELP_STR.split("\n") 

 

 

class ZONE: 

    CATEGORIES = 0 

    PREFRENCES = 1 

    ACTIONS = 2 

 

 

class Preferences(BaseMode): 

    def __init__(self, parent_mode, core_config, console_config, active_port, status, stdscr, encoding=None): 

        self.parent_mode = parent_mode 

        self.categories = [_("Interface"), _("Columns"), _("Downloads"), _("Network"), _("Bandwidth"), 

                           _("Other"), _("Daemon"), _("Queue"), _("Proxy"), _("Cache")]  # , _("Plugins")] 

        self.cur_cat = 0 

        self.popup = None 

        self.messages = deque() 

        self.action_input = None 

 

        self.core_config = core_config 

        self.console_config = console_config 

        self.active_port = active_port 

        self.status = status 

 

        self.active_zone = ZONE.CATEGORIES 

 

        # how wide is the left 'pane' with categories 

        self.div_off = 15 

 

        BaseMode.__init__(self, stdscr, encoding, False) 

 

        # create the panes 

        self.__calc_sizes() 

 

        self.action_input = SelectInput(self, None, None, ["Cancel", "Apply", "OK"], [0, 1, 2], 0) 

        self.refresh() 

 

    def __calc_sizes(self): 

        self.prefs_width = self.cols - self.div_off - 1 

        self.prefs_height = self.rows - 4 

        # Needs to be same order as self.categories 

        self.panes = [ 

            InterfacePane(self.div_off + 2, self, self.prefs_width), 

            ColumnsPane(self.div_off + 2, self, self.prefs_width), 

            DownloadsPane(self.div_off + 2, self, self.prefs_width), 

            NetworkPane(self.div_off + 2, self, self.prefs_width), 

            BandwidthPane(self.div_off + 2, self, self.prefs_width), 

            OtherPane(self.div_off + 2, self, self.prefs_width), 

            DaemonPane(self.div_off + 2, self, self.prefs_width), 

            QueuePane(self.div_off + 2, self, self.prefs_width), 

            ProxyPane(self.div_off + 2, self, self.prefs_width), 

            CachePane(self.div_off + 2, self, self.prefs_width) 

        ] 

 

    def __draw_catetories(self): 

        for i, category in enumerate(self.categories): 

            if i == self.cur_cat and self.active_zone == ZONE.CATEGORIES: 

                self.add_string(i + 1, "- {!black,white,bold!}%s" % category, pad=False) 

            elif i == self.cur_cat: 

                self.add_string(i + 1, "- {!black,white!}%s" % category, pad=False) 

            else: 

                self.add_string(i + 1, "- %s" % category) 

        self.stdscr.vline(1, self.div_off, "|", self.rows - 2) 

 

    def __draw_preferences(self): 

        self.panes[self.cur_cat].render(self, self.stdscr, self.prefs_width, self.active_zone == ZONE.PREFRENCES) 

 

    def __draw_actions(self): 

        selected = self.active_zone == ZONE.ACTIONS 

        self.stdscr.hline(self.rows - 3, self.div_off + 1, "_", self.cols) 

        self.action_input.render(self.stdscr, self.rows - 2, self.cols, selected, self.cols - 22) 

 

    def on_resize(self, *args): 

        BaseMode.on_resize_norefresh(self, *args) 

        self.__calc_sizes() 

 

        # Always refresh Legacy(it will also refresh AllTorrents), otherwise it will bug deluge out 

        legacy = component.get("LegacyUI") 

        legacy.on_resize(*args) 

        self.stdscr.erase() 

        self.refresh() 

 

    def refresh(self): 

        if self.popup is None and self.messages: 

            title, msg = self.messages.popleft() 

            self.popup = MessagePopup(self, title, msg) 

 

        self.stdscr.erase() 

        self.add_string(0, self.statusbars.topbar) 

        hstr = "%sPress [h] for help" % (" " * (self.cols - len(self.statusbars.bottombar) - 10)) 

        self.add_string(self.rows - 1, "%s%s" % (self.statusbars.bottombar, hstr)) 

 

        self.__draw_catetories() 

        self.__draw_actions() 

 

        # do this last since it moves the cursor 

        self.__draw_preferences() 

 

        if component.get("ConsoleUI").screen != self: 

            return 

 

        self.stdscr.noutrefresh() 

 

        if self.popup: 

            self.popup.refresh() 

 

        curses.doupdate() 

 

    def __category_read(self, c): 

        # Navigate prefs 

        if c == curses.KEY_UP: 

            self.cur_cat = max(0, self.cur_cat - 1) 

        elif c == curses.KEY_DOWN: 

            self.cur_cat = min(len(self.categories) - 1, self.cur_cat + 1) 

 

    def __prefs_read(self, c): 

        self.panes[self.cur_cat].handle_read(c) 

 

    def __apply_prefs(self): 

        new_core_config = {} 

        for pane in self.panes: 

            if not isinstance(pane, InterfacePane) and not isinstance(pane, ColumnsPane): 

                pane.add_config_values(new_core_config) 

        # Apply Core Prefs 

        if client.connected(): 

            # Only do this if we're connected to a daemon 

            config_to_set = {} 

            for key in new_core_config.keys(): 

                # The values do not match so this needs to be updated 

                if self.core_config[key] != new_core_config[key]: 

                    config_to_set[key] = new_core_config[key] 

 

            if config_to_set: 

                # Set each changed config value in the core 

                client.core.set_config(config_to_set) 

                client.force_call(True) 

                # Update the configuration 

                self.core_config.update(config_to_set) 

 

        # Update Interface Prefs 

        new_console_config = {} 

        didupdate = False 

        for pane in self.panes: 

            # could just access panes by index, but that would break if panes 

            # are ever reordered, so do it the slightly slower but safer way 

            if isinstance(pane, InterfacePane) or isinstance(pane, ColumnsPane): 

                pane.add_config_values(new_console_config) 

        for key in new_console_config.keys(): 

            # The values do not match so this needs to be updated 

            if self.console_config[key] != new_console_config[key]: 

                self.console_config[key] = new_console_config[key] 

                didupdate = True 

        if didupdate: 

            # changed something, save config and tell alltorrents 

            self.console_config.save() 

            self.parent_mode.update_config() 

 

    def __update_preferences(self, core_config): 

        self.core_config = core_config 

        for pane in self.panes: 

            pane.update_values(core_config) 

 

    def __actions_read(self, c): 

        self.action_input.handle_read(c) 

        if c == curses.KEY_ENTER or c == 10: 

            # take action 

            if self.action_input.selidx == 0:  # Cancel 

                self.back_to_parent() 

            elif self.action_input.selidx == 1:  # Apply 

                self.__apply_prefs() 

                client.core.get_config().addCallback(self.__update_preferences) 

            elif self.action_input.selidx == 2:  # OK 

                self.__apply_prefs() 

                self.back_to_parent() 

 

    def back_to_parent(self): 

        self.stdscr.erase() 

        component.get("ConsoleUI").set_mode(self.parent_mode) 

        self.parent_mode.resume() 

 

    def read_input(self): 

        c = self.stdscr.getch() 

 

        if self.popup: 

            if self.popup.handle_read(c): 

                self.popup = None 

            self.refresh() 

            return 

 

        if c > 31 and c < 256: 

            if chr(c) == "Q": 

                from twisted.internet import reactor 

                if client.connected(): 

                    def on_disconnect(result): 

                        reactor.stop() 

                    client.disconnect().addCallback(on_disconnect) 

                else: 

                    reactor.stop() 

                return 

            elif chr(c) == "h": 

                self.popup = Popup(self, "Preferences Help") 

                for l in HELP_LINES: 

                    self.popup.add_line(l) 

 

        if c == 9: 

            self.active_zone += 1 

            if self.active_zone > ZONE.ACTIONS: 

                self.active_zone = ZONE.CATEGORIES 

        elif c == 27 and self.active_zone == ZONE.CATEGORIES: 

            self.back_to_parent() 

        elif c == curses.KEY_BTAB: 

            self.active_zone -= 1 

            if self.active_zone < ZONE.CATEGORIES: 

                self.active_zone = ZONE.ACTIONS 

 

        elif c == 114 and isinstance(self.panes[self.cur_cat], CachePane): 

            client.core.get_cache_status().addCallback(self.panes[self.cur_cat].update_cache_status) 

 

        else: 

            if self.active_zone == ZONE.CATEGORIES: 

                self.__category_read(c) 

            elif self.active_zone == ZONE.PREFRENCES: 

                self.__prefs_read(c) 

            elif self.active_zone == ZONE.ACTIONS: 

                self.__actions_read(c) 

 

        self.refresh()