Example #1
0
 def CreatePanel(self):
     self.list = SelectableListCtrl(self)
     self.list.InsertColumn(0, 'Torrent')
     self.list.setResizeColumn(0)
     self.list.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)
     self.list.SetMinSize((1, 80))
     return self.list
Example #2
0
class NewTorrentPanel(HomePanel):
    def __init__(self, parent):
        HomePanel.__init__(self, parent, 'Newest Torrents', SEPARATOR_GREY,
                           (0, 1))
        self.Layout()

        session = parent.guiutility.utility.session
        self.torrentdb = session.open_dbhandler(NTFY_TORRENTS)
        session.add_observer(self.OnNotify, NTFY_TORRENTS, [NTFY_INSERT])

    def CreatePanel(self):
        self.list = SelectableListCtrl(self)
        self.list.InsertColumn(0, 'Torrent')
        self.list.setResizeColumn(0)
        self.list.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)
        self.list.SetMinSize((1, 80))
        return self.list

    def OnNotify(self, subject, type, infohash):
        try:
            if self.IsShownOnScreen():
                self.UpdateStats(infohash)
        except wx.PyDeadObjectError:
            pass

    def UpdateStats(self, infohash):
        def db_callback():
            torrent = self.torrentdb.getTorrent(infohash, include_mypref=False)
            if torrent:
                self._UpdateStats(torrent)

        startWorker(None,
                    db_callback,
                    uId=u"NewTorrentPanel_UpdateStats",
                    priority=GUI_PRI_DISPERSY)

    @forceWxThread
    def _UpdateStats(self, torrent):
        self.list.InsertStringItem(0, torrent['name'])
        size = self.list.GetItemCount()
        if size > 10:
            self.list.DeleteItem(size - 1)

    def OnDoubleClick(self, event):
        selected = self.list.GetFirstSelected()
        if selected != -1:
            selected_file = self.list.GetItemText(selected)
            self.guiutility.dosearch(selected_file)
Example #3
0
 def CreatePanel(self):
     self.list = SelectableListCtrl(self)
     self.list.InsertColumn(0, 'Torrent')
     self.list.setResizeColumn(0)
     self.list.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)
     self.list.SetMinSize((1, 80))
     return self.list
Example #4
0
    def AddComponents(self):
        self.graph_panel = wx.Panel(self, -1)
        self.graph_panel.Bind(wx.EVT_MOTION, self.OnMouse)
        self.graph_panel.Bind(wx.EVT_LEFT_UP, self.OnMouse)
        self.graph_panel.Bind(wx.EVT_PAINT, self.OnPaint)
        self.graph_panel.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)

        self.circuit_list = SelectableListCtrl(self,
                                               style=wx.LC_REPORT
                                               | wx.BORDER_SIMPLE)
        self.circuit_list.InsertColumn(0, 'ID', wx.LIST_FORMAT_LEFT, 25)
        self.circuit_list.InsertColumn(1, 'Online', wx.LIST_FORMAT_RIGHT, 50)
        self.circuit_list.InsertColumn(2, 'Hops', wx.LIST_FORMAT_RIGHT, 45)
        self.circuit_list.InsertColumn(3, u'Bytes \u2191',
                                       wx.LIST_FORMAT_RIGHT, 83)
        self.circuit_list.InsertColumn(4, u'Bytes \u2193',
                                       wx.LIST_FORMAT_RIGHT, 83)
        self.circuit_list.InsertColumn(5, 'Uptime', wx.LIST_FORMAT_RIGHT, 54)
        self.circuit_list.setResizeColumn(0)
        self.circuit_list.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
        self.circuit_list.Bind(wx.EVT_LIST_ITEM_DESELECTED,
                               self.OnItemSelected)
        self.circuit_to_listindex = {}

        if self.fullscreen:
            self.log_text = wx.TextCtrl(self,
                                        style=wx.TE_MULTILINE
                                        | wx.BORDER_SIMPLE
                                        | wx.HSCROLL & wx.VSCROLL)
            self.log_text.SetEditable(False)
            self.log_text.Show(self.fullscreen)
            self.num_circuits_label = wx.StaticText(
                self, -1, "You have 0 circuit(s); 0 relay(s); \
                0 exit socket(s); 0 candidate(s)")

        self.vSizer = wx.BoxSizer(wx.VERTICAL)
        self.vSizer.Add(self.circuit_list, 1,
                        wx.EXPAND | wx.RESERVE_SPACE_EVEN_IF_HIDDEN, 0)
        if self.fullscreen:
            self.vSizer.Add(self.log_text, 1, wx.EXPAND | wx.TOP, 10)
            self.vSizer.Add(self.num_circuits_label, 0, wx.EXPAND | wx.TOP, 10)

        self.main_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.main_sizer.Add(self.graph_panel, 3,
                            wx.EXPAND | wx.LEFT | wx.TOP | wx.BOTTOM, 10)
        self.main_sizer.Add(self.vSizer, 2, wx.EXPAND | wx.ALL, 10)
        self.SetSizer(self.main_sizer)
Example #5
0
class NewTorrentPanel(HomePanel):

    def __init__(self, parent):
        HomePanel.__init__(self, parent, 'Newest Torrents', SEPARATOR_GREY, (0, 1))
        self.Layout()

        session = parent.guiutility.utility.session
        self.torrentdb = session.open_dbhandler(NTFY_TORRENTS)
        session.add_observer(self.OnNotify, NTFY_TORRENTS, [NTFY_INSERT])

    def CreatePanel(self):
        self.list = SelectableListCtrl(self)
        self.list.InsertColumn(0, 'Torrent')
        self.list.setResizeColumn(0)
        self.list.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)
        self.list.SetMinSize((1, 80))
        return self.list

    def OnNotify(self, subject, type, infohash):
        try:
            if self.IsShownOnScreen():
                self.UpdateStats(infohash)
        except wx.PyDeadObjectError:
            pass

    def UpdateStats(self, infohash):
        def db_callback():
            torrent = self.torrentdb.getTorrent(infohash, include_mypref=False)
            if torrent:
                self._UpdateStats(torrent)

        startWorker(None, db_callback, uId=u"NewTorrentPanel_UpdateStats", priority=GUI_PRI_DISPERSY)

    @forceWxThread
    def _UpdateStats(self, torrent):
        self.list.InsertStringItem(0, torrent['name'])
        size = self.list.GetItemCount()
        if size > 10:
            self.list.DeleteItem(size - 1)

    def OnDoubleClick(self, event):
        selected = self.list.GetFirstSelected()
        if selected != -1:
            selected_file = self.list.GetItemText(selected)
            self.guiutility.dosearch(selected_file)
