root/trunk/src/history_manager.py

Revision 10549, 21.5 kB (checked in by asterix, 6 weeks ago)

revert thorstenp patches for now. They introduce bugs.

  • Property svn:executable set to *
Line 
1#!/usr/bin/env python
2# -*- coding:utf-8 -*-
3## src/history_manager.py
4##
5## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
6## Copyright (C) 2006-2007 Jean-Marie Traissard <jim AT lapin.org>
7##                         Nikos Kouremenos <kourem AT gmail.com>
8## Copyright (C) 2006-2008 Yann Leboulanger <asterix AT lagaule.org>
9## Copyright (C) 2007 Stephan Erb <steve-e AT h3c.de>
10## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
11##
12## This file is part of Gajim.
13##
14## Gajim is free software; you can redistribute it and/or modify
15## it under the terms of the GNU General Public License as published
16## by the Free Software Foundation; version 3 only.
17##
18## Gajim is distributed in the hope that it will be useful,
19## but WITHOUT ANY WARRANTY; without even the implied warranty of
20## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21## GNU General Public License for more details.
22##
23## You should have received a copy of the GNU General Public License
24## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
25##
26
27## NOTE: some method names may match those of logger.py but that's it
28## someday (TM) should have common class that abstracts db connections and helpers on it
29## the same can be said for history_window.py
30
31import os
32
33if os.name == 'nt':
34        import warnings
35        warnings.filterwarnings(action='ignore')
36
37        if os.path.isdir('gtk'):
38                # Used to create windows installer with GTK included
39                paths = os.environ['PATH']
40                list_ = paths.split(';')
41                new_list = []
42                for p in list_:
43                        if p.find('gtk') < 0 and p.find('GTK') < 0:
44                                new_list.append(p)
45                new_list.insert(0, 'gtk/lib')
46                new_list.insert(0, 'gtk/bin')
47                os.environ['PATH'] = ';'.join(new_list)
48                os.environ['GTK_BASEPATH'] = 'gtk'
49
50import sys
51import signal
52import gtk
53import gobject
54import time
55import locale
56
57from common import i18n
58import common.configpaths 
59common.configpaths.gajimpaths.init()
60common.configpaths.gajimpaths.init_profile()
61from common import exceptions
62import dialogs
63import gtkgui_helpers
64from common.logger import LOG_DB_PATH, constants
65
66#FIXME: constants should implement 2 way mappings
67status = dict((constants.__dict__[i], i[5:].lower()) for i in \
68        constants.__dict__.keys() if i.startswith('SHOW_')) 
69from common import gajim
70from common import helpers
71
72# time, message, subject
73(
74C_UNIXTIME,
75C_MESSAGE,
76C_SUBJECT,
77C_NICKNAME
78) = range(2, 6)
79
80
81try:
82        import sqlite3 as sqlite # python 2.5
83except ImportError:
84        try:
85                from pysqlite2 import dbapi2 as sqlite
86        except ImportError:
87                raise exceptions.PysqliteNotAvailable
88
89
90class HistoryManager:
91        def __init__(self):
92                path_to_file = os.path.join(gajim.DATA_DIR, 'pixmaps/gajim.png')
93                pix = gtk.gdk.pixbuf_new_from_file(path_to_file)
94                gtk.window_set_default_icon(pix) # set the icon to all newly opened windows
95               
96                if not os.path.exists(LOG_DB_PATH):
97                        dialogs.ErrorDialog(_('Cannot find history logs database'),
98                                '%s does not exist.' % LOG_DB_PATH)
99                        sys.exit()
100               
101                xml = gtkgui_helpers.get_glade('history_manager.glade')
102                self.window = xml.get_widget('history_manager_window')
103                self.jids_listview = xml.get_widget('jids_listview')
104                self.logs_listview = xml.get_widget('logs_listview')
105                self.search_results_listview = xml.get_widget('search_results_listview')
106                self.search_entry = xml.get_widget('search_entry')
107                self.logs_scrolledwindow = xml.get_widget('logs_scrolledwindow')
108                self.search_results_scrolledwindow = xml.get_widget(
109                        'search_results_scrolledwindow')
110                self.welcome_vbox = xml.get_widget('welcome_vbox')
111
112                self.jids_already_in = [] # holds jids that we already have in DB
113                self.AT_LEAST_ONE_DELETION_DONE = False
114               
115                self.con = sqlite.connect(LOG_DB_PATH, timeout = 20.0,
116                        isolation_level = 'IMMEDIATE')
117                self.cur = self.con.cursor()
118
119                self._init_jids_listview()
120                self._init_logs_listview()
121                self._init_search_results_listview()
122               
123                self._fill_jids_listview()
124               
125                self.search_entry.grab_focus()
126
127                self.window.show_all()
128               
129                xml.signal_autoconnect(self)
130       
131        def _init_jids_listview(self):
132                self.jids_liststore = gtk.ListStore(str, str) # jid, jid_id
133                self.jids_listview.set_model(self.jids_liststore)
134                self.jids_listview.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
135
136                renderer_text = gtk.CellRendererText() # holds jid
137                col = gtk.TreeViewColumn(_('Contacts'), renderer_text, text = 0)
138                self.jids_listview.append_column(col)
139               
140                self.jids_listview.get_selection().connect('changed',
141                        self.on_jids_listview_selection_changed)
142
143        def _init_logs_listview(self):
144                # log_line_id (HIDDEN), jid_id (HIDDEN), time, message, subject, nickname
145                self.logs_liststore = gtk.ListStore(str, str, str, str, str, str)
146                self.logs_listview.set_model(self.logs_liststore)
147                self.logs_listview.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
148
149                renderer_text = gtk.CellRendererText() # holds time
150                col = gtk.TreeViewColumn(_('Date'), renderer_text, text = C_UNIXTIME)
151                col.set_sort_column_id(C_UNIXTIME) # user can click this header and sort
152                col.set_resizable(True)
153                self.logs_listview.append_column(col)
154               
155                renderer_text = gtk.CellRendererText() # holds nickname
156                col = gtk.TreeViewColumn(_('Nickname'), renderer_text, text = C_NICKNAME)
157                col.set_sort_column_id(C_NICKNAME) # user can click this header and sort
158                col.set_resizable(True)
159                col.set_visible(False)
160                self.nickname_col_for_logs = col
161                self.logs_listview.append_column(col)
162
163                renderer_text = gtk.CellRendererText() # holds message
164                col = gtk.TreeViewColumn(_('Message'), renderer_text, markup = C_MESSAGE)
165                col.set_sort_column_id(C_MESSAGE) # user can click this header and sort
166                col.set_resizable(True)
167                self.message_col_for_logs = col
168                self.logs_listview.append_column(col)
169
170                renderer_text = gtk.CellRendererText() # holds subject
171                col = gtk.TreeViewColumn(_('Subject'), renderer_text, text = C_SUBJECT)
172                col.set_sort_column_id(C_SUBJECT) # user can click this header and sort
173                col.set_resizable(True)
174                col.set_visible(False)
175                self.subject_col_for_logs = col
176                self.logs_listview.append_column(col)
177
178        def _init_search_results_listview(self):
179                # log_line_id (HIDDEN), jid, time, message, subject, nickname
180                self.search_results_liststore = gtk.ListStore(str, str, str, str, str, str)
181                self.search_results_listview.set_model(self.search_results_liststore)
182               
183                renderer_text = gtk.CellRendererText() # holds JID (who said this)
184                col = gtk.TreeViewColumn(_('JID'), renderer_text, text = 1)
185                col.set_sort_column_id(1) # user can click this header and sort
186                col.set_resizable(True)
187                self.search_results_listview.append_column(col)
188               
189                renderer_text = gtk.CellRendererText() # holds time
190                col = gtk.TreeViewColumn(_('Date'), renderer_text, text = C_UNIXTIME)
191                col.set_sort_column_id(C_UNIXTIME) # user can click this header and sort
192                col.set_resizable(True)
193                self.search_results_listview.append_column(col)
194
195                renderer_text = gtk.CellRendererText() # holds message
196                col = gtk.TreeViewColumn(_('Message'), renderer_text, text = C_MESSAGE)
197                col.set_sort_column_id(C_MESSAGE) # user can click this header and sort
198                col.set_resizable(True)
199                self.search_results_listview.append_column(col)
200
201                renderer_text = gtk.CellRendererText() # holds subject
202                col = gtk.TreeViewColumn(_('Subject'), renderer_text, text = C_SUBJECT)
203                col.set_sort_column_id(C_SUBJECT) # user can click this header and sort
204                col.set_resizable(True)
205                self.search_results_listview.append_column(col)
206               
207                renderer_text = gtk.CellRendererText() # holds nickname
208                col = gtk.TreeViewColumn(_('Nickname'), renderer_text, text = C_NICKNAME)
209                col.set_sort_column_id(C_NICKNAME) # user can click this header and sort
210                col.set_resizable(True)
211                self.search_results_listview.append_column(col)
212       
213        def on_history_manager_window_delete_event(self, widget, event):
214                if self.AT_LEAST_ONE_DELETION_DONE:
215                        def on_yes(clicked):
216                                self.cur.execute('VACUUM')
217                                self.con.commit()
218                                gtk.main_quit()
219
220                        def on_no():
221                                gtk.main_quit()
222
223                        dialog = dialogs.YesNoDialog(
224                                _('Do you want to clean up the database? '
225                                '(STRONGLY NOT RECOMMENDED IF GAJIM IS RUNNING)'),
226                                _('Normally allocated database size will not be freed, '
227                                        'it will just become reusable. If you really want to reduce '
228                                        'database filesize, click YES, else click NO.'
229                                        '\n\nIn case you click YES, please wait...'),
230                                on_response_yes=on_yes, on_response_no=on_no)
231                        return
232                                                       
233                gtk.main_quit()
234       
235        def _fill_jids_listview(self):
236                # get those jids that have at least one entry in logs
237                self.cur.execute('SELECT jid, jid_id FROM jids WHERE jid_id IN (SELECT '
238                        'distinct logs.jid_id FROM logs) ORDER BY jid')
239                rows = self.cur.fetchall() # list of tupples: [(u'aaa@bbb',), (u'cc@dd',)]
240                for row in rows:
241                        self.jids_already_in.append(row[0]) # jid
242                        self.jids_liststore.append(row) # jid, jid_id
243       
244        def on_jids_listview_selection_changed(self, widget, data = None):
245                liststore, list_of_paths = self.jids_listview.get_selection()\
246                        .get_selected_rows()
247                paths_len = len(list_of_paths)
248                if paths_len == 0: # nothing is selected
249                        return
250
251                self.logs_liststore.clear() # clear the store
252               
253                self.welcome_vbox.hide()
254                self.search_results_scrolledwindow.hide()
255                self.logs_scrolledwindow.show()
256
257                list_of_rowrefs = []
258                for path in list_of_paths: # make them treerowrefs (it's needed)
259                        list_of_rowrefs.append(gtk.TreeRowReference(liststore, path))
260               
261                for rowref in list_of_rowrefs: # FILL THE STORE, for all rows selected
262                        path = rowref.get_path()
263                        if path is None:
264                                continue
265                        jid = liststore[path][0] # jid
266                        self._fill_logs_listview(jid)
267       
268        def _get_jid_id(self, jid):
269                '''jids table has jid and jid_id
270                logs table has log_id, jid_id, contact_name, time, kind, show, message
271                so to ask logs we need jid_id that matches our jid in jids table
272                this method wants jid and returns the jid_id for later sql-ing on logs
273                '''
274                if jid.find('/') != -1: # if it has a /
275                        jid_is_from_pm = self._jid_is_from_pm(jid)
276                        if not jid_is_from_pm: # it's normal jid with resource
277                                jid = jid.split('/', 1)[0] # remove the resource
278                self.cur.execute('SELECT jid_id FROM jids WHERE jid = ?', (jid,))
279                jid_id = self.cur.fetchone()[0]
280                return str(jid_id)
281
282        def _get_jid_from_jid_id(self, jid_id):
283                '''jids table has jid and jid_id
284                this method accepts jid_id and returns the jid for later sql-ing on logs
285                '''
286                self.cur.execute('SELECT jid FROM jids WHERE jid_id = ?', (jid_id,))
287                jid = self.cur.fetchone()[0]
288                return jid
289
290        def _jid_is_from_pm(self, jid):
291                '''if jid is gajim@conf/nkour it's likely a pm one, how we know
292                gajim@conf is not a normal guy and nkour is not his resource?
293                we ask if gajim@conf is already in jids (with type room jid)
294                this fails if user disables logging for room and only enables for
295                pm (so higly unlikely) and if we fail we do not go chaos
296                (user will see the first pm as if it was message in room's public chat)
297                and after that all okay'''
298               
299                possible_room_jid, possible_nick = jid.split('/', 1)
300               
301                self.cur.execute('SELECT jid_id FROM jids WHERE jid = ? AND type = ?',
302                        (possible_room_jid, constants.JID_ROOM_TYPE))
303                row = self.cur.fetchone()
304                if row is None:
305                        return False
306                else:
307                        return True
308
309        def _jid_is_room_type(self, jid):
310                '''returns True/False if given id is room type or not
311                eg. if it is room'''
312                self.cur.execute('SELECT type FROM jids WHERE jid = ?', (jid,))
313                row = self.cur.fetchone()
314                if row is None:
315                        raise
316                elif row[0] == constants.JID_ROOM_TYPE:
317                        return True
318                else: # normal type
319                        return False
320       
321        def _fill_logs_listview(self, jid):
322                '''fill the listview with all messages that user sent to or
323                received from JID'''
324                # no need to lower jid in this context as jid is already lowered
325                # as we use those jids from db
326                jid_id = self._get_jid_id(jid)
327                self.cur.execute('''
328                        SELECT log_line_id, jid_id, time, kind, message, subject, contact_name, show
329                        FROM logs
330                        WHERE jid_id = ?
331                        ORDER BY time
332                        ''', (jid_id,))
333
334                results = self.cur.fetchall()
335               
336                if self._jid_is_room_type(jid): # is it room?
337                        self.nickname_col_for_logs.set_visible(True)
338                        self.subject_col_for_logs.set_visible(False)
339                else:
340                        self.nickname_col_for_logs.set_visible(False)
341                        self.subject_col_for_logs.set_visible(True)
342
343                for row in results:
344                        # exposed in UI (TreeViewColumns) are only
345                        # time, message, subject, nickname
346                        # but store in liststore
347                        # log_line_id, jid_id, time, message, subject, nickname
348                        log_line_id, jid_id, time_, kind, message, subject, nickname, show = row
349                        try:
350                                time_ = time.strftime('%x', time.localtime(float(time_))).decode(
351                                        locale.getpreferredencoding())
352                        except ValueError:
353                                pass
354                        else:
355                                color = None
356                                if kind in (constants.KIND_SINGLE_MSG_RECV,
357                                constants.KIND_CHAT_MSG_RECV, constants.KIND_GC_MSG):
358                                        # it is the other side
359                                        color = gajim.config.get('inmsgcolor') # so incoming color
360                                elif kind in (constants.KIND_SINGLE_MSG_SENT,
361                                constants.KIND_CHAT_MSG_SENT): # it is us
362                                        color = gajim.config.get('outmsgcolor') # so outgoing color
363                                elif kind in (constants.KIND_STATUS,
364                                constants.KIND_GCSTATUS): # is is statuses
365                                        color = gajim.config.get('statusmsgcolor') # so status color
366                                        # include status into (status) message
367                                        if message is None:
368                                                message = ''
369                                        else:
370                                                message = ' : ' + message
371                                        message = helpers.get_uf_show(gajim.SHOW_LIST[show]) + message
372
373                                message_ = '<span'
374                                if color:
375                                        message_ += ' foreground="%s"' % color
376                                message_ += '>%s</span>' % \
377                                        gobject.markup_escape_text(message)
378                                self.logs_liststore.append((log_line_id, jid_id, time_, message_,
379                                        subject, nickname))
380
381        def _fill_search_results_listview(self, text):
382                '''ask db and fill listview with results that match text'''
383                self.search_results_liststore.clear()
384                like_sql = '%' + text + '%'
385                self.cur.execute('''
386                        SELECT log_line_id, jid_id, time, message, subject, contact_name
387                        FROM logs
388                        WHERE message LIKE ? OR subject LIKE ?
389                        ORDER BY time
390                        ''', (like_sql, like_sql))
391               
392                results = self.cur.fetchall()
393                for row in results:
394                        # exposed in UI (TreeViewColumns) are only
395                        # JID, time, message, subject, nickname
396                        # but store in liststore
397                        # log_line_id, jid (from jid_id), time, message, subject, nickname
398                        log_line_id, jid_id, time_, message, subject, nickname = row
399                        try:
400                                time_ = time.strftime('%x', time.localtime(float(time_))).decode(
401                                        locale.getpreferredencoding())
402                        except ValueError:
403                                pass
404                        else:
405                                jid = self._get_jid_from_jid_id(jid_id)
406                               
407                                self.search_results_liststore.append((log_line_id, jid, time_,
408                                        message, subject, nickname))
409
410        def on_logs_listview_key_press_event(self, widget, event):
411                liststore, list_of_paths = self.logs_listview.get_selection()\
412                        .get_selected_rows()
413                if event.keyval == gtk.keysyms.Delete:
414                        self._delete_logs(liststore, list_of_paths)
415                       
416        def on_listview_button_press_event(self, widget, event):
417                if event.button == 3: # right click
418                        xml = gtkgui_helpers.get_glade('history_manager.glade', 'context_menu')
419                        if widget.name != 'jids_listview':
420                                xml.get_widget('export_menuitem').hide()
421                        xml.get_widget('delete_menuitem').connect('activate',
422                                self.on_delete_menuitem_activate, widget)
423                       
424                        liststore, list_of_paths = self.jids_listview.get_selection()\
425                                .get_selected_rows()
426                       
427                        xml.signal_autoconnect(self)
428                        xml.get_widget('context_menu').popup(None, None, None,
429                                event.button, event.time)
430                        return True
431
432        def on_export_menuitem_activate(self, widget):
433                xml = gtkgui_helpers.get_glade('history_manager.glade', 'filechooserdialog')
434                xml.signal_autoconnect(self)
435               
436                dlg = xml.get_widget('filechooserdialog')
437                dlg.set_title(_('Exporting History Logs...'))
438                dlg.set_current_folder(gajim.HOME_DIR)
439                dlg.props.do_overwrite_confirmation = True
440                response = dlg.run()
441               
442                if response == gtk.RESPONSE_OK: # user want us to export ;)
443                        liststore, list_of_paths = self.jids_listview.get_selection()\
444                                .get_selected_rows()
445                        path_to_file = dlg.get_filename()
446                        self._export_jids_logs_to_file(liststore, list_of_paths, path_to_file)
447               
448                dlg.destroy()   
449       
450        def on_delete_menuitem_activate(self, widget, listview):
451                liststore, list_of_paths = listview.get_selection().get_selected_rows()
452                if listview.name == 'jids_listview':
453                        self._delete_jid_logs(liststore, list_of_paths)
454                elif listview.name in ('logs_listview', 'search_results_listview'):
455                        self._delete_logs(liststore, list_of_paths)
456                else: # Huh ? We don't know this widget
457                        return
458
459        def on_jids_listview_key_press_event(self, widget, event):
460                liststore, list_of_paths = self.jids_listview.get_selection()\
461                        .get_selected_rows()
462                if event.keyval == gtk.keysyms.Delete:
463                        self._delete_jid_logs(liststore, list_of_paths)
464
465        def _export_jids_logs_to_file(self, liststore, list_of_paths, path_to_file):
466                paths_len = len(list_of_paths)
467                if paths_len == 0: # nothing is selected
468                        return
469
470                list_of_rowrefs = []
471                for path in list_of_paths: # make them treerowrefs (it's needed)
472                        list_of_rowrefs.append(gtk.TreeRowReference(liststore, path))
473               
474                for rowref in list_of_rowrefs:
475                        path = rowref.get_path()
476                        if path is None:
477                                continue
478                        jid_id = liststore[path][1]
479                        self.cur.execute('''
480                                SELECT time, kind, message, contact_name FROM logs
481                                WHERE jid_id = ?
482                                ORDER BY time
483                                ''', (jid_id,))
484
485                # FIXME: we may have two contacts selected to export. fix that
486                # AT THIS TIME FIRST EXECUTE IS LOST! WTH!!!!!
487                results = self.cur.fetchall()
488                #print results[0]
489                file_ = open(path_to_file, 'w')
490                for row in results:
491                        # in store: time, kind, message, contact_name FROM logs
492                        # in text: JID or You or nickname (if it's gc_msg), time, message
493                        time_, kind, message, nickname = row
494                        if kind in (constants.KIND_SINGLE_MSG_RECV,
495                                constants.KIND_CHAT_MSG_RECV):
496                                who = self._get_jid_from_jid_id(jid_id)
497                        elif kind in (constants.KIND_SINGLE_MSG_SENT,
498                                constants.KIND_CHAT_MSG_SENT):
499                                who = _('You')
500                        elif kind == constants.KIND_GC_MSG:
501                                who = nickname
502                        else: # status or gc_status. do not save
503                                #print kind
504                                continue
505
506                        try:
507                                time_ = time.strftime('%x', time.localtime(float(time_))).decode(
508                                        locale.getpreferredencoding())
509                        except ValueError:
510                                pass
511
512                        file_.write(_('%(who)s on %(time)s said: %(message)s\n') % {'who': who,
513                                'time': time_, 'message': message})
514       
515        def _delete_jid_logs(self, liststore, list_of_paths):
516                paths_len = len(list_of_paths)
517                if paths_len == 0: # nothing is selected
518                        return
519
520                def on_ok(liststore, list_of_paths):
521                        # delete all rows from db that match jid_id
522                        list_of_rowrefs = []
523                        for path in list_of_paths: # make them treerowrefs (it's needed)
524                                list_of_rowrefs.append(gtk.TreeRowReference(liststore, path))
525
526                        for rowref in list_of_rowrefs:
527                                path = rowref.get_path()
528                                if path is None:
529                                        continue
530                                jid_id = liststore[path][1]
531                                del liststore[path] # remove from UI
532                                # remove from db
533                                self.cur.execute('''
534                                        DELETE FROM logs
535                                        WHERE jid_id = ?
536                                        ''', (jid_id,))
537
538                                # now delete "jid, jid_id" row from jids table
539                                self.cur.execute('''
540                                                DELETE FROM jids
541                                                WHERE jid_id = ?
542                                                ''', (jid_id,))
543
544                        self.con.commit()
545
546                        self.AT_LEAST_ONE_DELETION_DONE = True
547
548                pri_text = i18n.ngettext(
549                        'Do you really want to delete logs of the selected contact?',
550                        'Do you really want to delete logs of the selected contacts?',
551                        paths_len)
552                dialogs.ConfirmationDialog(pri_text,
553                        _('This is an irreversible operation.'), on_response_ok = (on_ok,
554                        liststore, list_of_paths))
555
556        def _delete_logs(self, liststore, list_of_paths):
557                paths_len = len(list_of_paths)
558                if paths_len == 0: # nothing is selected
559                        return
560
561                def on_ok(liststore, list_of_paths):
562                        # delete rows from db that match log_line_id
563                        list_of_rowrefs = []
564                        for path in list_of_paths: # make them treerowrefs (it's needed)
565                                list_of_rowrefs.append(gtk.TreeRowReference(liststore, path))
566
567                        for rowref in list_of_rowrefs:
568                                path = rowref.get_path()
569                                if path is None:
570                                        continue
571                                log_line_id = liststore[path][0]
572                                del liststore[path] # remove from UI
573                                # remove from db
574                                self.cur.execute('''
575                                        DELETE FROM logs
576                                        WHERE log_line_id = ?
577                                        ''', (log_line_id,))
578
579                        self.con.commit()
580
581                        self.AT_LEAST_ONE_DELETION_DONE = True
582
583                       
584                pri_text = i18n.ngettext(
585                        'Do you really want to delete the selected message?',
586                        'Do you really want to delete the selected messages?', paths_len)
587                dialogs.ConfirmationDialog(pri_text,
588                        _('This is an irreversible operation.'), on_response_ok = (on_ok,
589                        liststore, list_of_paths))
590
591        def on_search_db_button_clicked(self, widget):
592                text = self.search_entry.get_text()
593                if text == '':
594                        return
595
596                self.welcome_vbox.hide()
597                self.logs_scrolledwindow.hide()
598                self.search_results_scrolledwindow.show()
599               
600                self._fill_search_results_listview(text)
601
602        def on_search_results_listview_row_activated(self, widget, path, column):
603                # get log_line_id, jid_id from row we double clicked
604                log_line_id = self.search_results_liststore[path][0]
605                jid = self.search_results_liststore[path][1]
606                # make it string as in gtk liststores I have them all as strings
607                # as this is what db returns so I don't have to fight with types
608                jid_id = self._get_jid_id(jid)
609               
610               
611                iter_ = self.jids_liststore.get_iter_root()
612                while iter_:
613                        # self.jids_liststore[iter_][1] holds jid_ids
614                        if self.jids_liststore[iter_][1] == jid_id:
615                                break
616                        iter_ = self.jids_liststore.iter_next(iter_)
617               
618                if iter_ is None:
619                        return
620
621                path = self.jids_liststore.get_path(iter_)
622                self.jids_listview.set_cursor(path)
623               
624                iter_ = self.logs_liststore.get_iter_root()
625                while iter_:
626                        # self.logs_liststore[iter_][0] holds lon_line_ids
627                        if self.logs_liststore[iter_][0] == log_line_id:
628                                break
629                        iter_ = self.logs_liststore.iter_next(iter_)
630               
631                path = self.logs_liststore.