root/branches/gajim_0.9.1/src/history_window.py

Revision 4869, 12.6 kB (checked in by nk, 3 years ago)

hardcode Y M D H M S

  • Property svn:eol-style set to LF
Line 
1##      history_window.py
2##
3## Contributors for this file:
4##      - Yann Le Boulanger <asterix@lagaule.org>
5##      - Nikos Kouremenos <kourem@gmail.com>
6##
7## Copyright (C) 2003-2004 Yann Le Boulanger <asterix@lagaule.org>
8##                         Vincent Hanquez <tab@snarc.org>
9## Copyright (C) 2005 Yann Le Boulanger <asterix@lagaule.org>
10##                    Vincent Hanquez <tab@snarc.org>
11##                    Nikos Kouremenos <nkour@jabber.org>
12##                    Dimitur Kirov <dkirov@gmail.com>
13##                    Travis Shirk <travis@pobox.com>
14##                    Norman Rasmussen <norman@rasmussen.co.za>
15##
16## This program is free software; you can redistribute it and/or modify
17## it under the terms of the GNU General Public License as published
18## by the Free Software Foundation; version 2 only.
19##
20## This program is distributed in the hope that it will be useful,
21## but WITHOUT ANY WARRANTY; without even the implied warranty of
22## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23## GNU General Public License for more details.
24##
25
26import gtk
27import gtk.glade
28import gobject
29import time
30import calendar
31
32import gtkgui_helpers
33import conversation_textview
34
35from common import gajim
36from common import helpers
37from common import i18n
38
39from common.logger import Constants
40
41constants = Constants()
42
43_ = i18n._
44APP = i18n.APP
45gtk.glade.bindtextdomain(APP, i18n.DIR)
46gtk.glade.textdomain(APP)
47
48GTKGUI_GLADE = 'gtkgui.glade'
49
50# contact_name, time, kind, show, message
51(
52C_CONTACT_NAME,
53C_TIME,
54C_MESSAGE
55) = range(3)
56
57class HistoryWindow:
58        '''Class for browsing logs of conversations with contacts'''
59
60        def __init__(self, jid, account):
61                self.jid = jid
62                self.account = account
63                self.mark_days_idle_call_id = None
64               
65                xml = gtk.glade.XML(GTKGUI_GLADE, 'history_window', APP)
66                self.window = xml.get_widget('history_window')
67               
68                self.calendar = xml.get_widget('calendar')
69                scrolledwindow = xml.get_widget('scrolledwindow')
70                self.history_textview = conversation_textview.ConversationTextview(account)
71                scrolledwindow.add(self.history_textview)
72                self.history_buffer = self.history_textview.get_buffer()
73                self.query_entry = xml.get_widget('query_entry')
74                self.search_button = xml.get_widget('search_button')
75                query_builder_button = xml.get_widget('query_builder_button')
76                query_builder_button.hide()
77                query_builder_button.set_no_show_all(True)
78                self.expander_vbox = xml.get_widget('expander_vbox')
79               
80                self.results_treeview = xml.get_widget('results_treeview')
81                # contact_name, time, message
82                model = gtk.ListStore(str, str, str)
83                self.results_treeview.set_model(model)
84               
85                col = gtk.TreeViewColumn(_('Name'))
86                self.results_treeview.append_column(col)
87                renderer = gtk.CellRendererText()
88                col.pack_start(renderer)
89                col.set_attributes(renderer, text = C_CONTACT_NAME)
90                col.set_sort_column_id(C_CONTACT_NAME)
91                col.set_resizable(True)
92               
93                col = gtk.TreeViewColumn(_('Date'))
94                self.results_treeview.append_column(col)
95                renderer = gtk.CellRendererText()
96                col.pack_start(renderer)
97                col.set_attributes(renderer, text = C_TIME)
98                col.set_sort_column_id(C_TIME)
99                col.set_resizable(True)
100               
101                col = gtk.TreeViewColumn(_('Message'))
102                self.results_treeview.append_column(col)
103                renderer = gtk.CellRendererText()
104                col.pack_start(renderer)
105                col.set_attributes(renderer, text = C_MESSAGE)
106                col.set_resizable(True)
107               
108                if account and gajim.contacts[account].has_key(jid):
109                        contact = gajim.get_first_contact_instance_from_jid(account, jid)
110                        title = _('Conversation History with %s') % contact.name
111                else:
112                        title = _('Conversation History with %s') % jid
113                self.window.set_title(title)
114               
115                xml.signal_autoconnect(self)
116               
117                # fake event so we start mark days procedure for selected month
118                # selected month is current month as calendar defaults to selecting
119                # current date
120                self.calendar.emit('month-changed')
121
122                # select and show logs for last date we have logs with contact
123                # and if we don't have logs at all, default to today
124                result = gajim.logger.get_last_date_that_has_logs(self.jid)
125                if result is None:
126                        date = time.localtime()
127                else:
128                        tim = result
129                        date = time.localtime(tim)
130
131                y, m, d = date[0], date[1], date[2]
132                gtk_month = gtkgui_helpers.make_python_month_gtk_month(m)
133                self.calendar.select_month(gtk_month, y)
134                self.calendar.select_day(d)
135                self.add_lines_for_date(y, m, d)
136               
137                self.window.show_all()
138
139        def on_history_window_destroy(self, widget):
140                if self.mark_days_idle_call_id:
141                        # if user destroys the window, and we have a generator filling mark days
142                        # stop him!
143                        gobject.source_remove(self.mark_days_idle_call_id)
144                del gajim.interface.instances['logs'][self.jid]
145
146        def on_close_button_clicked(self, widget):
147                self.window.destroy()
148
149        def on_calendar_day_selected(self, widget):
150                year, month, day = widget.get_date() # integers
151                month = gtkgui_helpers.make_gtk_month_python_month(month)
152                self.add_lines_for_date(year, month, day)
153               
154        def do_possible_mark_for_days_in_this_month(self, widget, year, month):
155                '''this is a generator and does pseudo-threading via idle_add()
156                so it runs progressively! yea :)
157                asks for days in this month if they have logs it bolds them (marks them)'''
158                weekday, days_in_this_month = calendar.monthrange(year, month)
159                log_days = gajim.logger.get_days_with_logs(self.jid, year,
160                        month, days_in_this_month)
161                for day in log_days:
162                        widget.mark_day(day)
163                        yield True
164                yield False
165       
166        def on_calendar_month_changed(self, widget):
167                year, month, day = widget.get_date() # integers
168                # in gtk January is 1, in python January is 0,
169                # I want the second
170                # first day of month is 1 not 0
171                if self.mark_days_idle_call_id:
172                        # if user changed month, and we have a generator filling mark days
173                        # stop him from marking dates for the previously selected month
174                        gobject.source_remove(self.mark_days_idle_call_id)
175                widget.clear_marks()
176                month = gtkgui_helpers.make_gtk_month_python_month(month)
177                self.mark_days_idle_call_id = gobject.idle_add(
178                        self.do_possible_mark_for_days_in_this_month(widget, year, month).next)
179
180        def get_string_show_from_constant_int(self, show):
181                if show == constants.SHOW_ONLINE:
182                        show = 'online'
183                elif show == constants.SHOW_CHAT:
184                        show = 'chat'
185                elif show == constants.SHOW_AWAY:
186                        show = 'away'
187                elif show == constants.SHOW_XA:
188                        show = 'xa'
189                elif show == constants.SHOW_DND:
190                        show = 'dnd'
191                elif show == constants.SHOW_OFFLINE:
192                        show = 'offline'
193
194                return show
195
196        def add_lines_for_date(self, year, month, day):
197                '''adds all the lines for given date in textbuffer'''
198                self.history_buffer.set_text('') # clear the buffer first
199                self.last_time_printout = 0
200                lines = gajim.logger.get_conversation_for_date(self.jid, year, month, day)
201                # lines holds list with tupples that have:
202                # contact_name, time, kind, show, message
203                for line in lines:
204                        # line[0] is contact_name, line[1] is time of message
205                        # line[2] is kind, line[3] is show, line[4] is message
206                        self.add_new_line(line[0], line[1], line[2], line[3], line[4])
207       
208        def add_new_line(self, contact_name, tim, kind, show, message):
209                '''add a new line in textbuffer'''
210                if not message: # None or ''
211                        return
212                buf = self.history_buffer
213                end_iter = buf.get_end_iter()
214               
215                if gajim.config.get('print_time') == 'always':
216                        before_str = gajim.config.get('before_time')
217                        after_str = gajim.config.get('after_time')
218                        format = before_str + '%X' + after_str + ' '
219                        tim = time.strftime(format, time.localtime(float(tim)))
220                        buf.insert(end_iter, tim) # add time
221                elif gajim.config.get('print_time') == 'sometimes':
222                        every_foo_seconds = 60 * gajim.config.get(
223                                'print_ichat_every_foo_minutes')
224                        seconds_passed = tim - self.last_time_printout
225                        if seconds_passed > every_foo_seconds:
226                                self.last_time_printout = tim
227                                tim = time.strftime('%X ', time.localtime(float(tim)))
228                                buf.insert_with_tags_by_name(end_iter, tim + '\n',
229                                        'time_sometimes')                               
230
231                tag_name = ''
232                tag_msg = ''
233               
234                show = self.get_string_show_from_constant_int(show)
235               
236                if kind == constants.KIND_GC_MSG:
237                        tag_name = 'incoming'
238                elif kind in (constants.KIND_SINGLE_MSG_RECV, constants.KIND_CHAT_MSG_RECV):
239                        try:
240                                # is he in our roster? if yes use the name
241                                contact_name = gajim.contacts[self.account][self.jid][0].name
242                        except:
243                                room_jid, nick = gajim.get_room_and_nick_from_fjid(self.jid)
244                                # do we have him as gc_contact?
245                                if nick and gajim.gc_contacts[self.account].has_key(room_jid) and\
246                                        gajim.gc_contacts[self.account][room_jid].has_key(nick):
247                                        # so yes, it's pm!
248                                        contact_name = nick
249                                else:
250                                        contact_name = self.jid.split('@')[0]
251                        tag_name = 'incoming'
252                elif kind in (constants.KIND_SINGLE_MSG_SENT, constants.KIND_CHAT_MSG_SENT):
253                        contact_name = gajim.nicks[self.account]
254                        tag_name = 'outgoing'
255                elif kind == constants.KIND_GCSTATUS:
256                        # message here (if not None) is status message
257                        if message:
258                                message = _('%(nick)s is now %(status)s: %(status_msg)s') %\
259                                        {'nick': contact_name, 'status': helpers.get_uf_show(show),
260                                        'status_msg': message }
261                        else:
262                                message = _('%(nick)s is now %(status)s') % {'nick': contact_name,
263                                        'status': helpers.get_uf_show(show) }
264                        tag_msg = 'status'
265                else: # 'status'
266                        # message here (if not None) is status message
267                        if message:
268                                message = _('Status is now: %(status)s: %(status_msg)s') % \
269                                        {'status': helpers.get_uf_show(show), 'status_msg': message}
270                        else:
271                                message = _('Status is now: %(status)s') % { 'status':
272                                        helpers.get_uf_show(show) }
273                        tag_msg = 'status'
274
275                # do not do this if gcstats, avoid dupping contact_name
276                # eg. nkour: nkour is now Offline
277                if contact_name and kind != constants.KIND_GCSTATUS:
278                        # add stuff before and after contact name
279                        before_str = gajim.config.get('before_nickname')
280                        after_str = gajim.config.get('after_nickname')
281                        format = before_str + contact_name + after_str + ' '
282                        buf.insert_with_tags_by_name(end_iter, format, tag_name)
283
284                message = message + '\n'
285                if tag_msg:
286                        self.history_textview.print_real_text(message, [tag_msg])
287                else:
288                        self.history_textview.print_real_text(message)
289
290        def set_unset_expand_on_expander(self, widget):
291                '''expander has to have expand to TRUE so scrolledwindow resizes properly
292                and does not have a static size. when expander is not expanded we set
293                expand property (note the Box one) to FALSE
294                to do this, we first get the box and then apply to expander widget
295                the True/False thingy depending if it's expanded or not
296                this function is called in a timeout just after expanded state changes'''
297                parent = widget.get_parent() # vbox
298                expanded = widget.get_expanded()
299                w, h = self.window.get_size()
300                if expanded: # resize to larger in height the window
301                        self.window.resize(w, int(h*1.3))
302                else: # resize to smaller in height the window
303                        self.window.resize(w, int(h/1.3))
304                # now set expand so if manually resizing scrolledwindow resizes too
305                parent.child_set_property(widget, 'expand', expanded)
306       
307        def on_search_expander_activate(self, widget):
308                if widget.get_expanded(): # it's the OPPOSITE!, it's not expanded
309                        gobject.timeout_add(200, self.set_unset_expand_on_expander, widget)
310                else:
311                        gobject.timeout_add(200, self.set_unset_expand_on_expander, widget)
312                        self.search_button.grab_default()
313                        self.query_entry.grab_focus()
314       
315        def on_search_button_clicked(self, widget):
316                text = self.query_entry.get_text()
317                model = self.results_treeview.get_model()
318                model.clear()
319                if text == '':
320                        return
321                # contact_name, time, kind, show, message, subject
322                results = gajim.logger.get_search_results_for_query(self.jid, text)
323                #FIXME: investigate on kind and put name for normal chatting
324                #and add "subject:  | message: " in message column is kind is
325                # single*
326                # also do we need show at all?
327                for row in results:
328                        local_time = time.localtime(row[1])
329                        tim = time.strftime('%Y-%m-%d %H:%M:%S', local_time)
330                        model.append((row[0], tim, row[4]))
331                       
332        def on_results_treeview_row_activated(self, widget, path, column):
333                '''a row was double clicked, get date from row, and select it in calendar
334                which results to showing conversation logs for that date'''
335                # get currently selected date
336                cur_year, cur_month, cur_day = self.calendar.get_date()
337                cur_month = gtkgui_helpers.make_gtk_month_python_month(cur_month)
338                model = widget.get_model()
339                iter = model.get_iter(path)
340                # make it (Y, M, D, ...)
341                tim = time.strptime(model[iter][C_TIME], '%Y-%m-%d %H:%M:%S')
342                year = tim[0]
343                gtk_month = tim[1]
344                month = gtkgui_helpers.make_python_month_gtk_month(gtk_month)
345                day = tim[2]
346               
347                # avoid reruning mark days algo if same month and year!
348                if year != cur_year or gtk_month != cur_month:
349                        self.calendar.select_month(month, year)
350               
351                self.calendar.select_day(day)
352               
353                # self.history_buffer.get_bounds()
354                #FIXME: start_iter.forward_search(string, TEXT_SEARCH_VISIBLE_ONLY, None)
355                # on double click and scroll there and maybe even highlight it
Note: See TracBrowser for help on using the browser.