Example #6
0
    def AddComponents(self):
        self.graph_panel = wx.Panel(self, -1)
        self.graph_panel.Bind(wx.EVT_MOTION, self.OnMouse)
        self.graph_panel.Bind(wx.EVT_LEFT_UP, self.OnMouse)
        self.graph_panel.Bind(wx.EVT_PAINT, self.OnPaint)
        self.graph_panel.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)

        self.circuit_list = SelectableListCtrl(self, style=wx.LC_REPORT | wx.BORDER_SIMPLE)
        self.circuit_list.InsertColumn(0, 'ID', wx.LIST_FORMAT_LEFT, 25)
        self.circuit_list.InsertColumn(1, 'Online', wx.LIST_FORMAT_RIGHT, 50)
        self.circuit_list.InsertColumn(2, 'Hops', wx.LIST_FORMAT_RIGHT, 45)
        self.circuit_list.InsertColumn(3, u'Bytes \u2191', wx.LIST_FORMAT_RIGHT, 83)
        self.circuit_list.InsertColumn(4, u'Bytes \u2193', wx.LIST_FORMAT_RIGHT, 83)
        self.circuit_list.InsertColumn(5, 'Uptime', wx.LIST_FORMAT_RIGHT, 54)
        self.circuit_list.setResizeColumn(0)
        self.circuit_list.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
        self.circuit_list.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnItemSelected)
        self.circuit_to_listindex = {}

        if self.fullscreen:
            self.log_text = wx.TextCtrl(self, style=wx.TE_MULTILINE | wx.BORDER_SIMPLE | wx.HSCROLL & wx.VSCROLL)
            self.log_text.SetEditable(False)
            self.log_text.Show(self.fullscreen)
            self.num_circuits_label = wx.StaticText(self, -1, "You have 0 circuit(s); 0 relay(s); \
                0 exit socket(s); 0 candidate(s)")

        self.vSizer = wx.BoxSizer(wx.VERTICAL)
        self.vSizer.Add(self.circuit_list, 1, wx.EXPAND | wx.RESERVE_SPACE_EVEN_IF_HIDDEN, 0)
        if self.fullscreen:
            self.vSizer.Add(self.log_text, 1, wx.EXPAND | wx.TOP, 10)
            self.vSizer.Add(self.num_circuits_label, 0, wx.EXPAND | wx.TOP, 10)

        self.main_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.main_sizer.Add(self.graph_panel, 3, wx.EXPAND | wx.LEFT | wx.TOP | wx.BOTTOM, 10)
        self.main_sizer.Add(self.vSizer, 2, wx.EXPAND | wx.ALL, 10)
        self.SetSizer(self.main_sizer)
