Philipp Hörist pushed to branch master at gajim / gajim
Commits: b0bf7433 by Daniel at 2018-02-07T10:04:56+01:00 Add navigation for chat history
- - - - - 7b1bdc55 by Philipp Hörist at 2018-02-08T17:34:19+01:00 Merge branch 'history-navigation' into 'master'
Add navigation for chat history
See merge request gajim/gajim!202 - - - - -
3 changed files:
- gajim/common/logger.py - gajim/data/gui/history_window.ui - gajim/history_window.py
Changes:
===================================== gajim/common/logger.py ===================================== --- a/gajim/common/logger.py +++ b/gajim/common/logger.py @@ -732,6 +732,64 @@ class Logger: # attributes set to None because of the MAX() function return self.con.execute(sql, tuple(jids)).fetchone().time
+ def get_first_date_that_has_logs(self, account, jid): + """ + Get the timestamp of the first message we received for the jid. + + :param account: The account + + :param jid: The jid for which we request the first timestamp + + returns a timestamp or None + """ + jids = self._get_family_jids(account, jid) + + kinds = map(str, [KindConstant.STATUS, + KindConstant.GCSTATUS]) + + sql = ''' + SELECT MIN(time) as time FROM logs + NATURAL JOIN jids WHERE jid IN ({jids}) + AND kind NOT IN ({kinds}) + '''.format(jids=', '.join('?' * len(jids)), + kinds=', '.join(kinds)) + + # fetchone() returns always at least one Row with all + # attributes set to None because of the MIN() function + return self.con.execute(sql, tuple(jids)).fetchone().time + + def get_date_has_logs(self, account, jid, date): + """ + Get single timestamp of a message we received for the jid + in the time range of one day. + + :param account: The account + + :param jid: The jid for which we request the first timestamp + + :param date: datetime.datetime instance + example: datetime.datetime(year, month, day) + + returns a timestamp or None + """ + jids = self._get_family_jids(account, jid) + + kinds = map(str, [KindConstant.STATUS, + KindConstant.GCSTATUS]) + + delta = datetime.timedelta( + hours=23, minutes=59, seconds=59, microseconds=999999) + + sql = ''' + SELECT time + FROM logs NATURAL JOIN jids WHERE jid IN ({jids}) + AND time BETWEEN ? AND ? + '''.format(jids=', '.join('?' * len(jids))) + + return self.con.execute(sql, tuple(jids) + + (date.timestamp(), + (date + delta).timestamp())).fetchone() + def get_room_last_message_time(self, account, jid): """ Get the timestamp of the last message we received in a room.
===================================== gajim/data/gui/history_window.ui ===================================== --- a/gajim/data/gui/history_window.ui +++ b/gajim/data/gui/history_window.ui @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.20.0 --> +<!-- Generated with glade 3.20.2 --> <interface> <requires lib="gtk+" version="3.20"/> <object class="GtkApplicationWindow" id="history_window"> @@ -35,7 +35,7 @@ <object class="GtkEntry" id="query_entry"> <property name="can_focus">True</property> <property name="invisible_char">●</property> - <property name="placeholder_text">Enter name / JID of contact or groupchat</property> + <property name="placeholder_text" translatable="yes">Enter name / JID of contact or groupchat</property> </object> <packing> <property name="expand">True</property> @@ -102,11 +102,13 @@ <object class="GtkPaned" id="hpaned"> <property name="visible">True</property> <property name="can_focus">True</property> + <property name="margin_bottom">5</property> <property name="position">165</property> <child> <object class="GtkBox" id="vbox2"> <property name="visible">True</property> <property name="can_focus">False</property> + <property name="margin_right">5</property> <property name="orientation">vertical</property> <property name="spacing">6</property> <child> @@ -123,7 +125,98 @@ </packing> </child> <child> - <placeholder/> + <object class="GtkButtonBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">center</property> + <property name="spacing">5</property> + <property name="layout_style">expand</property> + <child> + <object class="GtkButton" id="button_first_day"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <signal name="clicked" handler="_change_date" swapped="no"/> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="icon_name">go-first-symbolic</property> + </object> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="button_previous_day"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <signal name="clicked" handler="_change_date" swapped="no"/> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="icon_name">go-previous-symbolic</property> + </object> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="button_next_day"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <signal name="clicked" handler="_change_date" swapped="no"/> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="icon_name">go-next-symbolic</property> + </object> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkButton" id="button_last_day"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <signal name="clicked" handler="_change_date" swapped="no"/> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="icon_name">go-last-symbolic</property> + </object> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> </child> </object> <packing>
===================================== gajim/history_window.py ===================================== --- a/gajim/history_window.py +++ b/gajim/history_window.py @@ -71,6 +71,10 @@ class HistoryWindow: self.window = xml.get_object('history_window') self.window.set_application(app.app) self.calendar = xml.get_object('calendar') + self.button_first_day = xml.get_object('button_first_day') + self.button_previous_day = xml.get_object('button_previous_day') + self.button_next_day = xml.get_object('button_next_day') + self.button_last_day = xml.get_object('button_last_day') scrolledwindow = xml.get_object('scrolledwindow') self.history_textview = conversation_textview.ConversationTextview( account, used_in_history_window = True) @@ -309,17 +313,25 @@ class HistoryWindow:
self.jids_to_search = [info_jid]
- # select logs for last date we have logs with contact - self.calendar.set_sensitive(True) - last_log = \ - app.logger.get_last_date_that_has_logs(self.account, self.jid) + # Get first/last date we have logs with contact (for log navigation) + self.first_log = app.logger.get_first_date_that_has_logs( + self.account, self.jid) + self.first_day = self._get_date_from_timestamp(self.first_log) + self.last_log = app.logger.get_last_date_that_has_logs( + self.account, self.jid) + self.last_day = self._get_date_from_timestamp(self.last_log)
- date = time.localtime(last_log) + # Select logs for last date we have logs with contact + self.calendar.set_sensitive(True) + gtk_month = gtkgui_helpers.make_python_month_gtk_month( + self.last_day.month) + self.calendar.select_month(gtk_month, self.last_day.year) + self.calendar.select_day(self.last_day.day)
- y, m, d = date[0], date[1], date[2] - gtk_month = gtkgui_helpers.make_python_month_gtk_month(m) - self.calendar.select_month(gtk_month, y) - self.calendar.select_day(d) + self.button_previous_day.set_sensitive(True) + self.button_next_day.set_sensitive(True) + self.button_first_day.set_sensitive(True) + self.button_last_day.set_sensitive(True)
self.search_entry.set_sensitive(True) self.search_entry.grab_focus() @@ -339,6 +351,10 @@ class HistoryWindow: self.checkbutton.set_sensitive(False) self.calendar.set_sensitive(False) self.calendar.clear_marks() + self.button_previous_day.set_sensitive(False) + self.button_next_day.set_sensitive(False) + self.button_first_day.set_sensitive(False) + self.button_last_day.set_sensitive(False)
self.results_window.set_property('visible', False)
@@ -377,6 +393,60 @@ class HistoryWindow: for date in log_days: widget.mark_day(date.day)
+ def _get_date_from_timestamp(self, timestamp): + # Conversion from timestamp to date + log = time.localtime(timestamp) + y, m, d = log[0], log[1], log[2] + date = datetime.datetime(y, m, d) + return(date) + + def _change_date(self, widget): + # Get day selected in calendar + y, m, d = self.calendar.get_date() + py_m = gtkgui_helpers.make_gtk_month_python_month(m) + _date = datetime.datetime(y, py_m, d) + + if widget is self.button_first_day: + gtk_m = gtkgui_helpers.make_python_month_gtk_month( + self.first_day.month) + self.calendar.select_month(gtk_m, self.first_day.year) + self.calendar.select_day(self.first_day.day) + return + elif widget is self.button_last_day: + gtk_m = gtkgui_helpers.make_python_month_gtk_month( + self.last_day.month) + self.calendar.select_month(gtk_m, self.last_day.year) + self.calendar.select_day(self.last_day.day) + return + elif widget is self.button_previous_day: + end_date = self.first_day + timedelta = datetime.timedelta(days=-1) + if end_date >= _date: + return + elif widget is self.button_next_day: + end_date = self.last_day + timedelta = datetime.timedelta(days=1) + if end_date <= _date: + return + + # Iterate through days until log entry found or + # supplied end_date (first_log / last_log) reached + logs = None + while logs is None: + _date = _date + timedelta + if _date == end_date: + break + try: + logs = app.logger.get_date_has_logs( + self.account, self.jid, _date) + except exceptions.PysqliteOperationalError as e: + dialogs.ErrorDialog(_('Disk Error'), str(e)) + return + + gtk_month = gtkgui_helpers.make_python_month_gtk_month(_date.month) + self.calendar.select_month(gtk_month, _date.year) + self.calendar.select_day(_date.day) + def _get_string_show_from_constant_int(self, show): if show == ShowConstant.ONLINE: show = 'online'
View it on GitLab: https://dev.gajim.org/gajim/gajim/compare/4bbc6b19729f3b0c0907c52a49806326d9...
--- View it on GitLab: https://dev.gajim.org/gajim/gajim/compare/4bbc6b19729f3b0c0907c52a49806326d9... You're receiving this email because of your account on dev.gajim.org.