|
|
@ -25,18 +25,22 @@ gobject.threads_init() |
|
|
|
import logging |
|
|
|
import logging |
|
|
|
import threading |
|
|
|
import threading |
|
|
|
import Queue |
|
|
|
import Queue |
|
|
|
|
|
|
|
import re |
|
|
|
|
|
|
|
|
|
|
|
from mitterlib.ui.helpers.image_helpers import find_image |
|
|
|
from mitterlib.ui.helpers.image_helpers import find_image |
|
|
|
|
|
|
|
|
|
|
|
#from mitterlib.constants import gpl_3, version |
|
|
|
from mitterlib.constants import gpl_3, version |
|
|
|
#from mitterlib.ui.helpers.utils import str_len |
|
|
|
#from mitterlib.ui.helpers.utils import str_len |
|
|
|
#from mitterlib.ui.helpers.notify import Notify |
|
|
|
#from mitterlib.ui.helpers.notify import Notify |
|
|
|
#from mitterlib.ui.helpers import timesince |
|
|
|
from mitterlib.ui.helpers import timesince |
|
|
|
|
|
|
|
|
|
|
|
# Constants |
|
|
|
# Constants |
|
|
|
|
|
|
|
|
|
|
|
_log = logging.getLogger('ui.pygtk') |
|
|
|
_log = logging.getLogger('ui.pygtk') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
URL_RE = re.compile( |
|
|
|
|
|
|
|
r'((?:(?:https?|ftp)://|www[-\w]*\.)[^\s\n\r]+[-\w+&@#%=~])', re.I) |
|
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------- |
|
|
|
# ---------------------------------------------------------------------- |
|
|
|
# Helper Functions (not related to objects or that don't need direct access to |
|
|
|
# Helper Functions (not related to objects or that don't need direct access to |
|
|
|
# the objects contents.) |
|
|
|
# the objects contents.) |
|
|
@ -75,65 +79,75 @@ class _WorkerThread(threading.Thread, _IdleObject): |
|
|
|
""" |
|
|
|
""" |
|
|
|
__gsignals__ = { |
|
|
|
__gsignals__ = { |
|
|
|
"completed": ( |
|
|
|
"completed": ( |
|
|
|
gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [ |
|
|
|
gobject.SIGNAL_RUN_LAST, |
|
|
|
gobject.TYPE_PYOBJECT]), # The list/NetworkData object |
|
|
|
gobject.TYPE_NONE, |
|
|
|
|
|
|
|
(gobject.TYPE_PYOBJECT,)), # list/networkdata |
|
|
|
"exception": ( |
|
|
|
"exception": ( |
|
|
|
gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [ |
|
|
|
gobject.SIGNAL_RUN_LAST, |
|
|
|
gobject.TYPE_PYOBJECT]) # The exception |
|
|
|
gobject.TYPE_NONE, |
|
|
|
|
|
|
|
(gobject.TYPE_PYOBJECT,)) # The exception |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, queue): |
|
|
|
def __init__(self, function, *args, **kwargs): |
|
|
|
threading.Thread.__init__(self) |
|
|
|
threading.Thread.__init__(self) |
|
|
|
_IdleObject.__init__(self) |
|
|
|
_IdleObject.__init__(self) |
|
|
|
self._queue = queue |
|
|
|
self._function = function |
|
|
|
|
|
|
|
self._args = args |
|
|
|
|
|
|
|
self._kwargs = kwargs |
|
|
|
|
|
|
|
|
|
|
|
def run(self): |
|
|
|
def run(self): |
|
|
|
while True: |
|
|
|
# call the function |
|
|
|
_log.debug('Thread %d waiting for work', self.get_ident()) |
|
|
|
_log.debug('Thread %s calling %s', self.ident, str(self._function)) |
|
|
|
work = self._queue.get() |
|
|
|
|
|
|
|
if work is None: |
|
|
|
|
|
|
|
# End the thread |
|
|
|
|
|
|
|
break |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# call the function |
|
|
|
|
|
|
|
_log.debug('Thread %s got work', self.get_ident()) |
|
|
|
|
|
|
|
(complete, exception, function, args, kwargs) = work |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_log.debug('Calling %s', str(function)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try: |
|
|
|
args = self._args |
|
|
|
result = function(*args, **kwargs) |
|
|
|
kwargs = self._kwargs |
|
|
|
except Exception, exc: # Catch ALL exceptions |
|
|
|
|
|
|
|
# XXX: Check if this catch all warnins too! |
|
|
|
|
|
|
|
_log.debug('Exception %s', str(exc)) |
|
|
|
|
|
|
|
self.emit("exception", exc) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.emit("completed", result) |
|
|
|
try: |
|
|
|
|
|
|
|
result = self._function(*args, **kwargs) |
|
|
|
|
|
|
|
except Exception, exc: # Catch ALL exceptions |
|
|
|
|
|
|
|
# XXX: Check if this catch all warnins too! |
|
|
|
|
|
|
|
_log.debug('Exception %s', str(exc)) |
|
|
|
|
|
|
|
self.emit("exception", exc) |
|
|
|
|
|
|
|
|
|
|
|
# work complete |
|
|
|
_log.debug('Thread %s completed', self.name) |
|
|
|
self._queue.task_done() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_log.debug('Thread %d ending', self.get_ident()) |
|
|
|
self.emit("completed", result) |
|
|
|
return |
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class _ThreadManager(object): |
|
|
|
class _ThreadManager(object): |
|
|
|
"""Manages the threads.""" |
|
|
|
"""Manages the threads.""" |
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, pool_size=2): |
|
|
|
def __init__(self, max_threads=2): |
|
|
|
"""Start the thread pool. The number of threads in the pool is defined |
|
|
|
"""Start the thread pool. The number of threads in the pool is defined |
|
|
|
by `pool_size`, defaults to 2.""" |
|
|
|
by `pool_size`, defaults to 2.""" |
|
|
|
self._queue = Queue.Queue() |
|
|
|
self._max_threads = max_threads |
|
|
|
self._thread_pool = [] |
|
|
|
self._thread_pool = [] |
|
|
|
|
|
|
|
self._running = [] |
|
|
|
|
|
|
|
self._thread_id = 0 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _remove_thread(self, widget, arg=None): |
|
|
|
|
|
|
|
"""Called when the thread completes. We remove it from the thread list |
|
|
|
|
|
|
|
(dictionary, actually) and start the next thread (if there is one).""" |
|
|
|
|
|
|
|
|
|
|
|
while pool_size > 0: |
|
|
|
# not actually a widget. It's the object that emitted the signal, in |
|
|
|
_log.debug('Starting thread %d', pool_size) |
|
|
|
# this case, the _WorkerThread object. |
|
|
|
new_thread = _WorkerThread(self._queue) |
|
|
|
thread_id = widget.name |
|
|
|
new_thread.start() |
|
|
|
|
|
|
|
self._thread_pool.append(new_thread) |
|
|
|
_log.debug('Thread %s completed, %d threads in the queue', thread_id, |
|
|
|
pool_size -= 1 |
|
|
|
len(self._thread_pool)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self._running.remove(thread_id) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if self._thread_pool: |
|
|
|
|
|
|
|
if len(self._running) < self._max_threads: |
|
|
|
|
|
|
|
next = self._thread_pool.pop() |
|
|
|
|
|
|
|
_log.debug('Dequeuing thread %s', thread.name) |
|
|
|
|
|
|
|
self._running.append(thread.name) |
|
|
|
|
|
|
|
thread.start() |
|
|
|
|
|
|
|
|
|
|
|
_log.debug('All threads started') |
|
|
|
|
|
|
|
return |
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
def add_work(self, complete_cb, exception_cb, func, *args, **kwargs): |
|
|
|
def add_work(self, complete_cb, exception_cb, func, *args, **kwargs): |
|
|
@ -144,9 +158,26 @@ class _ThreadManager(object): |
|
|
|
function to be called in the secondary threads. `args` and `kwargs` |
|
|
|
function to be called in the secondary threads. `args` and `kwargs` |
|
|
|
are parameters passed to the function.""" |
|
|
|
are parameters passed to the function.""" |
|
|
|
|
|
|
|
|
|
|
|
# wrap all the params in a nice tuple to be added to the queue. |
|
|
|
thread = _WorkerThread(func, *args, **kwargs) |
|
|
|
work = (complete_cb, exception_cb, func, args, kwargs) |
|
|
|
thread_id = '%s' % (self._thread_id) |
|
|
|
self._queue.put(work) |
|
|
|
|
|
|
|
|
|
|
|
thread.connect('completed', complete_cb) |
|
|
|
|
|
|
|
thread.connect('completed', self._remove_thread) |
|
|
|
|
|
|
|
thread.connect('exception', exception_cb) |
|
|
|
|
|
|
|
thread.setName(thread_id) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if len(self._running) < self._max_threads: |
|
|
|
|
|
|
|
# immediatelly start the thread |
|
|
|
|
|
|
|
self._running.append(thread_id) |
|
|
|
|
|
|
|
thread.start() |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
# add the thread to the queue |
|
|
|
|
|
|
|
running_names = ', '.join(self._running) |
|
|
|
|
|
|
|
_log.debug('Threads %s running, adding %s to the queue', |
|
|
|
|
|
|
|
running_names, thread_id) |
|
|
|
|
|
|
|
self._thread_pool.append(thread) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self._thread_id += 1 |
|
|
|
return |
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------- |
|
|
|
# ---------------------------------------------------------------------- |
|
|
@ -459,7 +490,7 @@ class Interface(object): |
|
|
|
message = re.sub(r'&(?!(amp;|gt;|lt;|quot;|apos;))', r'&', message) |
|
|
|
message = re.sub(r'&(?!(amp;|gt;|lt;|quot;|apos;))', r'&', message) |
|
|
|
|
|
|
|
|
|
|
|
# highlight URLs |
|
|
|
# highlight URLs |
|
|
|
message = url_re.sub(r'<span foreground="blue">\1</span>', |
|
|
|
message = URL_RE.sub(r'<span foreground="blue">\1</span>', |
|
|
|
message) |
|
|
|
message) |
|
|
|
|
|
|
|
|
|
|
|
# use a different highlight for the current user |
|
|
|
# use a different highlight for the current user |
|
|
@ -521,6 +552,23 @@ class Interface(object): |
|
|
|
gtk.main_quit() |
|
|
|
gtk.main_quit() |
|
|
|
return |
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ------------------------------------------------------------ |
|
|
|
|
|
|
|
# Network related functions |
|
|
|
|
|
|
|
# ------------------------------------------------------------ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _post_get_messages(self, widget, results): |
|
|
|
|
|
|
|
"""Function called after the data from the messages list is |
|
|
|
|
|
|
|
retrieved.""" |
|
|
|
|
|
|
|
_log.debug('Got results') |
|
|
|
|
|
|
|
#_log.debug('Results from the request: %s', str(results)) |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _exception_get_messages(self, widget, exception): |
|
|
|
|
|
|
|
"""Function called if the retrival of current messages returns an |
|
|
|
|
|
|
|
exception.""" |
|
|
|
|
|
|
|
_log.debug(str(exception)) |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
# ------------------------------------------------------------ |
|
|
|
# ------------------------------------------------------------ |
|
|
|
# Required functions for all interfaces |
|
|
|
# Required functions for all interfaces |
|
|
|
# ------------------------------------------------------------ |
|
|
|
# ------------------------------------------------------------ |
|
|
@ -548,8 +596,6 @@ class Interface(object): |
|
|
|
self._default_pixmap = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, |
|
|
|
self._default_pixmap = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, |
|
|
|
has_alpha=False, bits_per_sample=8, width=48, height=48) |
|
|
|
has_alpha=False, bits_per_sample=8, width=48, height=48) |
|
|
|
|
|
|
|
|
|
|
|
self._main_window = self._create_main_window() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#self._systray_setup() |
|
|
|
#self._systray_setup() |
|
|
|
# self.create_settings_dialog() |
|
|
|
# self.create_settings_dialog() |
|
|
|
# self.username_field.set_text(default_username) |
|
|
|
# self.username_field.set_text(default_username) |
|
|
@ -572,7 +618,14 @@ class Interface(object): |
|
|
|
"""Call function; displays the interface. This method should appear on |
|
|
|
"""Call function; displays the interface. This method should appear on |
|
|
|
every interface.""" |
|
|
|
every interface.""" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self._main_window = self._create_main_window() |
|
|
|
self._main_window.show_all() |
|
|
|
self._main_window.show_all() |
|
|
|
|
|
|
|
self._threads = _ThreadManager() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# queue the first fetch |
|
|
|
|
|
|
|
self._threads.add_work(self._post_get_messages, |
|
|
|
|
|
|
|
self._exception_get_messages, |
|
|
|
|
|
|
|
self._connection.messages) |
|
|
|
|
|
|
|
|
|
|
|
gtk.main() |
|
|
|
gtk.main() |
|
|
|
|
|
|
|
|
|
|
|