Example #7
0
class NetworkGraphPanel(wx.Panel):
    def __init__(self, parent, fullscreen=True):
        wx.Panel.__init__(self, parent, -1)

        self.SetBackgroundColour(wx.WHITE)
        self.guiutility = GUIUtility.getInstance()
        self.utility = self.guiutility.utility
        self.session = self.utility.session
        self.dispersy = self.utility.session.lm.dispersy

        self.swarm = GuiImageManager.getInstance().getImage(u"darknet.png")
        self.font_small = self.GetFont()
        self.font_large = self.GetFont()
        self.font_large.SetPointSize(self.font_large.GetPointSize() + 2)

        self.circuits = {}
        self.circuits_old = None
        self.hop_to_colour = {}
        self.colours = [
            wx.RED,
            wx.Colour(156, 18, 18),
            wx.Colour(183, 83, 83),
            wx.Colour(254, 134, 134),
            wx.Colour(254, 190, 190)
        ]

        self.selected_circuit = None
        self.hop_hover_evt = None
        self.hop_hover = None
        self.hop_active_evt = None
        self.hop_active = None

        self.hops = -1
        self.fullscreen = fullscreen
        self.radius = 20 if self.fullscreen else 12
        self.line_width = 2 if self.fullscreen else 1
        self.margin_x = self.margin_y = self.radius
        self.swarm_size = wx.Size(180, 60)

        self.AddComponents()

        self.tunnel_community = None
        self.try_community()

    def try_community(self):
        try:
            tunnel_community = (
                c for c in self.dispersy.get_communities()
                if isinstance(c, HiddenTunnelCommunity)).next()
            self.found_community(tunnel_community)
        except:
            wx.CallLater(1000, self.try_community)

    def found_community(self, tunnel_community):
        self.tunnel_community = tunnel_community

        self.my_address = Hop(self.tunnel_community.my_member._ec.pub())
        self.my_address.address = ('127.0.0.1', "SELF")

        self.circuit_timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.OnUpdateCircuits, self.circuit_timer)
        self.circuit_timer.Start(5000)

        if self.fullscreen:
            self.session.add_observer(
                self.OnExtended, NTFY_TUNNEL,
                [NTFY_CREATED, NTFY_EXTENDED, NTFY_BROKEN])
            self.session.add_observer(self.OnSelect, NTFY_TUNNEL,
                                      [NTFY_SELECT])
            self.session.add_observer(self.OnJoined, NTFY_TUNNEL,
                                      [NTFY_JOINED])
            self.session.add_observer(self.OnExtendedFor, NTFY_TUNNEL,
                                      [NTFY_EXTENDED_FOR])
            self.session.add_observer(self.OnIpRemoved, NTFY_TUNNEL,
                                      [NTFY_IP_REMOVED])
            self.session.add_observer(self.OnRpRemoved, NTFY_TUNNEL,
                                      [NTFY_RP_REMOVED])
            self.session.add_observer(self.OnIpRecreate, NTFY_TUNNEL,
                                      [NTFY_IP_RECREATE])
            self.session.add_observer(self.OnDhtLookup, NTFY_TUNNEL,
                                      [NTFY_DHT_LOOKUP])
            self.session.add_observer(self.OnKeyRequest, NTFY_TUNNEL,
                                      [NTFY_KEY_REQUEST])
            self.session.add_observer(self.OnKeyRespond, NTFY_TUNNEL,
                                      [NTFY_KEY_RESPOND])
            self.session.add_observer(self.OnKeyResponse, NTFY_TUNNEL,
                                      [NTFY_KEY_RESPONSE])
            self.session.add_observer(self.OnCreateE2E, NTFY_TUNNEL,
                                      [NTFY_CREATE_E2E])
            self.session.add_observer(self.OnCreatedE2E, NTFY_TUNNEL,
                                      [NTFY_ONCREATED_E2E])
            self.session.add_observer(self.OnIpCreated, NTFY_TUNNEL,
                                      [NTFY_IP_CREATED])
            self.session.add_observer(self.OnRpCreated, NTFY_TUNNEL,
                                      [NTFY_RP_CREATED])

    def AddComponents(self):
        self.graph_panel = wx.Panel(self, -1)
        self.graph_panel.Bind(wx.EVT_MOTION, self.OnMouse)
        self.graph_panel.Bind(wx.EVT_LEFT_UP, self.OnMouse)
        self.graph_panel.Bind(wx.EVT_PAINT, self.OnPaint)
        self.graph_panel.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)

        self.circuit_list = SelectableListCtrl(self,
                                               style=wx.LC_REPORT
                                               | wx.BORDER_SIMPLE)
        self.circuit_list.InsertColumn(0, 'ID', wx.LIST_FORMAT_LEFT, 25)
        self.circuit_list.InsertColumn(1, 'Online', wx.LIST_FORMAT_RIGHT, 50)
        self.circuit_list.InsertColumn(2, 'Hops', wx.LIST_FORMAT_RIGHT, 45)
        self.circuit_list.InsertColumn(3, u'Bytes \u2191',
                                       wx.LIST_FORMAT_RIGHT, 83)
        self.circuit_list.InsertColumn(4, u'Bytes \u2193',
                                       wx.LIST_FORMAT_RIGHT, 83)
        self.circuit_list.InsertColumn(5, 'Uptime', wx.LIST_FORMAT_RIGHT, 54)
        self.circuit_list.setResizeColumn(0)
        self.circuit_list.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
        self.circuit_list.Bind(wx.EVT_LIST_ITEM_DESELECTED,
                               self.OnItemSelected)
        self.circuit_to_listindex = {}

        if self.fullscreen:
            self.log_text = wx.TextCtrl(self,
                                        style=wx.TE_MULTILINE
                                        | wx.BORDER_SIMPLE
                                        | wx.HSCROLL & wx.VSCROLL)
            self.log_text.SetEditable(False)
            self.log_text.Show(self.fullscreen)
            self.num_circuits_label = wx.StaticText(
                self, -1, "You have 0 circuit(s); 0 relay(s); \
                0 exit socket(s); 0 candidate(s)")

        self.vSizer = wx.BoxSizer(wx.VERTICAL)
        self.vSizer.Add(self.circuit_list, 1,
                        wx.EXPAND | wx.RESERVE_SPACE_EVEN_IF_HIDDEN, 0)
        if self.fullscreen:
            self.vSizer.Add(self.log_text, 1, wx.EXPAND | wx.TOP, 10)
            self.vSizer.Add(self.num_circuits_label, 0, wx.EXPAND | wx.TOP, 10)

        self.main_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.main_sizer.Add(self.graph_panel, 3,
                            wx.EXPAND | wx.LEFT | wx.TOP | wx.BOTTOM, 10)
        self.main_sizer.Add(self.vSizer, 2, wx.EXPAND | wx.ALL, 10)
        self.SetSizer(self.main_sizer)

    def ShowTunnels(self, hops):
        self.circuit_list.Show(hops != 0)
        self.hops = hops
        self.OnUpdateCircuits(None)

    def OnItemSelected(self, event):
        selected = []
        item = self.circuit_list.GetFirstSelected()
        while item != -1:
            selected.append(item)
            item = self.circuit_list.GetNextSelected(item)

        self.selected_circuit = None
        for item in selected:
            for circuit_id, listindex in self.circuit_to_listindex.iteritems():
                if listindex == item and circuit_id in self.circuits:
                    self.selected_circuit = self.circuits[circuit_id]
        self.graph_panel.Refresh()

    def OnUpdateCircuits(self, event):
        if not self.tunnel_community:
            return

        if self.fullscreen:
            self.num_circuits_label.SetLabel(
                "You have %d circuit(s); %d relay(s); %d exit socket(s); %d candidate(s)"
                % (len(self.tunnel_community.circuits),
                   len(self.tunnel_community.relay_from_to),
                   len(self.tunnel_community.exit_sockets),
                   sum(1 for _ in self.tunnel_community.
                       dispersy_yield_verified_candidates())))

        new_circuits = dict(self.tunnel_community.circuits)
        self.circuits = {
            k: v
            for k, v in new_circuits.iteritems()
            if v.goal_hops == self.hops or self.hops < 0
        }

        # Add new circuits & update existing circuits
        for circuit_id, circuit in self.circuits.iteritems():
            if circuit_id not in self.circuit_to_listindex:
                pos = self.circuit_list.InsertStringItem(
                    sys.maxsize, str(circuit_id))
                self.circuit_to_listindex[circuit_id] = pos
            else:
                pos = self.circuit_to_listindex[circuit_id]
            self.circuit_list.SetStringItem(pos, 1, str(circuit.state))
            self.circuit_list.SetStringItem(
                pos, 2,
                str(len(circuit.hops)) + "/" + str(circuit.goal_hops))

            bytes_uploaded = circuit.bytes_up
            bytes_downloaded = circuit.bytes_down

            self.circuit_list.SetStringItem(pos, 3,
                                            size_format(bytes_uploaded))
            self.circuit_list.SetStringItem(pos, 4,
                                            size_format(bytes_downloaded))
            self.circuit_list.SetStringItem(
                pos, 5, "%d" % (time() - circuit.creation_time))

        # Remove old circuits
        old_circuits = [
            circuit_id for circuit_id in self.circuit_to_listindex
            if circuit_id not in self.circuits
        ]
        for circuit_id in old_circuits:
            listindex = self.circuit_to_listindex[circuit_id]
            self.circuit_list.DeleteItem(listindex)
            self.circuit_to_listindex.pop(circuit_id)
            for k, v in self.circuit_to_listindex.items():
                if v > listindex:
                    self.circuit_to_listindex[k] = v - 1

        self.graph_panel.Refresh()

    def AppendToLog(self, msg):
        if not self:
            return
        self.log_text.AppendText(
            '[%s]: %s' % (datetime.datetime.now().strftime("%H:%M:%S"), msg))

    @forceWxThread
    def OnExtended(self, subject, changeType, circuit):
        if not self:
            return
        if changeType == NTFY_CREATED:
            self.AppendToLog("Created circuit %s\n" % (circuit.circuit_id))
        if changeType == NTFY_EXTENDED:
            self.AppendToLog("Extended circuit %s\n" % (circuit.circuit_id))
        if changeType == NTFY_BROKEN:
            self.AppendToLog("Circuit %d has been broken\n" % circuit)

    @forceWxThread
    def OnSelect(self, subject, changeType, circuit, address):
        if not self:
            return
        self.AppendToLog("Circuit %d has been selected for destination %s\n" %
                         (circuit, address))

    @forceWxThread
    def OnJoined(self, subject, changeType, address, circuit_id):
        if not self:
            return
        self.AppendToLog("Joined an external circuit %d with %s:%d\n" %
                         (circuit_id, address[0], address[1]))

    @forceWxThread
    def OnExtendedFor(self, subject, changeType, extended_for, extended_with):
        if not self:
            return
        self.AppendToLog(
            "Extended an external circuit (%s:%d, %d) with (%s:%d, %d)\n" %
            (extended_for[0].sock_addr[0], extended_for[0].sock_addr[1],
             extended_for[1], extended_with[0].sock_addr[0],
             extended_with[0].sock_addr[1], extended_with[1]))

    @forceWxThread
    def OnIpRemoved(self, subject, changeType, circuit_id):
        if not self:
            return
        self.AppendToLog("Removed introduction circuit %d\n" % (circuit_id))

    @forceWxThread
    def OnIpRecreate(self, subject, changeType, circuit_id, info_hash):
        if not self:
            return
        self.AppendToLog(
            "Recreate introduction circuit to replace circuit %d for info_hash %s\n"
            % (circuit_id, info_hash))

    @forceWxThread
    def OnRpRemoved(self, subject, changeType, circuit_id):
        if not self:
            return
        self.AppendToLog("Removed rendezvous circuit %d\n" % (circuit_id))

    @forceWxThread
    def OnDhtLookup(self, subject, changeType, info_hash, peers):
        if not self:
            return
        self.AppendToLog(
            "DHT lookup for info_hash %s resulted in peers: %s\n" %
            (info_hash, repr(peers)))

    @forceWxThread
    def OnKeyRequest(self, subject, changeType, info_hash, peer):
        if not self:
            return
        self.AppendToLog("Request key for info_hash %s from %s\n" %
                         (info_hash, repr(peer)))

    @forceWxThread
    def OnKeyRespond(self, subject, changeType, info_hash, circuit_id):
        if not self:
            return
        self.AppendToLog("Respond with key for info_hash %s to circuit %s\n" %
                         (info_hash, circuit_id))

    @forceWxThread
    def OnKeyResponse(self, subject, changeType, info_hash, circuit_id):
        if not self:
            return
        self.AppendToLog("Respond with key for info_hash %s to circuit %s\n" %
                         (info_hash, circuit_id))

    @forceWxThread
    def OnCreateE2E(self, subject, changeType, info_hash):
        if not self:
            return
        self.AppendToLog("Create end-to-end for info_hash %s\n" % (info_hash))

    @forceWxThread
    def OnCreatedE2E(self, subject, changeType, info_hash, rp_addr):
        if not self:
            return
        self.AppendToLog("Connect rendezvous %s for info_hash %s\n" %
                         (repr(rp_addr[0]), info_hash))

    @forceWxThread
    def OnIpCreated(self, subject, changeType, info_hash, circuit_id):
        if not self:
            return
        self.AppendToLog(
            "Created introduction point %s for info_hash on circuit %d\n" %
            (info_hash, circuit_id))

    @forceWxThread
    def OnRpCreated(self, subject, changeType, info_hash, circuit_id):
        if not self:
            return
        self.AppendToLog(
            "Created rendezvous point %s for info_hash on circuit %d\n" %
            (info_hash, circuit_id))

    def OnMouse(self, event):
        if event.Moving():
            self.hop_hover_evt = event.GetPosition()
            self.graph_panel.Refresh()
        elif event.LeftUp():
            self.hop_active_evt = event.GetPosition()
            self.graph_panel.Refresh()

    def OnSize(self, evt):
        size = min(*evt.GetEventObject().GetSize())
        x = min(size + self.margin_x * 2 + self.swarm.GetSize().x,
                self.GetSize().x - self.circuit_list.GetSize().x)
        y = size + self.margin_y * 2
        self.graph_panel.SetSize((x, y))

    def OnEraseBackground(self, event):
        pass

    def OnPaint(self, event):
        eo = event.GetEventObject()
        dc = wx.BufferedPaintDC(eo)
        dc.SetFont(self.font_large)
        dc.Clear()
        gc = wx.GraphicsContext.Create(dc)

        swarm_size = self.swarm.GetSize()

        w = eo.GetSize().x - 2 * self.margin_x - 1 - swarm_size.x
        h = eo.GetSize().y - 2 * self.margin_y - 1

        swarm_pos = (eo.GetSize().x - swarm_size.x - 11,
                     h / 2 - swarm_size.y / 2 + 11)
        swarm_center = (eo.GetSize().x - swarm_size.x / 2,
                        h / 2 + self.margin_y)

        circuit_points = {}

        if self.hops != 0:
            num_circuits = len(self.circuits)
            for c_index, circuit in enumerate(
                    sorted(self.circuits.values(),
                           key=lambda c: c.circuit_id)):
                circuit_points[circuit] = [(self.margin_x,
                                            h / 2 + self.margin_y)]
                for h_index, hop in enumerate(circuit.hops):
                    circuit_points[circuit].append(
                        (w * (float(h_index + 1) /
                              (circuit.goal_hops + 1)) + self.margin_x,
                         h * (float(c_index + 0.5) / num_circuits) +
                         self.margin_y))
        else:
            circuit_points[None] = [(self.margin_x, h / 2 + self.margin_y)]
            gc.SetPen(wx.Pen(wx.Colour(229, 229, 229), self.line_width))

        # Draw edges
        for circuit, points in circuit_points.iteritems():
            for point1, point2 in zip(points[0::1], points[1::1]):
                if circuit == self.selected_circuit:
                    gc.SetPen(wx.Pen(wx.BLUE, self.line_width))
                else:
                    gc.SetPen(wx.Pen(wx.Colour(229, 229, 229),
                                     self.line_width))
                gc.DrawLines([point1, point2])

            # If exit node, draw edge to bittorrent swarm
            if not circuit or circuit.goal_hops == len(circuit.hops):
                gc.DrawLines([points[-1], swarm_center])

        # Draw vertices
        gc.SetPen(wx.Pen(wx.Colour(229, 229, 229), self.line_width))
        for circuit, points in circuit_points.iteritems():
            for index, point in enumerate(points):
                hop = (circuit, index)
                colour = self.hop_to_colour.get(hop, None)
                if not colour:
                    self.hop_to_colour[hop] = colour = random.choice(
                        self.colours[1:]) if index > 0 else self.colours[0]

                x, y = point
                gc.SetBrush(wx.Brush(colour))
                gc.DrawEllipse(x - self.radius / 2, y - self.radius / 2,
                               self.radius, self.radius)

        # Draw swarm and darknet
        gc.DrawBitmap(self.swarm, swarm_pos[0], swarm_pos[1], *swarm_size)
        self.DrawHoverAndInfo(gc, dc, circuit_points)

    def DrawHoverAndInfo(self, gc, dc, circuit_points):
        gc.SetBrush(wx.TRANSPARENT_BRUSH)

        if self.hop_hover_evt:
            self.hop_hover = self.PositionToCircuit(self.hop_hover_evt,
                                                    circuit_points)
            self.hop_hover_evt = None

        if self.hop_hover and self.hop_hover[0] in circuit_points:
            circuit, hop_index = self.hop_hover
            x, y = circuit_points[circuit][hop_index]
            pen = wx.Pen(wx.Colour(229, 229, 229), 1, wx.USER_DASH)
            pen.SetDashes([8, 4])
            gc.SetPen(pen)
            gc.DrawEllipse(x - self.radius, y - self.radius, self.radius * 2,
                           self.radius * 2)

        if self.hop_active_evt:
            self.hop_active = self.PositionToCircuit(self.hop_active_evt,
                                                     circuit_points)
            self.hop_active_evt = None

        if self.hop_active and self.hop_active[0] in circuit_points and \
           (not self.hop_active[0] or self.hop_active[1] <= len(self.hop_active[0].hops)):
            circuit, hop_index = self.hop_active
            hop = circuit.hops[hop_index -
                               1] if hop_index and circuit else None
            x, y = circuit_points[circuit][hop_index]

            # Draw cicle around node
            pen = wx.Pen(self.hop_to_colour.get(self.hop_active, wx.BLACK), 1,
                         wx.USER_DASH)
            pen.SetDashes([8, 4])
            gc.SetPen(pen)
            gc.DrawEllipse(x - self.radius, y - self.radius, self.radius * 2,
                           self.radius * 2)

            # Determine text
            dc.SetFont(self.font_small)
            if not hop:
                text = 'You\nPERMID ' + bin2str(
                    self.tunnel_community.my_member.public_key)[:10]
            else:
                text = 'PERMID ' + bin2str(
                    self.dispersy.crypto.key_to_hash(hop.public_key))[:10]
                if 'UNKNOWN HOST' not in hop.host:
                    text = 'IP %s:%s\n' % (hop.host, hop.port) + text

            # Draw info box + text
            box_width, box_height = self.GetTextExtent(dc, text)
            box_width += 10
            box_height += 10
            x = x - box_width - 1.1 * self.radius if x > self.graph_panel.GetSize(
            )[0] / 2 else x + 1.1 * self.radius
            y = y - box_height - 1.1 * self.radius if y > self.graph_panel.GetSize(
            )[1] / 2 else y + 1.1 * self.radius
            gc.SetBrush(wx.Brush(wx.Colour(216, 237, 255, 50)))
            gc.SetPen(wx.Pen(LIST_BLUE))
            gc.DrawRectangle(x, y, box_width, box_height)
            self.DrawText(dc, text, x + 5, y + 5)

    def GetTextExtent(self, dc, text):
        w_list, h_list = zip(
            *[dc.GetTextExtent(line) for line in text.split('\n')])
        return max(w_list), sum(h_list)

    def DrawText(self, dc, text, x, y):
        # For wxPython 2.8, newline separated text does not always work with gc.DrawText
        y_cur = y
        for line in text.split('\n'):
            dc.DrawText(line, x, y_cur)
            _, h = dc.GetTextExtent(line)
            y_cur += h

    def PositionToCircuit(self, position, circuit_points):
        for circuit, points in circuit_points.iteritems():
            for index, point in enumerate(points):
                if (position[0] - point[0])**2 + (
                        position[1] - point[1])**2 < self.radius**2:
                    return (circuit, index)
        return None

    def ResetSearchBox(self):
        pass
