Author: andar

Revision: 5083

Log:
        Add some missing files

Diff:
Added: trunk/deluge/ui/console/screen.py
===================================================================
--- trunk/deluge/ui/console/screen.py                           (rev 0)
+++ trunk/deluge/ui/console/screen.py   2009-04-18 18:14:57 UTC (rev 5083)
@@ -0,0 +1,267 @@
+#
+# screen.py
+#
+# Copyright (C) 2009 Andrew Resch <[email protected]>
+#
+# Deluge is free software.
+#
+# You may redistribute it and/or modify it under the terms of the
+# GNU General Public License, as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option)
+# any later version.
+#
+# deluge is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with deluge.    If not, write to:
+#      The Free Software Foundation, Inc.,
+#      51 Franklin Street, Fifth Floor
+#      Boston, MA    02110-1301, USA.
+#
+
+import curses
+import colors
+from deluge.log import LOG as log
+from twisted.internet import reactor
+
+class CursesStdIO(object):
+    """fake fd to be registered as a reader with the twisted reactor.
+       Curses classes needing input should extend this"""
+
+    def fileno(self):
+        """ We want to select on FD 0 """
+        return 0
+
+    def doRead(self):
+        """called when input is ready"""
+        pass
+    def logPrefix(self): return 'CursesClient'
+
+LINES_BUFFER_SIZE = 5000
+INPUT_HISTORY_SIZE = 500
+
+class Screen(CursesStdIO):
+    def __init__(self, stdscr, command_parser):
+        """
+        A curses screen designed to run as a reader in a twisted reactor.
+
+        :param command_parser: a function that will be passed a string when the
+            user hits enter
+        """
+        log.debug("Screen init!")
+        # Function to be called with commands
+        self.command_parser = command_parser
+        self.stdscr = stdscr
+        # Make the input calls non-blocking
+        self.stdscr.nodelay(1)
+
+        # Holds the user input and is cleared on 'enter'
+        self.input = ""
+        self.input_incomplete = ""
+        # Keep track of where the cursor is
+        self.input_cursor = 0
+        # Keep a history of inputs
+        self.input_history = []
+        self.input_history_index = 0
+
+        # Strings for the 2 status bars
+        self.topbar = ""
+        self.bottombar = ""
+
+        self.rows, self.cols = self.stdscr.getmaxyx()
+
+        # A list of strings to be displayed based on the offset (scroll)
+        self.lines = []
+        # The offset to display lines
+        self.display_lines_offset = 0
+
+        # Create some color pairs, should probably be moved to colors.py
+        # Regular text
+        #curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLACK)
+        # Status bar
+        #curses.init_pair(2, curses.COLOR_YELLOW, curses.COLOR_BLUE)
+
+
+        self.refresh()
+
+    def connectionLost(self, reason):
+        self.close()
+
+    def add_line(self, text):
+        """
+        Add a line to the screen.  This will be showed between the two bars.
+        The text can be formatted with color using the following format:
+
+        "{{fg, bg, attributes, ...}}"
+
+        See: http://docs.python.org/library/curses.html#constants for 
attributes.
+
+        Alternatively, it can use some built-in scheme for coloring.
+        See colors.py for built-in schemes.
+
+        "{{scheme}}"
+
+        Examples:
+
+        "{{blue, black, bold}}My Text is {{white, black}}cool"
+        "{{info}}I am some info text!"
+        "{{error}}Uh oh!"
+
+        :param text: str, the text to show
+        """
+        log.debug("adding line: %s", text)
+
+        self.lines.extend(text.splitlines())
+        while len(self.lines) > LINES_BUFFER_SIZE:
+            # Remove the oldest line if the max buffer size has been reached
+            self.lines.remove(0)
+
+        self.refresh()
+
+    def add_string(self, row, string):
+        """
+        Adds a string to the desired `:param:row`.
+
+        :param row: int, the row number to write the string
+
+        """
+        col = 0
+        parsed = colors.parse_color_string(string)
+        for index, (color, s) in enumerate(parsed):
+            if index + 1 == len(parsed):
+                # This is the last string so lets append some " " to it
+                s += " " * (self.cols - (col + len(s)) - 1)
+            self.stdscr.addstr(row, col, s, color)
+            col += len(s)
+
+    def refresh(self):
+        """
+        Refreshes the screen.
+        Updates the lines based on the`:attr:lines` based on the 
`:attr:display_lines_offset`
+        attribute and the status bars.
+        """
+        self.stdscr.clear()
+
+        # Update the status bars
+        self.add_string(0, self.topbar)
+        self.add_string(self.rows - 2, self.bottombar)
+
+        # The number of rows minus the status bars and the input line
+        available_lines = self.rows - 3
+        # If the amount of lines exceeds the number of rows, we need to figure 
out
+        # which ones to display based on the offset
+        if len(self.lines) > available_lines:
+            # Get the lines to display based on the offset
+            offset = len(self.lines) - self.display_lines_offset
+            lines = self.lines[-(available_lines - offset):offset]
+        elif len(self.lines) == available_lines:
+            lines = self.lines
+        else:
+            lines = [""] * (available_lines - len(self.lines))
+            lines.extend(self.lines)
+
+        # Add the lines to the screen
+        for index, line in enumerate(lines):
+            self.add_string(index + 1, line)
+
+        # Move the cursor
+        self.stdscr.move(self.rows - 1, self.input_cursor)
+        self.stdscr.refresh()
+
+    def doRead(self):
+        """
+        Called when there is data to be read, ie, input from the keyboard.
+        """
+        # We wrap this function to catch exceptions and shutdown the mainloop
+        try:
+            self._doRead()
+        except Exception, e:
+            log.exception(e)
+            reactor.stop()
+
+    def _doRead(self):
+        # Read the character
+        c = self.stdscr.getch()
+
+        # We clear the input string and send it to the command parser on ENTER
+        if c == curses.KEY_ENTER or c == 10:
+            if self.input:
+                self.add_line(">>> " + self.input)
+                self.command_parser(self.input)
+                if len(self.input_history) == INPUT_HISTORY_SIZE:
+                    # Remove the oldest input history if the max history size
+                    # is reached.
+                    self.input_history.remove(0)
+                self.input_history.append(self.input)
+                self.input_history_index = len(self.input_history)
+                self.input = ""
+                self.input_incomplete = ""
+                self.input_cursor = 0
+                self.stdscr.refresh()
+
+        # We use the UP and DOWN keys to cycle through input history
+        elif c == curses.KEY_UP:
+            if self.input_history_index - 1 >= 0:
+                if self.input_history_index == len(self.input_history):
+                    # We're moving from non-complete input so save it just 
incase
+                    # we move back down to it.
+                    self.input_incomplete = self.input
+                # Going back in the history
+                self.input_history_index -= 1
+                self.input = self.input_history[self.input_history_index]
+        elif c == curses.KEY_DOWN:
+            if self.input_history_index + 1 < len(self.input_history):
+                # Going forward in the history
+                self.input_history_index += 1
+                self.input = self.input_history[self.input_history_index]
+            elif self.input_history_index + 1 == len(self.input_history):
+                # We're moving back down to an incomplete input
+                self.input_history_index += 1
+                self.input = self.input_incomplete
+
+        # Cursor movement
+        elif c == curses.KEY_LEFT:
+            if self.input_cursor:
+                self.input_cursor -= 1
+        elif c == curses.KEY_RIGHT:
+            if self.input_cursor < len(self.input):
+                self.input_cursor += 1
+        elif c == curses.KEY_HOME:
+            self.input_cursor = 0
+        elif c == curses.KEY_END:
+            self.input_cursor = len(self.input)
+
+        # Delete a character in the input string based on cursor position
+        if c == curses.KEY_BACKSPACE or c == 127:
+            if self.input and self.input_cursor > 0:
+                self.input = self.input[:self.input_cursor - 1] + 
self.input[self.input_cursor:]
+                self.input_cursor -= 1
+
+        # A key to add to the input string
+        else:
+            if c > 31 and c < 127:
+                if self.input_cursor == len(self.input):
+                    self.input += chr(c)
+                else:
+                    # Insert into string
+                    self.input = self.input[:self.input_cursor] + chr(c) + 
self.input[self.input_cursor:]
+                # Move the cursor forward
+                self.input_cursor += 1
+
+        # Update the input string on the screen
+        #self.stdscr.addstr(self.rows - 1, 0, self.input + " " * (self.cols - 
len(self.input) - 2), curses.color_pair(1))
+        self.add_string(self.rows - 1, self.input)
+        self.stdscr.move(self.rows - 1, self.input_cursor)
+        self.stdscr.refresh()
+
+    def close(self):
+        """
+        Clean up the curses stuff on exit.
+        """
+        curses.nocbreak()
+        self.stdscr.keypad(0)
+        curses.echo()
+        curses.endwin()

