commit 68160bc14ee145c3ad736b2a0c33c6dc40ee1829
Author: Kamran Riaz Khan <krkhan(a)inspirated.com>
Date: Thu Jul 21 04:09:16 2011 +0500
Connections panel cleanup.
The older method relied on changing __class__ attributes of CLI entries
in order to add get_listing_row() methods to them. Changing __class__
directly is a bad practice, hence GUI entries now *wrap* CLI entries
instead of inherting them.
Summary:
* Each CLI entry instance is mapped via cache key to a GUI entry
instance.
* The GUI entry instance is stored inside the treestore rows.
* After every timeout, the entry lines returned by the CLI conn panel
are mapped to GUI instances (using cache, if applicable) and the
resulting GUI entry line is searched in treestore. If one isn't
found, the whole treestore is cleared and refreshed.
The whole treestore refreshing still doesn't sound very nice. Since
if a circuit is expanded by clicking the drop-down, a new connection
taking place would close the expander.
One possible approach of addressing the refresh would be to:
* Add new entries to treestore when they aren't found in the latter
* Delete old entries from treestore when they aren't found in CLI panel
This should theoretically produce 1-1 mapping between CLI panel's
entryLines and the treestore.
---
src/gui/arm.xml | 30 +++++++------
src/gui/connections/circEntry.py | 31 ++++++--------
src/gui/connections/connEntry.py | 42 +++++++++----------
src/gui/connections/connPanel.py | 87 +++++++++++++++++++++++++-------------
4 files changed, 107 insertions(+), 83 deletions(-)
diff --git a/src/gui/arm.xml b/src/gui/arm.xml
index 6c206fd..3b28423 100644
--- a/src/gui/arm.xml
+++ b/src/gui/arm.xml
@@ -12,20 +12,6 @@
<column type="gchararray"/>
</columns>
</object>
- <object class="GtkTreeStore" id="treestore_conn">
- <columns>
- <!-- column-name origin -->
- <column type="gchararray"/>
- <!-- column-name dest -->
- <column type="gchararray"/>
- <!-- column-name time -->
- <column type="gchararray"/>
- <!-- column-name type -->
- <column type="gchararray"/>
- <!-- column-name foreground -->
- <column type="gchararray"/>
- </columns>
- </object>
<object class="GtkListStore" id="liststore_log">
<columns>
<!-- column-name timestamp_raw -->
@@ -54,6 +40,22 @@
<column type="gchararray"/>
</columns>
</object>
+ <object class="GtkTreeStore" id="treestore_conn">
+ <columns>
+ <!-- column-name origin -->
+ <column type="gchararray"/>
+ <!-- column-name dest -->
+ <column type="gchararray"/>
+ <!-- column-name time -->
+ <column type="gchararray"/>
+ <!-- column-name type -->
+ <column type="gchararray"/>
+ <!-- column-name foreground -->
+ <column type="gchararray"/>
+ <!-- column-name entry_line -->
+ <column type="GObject"/>
+ </columns>
+ </object>
<object class="GtkWindow" id="window_main">
<property name="title" translatable="yes">garm</property>
<property name="default_width">640</property>
diff --git a/src/gui/connections/circEntry.py b/src/gui/connections/circEntry.py
index abbb631..04d3208 100644
--- a/src/gui/connections/circEntry.py
+++ b/src/gui/connections/circEntry.py
@@ -4,38 +4,33 @@ Connection panel entries for client circuits.
import time
-from cli.connections import circEntry, entries
+from cli.connections import entries
from gui.connections import connEntry
from util import gtkTools, uiTools
-class CircEntry(circEntry.CircEntry):
- @classmethod
- def convert_to_gui(self, instance):
- instance.__class__ = self
-
-class CircHeaderLine(circEntry.CircHeaderLine, connEntry.ConnectionLine):
- @classmethod
- def convert_to_gui(self, instance):
- instance.__class__ = self
+class CircHeaderLine(connEntry.ConnectionLine):
+ def __init__(self, cliLine):
+ connEntry.ConnectionLine.__init__(self, cliLine)
def get_listing_row(self, listingType):
row = connEntry.ConnectionLine.get_listing_row(self, listingType)
theme = gtkTools.Theme()
- return row[:-1] + (theme.colors['active'],)
+ return row[:-2] + (theme.colors['active'], self)
+
+class CircLine(connEntry.ConnectionLine):
+ def __init__(self, cliLine):
+ connEntry.ConnectionLine.__init__(self, cliLine)
-class CircLine(circEntry.CircLine, connEntry.ConnectionLine):
- @classmethod
- def convert_to_gui(self, instance):
- instance.__class__ = self
+ self.parentIter = None
def get_listing_row(self, listingType):
dst, etc = "", ""
if listingType == entries.ListingType.IP_ADDRESS:
- dst = self.getDestinationLabel(100, includeLocale=True)
- etc = self.foreign.getNickname()
+ dst = self.cliLine.getDestinationLabel(100, includeLocale=True)
+ etc = self.cliLine.foreign.getNickname()
theme = gtkTools.Theme()
- return (dst, etc, self.placementLabel, self.getType(), theme.colors['insensitive'])
+ return (dst, etc, self.cliLine.placementLabel, self.cliLine.getType(), theme.colors['insensitive'], self)
diff --git a/src/gui/connections/connEntry.py b/src/gui/connections/connEntry.py
index f8bcc84..6f4baed 100644
--- a/src/gui/connections/connEntry.py
+++ b/src/gui/connections/connEntry.py
@@ -5,36 +5,34 @@ Connection panel entries related to actual connections to or from the system
import time
-from cli.connections import connEntry, entries
+import gobject
+
+from cli.connections import entries
from cli.connections.connEntry import Category, CONFIG
from util import gtkTools, torTools, uiTools
-class ConnectionEntry(connEntry.ConnectionEntry):
- @classmethod
- def convert_to_gui(self, instance):
- instance.__class__ = self
+class ConnectionLine(gobject.GObject):
+ def __init__(self, cliLine):
+ gobject.GObject.__init__(self)
-class ConnectionLine(connEntry.ConnectionLine):
- @classmethod
- def convert_to_gui(self, instance):
- instance.__class__ = self
+ self.cliLine = cliLine
def get_listing_row(self, listingType):
conn = torTools.getConn()
- myType = self.getType()
- dstAddress = self.getDestinationLabel(26, includeLocale = True)
- localPort = ":%s" % self.local.getPort() if self.includePort else ""
+ myType = self.cliLine.getType()
+ dstAddress = self.cliLine.getDestinationLabel(26, includeLocale = True)
+ localPort = ":%s" % self.cliLine.local.getPort() if self.cliLine.includePort else ""
src, dst, etc = "", "", ""
if listingType == entries.ListingType.IP_ADDRESS:
- myExternalIpAddr = conn.getInfo("address", self.local.getIpAddr())
- addrDiffer = myExternalIpAddr != self.local.getIpAddr()
+ myExternalIpAddr = conn.getInfo("address", self.cliLine.local.getIpAddr())
+ addrDiffer = myExternalIpAddr != self.cliLine.local.getIpAddr()
isExpansionType = not myType in (Category.SOCKS, Category.HIDDEN, Category.CONTROL)
if isExpansionType: srcAddress = myExternalIpAddr + localPort
- else: srcAddress = self.local.getIpAddr() + localPort
+ else: srcAddress = self.cliLine.local.getIpAddr() + localPort
if myType in (Category.SOCKS, Category.CONTROL):
src = dstAddress
@@ -43,19 +41,19 @@ class ConnectionLine(connEntry.ConnectionLine):
src = srcAddress
dst = dstAddress
- if addrDiffer and isExpansionType and self.includeExpandedIpAddr and CONFIG["features.connection.showColumn.expandedIp"]:
- internalAddress = self.local.getIpAddr() + localPort
+ if addrDiffer and isExpansionType and self.cliLine.includeExpandedIpAddr and CONFIG["features.connection.showColumn.expandedIp"]:
+ internalAddress = self.cliLine.local.getIpAddr() + localPort
if myType == Category.INBOUND: (src, dst) = (src, internalAddress)
else: (src, dst) = (internalAddress, src)
- etc = self.getEtcContent(100, listingType)
+ etc = self.cliLine.getEtcContent(100, listingType)
else:
- src = "%s:%s" % (self.local.ipAddr, self.local.port)
- dst = "%s:%s" % (self.foreign.ipAddr, self.foreign.port)
+ src = "%s:%s" % (self.cliLine.local.ipAddr, self.cliLine.local.port)
+ dst = "%s:%s" % (self.cliLine.foreign.ipAddr, self.cliLine.foreign.port)
- timeLabel = uiTools.getTimeLabel(time.time() - self.startTime)
+ timeLabel = uiTools.getTimeLabel(time.time() - self.cliLine.startTime)
theme = gtkTools.Theme()
- return (src, dst, timeLabel, self.getType(), theme.colors['insensitive'])
+ return (src, dst, timeLabel, self.cliLine.getType(), theme.colors['insensitive'], self)
diff --git a/src/gui/connections/connPanel.py b/src/gui/connections/connPanel.py
index 4ab0ed8..55669f9 100644
--- a/src/gui/connections/connPanel.py
+++ b/src/gui/connections/connPanel.py
@@ -17,37 +17,48 @@ from gui.connections import circEntry, connEntry
from util import connections, sysTools, uiTools, torTools
from TorCtl import TorCtl
-REFRESH_RATE = 3
-
-def convert_to_gui(instance):
- cliToGuiMap = [ (cliCircEntry.CircEntry, circEntry.CircEntry),
- (cliCircEntry.CircHeaderLine, circEntry.CircHeaderLine),
+class GuiConverter:
+ cliToGuiMap = [ (cliCircEntry.CircHeaderLine, circEntry.CircHeaderLine),
(cliCircEntry.CircLine, circEntry.CircLine),
- (cliConnEntry.ConnectionEntry, connEntry.ConnectionEntry),
(cliConnEntry.ConnectionLine, connEntry.ConnectionLine)]
- for (cliClass, guiClass) in cliToGuiMap:
- if isinstance(instance, cliClass):
- guiClass.convert_to_gui(instance)
- break
+ def __init__(self):
+ self._cache = {}
+
+ def __call__(self, cliInstance):
+ cacheKey = self._calculate_cache_key(cliInstance)
+
+ if self._cache.has_key(cacheKey):
+ return self._cache[cacheKey]
+
+ line = None
+
+ for (cliClass, guiClass) in GuiConverter.cliToGuiMap:
+ if isinstance(cliInstance, cliClass):
+ line = guiClass(cliInstance)
+ self._cache[cacheKey] = line
+ break
+
+ return line
-def calculate_cache_key(entryLine):
- local = (entryLine.local.ipAddr, entryLine.local.port)
- foreign = (entryLine.foreign.ipAddr, entryLine.foreign.port)
+ def _calculate_cache_key(self, entryLine):
+ local = (entryLine.local.ipAddr, entryLine.local.port)
+ foreign = (entryLine.foreign.ipAddr, entryLine.foreign.port)
- return (entryLine.__class__, local, foreign)
+ return (entryLine.__class__, local, foreign)
+
+convert_to_gui = GuiConverter()
class ConnectionPanel(CliConnectionPanel):
def __init__(self, builder):
CliConnectionPanel.__init__(self, None)
self.builder = builder
- self.cache = {}
conn = torTools.getConn()
torPid = conn.getMyPid()
- torCmdName = sysTools.getProcessName(torPid, "tor")
- connections.getResolver(torCmdName, torPid, "tor")
+ torCmdName = sysTools.getProcessName(torPid, 'tor')
+ connections.getResolver(torCmdName, torPid, 'tor')
gobject.idle_add(self._fill_entries)
gobject.timeout_add(3000, self._timeout_fill_entries)
@@ -70,14 +81,15 @@ class ConnectionPanel(CliConnectionPanel):
# first pass checks whether we have enough entries cached to not update the treeview
index = 0
- for line in self._entryLines:
- convert_to_gui(line)
- cacheKey = calculate_cache_key(line)
+ for cliLine in self._entryLines:
+ line = convert_to_gui(cliLine)
+
+ treeIter = self._treestore_get_iter(line)
- if self.cache.has_key(cacheKey):
+ if treeIter:
if not isinstance(line, circEntry.CircLine):
- timeLabel = "%d s" % (time.time() - line.startTime)
- treeStore.set_value(self.cache[cacheKey], 2, timeLabel)
+ timeLabel = "%d s" % (time.time() - line.cliLine.startTime)
+ treeStore.set_value(treeIter, 2, timeLabel)
else:
break
@@ -91,10 +103,10 @@ class ConnectionPanel(CliConnectionPanel):
treeStore.clear()
headerIter = None
- for line in self._entryLines:
- convert_to_gui(line)
+ for cliLine in self._entryLines:
+ line = convert_to_gui(cliLine)
- if isinstance(line, connEntry.ConnectionLine) and line.isUnresolvedApp():
+ if isinstance(line, connEntry.ConnectionLine) and line.cliLine.isUnresolvedApp():
self._resolveApps()
row = line.get_listing_row(self._listingType)
@@ -107,8 +119,25 @@ class ConnectionPanel(CliConnectionPanel):
else:
currentIter = treeStore.append(None, row)
- cacheKey = calculate_cache_key(line)
- self.cache[cacheKey] = currentIter
-
self.valsLock.release()
+ def _treestore_get_iter(self, line):
+ def match_func(model, treeIter, data):
+ column, key = data
+ value = model.get_value(treeIter, column)
+ return value == key
+
+ def search(model, treeIter, func, data):
+ while treeIter:
+ if func(model, treeIter, data):
+ return treeIter
+ result = search(model, model.iter_children(treeIter), func, data)
+ if result: return result
+ treeIter = model.iter_next(treeIter)
+ return None
+
+ treeStore = self.builder.get_object('treestore_conn')
+ matchIter = search(treeStore, treeStore.iter_children(None), match_func, (5, line))
+
+ return matchIter
+