Commit b23912ca authored by Philipp Hörist's avatar Philipp Hörist
Browse files

New ChatControl

parent ef822961
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="3.16"/>
<template class="GroupMessageWidget" parent="GtkGrid">
<property name="name">MessageGrid</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">start</property>
<property name="margin_left">10</property>
<property name="margin_right">15</property>
<property name="orientation">vertical</property>
<property name="column_spacing">5</property>
<signal name="destroy" handler="on_MyWidget_destroy" swapped="no"/>
<child>
<object class="GtkBox">
<property name="width_request">16</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="valign">start</property>
<child>
<object class="GtkImage" id="message_corrected_icon">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="halign">start</property>
<property name="valign">start</property>
<property name="icon_name">document-edit-symbolic</property>
<property name="icon_size">1</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkImage" id="encryption_icon">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="valign">start</property>
<property name="icon_size">1</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="time_label">
<property name="name">MessageTime</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="valign">start</property>
<property name="label" translatable="yes">label</property>
<style>
<class name="MessageTime"/>
</style>
</object>
<packing>
<property name="left_attach">3</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</template>
</interface>
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="3.16"/>
<template class="LineSeparatorWidget" parent="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="margin_top">5</property>
<property name="orientation">vertical</property>
<property name="column_spacing">5</property>
<signal name="destroy" handler="on_MyWidget_destroy" swapped="no"/>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<style>
<class name="LineSeparator"/>
</style>
</template>
</interface>
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="3.16"/>
<template class="MessageWidget" parent="GtkGrid">
<property name="name">MessageGrid</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">start</property>
<property name="margin_left">10</property>
<property name="margin_right">15</property>
<property name="orientation">vertical</property>
<property name="column_spacing">5</property>
<signal name="destroy" handler="on_MyWidget_destroy" swapped="no"/>
<child>
<object class="GtkBox">
<property name="width_request">16</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="valign">start</property>
<child>
<object class="GtkImage" id="received_icon">
<property name="name">ReceivedIcon</property>
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="tooltip_text" translatable="yes">The Message has been received</property>
<property name="icon_name">emblem-ok-symbolic</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkImage" id="message_corrected_icon">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="halign">start</property>
<property name="valign">start</property>
<property name="icon_name">document-edit-symbolic</property>
<property name="icon_size">1</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkImage" id="encryption_icon">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="halign">start</property>
<property name="valign">start</property>
<property name="icon_size">1</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="time_label">
<property name="name">MessageTime</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="valign">start</property>
<property name="label" translatable="yes">label</property>
<style>
<class name="MessageTime"/>
</style>
</object>
<packing>
<property name="left_attach">3</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</template>
</interface>
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="3.16"/>
<template class="MessageHeaderWidget" parent="GtkGrid">
<property name="name">MessageGrid</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="margin_top">5</property>
<property name="margin_bottom">2</property>
<property name="orientation">vertical</property>
<property name="column_spacing">5</property>
<signal name="destroy" handler="on_MyWidget_destroy" swapped="no"/>
<child>
<object class="GtkLabel" id="nick_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="halign">start</property>
<property name="valign">center</property>
<property name="label" translatable="yes">label</property>
<property name="use_markup">True</property>
<property name="single_line_mode">True</property>
<style>
<class name="MessageHeaderLabel"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
</template>
</interface>
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="3.16"/>
<template class="StatusWidget" parent="GtkGrid">
<property name="name">MessageGrid</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">10</property>
<property name="margin_right">15</property>
<property name="margin_top">5</property>
<property name="orientation">vertical</property>
<property name="column_spacing">5</property>
<signal name="destroy" handler="on_MyWidget_destroy" swapped="no"/>
<child>
<object class="GtkLabel" id="time_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="valign">start</property>
<property name="label" translatable="yes">label</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkImage" id="icon">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="halign">start</property>
<property name="valign">start</property>
<property name="stock">gtk-missing-image</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</template>
</interface>
......@@ -4,4 +4,42 @@
#ChatControl-AuthenticationButton { padding-top: 0px; padding-bottom: 0px}
/* VCardWindow */
.VCard-GtkLinkButton { padding-left: 5px; border-left: none; }
\ No newline at end of file
.VCard-GtkLinkButton { padding-left: 5px; border-left: none; }
/*MessageListBox Styles*/
.MessageHeaderLabel { font-size: 10pt; font-weight: bold;}
.MessageTime { color: grey;}
list#MessageListBox row:selected textview text {
background-color: @theme_selected_bg_color;
color: @theme_selected_fg_color;
}
list#MessageListBox row:selected #MessageTime {
color: @theme_selected_fg_color;
}
#MessageTextView text selection { background-color: #8000FF; }
row { transition: none; }
row:backdrop { transition: none; }
row.activatable:selected:hover { background-color: @theme_selected_bg_color; }
row.activatable:hover { background-color: @theme_base_color; }
row.activatable:selected:active { box-shadow: none; }
row.activatable:active { box-shadow: none; }
list row { padding: 1px; outline-style: none; }
.theme_gc_nickname_color_0 { color: #4e9a06; }
.theme_gc_nickname_color_1 { color: #f57900; }
.theme_gc_nickname_color_2 { color: #ce5c00; }
.theme_gc_nickname_color_3 { color: #3465a4; }
.theme_gc_nickname_color_4 { color: #204a87; }
.theme_gc_nickname_color_5 { color: #75507b; }
.theme_gc_nickname_color_6 { color: #c17d11; }
.theme_gc_nickname_color_7 { color: #8f5902; }
.theme_gc_nickname_color_8 { color: #ef2929; }
.theme_gc_nickname_color_9 { color: #cc0000; }
.theme_gc_nickname_color_10 { color: #a40000; }
......@@ -47,7 +47,6 @@
from common import i18n
from common.stanza_session import EncryptedStanzaSession, ArchivingStanzaSession
from common.contacts import GC_Contact
from common.logger import KindConstant
from nbxmpp.protocol import NS_XHTML, NS_XHTML_IM, NS_FILE, NS_MUC
from nbxmpp.protocol import NS_ESESSION
from nbxmpp.protocol import NS_JINGLE_RTP_AUDIO, NS_JINGLE_RTP_VIDEO
......@@ -94,6 +93,9 @@ def __init__(self, parent_win, contact, acct, session, resource=None):
self.last_recv_message_marks = None
self.last_message_timestamp = None
id_ = self.conv_scrolledwindow.connect('scroll-event', self._on_mouse_scroll)
self.handlers[id_] = self.conv_scrolledwindow
# for muc use:
# widget = self.xml.get_object('muc_window_actions_button')
self.actions_button = self.xml.get_object('message_window_actions_button')
......@@ -884,7 +886,6 @@ def send_message(self, message, keyID='', chatstate=None, xhtml=None,
"""
Send a message to contact
"""
if self.encryption:
self.sendmessage = True
gajim.plugin_manager.gui_extension_point(
......@@ -932,7 +933,7 @@ def _on_sent(obj, msg_stanza, message, encrypted, xhtml, label):
encrypted=encrypted, xep0184_id=xep0184_id, xhtml=xhtml,
displaymarking=displaymarking, msg_stanza_id=id_,
correct_id=obj.correct_id,
additional_data=obj.additional_data)
additional_data=obj.additional_data, obj=obj)
ChatControlBase.send_message(self, message, keyID, type_='chat',
chatstate=chatstate_to_send, xhtml=xhtml, callback=_on_sent,
......@@ -993,7 +994,7 @@ def get_our_nick(self):
def print_conversation(self, text, frm='', tim=None, encrypted=False,
subject=None, xhtml=None, simple=False, xep0184_id=None,
displaymarking=None, msg_log_id=None, correct_id=None,
msg_stanza_id=None, additional_data=None):
msg_stanza_id=None, additional_data=None, obj=None):
"""
Print a line in the conversation
......@@ -1048,7 +1049,7 @@ def print_conversation(self, text, frm='', tim=None, encrypted=False,
subject=subject, old_kind=self.old_msg_kind, xhtml=xhtml,
simple=simple, xep0184_id=xep0184_id, displaymarking=displaymarking,
msg_log_id=msg_log_id, msg_stanza_id=msg_stanza_id,
correct_id=correct_id, additional_data=additional_data)
correct_id=correct_id, additional_data=additional_data, obj=obj)
if text.startswith('/me ') or text.startswith('/me\n'):
self.old_msg_kind = None
else:
......@@ -1251,7 +1252,8 @@ def shutdown(self):
if self.handlers[i].handler_is_connected(i):
self.handlers[i].disconnect(i)
del self.handlers[i]
self.conv_textview.del_handlers()
#TODO
# self.conv_textview.del_handlers()
if gajim.config.get('use_speller') and HAS_GTK_SPELL:
spell_obj = gtkspell.get_from_text_view(self.msg_textview)
if spell_obj:
......@@ -1409,70 +1411,34 @@ def _on_message_tv_buffer_changed(self, textbuffer):
gajim.connections[self.account].archiving_136_supported:
self.begin_archiving_negotiation()
def restore_conversation(self):
def _on_mouse_scroll(self, widget, event):
if event.direction == Gdk.ScrollDirection.UP:
print('on mouse scroll')
vadjust = self.conv_scrolledwindow.get_vadjustment()
if vadjust.get_value():
return False
self.was_at_the_end = False
time_offset = self.message_view.get_oldest_message()
self.restore_conversation(time_offset)
return False
def restore_conversation(self, time_offset=None):
jid = self.contact.jid
# don't restore lines if it's a transport
if gajim.jid_is_transport(jid):
return
# How many lines to restore and when to time them out
restore_how_many = gajim.config.get('restore_lines')
if restore_how_many <= 0:
try:
rows = gajim.logger.get_conversation_lines(
jid, 5, time_offset, self.account)
except exceptions.DatabaseMalformed:
import common.logger
dialogs.ErrorDialog(
_('Database Error'),
_('The database file (%s) cannot be read. Try to repair it or '
'remove it (all history will be lost).')
% common.logger.LOG_DB_PATH)
return
timeout = gajim.config.get('restore_timeout') # in minutes
# number of messages that are in queue and are already logged, we want
# to avoid duplication
pending_how_many = len(gajim.events.get_events(self.account, jid,
['chat', 'pm']))
if self.resource:
pending_how_many += len(gajim.events.get_events(self.account,
self.contact.get_full_jid(), ['chat', 'pm']))
rows = gajim.logger.get_last_conversation_lines(jid, restore_how_many,
pending_how_many, timeout, self.account)
local_old_kind = None
self.conv_textview.just_cleared = True
for row in rows: # row[0] time, row[1] has kind, row[2] the message, row[3] subject, row[4] additional_data
msg = row[2]
additional_data = row[4]
if not msg: # message is empty, we don't print it
continue
if row[1] in (KindConstant.CHAT_MSG_SENT,
KindConstant.SINGLE_MSG_SENT):
kind = 'outgoing'
name = self.get_our_nick()
elif row[1] in (KindConstant.SINGLE_MSG_RECV,
KindConstant.CHAT_MSG_RECV):
kind = 'incoming'
name = self.contact.get_shown_name()
elif row[1] == KindConstant.ERROR:
kind = 'status'
name = self.contact.get_shown_name()
tim = float(row[0])
if gajim.config.get('restored_messages_small'):
small_attr = ['small']
else:
small_attr = []
xhtml = None
if msg.startswith('<body '):
xhtml = msg
if row[3]:
msg = _('Subject: %(subject)s\n%(message)s') % \
{'subject': row[3], 'message': msg}
ChatControlBase.print_conversation_line(self, msg, kind, name,
tim, small_attr, small_attr + ['restored_message'],
small_attr + ['restored_message'], False,
old_kind=local_old_kind, xhtml=xhtml, additional_data=additional_data)
if row[2].startswith('/me ') or row[2].startswith('/me\n'):
local_old_kind = None
else:
local_old_kind = kind
if len(rows):
self.conv_textview.print_empty_line()
for row in rows:
self.message_view.add_from_db(row)
def read_queue(self):
"""
......@@ -1489,16 +1455,6 @@ def read_queue(self):
for event in events:
if event.type_ != self.type_id:
continue
if event.kind == 'error':
kind = 'info'
else:
kind = 'print_queue'
if event.sent_forwarded:
kind = 'out'
self.print_conversation(event.message, kind, tim=event.time,
encrypted=event.encrypted, subject=event.subject,
xhtml=event.xhtml, displaymarking=event.displaymarking,
correct_id=event.correct_id)
if isinstance(event.msg_log_id, int):
message_ids.append(event.msg_log_id)
......
......@@ -46,6 +46,7 @@
from common import gajim
from common import helpers
from common import ged
from message_listbox import MessageListBox
from message_control import MessageControl
from conversation_textview import ConversationTextview
from message_textview import MessageTextView
......@@ -294,29 +295,30 @@ def __init__(self, type_id, parent_win, widget_name, contact, acct,
Gtk.DestDefaults.HIGHLIGHT | Gtk.DestDefaults.DROP,
self.dnd_list, Gdk.DragAction.COPY)
self.message_view = MessageListBox(self)
# Create textviews and connect signals
self.conv_textview = ConversationTextview(self.account)
id_ = self.conv_textview.connect('quote', self.on_quote)
self.handlers[id_] = self.conv_textview.tv
id_ = self.conv_textview.tv.connect('key_press_event',
self._conv_textview_key_press_event)
self.handlers[id_] = self.conv_textview.tv
# id_ = self.conv_textview.connect('quote', self.on_quote)
# self.handlers[id_] = self.conv_textview.tv
# FIXME: DND on non editable TextView, find a better way
self.drag_entered = False
id_ = self.conv_textview.tv.connect('drag_data_received',
id_ = self.message_view.connect('drag_data_received',
self._on_drag_data_received)
self.handlers[id_] = self.conv_textview.tv
id_ = self.conv_textview.tv.connect('drag_motion', self._on_drag_motion)
self.handlers[id_] = self.conv_textview.tv
id_ = self.conv_textview.tv.connect('drag_leave', self._on_drag_leave)
self.handlers[id_] = self.conv_textview.tv
self.conv_textview.tv.drag_dest_set(Gtk.DestDefaults.MOTION |
self.handlers[id_] = self.message_view
id_ = self.message_view.connect('drag_motion', self._on_drag_motion)
self.handlers[id_] = self.message_view
id_ = self.message_view.connect('drag_leave', self._on_drag_leave)
self.handlers[id_] = self.message_view
self.message_view.drag_dest_set(Gtk.DestDefaults.MOTION |
Gtk.DestDefaults.HIGHLIGHT | Gtk.DestDefaults.DROP,
self.dnd_list, Gdk.DragAction.COPY)
self.conv_scrolledwindow = self.xml.get_object(
'conversation_scrolledwindow')
self.conv_scrolledwindow.add(self.conv_textview.tv)
self.conv_scrolledwindow.add(self.message_view)
widget = self.conv_scrolledwindow.get_vadjustment()
id_ = widget.connect('value-changed',
self.on_conversation_vadjustment_value_changed)
......@@ -374,7 +376,7 @@ def __init__(self, type_id, parent_win, widget_name, contact, acct,
# Attach speller
if gajim.config.get('use_speller') and HAS_GTK_SPELL:
self.set_speller()
self.conv_textview.tv.show()
self.message_view.show()
# For XEP-0172
self.user_nick = None
......@@ -563,18 +565,6 @@ def _on_send_button_clicked(self, widget):
# send the message
self.send_message(message, xhtml=xhtml)
def _conv_textview_key_press_event(self, widget, event):
# translate any layout to latin_layout
valid, entries = self.keymap.get_entries_for_keyval(event.keyval)
keycode = entries[0].keycode
if (event.get_state() & Gdk.ModifierType.CONTROL_MASK and keycode in (
self.keycode_c, self.keycode_ins)) or (
event.get_state() & Gdk.ModifierType.SHIFT_MASK and \
event.keyval in (Gdk.KEY_Page_Down, Gdk.KEY_Page_Up)):
return False
self.parent_win.notebook.event(event)
return True
def show_emoticons_menu(self):
if not gajim.config.get('emoticons_theme'):
return
......@@ -609,7 +599,7 @@ def _on_message_textview_key_press_event(self, widget, event):
# SHIFT + PAGE_[UP|DOWN]: send to conv_textview
elif event.keyval == Gdk.KEY_Page_Down or \
event.keyval == Gdk.KEY_Page_Up:
self.conv_textview.tv.event(event)
# self.conv_textview.tv.event(event)
return True
elif event.get_state() & Gdk.ModifierType.CONTROL_MASK:
if event.keyval == Gdk.KEY_Tab: # CTRL + TAB
......@@ -863,6 +853,7 @@ def _on_message_tv_buffer_changed(self, textbuffer):
self.send_chatstate('composing',