Added: trunk/deluge/ui/console/statusbars.py
===================================================================
--- trunk/deluge/ui/console/statusbars.py                               (rev 0)
+++ trunk/deluge/ui/console/statusbars.py       2009-04-18 18:14:57 UTC (rev 
5083)
@@ -0,0 +1,102 @@
+#
+# statusbars.py
+#
+# Copyright (C) 2009 Andrew Resch <[email protected]>
+#
+# Deluge is free software.
+#
+# You may redistribute it and/or modify it under the terms of the
+# GNU General Public License, as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option)
+# any later version.
+#
+# deluge is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with deluge.    If not, write to:
+#      The Free Software Foundation, Inc.,
+#      51 Franklin Street, Fifth Floor
+#      Boston, MA    02110-1301, USA.
+#
+
+
+import deluge.component as component
+import deluge.common
+from deluge.ui.client import client
+
+class StatusBars(component.Component):
+    def __init__(self):
+        component.Component.__init__(self, "StatusBars", 2, 
depend=["CoreConfig"])
+        self.config = component.get("CoreConfig")
+        self.screen = component.get("ConsoleUI").screen
+
+        # Hold some values we get from the core
+        self.connections = 0
+        self.download = ""
+        self.upload = ""
+        self.dht = 0
+
+    def start(self):
+        def on_coreconfig_ready(result):
+            self.__core_config_ready = True
+            self.update()
+
+        self.__core_config_ready = False
+        # We need to add a callback to wait for the CoreConfig to be ready
+        self.config.start_defer.addCallback(on_coreconfig_ready)
+
+    def update(self):
+        if not self.__core_config_ready:
+            return
+
+        if self.config["dht"]:
+            def on_get_dht_nodes(result):
+                self.dht = result
+            client.core.get_dht_nodes().addCallback(on_get_dht_nodes)
+
+        def on_get_num_connections(result):
+            self.connections = result
+        client.core.get_num_connections().addCallback(on_get_num_connections)
+
+        def on_get_session_status(status):
+            self.upload = deluge.common.fsize(status["payload_upload_rate"])
+            self.download = 
deluge.common.fsize(status["payload_download_rate"])
+            self.update_statusbars()
+
+        client.core.get_session_status([
+            "payload_upload_rate",
+            "payload_download_rate"]).addCallback(on_get_session_status)
+
+
+    def update_statusbars(self):
+        # Update the topbar string
+        self.screen.topbar = "{{status}}Deluge %s Console - " % 
deluge.common.get_version()
+        if client.connected():
+            info = client.connection_info()
+            self.screen.topbar += "%...@%s:%s" % (info[2], info[0], info[1])
+        else:
+            self.screen.topbar += "Not Connected"
+
+        # Update the bottombar string
+        self.screen.bottombar = "{{status}}C: %s" % self.connections
+
+        if self.config["max_connections_global"] > -1:
+            self.screen.bottombar += " (%s)" % 
self.config["max_connections_global"]
+
+        self.screen.bottombar += " D: %s/s" % self.download
+
+        if self.config["max_download_speed"] > -1:
+            self.screen.bottombar += " (%s/s)" % 
deluge.common.fsize(self.config["max_download_speed"])
+
+        self.screen.bottombar += " U: %s/s" % self.upload
+
+        if self.config["max_upload_speed"] > -1:
+            self.screen.bottombar += " (%s/s)" % 
deluge.common.fsize(self.config["max_upload_speed"])
+
+        if self.config["dht"]:
+            self.screen.bottombar += " DHT: %s" % self.dht
+
+        self.screen.refresh()



--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"deluge-commit" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/deluge-commit?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to