2006-10-07 15:22:01 +02:00
|
|
|
#!/usr/bin/env python
|
2006-02-03 01:01:53 +01:00
|
|
|
## history_manager.py
|
|
|
|
##
|
2007-01-17 00:26:38 +01:00
|
|
|
## Copyright (C) 2006-2007 Nikos Kouremenos <kourem@gmail.com>
|
2006-02-03 01:01:53 +01:00
|
|
|
##
|
2007-10-22 13:13:13 +02:00
|
|
|
## This file is part of Gajim.
|
|
|
|
##
|
|
|
|
## Gajim is free software; you can redistribute it and/or modify
|
2006-02-03 01:01:53 +01:00
|
|
|
## it under the terms of the GNU General Public License as published
|
2007-10-22 13:13:13 +02:00
|
|
|
## by the Free Software Foundation; version 3 only.
|
2006-02-03 01:01:53 +01:00
|
|
|
##
|
2007-10-22 13:13:13 +02:00
|
|
|
## Gajim is distributed in the hope that it will be useful,
|
2006-02-03 01:01:53 +01:00
|
|
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
## GNU General Public License for more details.
|
|
|
|
##
|
2007-10-22 13:13:13 +02:00
|
|
|
## You should have received a copy of the GNU General Public License
|
|
|
|
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
##
|
2006-02-03 01:01:53 +01:00
|
|
|
|
|
|
|
## NOTE: some method names may match those of logger.py but that's it
|
|
|
|
## someday (TM) should have common class that abstracts db connections and helpers on it
|
|
|
|
## the same can be said for history_window.py
|
|
|
|
|
|
|
|
import sys
|
|
|
|
import os
|
|
|
|
import signal
|
|
|
|
import gtk
|
2007-01-17 00:26:38 +01:00
|
|
|
import gobject
|
2006-02-03 01:01:53 +01:00
|
|
|
import time
|
2006-03-05 23:09:39 +01:00
|
|
|
import locale
|
2006-02-03 01:01:53 +01:00
|
|
|
|
2006-06-22 15:34:12 +02:00
|
|
|
from common import i18n
|
2007-08-20 00:51:40 +02:00
|
|
|
import common.configpaths
|
|
|
|
common.configpaths.gajimpaths.init()
|
|
|
|
common.configpaths.gajimpaths.init_profile()
|
2007-11-29 23:10:19 +01:00
|
|
|
from common import exceptions
|
2006-02-03 01:01:53 +01:00
|
|
|
import dialogs
|
2006-04-04 19:28:46 +02:00
|
|
|
import gtkgui_helpers
|
2006-02-03 01:01:53 +01:00
|
|
|
from common.logger import LOG_DB_PATH, constants
|
|
|
|
|
2006-10-08 12:04:32 +02:00
|
|
|
#FIXME: constants should implement 2 way mappings
|
|
|
|
status = dict((constants.__dict__[i], i[5:].lower()) for i in \
|
|
|
|
constants.__dict__.keys() if i.startswith('SHOW_'))
|
2006-02-10 17:12:16 +01:00
|
|
|
from common import gajim
|
2006-04-10 20:53:02 +02:00
|
|
|
from common import helpers
|
2006-02-03 01:01:53 +01:00
|
|
|
|
2006-02-06 21:06:10 +01:00
|
|
|
# time, message, subject
|
2006-02-03 01:01:53 +01:00
|
|
|
(
|
|
|
|
C_UNIXTIME,
|
|
|
|
C_MESSAGE,
|
2006-04-09 12:32:34 +02:00
|
|
|
C_SUBJECT,
|
|
|
|
C_NICKNAME
|
|
|
|
) = range(2, 6)
|
2006-02-03 01:01:53 +01:00
|
|
|
|
2006-10-12 00:31:51 +02:00
|
|
|
|
2006-02-03 01:01:53 +01:00
|
|
|
try:
|
2006-10-12 00:31:51 +02:00
|
|
|
import sqlite3 as sqlite # python 2.5
|
2006-02-03 01:01:53 +01:00
|
|
|
except ImportError:
|
2006-10-12 00:31:51 +02:00
|
|
|
try:
|
|
|
|
from pysqlite2 import dbapi2 as sqlite
|
|
|
|
except ImportError:
|
|
|
|
raise exceptions.PysqliteNotAvailable
|
2006-02-03 01:01:53 +01:00
|
|
|
|
|
|
|
|
|
|
|
class HistoryManager:
|
|
|
|
def __init__(self):
|
2006-05-21 22:42:26 +02:00
|
|
|
path_to_file = os.path.join(gajim.DATA_DIR, 'pixmaps/gajim.png')
|
|
|
|
pix = gtk.gdk.pixbuf_new_from_file(path_to_file)
|
|
|
|
gtk.window_set_default_icon(pix) # set the icon to all newly opened windows
|
|
|
|
|
2006-02-03 01:01:53 +01:00
|
|
|
if not os.path.exists(LOG_DB_PATH):
|
|
|
|
dialogs.ErrorDialog(_('Cannot find history logs database'),
|
2006-04-02 18:11:21 +02:00
|
|
|
'%s does not exist.' % LOG_DB_PATH)
|
2006-02-03 01:01:53 +01:00
|
|
|
sys.exit()
|
|
|
|
|
2006-05-02 17:53:25 +02:00
|
|
|
xml = gtkgui_helpers.get_glade('history_manager.glade')
|
2006-02-06 21:06:10 +01:00
|
|
|
self.window = xml.get_widget('history_manager_window')
|
|
|
|
self.jids_listview = xml.get_widget('jids_listview')
|
|
|
|
self.logs_listview = xml.get_widget('logs_listview')
|
2006-02-06 21:34:06 +01:00
|
|
|
self.search_results_listview = xml.get_widget('search_results_listview')
|
2006-02-06 21:06:10 +01:00
|
|
|
self.search_entry = xml.get_widget('search_entry')
|
|
|
|
self.logs_scrolledwindow = xml.get_widget('logs_scrolledwindow')
|
|
|
|
self.search_results_scrolledwindow = xml.get_widget(
|
|
|
|
'search_results_scrolledwindow')
|
|
|
|
self.welcome_label = xml.get_widget('welcome_label')
|
2007-08-09 10:57:59 +02:00
|
|
|
|
2006-02-03 01:01:53 +01:00
|
|
|
self.jids_already_in = [] # holds jids that we already have in DB
|
2006-02-06 22:35:54 +01:00
|
|
|
self.AT_LEAST_ONE_DELETION_DONE = False
|
2006-02-03 01:01:53 +01:00
|
|
|
|
|
|
|
self.con = sqlite.connect(LOG_DB_PATH, timeout = 20.0,
|
|
|
|
isolation_level = 'IMMEDIATE')
|
|
|
|
self.cur = self.con.cursor()
|
|
|
|
|
|
|
|
self._init_jids_listview()
|
|
|
|
self._init_logs_listview()
|
2006-02-06 21:34:06 +01:00
|
|
|
self._init_search_results_listview()
|
2006-02-03 01:01:53 +01:00
|
|
|
|
|
|
|
self._fill_jids_listview()
|
|
|
|
|
2006-02-06 21:06:10 +01:00
|
|
|
self.search_entry.grab_focus()
|
2006-02-03 01:01:53 +01:00
|
|
|
|
|
|
|
self.window.show_all()
|
|
|
|
|
2006-02-06 21:06:10 +01:00
|
|
|
xml.signal_autoconnect(self)
|
2006-02-03 01:01:53 +01:00
|
|
|
|
|
|
|
def _init_jids_listview(self):
|
2006-02-06 21:06:10 +01:00
|
|
|
self.jids_liststore = gtk.ListStore(str, str) # jid, jid_id
|
2006-02-03 01:01:53 +01:00
|
|
|
self.jids_listview.set_model(self.jids_liststore)
|
2006-02-06 21:06:10 +01:00
|
|
|
self.jids_listview.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
|
2006-02-04 21:35:57 +01:00
|
|
|
|
2006-02-03 01:01:53 +01:00
|
|
|
renderer_text = gtk.CellRendererText() # holds jid
|
2006-04-09 21:08:57 +02:00
|
|
|
col = gtk.TreeViewColumn(_('Contacts'), renderer_text, text = 0)
|
2006-02-03 01:01:53 +01:00
|
|
|
self.jids_listview.append_column(col)
|
2006-02-06 21:06:10 +01:00
|
|
|
|
|
|
|
self.jids_listview.get_selection().connect('changed',
|
|
|
|
self.on_jids_listview_selection_changed)
|
2006-02-04 21:35:57 +01:00
|
|
|
|
2006-02-03 01:01:53 +01:00
|
|
|
def _init_logs_listview(self):
|
2006-04-09 12:32:34 +02:00
|
|
|
# log_line_id (HIDDEN), jid_id (HIDDEN), time, message, subject, nickname
|
|
|
|
self.logs_liststore = gtk.ListStore(str, str, str, str, str, str)
|
2006-02-03 01:01:53 +01:00
|
|
|
self.logs_listview.set_model(self.logs_liststore)
|
2006-02-04 21:35:57 +01:00
|
|
|
self.logs_listview.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
|
|
|
|
|
2006-02-03 01:01:53 +01:00
|
|
|
renderer_text = gtk.CellRendererText() # holds time
|
2006-04-09 12:40:51 +02:00
|
|
|
col = gtk.TreeViewColumn(_('Date'), renderer_text, text = C_UNIXTIME)
|
2006-02-03 01:01:53 +01:00
|
|
|
col.set_sort_column_id(C_UNIXTIME) # user can click this header and sort
|
|
|
|
col.set_resizable(True)
|
|
|
|
self.logs_listview.append_column(col)
|
2006-04-09 21:08:57 +02:00
|
|
|
|
|
|
|
renderer_text = gtk.CellRendererText() # holds nickname
|
|
|
|
col = gtk.TreeViewColumn(_('Nickname'), renderer_text, text = C_NICKNAME)
|
|
|
|
col.set_sort_column_id(C_NICKNAME) # user can click this header and sort
|
|
|
|
col.set_resizable(True)
|
|
|
|
col.set_visible(False)
|
|
|
|
self.nickname_col_for_logs = col
|
|
|
|
self.logs_listview.append_column(col)
|
2006-02-04 21:35:57 +01:00
|
|
|
|
2006-02-03 01:01:53 +01:00
|
|
|
renderer_text = gtk.CellRendererText() # holds message
|
2006-04-09 12:32:34 +02:00
|
|
|
col = gtk.TreeViewColumn(_('Message'), renderer_text, markup = C_MESSAGE)
|
2006-02-03 01:01:53 +01:00
|
|
|
col.set_sort_column_id(C_MESSAGE) # user can click this header and sort
|
|
|
|
col.set_resizable(True)
|
2006-04-09 21:08:57 +02:00
|
|
|
self.message_col_for_logs = col
|
2006-02-03 01:01:53 +01:00
|
|
|
self.logs_listview.append_column(col)
|
2006-02-04 21:35:57 +01:00
|
|
|
|
2006-02-03 01:01:53 +01:00
|
|
|
renderer_text = gtk.CellRendererText() # holds subject
|
2006-04-09 12:32:34 +02:00
|
|
|
col = gtk.TreeViewColumn(_('Subject'), renderer_text, text = C_SUBJECT)
|
2006-02-03 01:01:53 +01:00
|
|
|
col.set_sort_column_id(C_SUBJECT) # user can click this header and sort
|
|
|
|
col.set_resizable(True)
|
2006-04-09 21:08:57 +02:00
|
|
|
col.set_visible(False)
|
|
|
|
self.subject_col_for_logs = col
|
2006-04-09 12:32:34 +02:00
|
|
|
self.logs_listview.append_column(col)
|
2006-02-06 21:34:06 +01:00
|
|
|
|
|
|
|
def _init_search_results_listview(self):
|
2006-04-09 12:32:34 +02:00
|
|
|
# log_line_id (HIDDEN), jid, time, message, subject, nickname
|
|
|
|
self.search_results_liststore = gtk.ListStore(str, str, str, str, str, str)
|
2006-02-06 21:34:06 +01:00
|
|
|
self.search_results_listview.set_model(self.search_results_liststore)
|
|
|
|
|
|
|
|
renderer_text = gtk.CellRendererText() # holds JID (who said this)
|
2006-04-09 12:32:34 +02:00
|
|
|
col = gtk.TreeViewColumn(_('JID'), renderer_text, text = 1)
|
2006-02-06 21:34:06 +01:00
|
|
|
col.set_sort_column_id(1) # user can click this header and sort
|
|
|
|
col.set_resizable(True)
|
|
|
|
self.search_results_listview.append_column(col)
|
|
|
|
|
|
|
|
renderer_text = gtk.CellRendererText() # holds time
|
2006-04-09 12:40:51 +02:00
|
|
|
col = gtk.TreeViewColumn(_('Date'), renderer_text, text = C_UNIXTIME)
|
2006-02-06 21:34:06 +01:00
|
|
|
col.set_sort_column_id(C_UNIXTIME) # user can click this header and sort
|
|
|
|
col.set_resizable(True)
|
|
|
|
self.search_results_listview.append_column(col)
|
|
|
|
|
|
|
|
renderer_text = gtk.CellRendererText() # holds message
|
2006-04-09 12:32:34 +02:00
|
|
|
col = gtk.TreeViewColumn(_('Message'), renderer_text, text = C_MESSAGE)
|
2006-02-06 21:34:06 +01:00
|
|
|
col.set_sort_column_id(C_MESSAGE) # user can click this header and sort
|
|
|
|
col.set_resizable(True)
|
|
|
|
self.search_results_listview.append_column(col)
|
|
|
|
|
|
|
|
renderer_text = gtk.CellRendererText() # holds subject
|
2006-04-09 12:32:34 +02:00
|
|
|
col = gtk.TreeViewColumn(_('Subject'), renderer_text, text = C_SUBJECT)
|
2006-02-06 21:34:06 +01:00
|
|
|
col.set_sort_column_id(C_SUBJECT) # user can click this header and sort
|
|
|
|
col.set_resizable(True)
|
|
|
|
self.search_results_listview.append_column(col)
|
2006-04-09 12:32:34 +02:00
|
|
|
|
|
|
|
renderer_text = gtk.CellRendererText() # holds nickname
|
|
|
|
col = gtk.TreeViewColumn(_('Nickname'), renderer_text, text = C_NICKNAME)
|
|
|
|
col.set_sort_column_id(C_NICKNAME) # user can click this header and sort
|
|
|
|
col.set_resizable(True)
|
|
|
|
self.search_results_listview.append_column(col)
|
2006-02-03 01:01:53 +01:00
|
|
|
|
|
|
|
def on_history_manager_window_delete_event(self, widget, event):
|
2006-02-06 22:35:54 +01:00
|
|
|
if self.AT_LEAST_ONE_DELETION_DONE:
|
|
|
|
dialog = dialogs.YesNoDialog(
|
2006-03-21 15:37:06 +01:00
|
|
|
_('Do you want to clean up the database? '
|
|
|
|
'(STRONGLY NOT RECOMMENDED IF GAJIM IS RUNNING)'),
|
2006-02-06 22:35:54 +01:00
|
|
|
_('Normally allocated database size will not be freed, '
|
|
|
|
'it will just become reusable. If you really want to reduce '
|
|
|
|
'database filesize, click YES, else click NO.'
|
|
|
|
'\n\nIn case you click YES, please wait...'))
|
|
|
|
if dialog.get_response() == gtk.RESPONSE_YES:
|
|
|
|
self.cur.execute('VACUUM')
|
|
|
|
self.con.commit()
|
|
|
|
|
2006-02-03 01:01:53 +01:00
|
|
|
gtk.main_quit()
|
|
|
|
|
|
|
|
def _fill_jids_listview(self):
|
2006-09-19 11:18:06 +02:00
|
|
|
# get those jids that have at least one entry in logs
|
|
|
|
self.cur.execute('SELECT jid, jid_id FROM jids WHERE jid_id IN (SELECT '
|
|
|
|
'distinct logs.jid_id FROM logs) ORDER BY jid')
|
2006-02-03 01:01:53 +01:00
|
|
|
rows = self.cur.fetchall() # list of tupples: [(u'aaa@bbb',), (u'cc@dd',)]
|
|
|
|
for row in rows:
|
2006-02-06 21:06:10 +01:00
|
|
|
self.jids_already_in.append(row[0]) # jid
|
|
|
|
self.jids_liststore.append(row) # jid, jid_id
|
2006-02-03 01:01:53 +01:00
|
|
|
|
2006-02-06 21:06:10 +01:00
|
|
|
def on_jids_listview_selection_changed(self, widget, data = None):
|
|
|
|
liststore, list_of_paths = self.jids_listview.get_selection()\
|
|
|
|
.get_selected_rows()
|
|
|
|
paths_len = len(list_of_paths)
|
|
|
|
if paths_len == 0: # nothing is selected
|
|
|
|
return
|
2006-04-09 13:02:43 +02:00
|
|
|
|
|
|
|
self.logs_liststore.clear() # clear the store
|
2006-02-06 21:06:10 +01:00
|
|
|
|
|
|
|
self.welcome_label.hide()
|
2006-02-06 21:34:06 +01:00
|
|
|
self.search_results_scrolledwindow.hide()
|
2006-02-06 21:06:10 +01:00
|
|
|
self.logs_scrolledwindow.show()
|
|
|
|
|
|
|
|
list_of_rowrefs = []
|
|
|
|
for path in list_of_paths: # make them treerowrefs (it's needed)
|
2007-02-04 14:01:04 +01:00
|
|
|
list_of_rowrefs.append(gtk.TreeRowReference(liststore, path))
|
2006-02-06 21:06:10 +01:00
|
|
|
|
2006-04-09 13:02:43 +02:00
|
|
|
for rowref in list_of_rowrefs: # FILL THE STORE, for all rows selected
|
2006-02-06 21:06:10 +01:00
|
|
|
path = rowref.get_path()
|
|
|
|
if path is None:
|
|
|
|
continue
|
|
|
|
jid = liststore[path][0] # jid
|
2006-04-09 13:02:43 +02:00
|
|
|
self._fill_logs_listview(jid)
|
2006-02-03 01:01:53 +01:00
|
|
|
|
|
|
|
def _get_jid_id(self, jid):
|
|
|
|
'''jids table has jid and jid_id
|
|
|
|
logs table has log_id, jid_id, contact_name, time, kind, show, message
|
|
|
|
so to ask logs we need jid_id that matches our jid in jids table
|
2006-02-06 21:34:06 +01:00
|
|
|
this method wants jid and returns the jid_id for later sql-ing on logs
|
2006-02-03 01:01:53 +01:00
|
|
|
'''
|
|
|
|
if jid.find('/') != -1: # if it has a /
|
|
|
|
jid_is_from_pm = self._jid_is_from_pm(jid)
|
|
|
|
if not jid_is_from_pm: # it's normal jid with resource
|
|
|
|
jid = jid.split('/', 1)[0] # remove the resource
|
2006-02-06 21:34:06 +01:00
|
|
|
self.cur.execute('SELECT jid_id FROM jids WHERE jid = ?', (jid,))
|
2006-02-03 01:01:53 +01:00
|
|
|
jid_id = self.cur.fetchone()[0]
|
2006-04-09 21:08:57 +02:00
|
|
|
return str(jid_id)
|
2006-02-03 01:01:53 +01:00
|
|
|
|
2006-02-06 21:34:06 +01:00
|
|
|
def _get_jid_from_jid_id(self, jid_id):
|
|
|
|
'''jids table has jid and jid_id
|
|
|
|
this method accepts jid_id and returns the jid for later sql-ing on logs
|
|
|
|
'''
|
|
|
|
self.cur.execute('SELECT jid FROM jids WHERE jid_id = ?', (jid_id,))
|
|
|
|
jid = self.cur.fetchone()[0]
|
|
|
|
return jid
|
|
|
|
|
2006-02-03 01:01:53 +01:00
|
|
|
def _jid_is_from_pm(self, jid):
|
|
|
|
'''if jid is gajim@conf/nkour it's likely a pm one, how we know
|
|
|
|
gajim@conf is not a normal guy and nkour is not his resource?
|
|
|
|
we ask if gajim@conf is already in jids (with type room jid)
|
|
|
|
this fails if user disables logging for room and only enables for
|
|
|
|
pm (so higly unlikely) and if we fail we do not go chaos
|
|
|
|
(user will see the first pm as if it was message in room's public chat)
|
|
|
|
and after that all okay'''
|
|
|
|
|
|
|
|
possible_room_jid, possible_nick = jid.split('/', 1)
|
|
|
|
|
2006-02-06 21:34:06 +01:00
|
|
|
self.cur.execute('SELECT jid_id FROM jids WHERE jid = ? AND type = ?',
|
2006-02-03 01:01:53 +01:00
|
|
|
(possible_room_jid, constants.JID_ROOM_TYPE))
|
|
|
|
row = self.cur.fetchone()
|
2006-04-09 21:08:57 +02:00
|
|
|
if row is None:
|
|
|
|
return False
|
2006-02-03 01:01:53 +01:00
|
|
|
else:
|
2006-04-09 21:08:57 +02:00
|
|
|
return True
|
|
|
|
|
|
|
|
def _jid_is_room_type(self, jid):
|
|
|
|
'''returns True/False if given id is room type or not
|
|
|
|
eg. if it is room'''
|
|
|
|
self.cur.execute('SELECT type FROM jids WHERE jid = ?', (jid,))
|
|
|
|
row = self.cur.fetchone()
|
|
|
|
if row is None:
|
|
|
|
raise
|
|
|
|
elif row[0] == constants.JID_ROOM_TYPE:
|
|
|
|
return True
|
|
|
|
else: # normal type
|
2006-02-03 01:01:53 +01:00
|
|
|
return False
|
|
|
|
|
2006-04-09 13:02:43 +02:00
|
|
|
def _fill_logs_listview(self, jid):
|
2006-02-03 01:01:53 +01:00
|
|
|
'''fill the listview with all messages that user sent to or
|
|
|
|
received from JID'''
|
|
|
|
# no need to lower jid in this context as jid is already lowered
|
|
|
|
# as we use those jids from db
|
|
|
|
jid_id = self._get_jid_id(jid)
|
|
|
|
self.cur.execute('''
|
2006-04-10 20:53:02 +02:00
|
|
|
SELECT log_line_id, jid_id, time, kind, message, subject, contact_name, show
|
2006-04-09 12:32:34 +02:00
|
|
|
FROM logs
|
2006-02-06 21:34:06 +01:00
|
|
|
WHERE jid_id = ?
|
2006-02-03 01:01:53 +01:00
|
|
|
ORDER BY time
|
2006-02-06 21:34:06 +01:00
|
|
|
''', (jid_id,))
|
2006-04-09 21:08:57 +02:00
|
|
|
|
2006-02-03 01:01:53 +01:00
|
|
|
results = self.cur.fetchall()
|
2006-04-09 21:08:57 +02:00
|
|
|
|
|
|
|
if self._jid_is_room_type(jid): # is it room?
|
|
|
|
self.nickname_col_for_logs.set_visible(True)
|
|
|
|
self.subject_col_for_logs.set_visible(False)
|
|
|
|
else:
|
|
|
|
self.nickname_col_for_logs.set_visible(False)
|
|
|
|
self.subject_col_for_logs.set_visible(True)
|
|
|
|
|
2006-02-03 01:01:53 +01:00
|
|
|
for row in results:
|
2006-04-09 12:32:34 +02:00
|
|
|
# exposed in UI (TreeViewColumns) are only
|
|
|
|
# time, message, subject, nickname
|
|
|
|
# but store in liststore
|
|
|
|
# log_line_id, jid_id, time, message, subject, nickname
|
2006-04-10 20:53:02 +02:00
|
|
|
log_line_id, jid_id, time_, kind, message, subject, nickname, show = row
|
2006-02-03 22:07:43 +01:00
|
|
|
try:
|
2006-03-05 23:09:39 +01:00
|
|
|
time_ = time.strftime('%x', time.localtime(float(time_))).decode(
|
|
|
|
locale.getpreferredencoding())
|
2006-02-03 22:07:43 +01:00
|
|
|
except ValueError:
|
|
|
|
pass
|
|
|
|
else:
|
2006-10-08 12:04:32 +02:00
|
|
|
color = None
|
2006-04-04 19:10:58 +02:00
|
|
|
if kind in (constants.KIND_SINGLE_MSG_RECV,
|
2006-04-09 12:32:34 +02:00
|
|
|
constants.KIND_CHAT_MSG_RECV, constants.KIND_GC_MSG):
|
|
|
|
# it is the other side
|
2006-04-04 19:10:58 +02:00
|
|
|
color = gajim.config.get('inmsgcolor') # so incoming color
|
|
|
|
elif kind in (constants.KIND_SINGLE_MSG_SENT,
|
|
|
|
constants.KIND_CHAT_MSG_SENT): # it is us
|
|
|
|
color = gajim.config.get('outmsgcolor') # so outgoing color
|
2006-04-04 19:26:22 +02:00
|
|
|
elif kind in (constants.KIND_STATUS,
|
|
|
|
constants.KIND_GCSTATUS): # is is statuses
|
|
|
|
color = gajim.config.get('statusmsgcolor') # so status color
|
2006-04-10 20:53:02 +02:00
|
|
|
# include status into (status) message
|
|
|
|
if message is None:
|
|
|
|
message = ''
|
|
|
|
else:
|
|
|
|
message = ' : ' + message
|
2006-10-08 12:04:32 +02:00
|
|
|
message = helpers.get_uf_show(gajim.SHOW_LIST[show]) + message
|
|
|
|
|
2006-10-08 12:08:16 +02:00
|
|
|
message_ = '<span'
|
2006-10-08 12:04:32 +02:00
|
|
|
if color:
|
2006-10-08 12:08:16 +02:00
|
|
|
message_ += ' foreground="%s"' % color
|
|
|
|
message_ += '>%s</span>' % \
|
2007-01-17 00:26:38 +01:00
|
|
|
gobject.markup_escape_text(message)
|
2006-10-08 12:08:16 +02:00
|
|
|
self.logs_liststore.append((log_line_id, jid_id, time_, message_,
|
2006-04-09 12:32:34 +02:00
|
|
|
subject, nickname))
|
2006-02-03 01:01:53 +01:00
|
|
|
|
2006-02-06 21:06:10 +01:00
|
|
|
def _fill_search_results_listview(self, text):
|
|
|
|
'''ask db and fill listview with results that match text'''
|
2006-02-08 23:46:20 +01:00
|
|
|
self.search_results_liststore.clear()
|
2006-02-06 21:34:06 +01:00
|
|
|
like_sql = '%' + text + '%'
|
2006-02-06 21:06:10 +01:00
|
|
|
self.cur.execute('''
|
2006-04-09 12:32:34 +02:00
|
|
|
SELECT log_line_id, jid_id, time, message, subject, contact_name
|
|
|
|
FROM logs
|
2006-02-06 21:34:06 +01:00
|
|
|
WHERE message LIKE ? OR subject LIKE ?
|
2006-02-06 21:06:10 +01:00
|
|
|
ORDER BY time
|
2006-02-06 21:34:06 +01:00
|
|
|
''', (like_sql, like_sql))
|
2006-02-06 21:06:10 +01:00
|
|
|
|
|
|
|
results = self.cur.fetchall()
|
|
|
|
for row in results:
|
2006-04-09 12:32:34 +02:00
|
|
|
# exposed in UI (TreeViewColumns) are only
|
|
|
|
# JID, time, message, subject, nickname
|
|
|
|
# but store in liststore
|
|
|
|
# log_line_id, jid (from jid_id), time, message, subject, nickname
|
|
|
|
log_line_id, jid_id, time_, message, subject, nickname = row
|
2006-02-06 21:34:06 +01:00
|
|
|
try:
|
2006-03-05 23:09:39 +01:00
|
|
|
time_ = time.strftime('%x', time.localtime(float(time_))).decode(
|
|
|
|
locale.getpreferredencoding())
|
2006-02-06 21:34:06 +01:00
|
|
|
except ValueError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
jid = self._get_jid_from_jid_id(jid_id)
|
|
|
|
|
2006-04-09 12:32:34 +02:00
|
|
|
self.search_results_liststore.append((log_line_id, jid, time_,
|
|
|
|
message, subject, nickname))
|
2006-02-06 21:06:10 +01:00
|
|
|
|
2006-02-04 21:35:57 +01:00
|
|
|
def on_logs_listview_key_press_event(self, widget, event):
|
|
|
|
liststore, list_of_paths = self.logs_listview.get_selection()\
|
|
|
|
.get_selected_rows()
|
|
|
|
if event.keyval == gtk.keysyms.Delete:
|
2006-03-20 09:20:49 +01:00
|
|
|
self._delete_logs(liststore, list_of_paths)
|
2006-02-04 22:34:16 +01:00
|
|
|
|
2006-03-20 09:20:49 +01:00
|
|
|
def on_listview_button_press_event(self, widget, event):
|
2006-02-10 17:01:47 +01:00
|
|
|
if event.button == 3: # right click
|
2006-05-02 18:10:13 +02:00
|
|
|
xml = gtkgui_helpers.get_glade('history_manager.glade', 'context_menu')
|
2006-03-20 12:28:02 +01:00
|
|
|
if widget.name != 'jids_listview':
|
2006-03-20 09:20:49 +01:00
|
|
|
xml.get_widget('export_menuitem').hide()
|
2006-03-20 12:28:02 +01:00
|
|
|
xml.get_widget('delete_menuitem').connect('activate',
|
2006-03-20 09:20:49 +01:00
|
|
|
self.on_delete_menuitem_activate, widget)
|
2006-02-10 17:01:47 +01:00
|
|
|
|
|
|
|
liststore, list_of_paths = self.jids_listview.get_selection()\
|
|
|
|
.get_selected_rows()
|
2006-02-06 22:35:54 +01:00
|
|
|
|
2006-02-10 17:01:47 +01:00
|
|
|
xml.signal_autoconnect(self)
|
|
|
|
xml.get_widget('context_menu').popup(None, None, None,
|
|
|
|
event.button, event.time)
|
2006-03-20 09:20:49 +01:00
|
|
|
return True
|
2006-02-10 17:01:47 +01:00
|
|
|
|
|
|
|
def on_export_menuitem_activate(self, widget):
|
2006-05-02 18:10:13 +02:00
|
|
|
xml = gtkgui_helpers.get_glade('history_manager.glade', 'filechooserdialog')
|
2006-02-10 17:01:47 +01:00
|
|
|
xml.signal_autoconnect(self)
|
2006-02-10 17:12:16 +01:00
|
|
|
|
2006-02-10 17:01:47 +01:00
|
|
|
dlg = xml.get_widget('filechooserdialog')
|
2006-02-10 17:12:16 +01:00
|
|
|
dlg.set_title(_('Exporting History Logs...'))
|
|
|
|
dlg.set_current_folder(gajim.HOME_DIR)
|
2007-01-17 00:26:38 +01:00
|
|
|
dlg.props.do_overwrite_confirmation = True
|
2006-02-10 17:01:47 +01:00
|
|
|
response = dlg.run()
|
|
|
|
|
|
|
|
if response == gtk.RESPONSE_OK: # user want us to export ;)
|
|
|
|
liststore, list_of_paths = self.jids_listview.get_selection()\
|
|
|
|
.get_selected_rows()
|
|
|
|
path_to_file = dlg.get_filename()
|
|
|
|
self._export_jids_logs_to_file(liststore, list_of_paths, path_to_file)
|
|
|
|
|
|
|
|
dlg.destroy()
|
|
|
|
|
2006-03-20 09:20:49 +01:00
|
|
|
def on_delete_menuitem_activate(self, widget, listview):
|
|
|
|
liststore, list_of_paths = listview.get_selection().get_selected_rows()
|
2006-03-20 12:28:02 +01:00
|
|
|
if listview.name == 'jids_listview':
|
2006-03-20 09:20:49 +01:00
|
|
|
self._delete_jid_logs(liststore, list_of_paths)
|
2006-03-20 12:28:02 +01:00
|
|
|
elif listview.name in ('logs_listview', 'search_results_listview'):
|
2006-03-20 09:20:49 +01:00
|
|
|
self._delete_logs(liststore, list_of_paths)
|
|
|
|
else: # Huh ? We don't know this widget
|
|
|
|
return
|
2006-02-10 17:01:47 +01:00
|
|
|
|
2006-02-06 21:06:10 +01:00
|
|
|
def on_jids_listview_key_press_event(self, widget, event):
|
|
|
|
liststore, list_of_paths = self.jids_listview.get_selection()\
|
|
|
|
.get_selected_rows()
|
2006-02-10 17:01:47 +01:00
|
|
|
if event.keyval == gtk.keysyms.Delete:
|
|
|
|
self._delete_jid_logs(liststore, list_of_paths)
|
|
|
|
|
|
|
|
def _export_jids_logs_to_file(self, liststore, list_of_paths, path_to_file):
|
2006-02-06 21:06:10 +01:00
|
|
|
paths_len = len(list_of_paths)
|
|
|
|
if paths_len == 0: # nothing is selected
|
|
|
|
return
|
2006-04-09 13:02:43 +02:00
|
|
|
|
2006-02-10 17:01:47 +01:00
|
|
|
list_of_rowrefs = []
|
|
|
|
for path in list_of_paths: # make them treerowrefs (it's needed)
|
2007-02-04 14:01:04 +01:00
|
|
|
list_of_rowrefs.append(gtk.TreeRowReference(liststore, path))
|
2006-02-10 17:01:47 +01:00
|
|
|
|
|
|
|
for rowref in list_of_rowrefs:
|
|
|
|
path = rowref.get_path()
|
|
|
|
if path is None:
|
|
|
|
continue
|
|
|
|
jid_id = liststore[path][1]
|
|
|
|
self.cur.execute('''
|
2006-04-09 12:40:51 +02:00
|
|
|
SELECT time, kind, message, contact_name FROM logs
|
2006-02-10 17:01:47 +01:00
|
|
|
WHERE jid_id = ?
|
|
|
|
ORDER BY time
|
|
|
|
''', (jid_id,))
|
2006-04-09 13:02:43 +02:00
|
|
|
|
|
|
|
# FIXME: we may have two contacts selected to export. fix that
|
|
|
|
# AT THIS TIME FIRST EXECUTE IS LOST! WTH!!!!!
|
2006-02-10 17:01:47 +01:00
|
|
|
results = self.cur.fetchall()
|
2006-04-09 13:02:43 +02:00
|
|
|
#print results[0]
|
2006-02-10 17:01:47 +01:00
|
|
|
file_ = open(path_to_file, 'w')
|
|
|
|
for row in results:
|
2006-04-09 12:40:51 +02:00
|
|
|
# in store: time, kind, message, contact_name FROM logs
|
|
|
|
# in text: JID or You or nickname (if it's gc_msg), time, message
|
|
|
|
time_, kind, message, nickname = row
|
2006-02-10 17:01:47 +01:00
|
|
|
if kind in (constants.KIND_SINGLE_MSG_RECV,
|
|
|
|
constants.KIND_CHAT_MSG_RECV):
|
|
|
|
who = self._get_jid_from_jid_id(jid_id)
|
|
|
|
elif kind in (constants.KIND_SINGLE_MSG_SENT,
|
|
|
|
constants.KIND_CHAT_MSG_SENT):
|
|
|
|
who = _('You')
|
2006-04-09 12:40:51 +02:00
|
|
|
elif kind == constants.KIND_GC_MSG:
|
|
|
|
who = nickname
|
|
|
|
else: # status or gc_status. do not save
|
2006-04-09 13:02:43 +02:00
|
|
|
#print kind
|
2006-02-10 17:01:47 +01:00
|
|
|
continue
|
2006-02-06 21:06:10 +01:00
|
|
|
|
2006-02-10 17:01:47 +01:00
|
|
|
try:
|
2006-03-05 23:09:39 +01:00
|
|
|
time_ = time.strftime('%x', time.localtime(float(time_))).decode(
|
|
|
|
locale.getpreferredencoding())
|
2006-02-10 17:01:47 +01:00
|
|
|
except ValueError:
|
|
|
|
pass
|
|
|
|
|
2006-09-09 00:27:04 +02:00
|
|
|
file_.write(_('%(who)s on %(time)s said: %(message)s\n') % {'who': who,
|
|
|
|
'time': time_, 'message': message})
|
2006-02-10 17:01:47 +01:00
|
|
|
|
|
|
|
def _delete_jid_logs(self, liststore, list_of_paths):
|
|
|
|
paths_len = len(list_of_paths)
|
|
|
|
if paths_len == 0: # nothing is selected
|
|
|
|
return
|
2006-04-02 18:11:21 +02:00
|
|
|
|
2007-10-07 19:22:47 +02:00
|
|
|
def on_ok(liststore, list_of_paths):
|
2006-04-02 18:11:21 +02:00
|
|
|
# delete all rows from db that match jid_id
|
|
|
|
list_of_rowrefs = []
|
|
|
|
for path in list_of_paths: # make them treerowrefs (it's needed)
|
2007-02-04 14:01:04 +01:00
|
|
|
list_of_rowrefs.append(gtk.TreeRowReference(liststore, path))
|
2006-04-02 18:11:21 +02:00
|
|
|
|
|
|
|
for rowref in list_of_rowrefs:
|
|
|
|
path = rowref.get_path()
|
|
|
|
if path is None:
|
|
|
|
continue
|
|
|
|
jid_id = liststore[path][1]
|
|
|
|
del liststore[path] # remove from UI
|
|
|
|
# remove from db
|
|
|
|
self.cur.execute('''
|
|
|
|
DELETE FROM logs
|
|
|
|
WHERE jid_id = ?
|
|
|
|
''', (jid_id,))
|
|
|
|
|
|
|
|
# now delete "jid, jid_id" row from jids table
|
|
|
|
self.cur.execute('''
|
|
|
|
DELETE FROM jids
|
|
|
|
WHERE jid_id = ?
|
|
|
|
''', (jid_id,))
|
|
|
|
|
|
|
|
self.con.commit()
|
|
|
|
|
|
|
|
self.AT_LEAST_ONE_DELETION_DONE = True
|
|
|
|
|
2006-02-10 17:01:47 +01:00
|
|
|
pri_text = i18n.ngettext(
|
|
|
|
'Do you really want to delete logs of the selected contact?',
|
|
|
|
'Do you really want to delete logs of the selected contacts?',
|
|
|
|
paths_len)
|
2007-10-07 19:22:47 +02:00
|
|
|
dialogs.ConfirmationDialog(pri_text,
|
2006-04-02 18:11:21 +02:00
|
|
|
_('This is an irreversible operation.'), on_response_ok = (on_ok,
|
|
|
|
liststore, list_of_paths))
|
2006-02-10 17:01:47 +01:00
|
|
|
|
2006-03-20 09:20:49 +01:00
|
|
|
def _delete_logs(self, liststore, list_of_paths):
|
|
|
|
paths_len = len(list_of_paths)
|
|
|
|
if paths_len == 0: # nothing is selected
|
|
|
|
return
|
2006-04-02 18:11:21 +02:00
|
|
|
|
2007-10-07 19:22:47 +02:00
|
|
|
def on_ok(liststore, list_of_paths):
|
2006-04-02 18:11:21 +02:00
|
|
|
# delete rows from db that match log_line_id
|
|
|
|
list_of_rowrefs = []
|
|
|
|
for path in list_of_paths: # make them treerowrefs (it's needed)
|
2007-02-04 14:01:04 +01:00
|
|
|
list_of_rowrefs.append(gtk.TreeRowReference(liststore, path))
|
2006-04-02 18:11:21 +02:00
|
|
|
|
|
|
|
for rowref in list_of_rowrefs:
|
|
|
|
path = rowref.get_path()
|
|
|
|
if path is None:
|
|
|
|
continue
|
|
|
|
log_line_id = liststore[path][0]
|
|
|
|
del liststore[path] # remove from UI
|
|
|
|
# remove from db
|
|
|
|
self.cur.execute('''
|
|
|
|
DELETE FROM logs
|
|
|
|
WHERE log_line_id = ?
|
|
|
|
''', (log_line_id,))
|
|
|
|
|
|
|
|
self.con.commit()
|
|
|
|
|
|
|
|
self.AT_LEAST_ONE_DELETION_DONE = True
|
|
|
|
|
2006-03-20 09:20:49 +01:00
|
|
|
|
|
|
|
pri_text = i18n.ngettext(
|
|
|
|
'Do you really want to delete the selected message?',
|
|
|
|
'Do you really want to delete the selected messages?', paths_len)
|
2007-10-07 19:22:47 +02:00
|
|
|
dialogs.ConfirmationDialog(pri_text,
|
2006-04-02 18:11:21 +02:00
|
|
|
_('This is an irreversible operation.'), on_response_ok = (on_ok,
|
|
|
|
liststore, list_of_paths))
|
2006-02-04 21:35:57 +01:00
|
|
|
|
2006-02-06 21:06:10 +01:00
|
|
|
def on_search_db_button_clicked(self, widget):
|
|
|
|
text = self.search_entry.get_text()
|
|
|
|
if text == '':
|
|
|
|
return
|
2006-02-06 21:34:06 +01:00
|
|
|
|
2006-02-06 21:06:10 +01:00
|
|
|
self.welcome_label.hide()
|
2006-02-06 21:34:06 +01:00
|
|
|
self.logs_scrolledwindow.hide()
|
2006-02-06 21:06:10 +01:00
|
|
|
self.search_results_scrolledwindow.show()
|
2006-02-06 21:34:06 +01:00
|
|
|
|
|
|
|
self._fill_search_results_listview(text)
|
|
|
|
|
2006-02-06 21:06:10 +01:00
|
|
|
def on_search_results_listview_row_activated(self, widget, path, column):
|
2006-02-08 23:46:20 +01:00
|
|
|
# get log_line_id, jid_id from row we double clicked
|
|
|
|
log_line_id = self.search_results_liststore[path][0]
|
|
|
|
jid = self.search_results_liststore[path][1]
|
|
|
|
# make it string as in gtk liststores I have them all as strings
|
|
|
|
# as this is what db returns so I don't have to fight with types
|
2006-04-09 21:08:57 +02:00
|
|
|
jid_id = self._get_jid_id(jid)
|
2006-02-08 23:46:20 +01:00
|
|
|
|
2006-02-09 00:41:01 +01:00
|
|
|
|
2006-02-08 23:46:20 +01:00
|
|
|
iter_ = self.jids_liststore.get_iter_root()
|
|
|
|
while iter_:
|
|
|
|
# self.jids_liststore[iter_][1] holds jid_ids
|
|
|
|
if self.jids_liststore[iter_][1] == jid_id:
|
|
|
|
break
|
|
|
|
iter_ = self.jids_liststore.iter_next(iter_)
|
|
|
|
|
|
|
|
if iter_ is None:
|
|
|
|
return
|
|
|
|
|
|
|
|
path = self.jids_liststore.get_path(iter_)
|
|
|
|
self.jids_listview.set_cursor(path)
|
2006-02-09 00:41:01 +01:00
|
|
|
|
|
|
|
iter_ = self.logs_liststore.get_iter_root()
|
|
|
|
while iter_:
|
|
|
|
# self.logs_liststore[iter_][0] holds lon_line_ids
|
|
|
|
if self.logs_liststore[iter_][0] == log_line_id:
|
|
|
|
break
|
|
|
|
iter_ = self.logs_liststore.iter_next(iter_)
|
|
|
|
|
|
|
|
path = self.logs_liststore.get_path(iter_)
|
|
|
|
self.logs_listview.scroll_to_cell(path)
|
2006-02-04 21:35:57 +01:00
|
|
|
|
2006-02-03 01:01:53 +01:00
|
|
|
if __name__ == '__main__':
|
|
|
|
signal.signal(signal.SIGINT, signal.SIG_DFL) # ^C exits the application
|
|
|
|
HistoryManager()
|
|
|
|
gtk.main()
|