search in history by ctrl + F - initial commit
This commit is contained in:
parent
1d33d298c3
commit
8b56184510
5 changed files with 185 additions and 23 deletions
|
@ -30,7 +30,8 @@ class Contact(basecontact.BaseContact):
|
|||
self._unsaved_messages = 0
|
||||
self._history_loaded = self._new_actions = False
|
||||
self._receipts = 0
|
||||
self._curr_text = ''
|
||||
self._curr_text = self._search_string = ''
|
||||
self._search_index = 0
|
||||
|
||||
def __del__(self):
|
||||
self.set_visibility(False)
|
||||
|
@ -94,6 +95,10 @@ class Contact(basecontact.BaseContact):
|
|||
else:
|
||||
return ''
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------
|
||||
# Unsent messages
|
||||
# -----------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def get_unsent_messages(self):
|
||||
"""
|
||||
:return list of unsent messages
|
||||
|
@ -108,6 +113,17 @@ class Contact(basecontact.BaseContact):
|
|||
messages = filter(lambda x: x.get_type() <= 1 and x.get_owner() == MESSAGE_OWNER['NOT_SENT'], self._corr)
|
||||
return list(map(lambda x: x.get_data(), messages))
|
||||
|
||||
def mark_as_sent(self):
|
||||
try:
|
||||
message = list(filter(lambda x: x.get_owner() == MESSAGE_OWNER['NOT_SENT'], self._corr))[0]
|
||||
message.mark_as_sent()
|
||||
except Exception as ex:
|
||||
util.log('Mark as sent ex: ' + str(ex))
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------
|
||||
# Message deletion
|
||||
# -----------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def delete_message(self, time):
|
||||
elem = list(filter(lambda x: type(x) is TextMessage and x.get_data()[2] == time, self._corr))[0]
|
||||
tmp = list(filter(lambda x: x.get_type() <= 1, self._corr))
|
||||
|
@ -118,7 +134,7 @@ class Contact(basecontact.BaseContact):
|
|||
|
||||
def delete_old_messages(self):
|
||||
"""
|
||||
Delete old messages (reduces RAM if messages saving is not enabled)
|
||||
Delete old messages (reduces RAM usage if messages saving is not enabled)
|
||||
"""
|
||||
old = filter(lambda x: x.get_type() == 2 and (x.get_status() >= 2 or x.get_status() is None),
|
||||
self._corr[:-SAVE_MESSAGES])
|
||||
|
@ -126,13 +142,7 @@ class Contact(basecontact.BaseContact):
|
|||
l = max(len(self._corr) - SAVE_MESSAGES, 0) - len(old)
|
||||
self._unsaved_messages -= l
|
||||
self._corr = old + self._corr[-SAVE_MESSAGES:]
|
||||
|
||||
def mark_as_sent(self):
|
||||
try:
|
||||
message = list(filter(lambda x: x.get_owner() == MESSAGE_OWNER['NOT_SENT'], self._corr))[0]
|
||||
message.mark_as_sent()
|
||||
except Exception as ex:
|
||||
util.log('Mark as sent ex: ' + str(ex))
|
||||
self._search_index = 0
|
||||
|
||||
def clear_corr(self, save_unsent=False):
|
||||
"""
|
||||
|
@ -140,6 +150,7 @@ class Contact(basecontact.BaseContact):
|
|||
"""
|
||||
if hasattr(self, '_message_getter'):
|
||||
del self._message_getter
|
||||
self._search_index = 0
|
||||
# don't delete data about active file transfer
|
||||
if not save_unsent:
|
||||
self._corr = list(filter(lambda x: x.get_type() == 2 and
|
||||
|
@ -151,6 +162,43 @@ class Contact(basecontact.BaseContact):
|
|||
self._corr))
|
||||
self._unsaved_messages = len(self.get_unsent_messages())
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------
|
||||
# Chat history search
|
||||
# -----------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def search_string(self, search_string):
|
||||
self._search_string, self._search_index = search_string, 0
|
||||
return self.search_prev()
|
||||
|
||||
def search_prev(self):
|
||||
while True:
|
||||
l = len(self._corr)
|
||||
for i in range(self._search_index - 1, -l - 1, -1):
|
||||
if type(self._corr[i]) is not TextMessage:
|
||||
continue
|
||||
if self._search_string.lower() in self._corr[i].get_data()[0].lower():
|
||||
self._search_index = i
|
||||
return i
|
||||
self._search_index = -l
|
||||
self.load_corr(False)
|
||||
if len(self._corr) == l:
|
||||
return None # not found
|
||||
|
||||
def search_next(self):
|
||||
if not self._search_index:
|
||||
return None
|
||||
for i in range(self._search_index + 1, 0):
|
||||
if type(self._corr[i]) is not TextMessage:
|
||||
continue
|
||||
if self._search_string.lower() in self._corr[i].get_data()[0].lower():
|
||||
self._search_index = i
|
||||
return i
|
||||
return None # not found
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------
|
||||
# Current text - text from message area
|
||||
# -----------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def get_curr_text(self):
|
||||
return self._curr_text
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ from widgets import DataLabel, create_menu
|
|||
import html as h
|
||||
import smileys
|
||||
import settings
|
||||
import re
|
||||
|
||||
|
||||
class MessageEdit(QtGui.QTextBrowser):
|
||||
|
@ -189,6 +190,12 @@ class MessageItem(QtGui.QWidget):
|
|||
self.message.setFixedHeight(self.height())
|
||||
self.name.setPixmap(pixmap.scaled(30, 30, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation))
|
||||
|
||||
def select_text(self, text=''):
|
||||
tmp = self.message.toHtml()
|
||||
pattern = re.compile(re.escape(text), re.IGNORECASE) # TODO: save case
|
||||
tmp = pattern.sub('<font color="red">{}</font>'.format(text), tmp)
|
||||
self.message.setHtml(tmp)
|
||||
|
||||
|
||||
class ContactItem(QtGui.QWidget):
|
||||
"""
|
||||
|
|
|
@ -407,6 +407,8 @@ class MainWindow(QtGui.QMainWindow, Singleton):
|
|||
clipboard.setText(s)
|
||||
elif event.key() == QtCore.Qt.Key_Z and event.modifiers() & QtCore.Qt.ControlModifier and self.messages.selectedIndexes():
|
||||
self.messages.clearSelection()
|
||||
elif event.key() == QtCore.Qt.Key_F and event.modifiers() & QtCore.Qt.ControlModifier:
|
||||
self.show_search_field()
|
||||
else:
|
||||
super(MainWindow, self).keyPressEvent(event)
|
||||
|
||||
|
@ -699,3 +701,12 @@ class MainWindow(QtGui.QMainWindow, Singleton):
|
|||
ind = self.online_contacts.currentIndex()
|
||||
d = {0: 0, 1: 1, 2: 2, 3: 4, 4: 1 | 4, 5: 2 | 4}
|
||||
self.profile.filtration_and_sorting(d[ind], self.contact_name.text())
|
||||
|
||||
def show_search_field(self):
|
||||
if hasattr(self, 'search_field') and self.search_field.isVisible():
|
||||
return
|
||||
self.search_field = SearchScreen(self.messages, self.messages.width(), self.messages.parent())
|
||||
x, y = self.messages.x(), self.messages.y() + self.messages.height() - 40
|
||||
self.search_field.setGeometry(x, y, self.messages.width(), 40)
|
||||
self.messages.setGeometry(x, self.messages.y(), self.messages.width(), self.messages.height() - 40)
|
||||
self.search_field.show()
|
||||
|
|
|
@ -2,7 +2,7 @@ try:
|
|||
from PySide import QtCore, QtGui
|
||||
except ImportError:
|
||||
from PyQt4 import QtCore, QtGui
|
||||
from widgets import RubberBand, create_menu, QRightClickButton, CenteredWidget
|
||||
from widgets import RubberBand, create_menu, QRightClickButton, CenteredWidget, LineEdit
|
||||
from profile import Profile
|
||||
import smileys
|
||||
import util
|
||||
|
@ -404,3 +404,96 @@ class MainMenuButton(QtGui.QPushButton):
|
|||
metrics = QtGui.QFontMetrics(self.font())
|
||||
self.setFixedWidth(metrics.size(QtCore.Qt.TextSingleLine, text).width() + 20)
|
||||
super().setText(text)
|
||||
|
||||
|
||||
class ClickableLabel(QtGui.QLabel):
|
||||
|
||||
def __init__(self, *args):
|
||||
super().__init__(*args)
|
||||
|
||||
def mouseReleaseEvent(self, ev):
|
||||
self.emit(QtCore.SIGNAL('clicked()'))
|
||||
|
||||
|
||||
class SearchScreen(QtGui.QWidget):
|
||||
|
||||
def __init__(self, messages, width, *args):
|
||||
super().__init__(*args)
|
||||
self.setMaximumSize(width, 40)
|
||||
self.setMinimumSize(width, 40)
|
||||
self._messages = messages
|
||||
|
||||
self.search_text = LineEdit(self)
|
||||
self.search_text.setGeometry(0, 0, width - 100, 40)
|
||||
|
||||
self.search_button = ClickableLabel(self)
|
||||
self.search_button.setGeometry(width - 100, 0, 40, 40)
|
||||
pixmap = QtGui.QPixmap()
|
||||
pixmap.load(util.curr_directory() + '/images/search.png')
|
||||
self.search_button.setScaledContents(False)
|
||||
self.search_button.setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.search_button.setPixmap(pixmap)
|
||||
self.connect(self.search_button, QtCore.SIGNAL('clicked()'), self.search)
|
||||
|
||||
self.prev_button = QtGui.QPushButton(self)
|
||||
self.prev_button.setGeometry(width - 60, 0, 20, 20)
|
||||
self.prev_button.clicked.connect(self.prev)
|
||||
self.prev_button.setText('\u25B2')
|
||||
|
||||
self.next_button = QtGui.QPushButton(self)
|
||||
self.next_button.setGeometry(width - 60, 20, 20, 20)
|
||||
self.next_button.clicked.connect(self.next)
|
||||
self.next_button.setText('\u25BC')
|
||||
|
||||
self.close_button = QtGui.QPushButton(self)
|
||||
self.close_button.setGeometry(width - 40, 0, 40, 40)
|
||||
self.close_button.clicked.connect(self.close)
|
||||
self.close_button.setText('×')
|
||||
self.close_button.setAlignment(QtCore.Qt.AlignCenter)
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(32)
|
||||
font.setBold(True)
|
||||
self.close_button.setFont(font)
|
||||
|
||||
def search(self):
|
||||
text = self.search_text.text() # TODO: clean selection
|
||||
friend = Profile.get_instance().get_curr_friend()
|
||||
if text and friend:
|
||||
index = friend.search_string(text)
|
||||
self.load_messages(index)
|
||||
|
||||
def prev(self):
|
||||
friend = Profile.get_instance().get_curr_friend()
|
||||
if friend is not None:
|
||||
index = friend.search_prev()
|
||||
self.load_messages(index)
|
||||
|
||||
def next(self):
|
||||
friend = Profile.get_instance().get_curr_friend()
|
||||
if friend is not None:
|
||||
index = friend.search_next()
|
||||
if index is not None:
|
||||
text = self.search_text.text()
|
||||
count = self._messages.count()
|
||||
index += count
|
||||
item = self._messages.item(index)
|
||||
self._messages.scrollToItem(item)
|
||||
self._messages.itemWidget(item).select_text(text)
|
||||
|
||||
def load_messages(self, index):
|
||||
if index is not None:
|
||||
profile = Profile.get_instance()
|
||||
count = self._messages.count()
|
||||
while count + index < 0:
|
||||
profile.load_history()
|
||||
count = self._messages.count()
|
||||
index += count
|
||||
item = self._messages.item(index)
|
||||
self._messages.scrollToItem(item)
|
||||
text = self.search_text.text()
|
||||
self._messages.itemWidget(item).select_text(text)
|
||||
|
||||
def closeEvent(self, *args):
|
||||
Profile.get_instance().update() # TODO: clean selection?
|
||||
self._messages.setGeometry(0, 0, self._messages.width(), self._messages.height() + 40)
|
||||
super().closeEvent(*args)
|
||||
|
|
|
@ -183,6 +183,9 @@ class Profile(basecontact.BaseContact, Singleton):
|
|||
return None
|
||||
return self._contacts[num]
|
||||
|
||||
def get_curr_friend(self):
|
||||
return self._contacts[self._active_friend] if self._active_friend + 1 else None
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------
|
||||
# Work with active friend
|
||||
# -----------------------------------------------------------------------------------------------------------------
|
||||
|
@ -211,7 +214,7 @@ class Profile(basecontact.BaseContact, Singleton):
|
|||
if value is not None:
|
||||
if self._active_friend + 1 and self._active_friend != value:
|
||||
try:
|
||||
self._contacts[self._active_friend].curr_text = self._screen.messageEdit.toPlainText()
|
||||
self.get_curr_friend().curr_text = self._screen.messageEdit.toPlainText()
|
||||
except:
|
||||
pass
|
||||
friend = self._contacts[value]
|
||||
|
@ -261,7 +264,7 @@ class Profile(basecontact.BaseContact, Singleton):
|
|||
else:
|
||||
self._screen.call_finished()
|
||||
else:
|
||||
friend = self._contacts[self._active_friend]
|
||||
friend = self.get_curr_friend()
|
||||
|
||||
self._screen.account_name.setText(friend.name)
|
||||
self._screen.account_status.setText(friend.status_message)
|
||||
|
@ -287,18 +290,18 @@ class Profile(basecontact.BaseContact, Singleton):
|
|||
|
||||
def get_last_message(self):
|
||||
if self._active_friend + 1:
|
||||
return self._contacts[self._active_friend].get_last_message_text()
|
||||
return self.get_curr_friend().get_last_message_text()
|
||||
else:
|
||||
return ''
|
||||
|
||||
def get_active_number(self):
|
||||
return self._contacts[self._active_friend].number if self._active_friend + 1 else -1
|
||||
return self.get_curr_friend().number if self._active_friend + 1 else -1
|
||||
|
||||
def get_active_name(self):
|
||||
return self._contacts[self._active_friend].name if self._active_friend + 1 else ''
|
||||
return self.get_curr_friend().name if self._active_friend + 1 else ''
|
||||
|
||||
def is_active_online(self):
|
||||
return self._active_friend + 1 and self._contacts[self._active_friend].status is not None
|
||||
return self._active_friend + 1 and self.get_curr_friend().status is not None
|
||||
|
||||
def new_name(self, number, name):
|
||||
friend = self.get_friend_by_number(number)
|
||||
|
@ -373,7 +376,7 @@ class Profile(basecontact.BaseContact, Singleton):
|
|||
"""
|
||||
if Settings.get_instance()['typing_notifications'] and self._active_friend + 1:
|
||||
try:
|
||||
friend = self._contacts[self._active_friend]
|
||||
friend = self.get_curr_friend()
|
||||
if friend.status is not None:
|
||||
self._tox.self_set_typing(friend.number, typing)
|
||||
except:
|
||||
|
@ -443,7 +446,7 @@ class Profile(basecontact.BaseContact, Singleton):
|
|||
t = time.time()
|
||||
self.create_message_item(message, t, MESSAGE_OWNER['FRIEND'], message_type)
|
||||
self._messages.scrollToBottom()
|
||||
self._contacts[self._active_friend].append_message(
|
||||
self.get_curr_friend().append_message(
|
||||
TextMessage(message, MESSAGE_OWNER['FRIEND'], t, message_type))
|
||||
else:
|
||||
friend = self.get_friend_by_number(friend_num)
|
||||
|
@ -482,7 +485,7 @@ class Profile(basecontact.BaseContact, Singleton):
|
|||
friend.append_message(TextMessage(text, MESSAGE_OWNER['NOT_SENT'], t, message_type))
|
||||
|
||||
def delete_message(self, time):
|
||||
friend = self._contacts[self._active_friend]
|
||||
friend = self.get_curr_friend()
|
||||
friend.delete_message(time)
|
||||
self._history.delete_message(friend.tox_id, time)
|
||||
self.update()
|
||||
|
@ -536,7 +539,7 @@ class Profile(basecontact.BaseContact, Singleton):
|
|||
if not self._load_history:
|
||||
return
|
||||
self._load_history = False
|
||||
friend = self._contacts[self._active_friend]
|
||||
friend = self.get_curr_friend()
|
||||
friend.load_corr(False)
|
||||
data = friend.get_corr()
|
||||
if not data:
|
||||
|
@ -624,7 +627,7 @@ class Profile(basecontact.BaseContact, Singleton):
|
|||
pixmap = None
|
||||
if self._show_avatars:
|
||||
if owner == MESSAGE_OWNER['FRIEND']:
|
||||
pixmap = self._contacts[self._active_friend].get_pixmap()
|
||||
pixmap = self.get_curr_friend().get_pixmap()
|
||||
else:
|
||||
pixmap = self.get_pixmap()
|
||||
return self._factory.message_item(text, time, name, owner != MESSAGE_OWNER['NOT_SENT'],
|
||||
|
@ -968,7 +971,7 @@ class Profile(basecontact.BaseContact, Singleton):
|
|||
0, -1)
|
||||
|
||||
def cancel_not_started_transfer(self, time):
|
||||
self._contacts[self._active_friend].delete_one_unsent_file(time)
|
||||
self.get_curr_friend().delete_one_unsent_file(time)
|
||||
self.update()
|
||||
|
||||
def pause_transfer(self, friend_number, file_number, by_friend=False):
|
||||
|
@ -1202,7 +1205,7 @@ class Profile(basecontact.BaseContact, Singleton):
|
|||
else:
|
||||
text = QtGui.QApplication.translate("incoming_call", "Outgoing audio call", None,
|
||||
QtGui.QApplication.UnicodeUTF8)
|
||||
self._contacts[self._active_friend].append_message(InfoMessage(text, time.time()))
|
||||
self.get_curr_friend().append_message(InfoMessage(text, time.time()))
|
||||
self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE'])
|
||||
self._messages.scrollToBottom()
|
||||
elif num in self._call: # finish or cancel call if you call with active friend
|
||||
|
|
Loading…
Reference in a new issue