Commit 55278263 authored by Martin's avatar Martin

Imported upstream master from 2017-10-29

parent f386b9ce
......@@ -44,7 +44,7 @@
- [Daily Linux](https://www.gajim.org/downloads/snap/?M=D)
- [Daily Windows](https://gajim.org/downloads/snap/win)
#### Linux / Mac
#### Linux
``./setup.py install --root=/``
......@@ -52,6 +52,10 @@ or
``pip install .`` (python-pip is required)
#### Mac
see [Wiki](https://dev.gajim.org/gajim/gajim/wikis/help/gajimmacosx#python3brew)
#### Developing
For developing you dont have to install Gajim.
......
This diff is collapsed.
This diff is collapsed.
......@@ -41,12 +41,6 @@ class StandardCommonCommands(CommandContainer):
AUTOMATIC = True
HOSTS = ChatCommands, PrivateChatCommands, GroupChatCommands
@command
@doc(_("Hide the chat buttons"))
def compact(self):
new_status = not self.hide_chat_buttons
self.chat_buttons_set_visible(new_status)
@command(overlap=True)
@doc(_("Show help on a given command or a list of available commands if -a is given"))
def help(self, command=None, all=False):
......
......@@ -264,7 +264,6 @@ class Config:
'show_roster_on_startup':[opt_str, 'always', _('Show roster on startup.\n\'always\' - Always show roster.\n\'never\' - Never show roster.\n\'last_state\' - Restore the last state roster.')],
'show_avatar_in_chat': [opt_bool, True, _('If False, you will no longer see the avatar in the chat window.')],
'escape_key_closes': [opt_bool, True, _('If True, pressing the escape key closes a tab/window.')],
'compact_view': [opt_bool, False, _('Hides the buttons in chat windows.')],
'hide_groupchat_banner': [opt_bool, False, _('Hides the banner in a group chat window')],
'hide_chat_banner': [opt_bool, False, _('Hides the banner in two persons chat window')],
'hide_groupchat_occupants_list': [opt_bool, False, _('Hides the group chat occupants list in group chat window.')],
......
......@@ -2342,6 +2342,13 @@ class Connection(CommonConnection, ConnectionHandlers):
if rule['type'] == 'group':
roster.draw_group(rule['value'], self.name)
def bookmarks_available(self):
if self.private_storage_supported:
return True
if self.pubsub_publish_options_supported:
return True
return False
def _request_bookmarks_xml(self):
if not app.account_is_connected(self.name):
return
......
......@@ -30,8 +30,8 @@ class OptionType(IntEnum):
class AvatarSize(IntEnum):
ROSTER = 32
CHAT = 48
NOTIFICATION = 48
CHAT = 52
PROFILE = 64
TOOLTIP = 125
VCARD = 200
......
......@@ -290,13 +290,17 @@ class Logger:
:param jid: The JID
:param type_: The JIDConstant type
:param kind: The KindConstant
:param type_: The JIDConstant
return the jid id
"""
if kind in (KindConstant.GC_MSG, KindConstant.GCSTATUS):
type_ = JIDConstant.ROOM_TYPE
elif kind is not None:
type_ = JIDConstant.NORMAL_TYPE
result = self._jid_ids.get(jid, None)
if result is not None:
......
......@@ -50,12 +50,7 @@ from gajim import message_control
from gajim.chat_control_base import ChatControlBase
from gajim import dataforms_widget
from gajim import gui_menu_builder
try:
from gajim import gtkspell
HAS_GTK_SPELL = True
except (ImportError, ValueError):
HAS_GTK_SPELL = False
from gajim import gtkspell
from gajim.common import helpers
from gajim.common import app
......@@ -187,16 +182,12 @@ class PreferencesWindow:
else:
show_roster_combobox.set_active(0)
# Compact View
st = app.config.get('compact_view')
self.xml.get_object('compact_view_checkbutton').set_active(st)
# Ignore XHTML
st = app.config.get('ignore_incoming_xhtml')
self.xml.get_object('xhtml_checkbutton').set_active(st)
# use speller
if HAS_GTK_SPELL:
if gtkspell.HAS_GTK_SPELL:
st = app.config.get('use_speller')
self.xml.get_object('speller_checkbutton').set_active(st)
else:
......@@ -657,12 +648,6 @@ class PreferencesWindow:
config_type = c_config.opt_show_roster_on_startup[active]
app.config.set('show_roster_on_startup', config_type)
def on_compact_view_checkbutton_toggled(self, widget):
active = widget.get_active()
for ctrl in self._get_all_controls():
ctrl.chat_buttons_set_visible(active)
app.config.set('compact_view', active)
def on_xhtml_checkbutton_toggled(self, widget):
self.on_checkbutton_toggled(widget, 'ignore_incoming_xhtml')
helpers.update_optional_features()
......@@ -670,23 +655,13 @@ class PreferencesWindow:
def apply_speller(self):
for ctrl in self._get_all_controls():
if isinstance(ctrl, ChatControlBase):
try:
spell_obj = gtkspell.get_from_text_view(ctrl.msg_textview)
except (TypeError, RuntimeError, OSError):
spell_obj = None
if not spell_obj:
ctrl.set_speller()
ctrl.set_speller()
def remove_speller(self):
for ctrl in self._get_all_controls():
if isinstance(ctrl, ChatControlBase):
try:
spell_obj = gtkspell.get_from_text_view(ctrl.msg_textview)
except (TypeError, RuntimeError):
spell_obj = None
if spell_obj:
spell_obj.detach()
if ctrl.spell is not None:
ctrl.remove_speller()
def on_speller_checkbutton_toggled(self, widget):
active = widget.get_active()
......@@ -695,15 +670,10 @@ class PreferencesWindow:
lang = app.config.get('speller_language')
if not lang:
lang = app.LANG
tv = Gtk.TextView()
try:
gtkspell.Spell(tv, lang)
except (TypeError, RuntimeError, OSError):
dialogs.ErrorDialog(
_('Dictionary for lang %s not available') % lang,
_('You have to install %s dictionary to use spellchecking, or '
'choose another language by setting the speller_language option.'
) % lang)
available = gtkspell.test_language(lang)
if not available:
dialogs.AspellDictError(lang)
app.config.set('use_speller', False)
widget.set_active(False)
else:
......
This diff is collapsed.
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.18.3 -->
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="3.12"/>
<object class="GtkEventBox" id="chat_tab_ebox">
......@@ -28,10 +28,10 @@
<property name="width_request">70</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="use_markup">True</property>
<property name="ellipsize">end</property>
<property name="max_width_chars">9</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="expand">False</property>
......@@ -64,15 +64,35 @@
</object>
</child>
</object>
<object class="GtkHeaderBar" id="headerbar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="show_close_button">True</property>
<child>
<object class="GtkMenuButton" id="header_menu">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">open-menu-symbolic</property>
</object>
</child>
</object>
</child>
</object>
<object class="GtkApplicationWindow" id="message_window">
<property name="name">MessageWindow</property>
<property name="can_focus">False</property>
<property name="default_width">480</property>
<property name="default_height">440</property>
<property name="show_menubar">False</property>
<child>
<object class="GtkNotebook" id="notebook">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="margin_top">2</property>
<property name="scrollable">True</property>
</object>
</child>
......
......@@ -426,23 +426,6 @@
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="compact_view_checkbutton">
<property name="label" translatable="yes">Ma_ke message windows compact</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Hide all buttons in chat windows</property>
<property name="use_underline">True</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_compact_view_checkbutton_toggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="xhtml_checkbutton">
<property name="label" translatable="yes">_Ignore rich content in incoming messages</property>
......@@ -457,7 +440,7 @@
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">4</property>
<property name="top_attach">3</property>
<property name="width">2</property>
</packing>
</child>
......@@ -474,7 +457,7 @@
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">5</property>
<property name="top_attach">4</property>
<property name="width">2</property>
</packing>
</child>
......@@ -490,7 +473,7 @@
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">6</property>
<property name="top_attach">5</property>
<property name="width">2</property>
</packing>
</child>
......@@ -555,7 +538,7 @@
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">7</property>
<property name="top_attach">6</property>
<property name="width">2</property>
</packing>
</child>
......
......@@ -4,6 +4,7 @@
<requires lib="gtk+" version="3.12"/>
<object class="GtkAccelGroup" id="accelgroup1"/>
<object class="GtkApplicationWindow" id="roster_window">
<property name="name">RosterWindow</property>
<property name="width_request">85</property>
<property name="height_request">200</property>
<property name="can_focus">False</property>
......@@ -115,4 +116,28 @@
</object>
</child>
</object>
<object class="GtkHeaderBar" id="headerbar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="title">Gajim</property>
<property name="show_close_button">True</property>
<child>
<object class="GtkMenuButton" id="header_menu">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="no_show_all">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">open-menu-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="pack_type">end</property>
</packing>
</child>
</object>
</interface>
/* Gajim Application CSS File */
.msgtextview-button {
.chatcontrol-actionbar-button {
padding: 0px 5px 0px 5px;
background-color: @theme_base_color;
border: 1px solid;
border-radius: 0px;
border-color: @borders;
border: none;
}
.msgtextview-button:hover, .msgtextview-button:checked {
color: @theme_base_color;
border-color: @borders;
text-shadow: none;
-gtk-icon-shadow: none;
box-shadow: none;
.scrolled-no-border {border: none}
.scrolled-no-border undershoot.top, undershoot.bottom { background-image: none; }
.actionbar-no-border box {border: none}
.actionbar-no-border button {
padding: 0px;
background-color: @theme_base_color;
border: none;
background-image: none;
}
#MessageWindow, #RosterWindow paned { background-color: @theme_base_color; }
.scrolledtextview { border:none; }
.msgtextview-button.left { border-right: none; }
.msgtextview-button.right { border-left: none; }
.chatcontrol-separator {margin-bottom: 6px;}
.scrolledtextview { border-left:none; }
.scrolledtextview.authentication { border-right:none; }
#SubjectPopover box { padding: 10px; }
/* VCardWindow */
.VCard-GtkLinkButton { padding-left: 5px; border-left: none; }
......
......@@ -42,6 +42,7 @@ from gajim import gtkgui_helpers
from gajim import vcard
from gajim import conversation_textview
from gajim import dataforms_widget
from gajim import gtkspell
from random import randrange
from gajim.common import pep
......@@ -50,12 +51,6 @@ from gajim.common import const
from gajim.options_dialog import OptionsDialog
from gajim.common.const import Option, OptionKind, OptionType
try:
from gajim import gtkspell
HAS_GTK_SPELL = True
except (ImportError, ValueError):
HAS_GTK_SPELL = False
# those imports are not used in this file, but in files that 'import dialogs'
# so they can do dialog.GajimThemesWindow() for example
from gajim.filetransfers_window import FileTransfersWindow
......@@ -1486,11 +1481,11 @@ class FileChooserDialog(Gtk.FileChooserDialog):
class AspellDictError:
def __init__(self, lang):
ErrorDialog(
_('Dictionary for lang %s not available') % lang,
_('You have to install %s dictionary to use spellchecking, or '
'choose another language by setting the speller_language option.'
'\n\nHighlighting misspelled words feature will not be used') % lang)
app.config.set('use_speller', False)
_('Dictionary for lang "%s" not available') % lang,
_('You have to install the dictionary "%s" to use spellchecking, '
'or choose another language by setting the speller_language '
'option.\n\n'
'Highlighting misspelled words feature will not be used') % lang)
class ConfirmationDialog(HigDialog):
"""
......@@ -3082,14 +3077,14 @@ class SingleMessageWindow:
else:
self.to_entry.set_text(to)
if app.config.get('use_speller') and HAS_GTK_SPELL and action == 'send':
if app.config.get('use_speller') and gtkspell.HAS_GTK_SPELL and action == 'send':
try:
lang = app.config.get('speller_language')
if not lang:
lang = app.LANG
gtkspell.Spell(self.conversation_textview.tv, lang)
gtkspell.Spell(self.message_textview, lang)
except (GObject.GError, TypeError, RuntimeError, OSError):
self.spell = gtkspell.Spell(self.message_textview, lang)
self.spell.attach(self.message_textview)
except OSError:
AspellDictError(lang)
self.prepare_widgets_for(self.action)
......
......@@ -289,6 +289,7 @@ class EmoticonPopover(Gtk.Popover):
self.append_emoticon(child.get_child().get_text())
def append_emoticon(self, pix):
self.text_widget.remove_placeholder()
buffer_ = self.text_widget.get_buffer()
if buffer_.get_char_count():
buffer_.insert_at_cursor(' ')
......
......@@ -212,8 +212,15 @@ class GajimApplication(Gtk.Application):
builder = Gtk.Builder()
builder.set_translation_domain(i18n.APP)
builder.add_from_file(path)
self.set_menubar(builder.get_object("menubar"))
self.set_app_menu(builder.get_object("appmenu"))
menubar = builder.get_object("menubar")
appmenu = builder.get_object("appmenu")
if os.name != 'nt':
self.set_app_menu(appmenu)
else:
# Dont set Application Menu for Windows
# Add it to the menubar instead
menubar.prepend_submenu('Gajim', appmenu)
self.set_menubar(menubar)
def do_activate(self):
Gtk.Application.do_activate(self)
......
This diff is collapsed.
......@@ -57,6 +57,7 @@ class Color:
BLACK = Gdk.RGBA(red=0, green=0, blue=0, alpha=1)
GREEN = Gdk.RGBA(red=115/255, green=210/255, blue=22/255, alpha=1)
RED = Gdk.RGBA(red=204/255, green=0, blue=0, alpha=1)
GREY = Gdk.RGBA(red=195/255, green=195/255, blue=192/255, alpha=1)
def get_icon_pixmap(icon_name, size=16, color=None, quiet=False):
try:
......
......@@ -19,9 +19,15 @@
from gi.repository import GObject
from gi.repository import Gtk
from gi.repository import GLib
import gi
gi.require_version('GtkSpell', '3.0')
from gi.repository import GtkSpell
try:
gi.require_version('GtkSpell', '3.0')
from gi.repository import GtkSpell
HAS_GTK_SPELL = True
except (ImportError, ValueError):
HAS_GTK_SPELL = False
def ensure_attached(func):
def f(self, *args, **kwargs):
......@@ -47,12 +53,13 @@ class Spell(GObject.GObject):
if spell:
raise RuntimeError("Textview has already a Spell obj attached")
self.spell = GtkSpell.Checker.new()
if not self.spell:
raise OSError("Unable to create spell object.")
if not self.spell.attach(textview):
raise OSError("Unable to attach spell object.")
if not self.spell.set_language(language):
raise OSError("Unable to set language: '%s'" % language)
try:
self.spell.set_language(language)
except GLib.GError as error:
if error.domain == 'gtkspell-error-quark':
raise OSError("Unable to set language: '%s'" % language)
self.spell.connect('language-changed', self.on_language_changed)
else:
......@@ -73,12 +80,25 @@ class Spell(GObject.GObject):
def recheck_all(self):
self.spell.recheck_all()
@ensure_attached
def detach(self):
self.spell.detach()
self.spell = None
if self.spell is not None:
self.spell.detach()
def attach(self, textview):
spell = GtkSpell.Checker.get_from_text_view(textview)
if spell is None:
print('attached')
self.spell.attach(textview)
GObject.type_register(Spell)
def get_from_text_view(textview):
return Spell(textview, create=False)
def test_language(lang):
spell = GtkSpell.Checker.new()
try:
spell.set_language(lang)
except GLib.GError as error:
if error.domain == 'gtkspell-error-quark':
return False
return True
......@@ -2858,18 +2858,6 @@ class Interface:
# get transports type from DB
app.transport_type = app.logger.get_transports_type()
# test is dictionnary is present for speller
if app.config.get('use_speller'):
lang = app.config.get('speller_language')
if not lang:
lang = app.LANG
tv = Gtk.TextView()
try:
from gajim import gtkspell
spell = gtkspell.Spell(tv, lang)
except (ImportError, TypeError, RuntimeError, OSError, ValueError):
dialogs.AspellDictError(lang)
if app.config.get('soundplayer') == '':
# only on first time Gajim starts
commands = ('paplay', 'aplay', 'play', 'ossplay')
......
......@@ -606,6 +606,65 @@ Build dynamic Application Menus
'''
def get_singlechat_menu(control_id):
singlechat_menu = [
('win.send-file-', _('Send File...')),
('win.invite-contacts-', _('Invite Contacts')),
('win.add-to-roster-', _('Add to Roster')),
('win.toggle-audio-', _('Audio Session')),
('win.toggle-video-', _('Video Session')),
('win.information-', _('Information')),
('win.browse-history-', _('History')),
]
def build_menu(preset):
menu = Gio.Menu()
for item in preset:
action_name, label = item
if action_name == 'win.browse-history-':
menu.append(label, action_name + control_id + '::none')
else:
menu.append(label, action_name + control_id)
return menu
return build_menu(singlechat_menu)
def get_groupchat_menu(control_id):
groupchat_menu = [
(_('Manage Room'), [
('win.change-subject-', _('Change Subject')),
('win.configure-', _('Configure Room')),
('win.destroy-', _('Destroy Room')),
]),
('win.change-nick-', _('Change Nick')),
('win.bookmark-', _('Bookmark Room')),
('win.request-voice-', _('Request Voice')),
('win.notify-on-message-', _('Notify on all messages')),
('win.minimize-', _('Minimize on close')),
('win.browse-history-', _('History')),
('win.disconnect-', _('Disconnect')),
]
def build_menu(preset):
menu = Gio.Menu()
for item in preset:
if isinstance(item[1], str):
action_name, label = item
if action_name == 'win.browse-history-':
menu.append(label, action_name + control_id + '::none')
else:
menu.append(label, action_name + control_id)
else:
label, sub_menu = item
# This is a submenu
submenu = build_menu(sub_menu)
menu.append_submenu(label, submenu)
return menu
return build_menu(groupchat_menu)
def get_bookmarks_menu(account, rebuild=False):
if not app.connections[account].bookmarks:
return None
......@@ -708,7 +767,11 @@ def get_account_menu(account):
def build_accounts_menu():
menubar = app.app.get_menubar()
# Accounts Submenu
acc_menu = menubar.get_item_link(0, 'submenu')
menu_position = 0
if os.name == 'nt':
menu_position = 1
acc_menu = menubar.get_item_link(menu_position, 'submenu')
acc_menu.remove_all()
accounts_list = sorted(app.contacts.get_accounts())
if not accounts_list:
......@@ -721,8 +784,8 @@ def build_accounts_menu():
acc, get_account_menu(acc))
else:
acc_menu = get_account_menu(accounts_list[0])
menubar.remove(0)
menubar.insert_submenu(0, 'Accounts', acc_menu)
menubar.remove(menu_position)
menubar.insert_submenu(menu_position, 'Accounts', acc_menu)
def build_bookmark_menu(account):
......@@ -731,8 +794,12 @@ def build_bookmark_menu(account):
if not bookmark_menu:
return
menu_position = 0
if os.name == 'nt':
menu_position = 1
# Accounts Submenu
acc_menu = menubar.get_item_link(0, 'submenu')
acc_menu = menubar.get_item_link(menu_position, 'submenu')
# We have more than one Account active
if acc_menu.get_item_link(0, 'submenu'):
......
......@@ -57,7 +57,6 @@ class MessageControl(object):
self.widget_name = widget_name
self.contact = contact
self.account = account
self.hide_chat_buttons = False
self.resource = resource
# control_id is a unique id for the control,
# its used as action name for actions that belong to a control
......@@ -175,12 +174,6 @@ class MessageControl(object):
"""
return None
def chat_buttons_set_visible(self, state):
"""
Derived classes MAY implement this
"""
self.hide_chat_buttons = state
def got_connected(self):
pass
......
......@@ -24,7 +24,6 @@
import gc
from gi.repository import Gtk
from gi.repository import GObject
from gi.repository import GLib
from gi.repository import Pango
......@@ -37,6 +36,7 @@ class MessageTextView(Gtk.TextView):
chat/groupchat windows
"""
UNDO_LIMIT = 20
PLACEHOLDER = _('Write a message..')
def __init__(self):
Gtk.TextView.__init__(self)
......@@ -57,13 +57,16 @@ class MessageTextView(Gtk.TextView):
self.undo_list = []
# needed to know if we undid something
self.undo_pressed = False
self.lang = None # Lang used for spell checking
_buffer = self.get_buffer()
self.begin_tags = {}
self.end_tags = {}
self.color_tags = []
self.fonts_tags = []
self.other_tags = {}
self.placeholder_tag = _buffer.create_tag('placeholder')
self.placeholder_tag.set_property('foreground_rgba',
gtkgui_helpers.Color.GREY)
self.other_tags['bold'] = _buffer.create_tag('bold')
self.other_tags['bold'].set_property('weight', Pango.Weight.BOLD)
self.begin_tags['bold'] = '<strong>'
......@@ -82,6 +85,33 @@ class MessageTextView(Gtk.TextView):
self.end_tags['strike'] = '</span>'
self.connect_after('paste-clipboard', self.after_paste_clipboard)