Example #8
0
class NetworkGraphPanel(wx.Panel):

    def __init__(self, parent, fullscreen=True):
        wx.Panel.__init__(self, parent, -1)

        self.SetBackgroundColour(wx.WHITE)
        self.guiutility = GUIUtility.getInstance()
        self.utility = self.guiutility.utility
        self.session = self.utility.session
        self.dispersy = self.utility.session.lm.dispersy

        self.swarm = GuiImageManager.getInstance().getImage(u"darknet.png")
        self.font_small = self.GetFont()
        self.font_large = self.GetFont()
        self.font_large.SetPointSize(self.font_large.GetPointSize() + 2)

        self.circuits = {}
        self.circuits_old = None
        self.hop_to_colour = {}
        self.colours = [wx.RED, wx.Colour(156, 18, 18),
                        wx.Colour(183, 83, 83),
                        wx.Colour(254, 134, 134),
                        wx.Colour(254, 190, 190)]

        self.selected_circuit = None
        self.hop_hover_evt = None
        self.hop_hover = None
        self.hop_active_evt = None
        self.hop_active = None

        self.hops = -1
        self.fullscreen = fullscreen
        self.radius = 20 if self.fullscreen else 12
        self.line_width = 2 if self.fullscreen else 1
        self.margin_x = self.margin_y = self.radius
        self.swarm_size = wx.Size(180, 60)

        self.AddComponents()

        self.tunnel_community = None
        self.try_community()

    def try_community(self):
        try:
            tunnel_community = (
                c for c in self.dispersy.get_communities(
                ) if isinstance(
                    c,
                    HiddenTunnelCommunity)).next(
            )
            self.found_community(tunnel_community)
        except:
            wx.CallLater(1000, self.try_community)

    def found_community(self, tunnel_community):
        self.tunnel_community = tunnel_community

        self.my_address = Hop(self.tunnel_community.my_member._ec.pub())
        self.my_address.address = ('127.0.0.1', "SELF")

        self.circuit_timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.OnUpdateCircuits, self.circuit_timer)
        self.circuit_timer.Start(5000)

        if self.fullscreen:
            self.session.add_observer(self.OnExtended, NTFY_TUNNEL, [NTFY_CREATED, NTFY_EXTENDED, NTFY_BROKEN])
            self.session.add_observer(self.OnSelect, NTFY_TUNNEL, [NTFY_SELECT])
            self.session.add_observer(self.OnJoined, NTFY_TUNNEL, [NTFY_JOINED])
            self.session.add_observer(self.OnExtendedFor, NTFY_TUNNEL, [NTFY_EXTENDED_FOR])
            self.session.add_observer(self.OnIpRemoved, NTFY_TUNNEL, [NTFY_IP_REMOVED])
            self.session.add_observer(self.OnRpRemoved, NTFY_TUNNEL, [NTFY_RP_REMOVED])
            self.session.add_observer(self.OnIpRecreate, NTFY_TUNNEL, [NTFY_IP_RECREATE])
            self.session.add_observer(self.OnDhtLookup, NTFY_TUNNEL, [NTFY_DHT_LOOKUP])
            self.session.add_observer(self.OnKeyRequest, NTFY_TUNNEL, [NTFY_KEY_REQUEST])
            self.session.add_observer(self.OnKeyRespond, NTFY_TUNNEL, [NTFY_KEY_RESPOND])
            self.session.add_observer(self.OnKeyResponse, NTFY_TUNNEL, [NTFY_KEY_RESPONSE])
            self.session.add_observer(self.OnCreateE2E, NTFY_TUNNEL, [NTFY_CREATE_E2E])
            self.session.add_observer(self.OnCreatedE2E, NTFY_TUNNEL, [NTFY_ONCREATED_E2E])
            self.session.add_observer(self.OnIpCreated, NTFY_TUNNEL, [NTFY_IP_CREATED])
            self.session.add_observer(self.OnRpCreated, NTFY_TUNNEL, [NTFY_RP_CREATED])

    def AddComponents(self):
        self.graph_panel = wx.Panel(self, -1)
        self.graph_panel.Bind(wx.EVT_MOTION, self.OnMouse)
        self.graph_panel.Bind(wx.EVT_LEFT_UP, self.OnMouse)
        self.graph_panel.Bind(wx.EVT_PAINT, self.OnPaint)
        self.graph_panel.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)

        self.circuit_list = SelectableListCtrl(self, style=wx.LC_REPORT | wx.BORDER_SIMPLE)
        self.circuit_list.InsertColumn(0, 'ID', wx.LIST_FORMAT_LEFT, 25)
        self.circuit_list.InsertColumn(1, 'Online', wx.LIST_FORMAT_RIGHT, 50)
        self.circuit_list.InsertColumn(2, 'Hops', wx.LIST_FORMAT_RIGHT, 45)
        self.circuit_list.InsertColumn(3, u'Bytes \u2191', wx.LIST_FORMAT_RIGHT, 83)
        self.circuit_list.InsertColumn(4, u'Bytes \u2193', wx.LIST_FORMAT_RIGHT, 83)
        self.circuit_list.InsertColumn(5, 'Uptime', wx.LIST_FORMAT_RIGHT, 54)
        self.circuit_list.setResizeColumn(0)
        self.circuit_list.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
        self.circuit_list.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnItemSelected)
        self.circuit_to_listindex = {}

        if self.fullscreen:
            self.log_text = wx.TextCtrl(self, style=wx.TE_MULTILINE | wx.BORDER_SIMPLE | wx.HSCROLL & wx.VSCROLL)
            self.log_text.SetEditable(False)
            self.log_text.Show(self.fullscreen)
            self.num_circuits_label = wx.StaticText(self, -1, "You have 0 circuit(s); 0 relay(s); \
                0 exit socket(s); 0 candidate(s)")

        self.vSizer = wx.BoxSizer(wx.VERTICAL)
        self.vSizer.Add(self.circuit_list, 1, wx.EXPAND | wx.RESERVE_SPACE_EVEN_IF_HIDDEN, 0)
        if self.fullscreen:
            self.vSizer.Add(self.log_text, 1, wx.EXPAND | wx.TOP, 10)
            self.vSizer.Add(self.num_circuits_label, 0, wx.EXPAND | wx.TOP, 10)

        self.main_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.main_sizer.Add(self.graph_panel, 3, wx.EXPAND | wx.LEFT | wx.TOP | wx.BOTTOM, 10)
        self.main_sizer.Add(self.vSizer, 2, wx.EXPAND | wx.ALL, 10)
        self.SetSizer(self.main_sizer)

    def ShowTunnels(self, hops):
        self.circuit_list.Show(hops != 0)
        self.hops = hops
        self.OnUpdateCircuits(None)

    def OnItemSelected(self, event):
        selected = []
        item = self.circuit_list.GetFirstSelected()
        while item != -1:
            selected.append(item)
            item = self.circuit_list.GetNextSelected(item)

        self.selected_circuit = None
        for item in selected:
            for circuit_id, listindex in self.circuit_to_listindex.iteritems():
                if listindex == item and circuit_id in self.circuits:
                    self.selected_circuit = self.circuits[circuit_id]
        self.graph_panel.Refresh()

    def OnUpdateCircuits(self, event):
        if not self.tunnel_community:
            return

        if self.fullscreen:
            self.num_circuits_label.SetLabel("You have %d circuit(s); %d relay(s); %d exit socket(s); %d candidate(s)" %
                                             (len(self.tunnel_community.circuits),
                                              len(self.tunnel_community.relay_from_to),
                                              len(self.tunnel_community.exit_sockets),
                                              sum(1 for _ in self.tunnel_community.dispersy_yield_verified_candidates())))

        new_circuits = dict(self.tunnel_community.circuits)
        self.circuits = {k: v for k, v in new_circuits.iteritems() if v.goal_hops == self.hops or self.hops < 0}

        # Add new circuits & update existing circuits
        for circuit_id, circuit in self.circuits.iteritems():
            if circuit_id not in self.circuit_to_listindex:
                pos = self.circuit_list.InsertStringItem(sys.maxsize, str(circuit_id))
                self.circuit_to_listindex[circuit_id] = pos
            else:
                pos = self.circuit_to_listindex[circuit_id]
            self.circuit_list.SetStringItem(pos, 1, str(circuit.state))
            self.circuit_list.SetStringItem(pos, 2, str(len(circuit.hops)) + "/" + str(circuit.goal_hops))

            bytes_uploaded = circuit.bytes_up
            bytes_downloaded = circuit.bytes_down

            self.circuit_list.SetStringItem(pos, 3, size_format(bytes_uploaded))
            self.circuit_list.SetStringItem(pos, 4, size_format(bytes_downloaded))
            self.circuit_list.SetStringItem(pos, 5, "%d" % (time() - circuit.creation_time))

        # Remove old circuits
        old_circuits = [circuit_id for circuit_id in self.circuit_to_listindex if circuit_id not in self.circuits]
        for circuit_id in old_circuits:
            listindex = self.circuit_to_listindex[circuit_id]
            self.circuit_list.DeleteItem(listindex)
            self.circuit_to_listindex.pop(circuit_id)
            for k, v in self.circuit_to_listindex.items():
                if v > listindex:
                    self.circuit_to_listindex[k] = v - 1

        self.graph_panel.Refresh()

    def AppendToLog(self, msg):
        if not self:
            return
        self.log_text.AppendText('[%s]: %s' % (datetime.datetime.now().strftime("%H:%M:%S"), msg))

    @forceWxThread
    def OnExtended(self, subject, changeType, circuit):
        if not self:
            return
        if changeType == NTFY_CREATED:
            self.AppendToLog("Created circuit %s\n" % (circuit.circuit_id))
        if changeType == NTFY_EXTENDED:
            self.AppendToLog("Extended circuit %s\n" % (circuit.circuit_id))
        if changeType == NTFY_BROKEN:
            self.AppendToLog("Circuit %d has been broken\n" % circuit)

    @forceWxThread
    def OnSelect(self, subject, changeType, circuit, address):
        if not self:
            return
        self.AppendToLog("Circuit %d has been selected for destination %s\n" % (circuit, address))

    @forceWxThread
    def OnJoined(self, subject, changeType, address, circuit_id):
        if not self:
            return
        self.AppendToLog("Joined an external circuit %d with %s:%d\n" % (circuit_id, address[0], address[1]))

    @forceWxThread
    def OnExtendedFor(self, subject, changeType, extended_for, extended_with):
        if not self:
            return
        self.AppendToLog("Extended an external circuit (%s:%d, %d) with (%s:%d, %d)\n" % (
            extended_for[0].sock_addr[0], extended_for[0].sock_addr[1], extended_for[1], extended_with[0].sock_addr[0],
            extended_with[0].sock_addr[1], extended_with[1]))

    @forceWxThread
    def OnIpRemoved(self, subject, changeType, circuit_id):
        if not self:
            return
        self.AppendToLog("Removed introduction circuit %d\n" % (circuit_id))

    @forceWxThread
    def OnIpRecreate(self, subject, changeType, circuit_id, info_hash):
        if not self:
            return
        self.AppendToLog("Recreate introduction circuit to replace circuit %d for info_hash %s\n" % (circuit_id, info_hash))

    @forceWxThread
    def OnRpRemoved(self, subject, changeType, circuit_id):
        if not self:
            return
        self.AppendToLog("Removed rendezvous circuit %d\n" % (circuit_id))

    @forceWxThread
    def OnDhtLookup(self, subject, changeType, info_hash, peers):
        if not self:
            return
        self.AppendToLog("DHT lookup for info_hash %s resulted in peers: %s\n" % (info_hash, repr(peers)))

    @forceWxThread
    def OnKeyRequest(self, subject, changeType, info_hash, peer):
        if not self:
            return
        self.AppendToLog("Request key for info_hash %s from %s\n" % (info_hash, repr(peer)))

    @forceWxThread
    def OnKeyRespond(self, subject, changeType, info_hash, circuit_id):
        if not self:
            return
        self.AppendToLog("Respond with key for info_hash %s to circuit %s\n" % (info_hash, circuit_id))

    @forceWxThread
    def OnKeyResponse(self, subject, changeType, info_hash, circuit_id):
        if not self:
            return
        self.AppendToLog("Respond with key for info_hash %s to circuit %s\n" % (info_hash, circuit_id))

    @forceWxThread
    def OnCreateE2E(self, subject, changeType, info_hash):
        if not self:
            return
        self.AppendToLog("Create end-to-end for info_hash %s\n" % (info_hash))

    @forceWxThread
    def OnCreatedE2E(self, subject, changeType, info_hash, rp_addr):
        if not self:
            return
        self.AppendToLog("Connect rendezvous %s for info_hash %s\n" % (repr(rp_addr[0]), info_hash))

    @forceWxThread
    def OnIpCreated(self, subject, changeType, info_hash, circuit_id):
        if not self:
            return
        self.AppendToLog("Created introduction point %s for info_hash on circuit %d\n" % (info_hash, circuit_id))

    @forceWxThread
    def OnRpCreated(self, subject, changeType, info_hash, circuit_id):
        if not self:
            return
        self.AppendToLog("Created rendezvous point %s for info_hash on circuit %d\n" % (info_hash, circuit_id))

    def OnMouse(self, event):
        if event.Moving():
            self.hop_hover_evt = event.GetPosition()
            self.graph_panel.Refresh()
        elif event.LeftUp():
            self.hop_active_evt = event.GetPosition()
            self.graph_panel.Refresh()

    def OnSize(self, evt):
        size = min(*evt.GetEventObject().GetSize())
        x = min(size + self.margin_x * 2 + self.swarm.GetSize().x, self.GetSize().x - self.circuit_list.GetSize().x)
        y = size + self.margin_y * 2
        self.graph_panel.SetSize((x, y))

    def OnEraseBackground(self, event):
        pass

    def OnPaint(self, event):
        eo = event.GetEventObject()
        dc = wx.BufferedPaintDC(eo)
        dc.SetFont(self.font_large)
        dc.Clear()
        gc = wx.GraphicsContext.Create(dc)

        swarm_size = self.swarm.GetSize()

        w = eo.GetSize().x - 2 * self.margin_x - 1 - swarm_size.x
        h = eo.GetSize().y - 2 * self.margin_y - 1

        swarm_pos = (eo.GetSize().x - swarm_size.x - 11, h / 2 - swarm_size.y / 2 + 11)
        swarm_center = (eo.GetSize().x - swarm_size.x / 2, h / 2 + self.margin_y)

        circuit_points = {}

        if self.hops != 0:
            num_circuits = len(self.circuits)
            for c_index, circuit in enumerate(sorted(self.circuits.values(), key=lambda c: c.circuit_id)):
                circuit_points[circuit] = [(self.margin_x, h / 2 + self.margin_y)]
                for h_index, hop in enumerate(circuit.hops):
                    circuit_points[circuit].append((w * (float(h_index + 1) / (circuit.goal_hops + 1)) + self.margin_x,
                                                    h * (float(c_index + 0.5) / num_circuits) + self.margin_y))
        else:
            circuit_points[None] = [(self.margin_x, h / 2 + self.margin_y)]
            gc.SetPen(wx.Pen(wx.Colour(229, 229, 229), self.line_width))

        # Draw edges
        for circuit, points in circuit_points.iteritems():
            for point1, point2 in zip(points[0::1], points[1::1]):
                if circuit == self.selected_circuit:
                    gc.SetPen(wx.Pen(wx.BLUE, self.line_width))
                else:
                    gc.SetPen(wx.Pen(wx.Colour(229, 229, 229), self.line_width))
                gc.DrawLines([point1, point2])

            # If exit node, draw edge to bittorrent swarm
            if not circuit or circuit.goal_hops == len(circuit.hops):
                gc.DrawLines([points[-1], swarm_center])

        # Draw vertices
        gc.SetPen(wx.Pen(wx.Colour(229, 229, 229), self.line_width))
        for circuit, points in circuit_points.iteritems():
            for index, point in enumerate(points):
                hop = (circuit, index)
                colour = self.hop_to_colour.get(hop, None)
                if not colour:
                    self.hop_to_colour[hop] = colour = random.choice(self.colours[1:]) if index > 0 else self.colours[0]

                x, y = point
                gc.SetBrush(wx.Brush(colour))
                gc.DrawEllipse(x - self.radius / 2, y - self.radius / 2, self.radius, self.radius)

        # Draw swarm and darknet
        gc.DrawBitmap(self.swarm, swarm_pos[0], swarm_pos[1], *swarm_size)
        self.DrawHoverAndInfo(gc, dc, circuit_points)

    def DrawHoverAndInfo(self, gc, dc, circuit_points):
        gc.SetBrush(wx.TRANSPARENT_BRUSH)

        if self.hop_hover_evt:
            self.hop_hover = self.PositionToCircuit(self.hop_hover_evt, circuit_points)
            self.hop_hover_evt = None

        if self.hop_hover and self.hop_hover[0] in circuit_points:
            circuit, hop_index = self.hop_hover
            x, y = circuit_points[circuit][hop_index]
            pen = wx.Pen(wx.Colour(229, 229, 229), 1, wx.USER_DASH)
            pen.SetDashes([8, 4])
            gc.SetPen(pen)
            gc.DrawEllipse(x - self.radius, y - self.radius, self.radius * 2, self.radius * 2)

        if self.hop_active_evt:
            self.hop_active = self.PositionToCircuit(self.hop_active_evt, circuit_points)
            self.hop_active_evt = None

        if self.hop_active and self.hop_active[0] in circuit_points and \
           (not self.hop_active[0] or self.hop_active[1] <= len(self.hop_active[0].hops)):
            circuit, hop_index = self.hop_active
            hop = circuit.hops[hop_index - 1] if hop_index and circuit else None
            x, y = circuit_points[circuit][hop_index]

            # Draw cicle around node
            pen = wx.Pen(self.hop_to_colour.get(self.hop_active, wx.BLACK), 1, wx.USER_DASH)
            pen.SetDashes([8, 4])
            gc.SetPen(pen)
            gc.DrawEllipse(x - self.radius, y - self.radius, self.radius * 2, self.radius * 2)

            # Determine text
            dc.SetFont(self.font_small)
            if not hop:
                text = 'You\nPERMID ' + bin2str(self.tunnel_community.my_member.public_key)[:10]
            else:
                text = 'PERMID ' + bin2str(self.dispersy.crypto.key_to_hash(hop.public_key))[:10]
                if 'UNKNOWN HOST' not in hop.host:
                    text = 'IP %s:%s\n' % (hop.host, hop.port) + text

            # Draw info box + text
            box_width, box_height = self.GetTextExtent(dc, text)
            box_width += 10
            box_height += 10
            x = x - box_width - 1.1 * self.radius if x > self.graph_panel.GetSize()[0] / 2 else x + 1.1 * self.radius
            y = y - box_height - 1.1 * self.radius if y > self.graph_panel.GetSize()[1] / 2 else y + 1.1 * self.radius
            gc.SetBrush(wx.Brush(wx.Colour(216, 237, 255, 50)))
            gc.SetPen(wx.Pen(LIST_BLUE))
            gc.DrawRectangle(x, y, box_width, box_height)
            self.DrawText(dc, text, x + 5, y + 5)

    def GetTextExtent(self, dc, text):
        w_list, h_list = zip(*[dc.GetTextExtent(line) for line in text.split('\n')])
        return max(w_list), sum(h_list)

    def DrawText(self, dc, text, x, y):
        # For wxPython 2.8, newline separated text does not always work with gc.DrawText
        y_cur = y
        for line in text.split('\n'):
            dc.DrawText(line, x, y_cur)
            _, h = dc.GetTextExtent(line)
            y_cur += h

    def PositionToCircuit(self, position, circuit_points):
        for circuit, points in circuit_points.iteritems():
            for index, point in enumerate(points):
                if (position[0] - point[0]) ** 2 + (position[1] - point[1]) ** 2 < self.radius ** 2:
                    return (circuit, index)
        return None

    def ResetSearchBox(self):
        pass