Rework Gajim Theming

- Save all Theme settings to .css instead of the config file
- Add a gajim-dark.css
- Refactor the ThemesWindow
This commit is contained in:
Philipp Hörist 2018-03-01 22:47:01 +01:00 committed by Philipp Hörist
parent ee3cc9cbab
commit 9d5131b8b4
37 changed files with 1458 additions and 2014 deletions

View File

@ -160,9 +160,7 @@ class GajimApplication(Gtk.Application):
self.interface.roster.window.present()
return
from gajim.gui_interface import Interface
from gajim import gtkgui_helpers
self.interface = Interface()
gtkgui_helpers.load_css()
self.interface.run(self)
self.add_actions()
from gajim import gui_menu_builder

View File

@ -674,7 +674,6 @@ class ChatControl(ChatControlBase):
status_reduced = ''
status_escaped = GLib.markup_escape_text(status_reduced)
font_attrs, font_attrs_small = self.get_font_attrs()
st = app.config.get('displayed_chat_state_notifications')
cs = contact.chatstate
if cs and st in ('composing_only', 'all'):
@ -685,22 +684,21 @@ class ChatControl(ChatControlBase):
else:
chatstate = ''
label_text = '<span %s>%s</span><span %s>%s %s</span>' \
% (font_attrs, name, font_attrs_small, acct_info, chatstate)
label_text = '<span>%s</span><span size="x-small" weight="light">%s %s</span>' \
% (name, acct_info, chatstate)
if acct_info:
acct_info = i18n.direction_mark + ' ' + acct_info
label_tooltip = '%s%s %s' % (name, acct_info, chatstate)
else:
# weight="heavy" size="x-large"
label_text = '<span %s>%s</span><span %s>%s</span>' % \
(font_attrs, name, font_attrs_small, acct_info)
label_text = '<span>%s</span><span size="x-small" weight="light">%s</span>' % \
(name, acct_info)
if acct_info:
acct_info = i18n.direction_mark + ' ' + acct_info
label_tooltip = '%s%s' % (name, acct_info)
if status_escaped:
status_text = self.urlfinder.sub(self.make_href, status_escaped)
status_text = '<span %s>%s</span>' % (font_attrs_small, status_text)
status_text = '<span size="x-small" weight="light">%s</span>' % status_text
self.banner_status_label.set_tooltip_text(status)
self.banner_status_label.set_no_show_all(False)
self.banner_status_label.show()
@ -881,7 +879,7 @@ class ChatControl(ChatControlBase):
if self.correcting:
self.correcting = False
gtkgui_helpers.remove_css_class(
self.msg_textview, 'msgcorrectingcolor')
self.msg_textview, 'gajim-msg-correcting')
self.print_conversation(obj.message, self.contact.jid, tim=obj.timestamp,
encrypted=obj.encrypted, xep0184_id=xep0184_id, xhtml=obj.xhtml,

View File

@ -38,6 +38,7 @@ from gi.repository import Gio
from gajim import gtkgui_helpers
from gajim import message_control
from gajim.gtk import NonModalConfirmationDialog
from gajim.gtk.util import convert_rgb_to_hex
from gajim import notify
import re
@ -52,6 +53,7 @@ from gajim.conversation_textview import ConversationTextview
from gajim.message_textview import MessageTextView
from gajim.common.contacts import GC_Contact
from gajim.common.connection_handlers_events import MessageOutgoingEvent
from gajim.common.const import StyleAttr
from gajim.command_system.implementation.middleware import ChatCommandProcessor
from gajim.command_system.implementation.middleware import CommandTools
@ -87,40 +89,13 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
keycode_ins = None
def make_href(self, match):
url_color = app.config.get('urlmsgcolor')
url_color = app.css_config.get_value('.gajim-url', StyleAttr.COLOR)
color = convert_rgb_to_hex(url_color)
url = match.group()
if not '://' in url:
if '://' not in url:
url = 'http://' + url
return '<a href="%s"><span color="%s">%s</span></a>' % (url,
url_color, match.group())
def get_font_attrs(self):
"""
Get pango font attributes for banner from theme settings
"""
theme = app.config.get('roster_theme')
bannerfont = app.config.get_per('themes', theme, 'bannerfont')
bannerfontattrs = app.config.get_per('themes', theme, 'bannerfontattrs')
if bannerfont:
font = Pango.FontDescription(bannerfont)
else:
font = Pango.FontDescription('Normal')
if bannerfontattrs:
# B attribute is set by default
if 'B' in bannerfontattrs:
font.set_weight(Pango.Weight.HEAVY)
if 'I' in bannerfontattrs:
font.set_style(Pango.Style.ITALIC)
font_attrs = 'font_desc="%s"' % font.to_string()
# in case there is no font specified we use x-large font size
if font.get_size() == 0:
font_attrs = '%s size="x-large"' % font_attrs
font.set_weight(Pango.Weight.NORMAL)
font_attrs_small = 'font_desc="%s" size="small"' % font.to_string()
return (font_attrs, font_attrs_small)
return '<a href="%s"><span foreground="%s">%s</span></a>' % (
url, color, match.group())
def get_nb_unread(self):
jid = self.contact.jid
@ -381,6 +356,8 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
self._nec_ping)
app.ged.register_event_handler('sec-label-received', ged.GUI1,
self._sec_labels_received)
app.ged.register_event_handler('style-changed', ged.GUI1,
self._style_changed)
# This is basically a very nasty hack to surpass the inability
# to properly use the super, because of the old code.
@ -557,6 +534,9 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
self._nec_our_status)
app.ged.remove_event_handler('sec-label-received', ged.GUI1,
self._sec_labels_received)
app.ged.remove_event_handler('style-changed', ged.GUI1,
self._style_changed)
def on_msg_textview_populate_popup(self, textview, menu):
"""
@ -1099,6 +1079,9 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
def on_clear_formatting_menuitem_activate(self, widget):
self.msg_textview.clear_tags()
def _style_changed(self, *args):
self.update_tags()
def update_tags(self):
self.conv_textview.update_tags()
@ -1365,14 +1348,14 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
history[pos - 1].startswith('/') or history[pos - 1].startswith('/me')):
self.correcting = True
gtkgui_helpers.add_css_class(
self.msg_textview, 'msgcorrectingcolor')
self.msg_textview, 'gajim-msg-correcting')
message = history[pos - 1]
msg_buf.set_text(message)
return
if self.correcting:
# We were previously correcting
gtkgui_helpers.remove_css_class(
self.msg_textview, 'msgcorrectingcolor')
self.msg_textview, 'gajim-msg-correcting')
self.correcting = False
pos += -1 if direction == 'up' else +1
if pos == -1:

View File

@ -43,6 +43,7 @@ from gajim.common import configpaths
from gajim.common import ged as ged_module
from gajim.common.contacts import LegacyContactsAPI
from gajim.common.events import Events
from gajim.common.css_config import CSSConfig
interface = None # The actual interface (the gtk one for the moment)
thread_interface = lambda *args: None # Interface to run a thread and then a callback
@ -66,6 +67,8 @@ gajimpaths = configpaths.gajimpaths
RecentGroupchat = namedtuple('RecentGroupchat', ['room', 'server', 'nickname'])
css_config = None
os_info = None # used to cache os information
transport_type = {} # list the type of transport
@ -625,3 +628,7 @@ def get_app_window(cls, account=None):
continue
return win
return None
def load_css_config():
global css_config
css_config = CSSConfig()

View File

@ -92,13 +92,6 @@ class Config:
'mood_iconset': [ opt_str, DEFAULT_MOOD_ICONSET, '', True ],
'activity_iconset': [ opt_str, DEFAULT_ACTIVITY_ICONSET, '', True ],
'use_transports_iconsets': [ opt_bool, True, '', True ],
'inmsgcolor': [ opt_color, '#a40000', _('Incoming nickname color.'), True ],
'outmsgcolor': [ opt_color, '#3465a4', _('Outgoing nickname color.'), True ],
'inmsgtxtcolor': [ opt_color, '', _('Incoming text color.'), True ],
'outmsgtxtcolor': [ opt_color, '#555753', _('Outgoing text color.'), True ],
'statusmsgcolor': [ opt_color, '#4e9a06', _('Status message text color.'), True ],
'markedmsgcolor': [ opt_color, '#ff8080', '', True ],
'urlmsgcolor': [ opt_color, '#204a87', '', True ],
'notif_signin_color': [ opt_color, '#32CD32', _('Contact signed in notification color.') ], # limegreen
'notif_signout_color': [ opt_color, '#FF0000', _('Contact signout notification color') ], # red
'notif_message_color': [ opt_color, '#1E90FF', _('New message notification color.') ], # dodgerblue
@ -108,11 +101,6 @@ class Config:
'notif_invite_color': [ opt_color, '#D2B48C', _('Groupchat invitation notification color') ], # tan1
'notif_status_color': [ opt_color, '#D8BFD8', _('Background color of status changed notification') ], # thistle2
'notif_other_color': [ opt_color, '#FFFFFF', _('Other dialogs color.') ], # white
'inmsgfont': [ opt_str, '', _('Incoming nickname font.'), True ],
'outmsgfont': [ opt_str, '', _('Outgoing nickname font.'), True ],
'inmsgtxtfont': [ opt_str, '', _('Incoming text font.'), True ],
'outmsgtxtfont': [ opt_str, '', _('Outgoing text font.'), True ],
'statusmsgfont': [ opt_str, '', _('Status message text font.'), True ],
'collapsed_rows': [ opt_str, '', _('List (space separated) of rows (accounts and groups) that are collapsed.'), True ],
'roster_theme': [ opt_str, _('default'), '', True ],
'mergeaccounts': [ opt_bool, False, '', True ],
@ -205,7 +193,6 @@ class Config:
'notify_on_file_complete': [opt_bool, True],
'file_transfers_port': [opt_int, 28011],
'ft_add_hosts_to_send': [opt_str, '', _('Comma separated list of sent hosts, in addition of local interfaces, for File Transfer in case of address translation/port forwarding.')],
'conversation_font': [opt_str, ''],
'use_kib_mib': [opt_bool, False, _('IEC standard says KiB = 1024 bytes, KB = 1000 bytes.')],
'notify_on_all_muc_messages': [opt_bool, False],
'trayicon_notification_on_events': [opt_bool, True, _('Notify of events in the notification area.')],
@ -249,9 +236,6 @@ class Config:
'print_status_in_muc': [opt_str, 'none', _('Can be "none", "all" or "in_and_out". If "none", Gajim will no longer print status line in groupchats when a member changes their status and/or their status message. If "all" Gajim will print all status messages. If "in_and_out", Gajim will only print FOO enters/leaves group chat.')],
'log_contact_status_changes': [opt_bool, False],
'log_xhtml_messages': [opt_bool, False, _('Log XHTML messages instead of plain text messages.')],
'just_connected_bg_color': [opt_str, '#adc3c6', _('Background color of contacts when they just signed in.')],
'just_disconnected_bg_color': [opt_str, '#ab6161', _('Background color of contacts when they just signed out.')],
'restored_messages_color': [opt_color, '#555753'],
'restored_messages_small': [opt_bool, True, _('If true, restored messages will use a smaller font than the default one.')],
'hide_avatar_of_transport': [opt_bool, False, _('Don\'t show avatar for the transport itself.')],
'roster_window_skip_taskbar': [opt_bool, False, _('Don\'t show roster in the system taskbar.')],
@ -438,35 +422,7 @@ class Config:
'bosh_http_pipelining': [ opt_bool, False ],
'bosh_wait_for_restart_response': [ opt_bool, False ],
}, {}),
'themes': ({
'accounttextcolor': [ opt_color, 'black', '', True ],
'accountbgcolor': [ opt_color, 'white', '', True ],
'accountfont': [ opt_str, '', '', True ],
'accountfontattrs': [ opt_str, 'B', '', True ],
'grouptextcolor': [ opt_color, 'black', '', True ],
'groupbgcolor': [ opt_color, 'white', '', True ],
'groupfont': [ opt_str, '', '', True ],
'groupfontattrs': [ opt_str, 'I', '', True ],
'contacttextcolor': [ opt_color, 'black', '', True ],
'contactbgcolor': [ opt_color, 'white', '', True ],
'contactfont': [ opt_str, '', '', True ],
'contactfontattrs': [ opt_str, '', '', True ],
'bannertextcolor': [ opt_color, 'black', '', True ],
'bannerbgcolor': [ opt_color, '', '', True ],
'bannerfont': [ opt_str, '', '', True ],
'bannerfontattrs': [ opt_str, 'B', '', True ],
'msgcorrectingcolor': [opt_color, '#eee8aa'],
# http://www.pitt.edu/~nisg/cis/web/cgi/rgb.html
'state_inactive_color': [ opt_color, 'grey62' ],
'state_composing_color': [ opt_color, 'green4' ],
'state_paused_color': [ opt_color, 'mediumblue' ],
'state_gone_color': [ opt_color, 'grey' ],
# MUC chat states
'state_muc_msg_color': [ opt_color, 'mediumblue' ],
'state_muc_directed_msg_color': [ opt_color, 'red2' ],
}, {}),
'contacts': ({
'speller_language': [ opt_str, '', _('Language for which misspelled words will be checked')],
}, {}),
@ -522,29 +478,6 @@ class Config:
'muc_message_received': [ False, 'gc_message2.wav', _('Sound to play when any MUC message arrives.') ],
}
themes_default = {
# sorted alphanum
_('default'): [ '', '', '', 'B', '', '', '', 'I', '', '', '', '', '', '',
'', 'B' ],
_('green'): [ '', '#94aa8c', '', 'B', '#0000ff', '#eff3e7',
'', 'I', '#000000', '', '', '', '',
'#94aa8c', '', 'B' ],
_('grocery'): [ '', '#6bbe18', '', 'B', '#12125a', '#ceefad',
'', 'I', '#000000', '#efb26b', '', '', '',
'#108abd', '', 'B' ],
_('human'): [ '', '#996442', '', 'B', '#ab5920', '#e3ca94',
'', 'I', '#000000', '', '', '', '',
'#996442', '', 'B' ],
_('marine'): [ '', '#918caa', '', 'B', '', '#e9e7f3',
'', 'I', '#000000', '', '', '', '',
'#918caa', '', 'B' ],
}
proxies_default = {
_('Tor'): ['socks5', 'localhost', 9050],
}

View File

@ -128,6 +128,7 @@ class ConfigPaths:
source_paths = [
('DATA', os.path.join(basedir, 'data')),
('STYLE', os.path.join(basedir, 'data', 'style')),
('GUI', os.path.join(basedir, 'data', 'gui')),
('ICONS', os.path.join(basedir, 'data', 'icons')),
('HOME', os.path.expanduser('~')),
@ -214,6 +215,10 @@ class ConfigPaths:
# Cache paths
('CACHE_DB', 'cache.db', PathLocation.CACHE, PathType.FILE),
('AVATAR', 'avatars', PathLocation.CACHE, PathType.FOLDER),
# Config paths
('MY_THEME', 'theme', PathLocation.CONFIG, PathType.FOLDER),
]
for path in paths:

View File

@ -1378,3 +1378,11 @@ class InformationEvent(nec.NetworkIncomingEvent):
else:
self.args = (self.args,)
return True
class StyleChanged(nec.NetworkIncomingEvent):
name = 'style-changed'
base_network_events = []
def generate(self):
return True

View File

@ -1,9 +1,12 @@
from enum import IntEnum, unique
from enum import IntEnum, Enum, unique
from collections import namedtuple
Option = namedtuple('Option', 'kind label type value name callback data desc enabledif props')
Option.__new__.__defaults__ = (None,) * len(Option._fields)
DialogButton = namedtuple('DialogButton', 'text callback action')
DialogButton.__new__.__defaults__ = (None, None)
@unique
class OptionKind(IntEnum):
@ -117,6 +120,24 @@ class JIDConstant(IntEnum):
NORMAL_TYPE = 0
ROOM_TYPE = 1
@unique
class StyleAttr(Enum):
COLOR = 'color'
BACKGROUND = 'background'
FONT = 'font'
@unique
class CSSPriority(IntEnum):
APPLICATION = 600
APPLICATION_DARK = 601
DEFAULT_THEME = 610
DEFAULT_THEME_DARK = 611
USER_THEME = 650
@unique
class ButtonAction(Enum):
DESTRUCTIVE = 'destructive-action'
SUGGESTED = 'suggested-action'
@unique
class IdleState(IntEnum):

492
gajim/common/css_config.py Normal file
View File

@ -0,0 +1,492 @@
# Copyright (C) 2018 Philipp Hörist <philipp AT hoerist.com>
#
# This file is part of Gajim.
#
# Gajim is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Gajim is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
import os
import math
import logging
import cssutils
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import Pango
from gajim.common import app
from gajim.common import configpaths
from gajim.common.const import StyleAttr, CSSPriority
log = logging.getLogger('gajim.c.css')
_settings = Gtk.Settings.get_default()
PREFER_DARK = False
if _settings is not None:
PREFER_DARK = _settings.get_property('gtk-application-prefer-dark-theme')
class CSSConfig():
def __init__(self):
"""CSSConfig handles loading and storing of all relevant Gajim style files
The order in which CSSConfig loads the styles
1. gajim.css
2. gajim-dark.css (Only if gtk-application-prefer-dark-theme = True)
3. default.css or default-dark.css (from gajim/data/style)
4. user-theme.css (from ~/.config/Gajim/theme)
# gajim.css:
This is the main style and the application default
# gajim-dark.css
Has only entrys which we want to override in gajim.css
# default.css or default-dark.css
Has all the values that are changeable via UI (see themes.py).
Depending on `gtk-application-prefer-dark-theme` either default.css or
default-dark.css gets loaded
# user-theme.css
These are the themes the Themes Dialog stores. Because they are
loaded at last they overwrite everything else. Users should add custom
css here."""
# Delete empty rules
cssutils.ser.prefs.keepEmptyRules = False
# Holds the currently selected theme in the Theme Editor
self._pre_css = None
self._pre_css_path = None
# Holds the default theme, its used if values are not found
# in the selected theme
self._default_css = None
self._default_css_path = None
# Holds the currently selected theme
self._css = None
self._css_path = None
# User Theme CSS Provider
self._provider = Gtk.CssProvider()
# Cache of recently requested values
self._cache = {}
# Holds all currently available themes
self.themes = []
self._load_css()
self._gather_available_themes()
self._load_default()
self._load_selected()
self._activate_theme()
Gtk.StyleContext.add_provider_for_screen(
Gdk.Screen.get_default(),
self._provider,
CSSPriority.USER_THEME)
def _load_css(self):
self._load_css_from_file('gajim.css', CSSPriority.APPLICATION)
if PREFER_DARK:
self._load_css_from_file('gajim-dark.css',
CSSPriority.APPLICATION_DARK)
self._load_css_from_file('default.css', CSSPriority.DEFAULT_THEME)
if PREFER_DARK:
self._load_css_from_file('default-dark.css',
CSSPriority.DEFAULT_THEME_DARK)
def _load_css_from_file(self, filename, priority):
path = os.path.join(configpaths.get('STYLE'), filename)
try:
with open(path, "r") as f:
css = f.read()
except Exception as exc:
log.error('Error loading css: %s', exc)
return
self._activate_css(css, priority)
def _activate_css(self, css, priority):
try:
provider = Gtk.CssProvider()
provider.load_from_data(bytes(css.encode('utf-8')))
Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(),
provider,
priority)
except Exception:
log.exception('Error loading application css')
@staticmethod
def _pango_to_css_weight(number):
# Pango allows for weight values between 100 and 1000
# CSS allows only full hundred numbers like 100, 200 ..
number = int(number)
if number < 100:
return 100
if number > 900:
return 900
return int(math.ceil(number / 100.0)) * 100
def _gather_available_themes(self):
files = os.listdir(configpaths.get('MY_THEME'))
self.themes = [file[:-4] for file in files if file.endswith('.css')]
if 'default' in self.themes:
# Ignore user created themes that are named 'default'
self.themes.remove('default')
@classmethod
def get_theme_path(cls, theme, user=True):
if theme == 'default' and PREFER_DARK:
theme = 'default-dark'
if user:
return os.path.join(configpaths.get('MY_THEME'), '%s.css' % theme)
return os.path.join(configpaths.get('STYLE'), '%s.css' % theme)
def _determine_theme_path(self):
# Gets the path of the currently active theme.
# If it does not exist, it falls back to the default theme
theme = app.config.get('roster_theme')
if theme == 'default':
return self.get_theme_path(theme, user=False)
theme_path = self.get_theme_path(theme)
if not theme or not os.path.exists(theme_path):
log.warning('Theme %s not found, fallback to default', theme)
app.config.set('roster_theme', 'default')
log.info('Use Theme: default')
return self.get_theme_path('default', user=False)
log.info('Use Theme: %s', theme)
return theme_path
def _load_selected(self, new_path=None):
if new_path is None:
self._css_path = self._determine_theme_path()
else:
self._css_path = new_path
self._css = cssutils.parseFile(self._css_path)
def _load_default(self):
self._default_css_path = self.get_theme_path('default', user=False)
self._default_css = cssutils.parseFile(self._default_css_path)
def _load_pre(self, theme):
log.info('Preload theme %s', theme)
self._pre_css_path = self.get_theme_path(theme)
self._pre_css = cssutils.parseFile(self._pre_css_path)
def _write(self, pre):
path = self._css_path
css = self._css
if pre:
path = self._pre_css_path
css = self._pre_css
with open(path, 'w', encoding='utf-8') as file:
file.write(css.cssText.decode('utf-8'))
active = self._pre_css_path == self._css_path
if not pre or active:
self._load_selected()
self._activate_theme()
def set_value(self, selector, attr, value, pre=False):
if attr == StyleAttr.FONT:
# forward to set_font() for convenience
return self.set_font(selector, value, pre)
if isinstance(attr, StyleAttr):
attr = attr.value
css = self._css
if pre:
css = self._pre_css
for rule in css:
if rule.type != rule.STYLE_RULE:
continue
if rule.selectorText == selector:
log.info('Set %s %s %s', selector, attr, value)
rule.style[attr] = value
if not pre:
self._add_to_cache(selector, attr, value)
self._write(pre)
return
# The rule was not found, so we add it to this theme
log.info('Set %s %s %s', selector, attr, value)
rule = cssutils.css.CSSStyleRule(selectorText=selector)
rule.style[attr] = value
css.add(rule)
self._write(pre)
def set_font(self, selector, description, pre=False):
css = self._css
if pre:
css = self._pre_css
family, size, style, weight = self._get_attr_from_description(
description)
for rule in css:
if rule.type != rule.STYLE_RULE:
continue
if rule.selectorText == selector:
log.info('Set Font for: %s %s %s %s %s',
selector, family, size, style, weight)
rule.style['font-family'] = family
rule.style['font-style'] = style
rule.style['font-size'] = '%spt' % size
rule.style['font-weight'] = weight
if not pre:
self._add_to_cache(
selector, 'fontdescription', description)
self._write(pre)
return
# The rule was not found, so we add it to this theme
log.info('Set Font for: %s %s %s %s %s',
selector, family, size, style, weight)
rule = cssutils.css.CSSStyleRule(selectorText=selector)
rule.style['font-family'] = family
rule.style['font-style'] = style
rule.style['font-size'] = '%spt' % size
rule.style['font-weight'] = weight
css.add(rule)
self._write(pre)
def _get_attr_from_description(self, description):
size = description.get_size() / Pango.SCALE
style = self._get_string_from_pango_style(description.get_style())
weight = self._pango_to_css_weight(int(description.get_weight()))
family = description.get_family()
return family, size, style, weight
def _get_default_rule(self, selector, attr):
for rule in self._default_css:
if rule.type != rule.STYLE_RULE:
continue
if rule.selectorText == selector:
log.info('Get Default Rule %s', selector)
return rule
def get_font(self, selector, pre=False):
if pre:
css = self._pre_css
else:
css = self._css
try:
return self._get_from_cache(selector, 'fontdescription')
except KeyError:
pass
if css is None:
return
for rule in css:
if rule.type != rule.STYLE_RULE:
continue
if rule.selectorText == selector:
log.info('Get Font for: %s', selector)
style = rule.style.getPropertyValue('font-style') or None
size = rule.style.getPropertyValue('font-size') or None
weight = rule.style.getPropertyValue('font-weight') or None
family = rule.style.getPropertyValue('font-family') or None
desc = self._get_description_from_css(
family, size, style, weight)
if not pre:
self._add_to_cache(selector, 'fontdescription', desc)
return desc
def _get_description_from_css(self, family, size, style, weight):
if family is None:
return
desc = Pango.FontDescription()
desc.set_family(family)
if weight is not None:
desc.set_weight(Pango.Weight(int(weight)))
if style is not None:
desc.set_style(self._get_pango_style_from_string(style))
if size is not None:
desc.set_size(int(size[:-2]) * Pango.SCALE)
return desc
@staticmethod
def _get_pango_style_from_string(style: str) -> int:
if style == 'normal':
return Pango.Style(0)
if style == 'oblique':
return Pango.Style(1)
# Pango.Style.ITALIC:
return Pango.Style(2)
@staticmethod
def _get_string_from_pango_style(style: Pango.Style) -> str:
if style == Pango.Style.NORMAL:
return 'normal'
if style == Pango.Style.ITALIC:
return 'italic'
# Pango.Style.OBLIQUE:
return 'oblique'
def get_value(self, selector, attr, pre=False):
if attr == StyleAttr.FONT:
# forward to get_font() for convenience
return self.get_font(selector, pre)
if isinstance(attr, StyleAttr):
attr = attr.value
if pre:
css = self._pre_css
else:
css = self._css
try:
return self._get_from_cache(selector, attr)
except KeyError:
pass
if css is not None:
for rule in css:
if rule.type != rule.STYLE_RULE:
continue
if rule.selectorText == selector:
log.info('Get %s %s: %s',
selector, attr, rule.style[attr] or None)
value = rule.style.getPropertyValue(attr) or None
if not pre:
self._add_to_cache(selector, attr, value)
return value
# We didnt find the selector in the selected theme
# search in default theme
if not pre:
rule = self._get_default_rule(selector, attr)
if rule is not None:
self._add_to_cache(selector, attr, rule.style[attr])
return rule.style[attr]
def remove_value(self, selector, attr, pre=False):
if attr == StyleAttr.FONT:
# forward to remove_font() for convenience
return self.remove_font(selector, pre)
if isinstance(attr, StyleAttr):
attr = attr.value
css = self._css
if pre:
css = self._pre_css
for rule in css:
if rule.type != rule.STYLE_RULE:
continue
if rule.selectorText == selector:
log.info('Remove %s %s', selector, attr)
rule.style.removeProperty(attr)
break
self._write(pre)
def remove_font(self, selector, pre=False):
css = self._css
if pre:
css = self._pre_css
for rule in css:
if rule.type != rule.STYLE_RULE:
continue
if rule.selectorText == selector:
log.info('Remove Font from: %s', selector)
rule.style.removeProperty('font-family')
rule.style.removeProperty('font-size')
rule.style.removeProperty('font-style')
rule.style.removeProperty('font-weight')
break
self._write(pre)
def change_theme(self, theme):
user = not theme == 'default'
theme_path = self.get_theme_path(theme, user=user)
if not os.path.exists(theme_path):
log.error('Change Theme: Theme %s does not exist', theme_path)
return False
self._load_selected(theme_path)
self._activate_theme()
app.config.set('roster_theme', theme)
log.info('Change Theme: Successful switched to %s', theme)
return True
def change_preload_theme(self, theme):
theme_path = self.get_theme_path(theme)
if not os.path.exists(theme_path):
log.error('Change Preload Theme: Theme %s does not exist',
theme_path)
return False
self._load_pre(theme)
log.info('Successful switched to %s', theme)
return True
def rename_theme(self, old_theme, new_theme):
if old_theme not in self.themes:
log.error('Rename Theme: Old theme %s not found', old_theme)
return False
if new_theme in self.themes:
log.error('Rename Theme: New theme %s exists already', new_theme)
return False
old_theme_path = self.get_theme_path(old_theme)
new_theme_path = self.get_theme_path(new_theme)
os.rename(old_theme_path, new_theme_path)
self.themes.remove(old_theme)
self.themes.append(new_theme)
self._load_pre(new_theme)
log.info('Rename Theme: Successful renamed theme from %s to %s',
old_theme, new_theme)
return True
def _activate_theme(self):
log.info('Activate theme')
self._invalidate_cache()
self._provider.load_from_data(self._css.cssText)
def add_new_theme(self, theme):
theme_path = self.get_theme_path(theme)
if os.path.exists(theme_path):
log.error('Add Theme: %s exists already', theme_path)
return False
with open(theme_path, 'w', encoding='utf8'):
pass
self.themes.append(theme)
log.info('Add Theme: Successful added theme %s', theme)
return True
def remove_theme(self, theme):
theme_path = self.get_theme_path(theme)
if os.path.exists(theme_path):
os.remove(theme_path)
self.themes.remove(theme)
log.info('Remove Theme: Successful removed theme %s', theme)
def _add_to_cache(self, selector, attr, value):
self._cache[selector + attr] = value
def _get_from_cache(self, selector, attr):
return self._cache[selector + attr]
def _invalidate_cache(self):
self._cache = {}

View File

@ -49,6 +49,7 @@ from gajim.common import i18n
from calendar import timegm
from gajim.common.fuzzyclock import FuzzyClock
from gajim import emoticons
from gajim.common.const import StyleAttr
from gajim.htmltextview import HtmlTextView
@ -216,42 +217,47 @@ class ConversationTextview(GObject.GObject):
self.last_time_printout = 0
style = self.tv.get_style_context()
style.add_class('font_custom')
style.add_class('gajim-conversation-font')
buffer_ = self.tv.get_buffer()
end_iter = buffer_.get_end_iter()
buffer_.create_mark('end', end_iter, False)
self.tagIn = buffer_.create_tag('incoming')
color = app.config.get('inmsgcolor')
font = Pango.FontDescription(app.config.get('inmsgfont'))
color = app.css_config.get_value(
'.gajim-incoming-nickname', StyleAttr.COLOR)
self.tagIn.set_property('foreground', color)
self.tagIn.set_property('font-desc', font)
desc = app.css_config.get_font('.gajim-incoming-nickname')
self.tagIn.set_property('font-desc', desc)
self.tagOut = buffer_.create_tag('outgoing')
color = app.config.get('outmsgcolor')
font = Pango.FontDescription(app.config.get('outmsgfont'))
color = app.css_config.get_value(
'.gajim-outgoing-nickname', StyleAttr.COLOR)
self.tagOut.set_property('foreground', color)
self.tagOut.set_property('font-desc', font)
desc = app.css_config.get_font('.gajim-outgoing-nickname')
self.tagOut.set_property('font-desc', desc)
self.tagStatus = buffer_.create_tag('status')
color = app.config.get('statusmsgcolor')
font = Pango.FontDescription(app.config.get('satusmsgfont'))
color = app.css_config.get_value(
'.gajim-status-message', StyleAttr.COLOR)
self.tagStatus.set_property('foreground', color)
self.tagStatus.set_property('font-desc', font)
desc = app.css_config.get_font('.gajim-status-message')
self.tagStatus.set_property('font-desc', desc)
self.tagInText = buffer_.create_tag('incomingtxt')
color = app.config.get('inmsgtxtcolor')
font = Pango.FontDescription(app.config.get('inmsgtxtfont'))
color = app.css_config.get_value(
'.gajim-incoming-message-text', StyleAttr.COLOR)
if color:
self.tagInText.set_property('foreground', color)
self.tagInText.set_property('font-desc', font)
desc = app.css_config.get_font('.gajim-incoming-message-text')
self.tagInText.set_property('font-desc', desc)
self.tagOutText = buffer_.create_tag('outgoingtxt')
color = app.config.get('outmsgtxtcolor')
color = app.css_config.get_value(
'.gajim-outgoing-message-text', StyleAttr.COLOR)
if color:
font = Pango.FontDescription(app.config.get('outmsgtxtfont'))
self.tagOutText.set_property('foreground', color)
self.tagOutText.set_property('font-desc', font)
self.tagOutText.set_property('foreground', color)
desc = app.css_config.get_font('.gajim-outgoing-message-text')
self.tagOutText.set_property('font-desc', desc)
colors = app.config.get('gc_nicknames_colors')
colors = colors.split(':')
@ -261,7 +267,8 @@ class ConversationTextview(GObject.GObject):
tag.set_property('foreground', color)
self.tagMarked = buffer_.create_tag('marked')
color = app.config.get('markedmsgcolor')
color = app.css_config.get_value(
'.gajim-highlight-message', StyleAttr.COLOR)
self.tagMarked.set_property('foreground', color)
self.tagMarked.set_property('weight', Pango.Weight.BOLD)
@ -276,7 +283,7 @@ class ConversationTextview(GObject.GObject):
tag.set_property('scale', 0.8333333333333)
tag = buffer_.create_tag('restored_message')
color = app.config.get('restored_messages_color')
color = app.css_config.get_value('.gajim-restored-message', StyleAttr.COLOR)
tag.set_property('foreground', color)
self.tv.create_tags()
@ -355,13 +362,13 @@ class ConversationTextview(GObject.GObject):
self.tv.destroy()
def update_tags(self):
self.tagIn.set_property('foreground', app.config.get('inmsgcolor'))
self.tagOut.set_property('foreground', app.config.get('outmsgcolor'))
self.tagIn.set_property('foreground', app.css_config.get_value('.gajim-incoming-nickname', StyleAttr.COLOR))
self.tagOut.set_property('foreground', app.css_config.get_value('.gajim-outgoing-nickname', StyleAttr.COLOR))
self.tagStatus.set_property('foreground',
app.config.get('statusmsgcolor'))
app.css_config.get_value('.gajim-status-message', StyleAttr.COLOR))
self.tagMarked.set_property('foreground',
app.config.get('markedmsgcolor'))
color = app.config.get('urlmsgcolor')
app.css_config.get_value('.gajim-highlight-message', StyleAttr.COLOR))
color = app.css_config.get_value('.gajim-url', StyleAttr.COLOR)
self.tv.tagURL.set_property('foreground', color)
self.tv.tagMail.set_property('foreground', color)
self.tv.tagXMPP.set_property('foreground', color)

View File

@ -567,6 +567,9 @@
</child>
</object>
</child>
<style>
<class name="gajim-banner"/>
</style>
</object>
<packing>
<property name="expand">False</property>

View File

@ -1,565 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.18.3 -->
<interface>
<requires lib="gtk+" version="3.12"/>
<object class="GtkListStore" id="liststore1">
<columns>
<!-- column-name item -->
<column type="gchararray"/>
</columns>
<data>
<row>
<col id="0" translatable="yes">Account row</col>
</row>
<row>
<col id="0" translatable="yes">Group row</col>
</row>
<row>
<col id="0" translatable="yes">Contact row</col>
</row>
<row>
<col id="0" translatable="yes">Chat Banner</col>
</row>
</data>
</object>
<object class="GtkWindow" id="gajim_themes_window">
<property name="can_focus">False</property>
<property name="border_width">12</property>
<property name="title" translatable="yes">Gajim Themes Customization</property>
<property name="resizable">False</property>
<property name="window_position">center-on-parent</property>
<property name="type_hint">dialog</property>
<child>
<object class="GtkBox" id="vbox97">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">12</property>
<child>
<object class="GtkBox" id="hbox2979">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">6</property>
<property name="spacing">12</property>
<child>
<object class="GtkBox" id="vbox98">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkScrolledWindow" id="themes_scrolledwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeView" id="themes_treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">False</property>
<child internal-child="selection">
<object class="GtkTreeSelection" id="treeview-selection1"/>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButtonBox" id="hbuttonbox27">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<property name="layout_style">center</property>
<child>
<object class="GtkButton" id="add_button">
<property name="label">gtk-add</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="receives_default">False</property>
<property name="use_stock">True</property>
<signal name="clicked" handler="on_add_button_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="remove_button">
<property name="label">gtk-remove</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="receives_default">False</property>
<property name="use_stock">True</property>
<signal name="clicked" handler="on_remove_button_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="theme_options_vbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkComboBox" id="options_combobox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="model">liststore1</property>
<signal name="changed" handler="on_options_combobox_changed" swapped="no"/>
<child>
<object class="GtkCellRendererText" id="cellrenderertext1"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="grid1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="row_spacing">6</property>
<property name="column_spacing">6</property>
<child>
<object class="GtkCheckButton" id="textcolor_checkbutton">
<property name="label" translatable="yes">Text _color:</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_textcolor_checkbutton_toggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="background_checkbutton">
<property name="label" translatable="yes">_Background:</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_background_checkbutton_toggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="textfont_checkbutton">
<property name="label" translatable="yes">Text _font:</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_textfont_checkbutton_toggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label334">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Font style:</property>
<property name="use_underline">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkColorButton" id="text_colorbutton">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<signal name="color-set" handler="on_text_colorbutton_color_set" swapped="no"/>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkColorButton" id="background_colorbutton">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<signal name="color-set" handler="on_background_colorbutton_color_set" swapped="no"/>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkFontButton" id="text_fontbutton">
<property name="width_request">15</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="font">Sans 12</property>
<property name="show_style">False</property>
<signal name="font-set" handler="on_text_fontbutton_font_set" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="bold_togglebutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Bold</property>
<signal name="toggled" handler="on_bold_togglebutton_toggled" swapped="no"/>
<child>
<object class="GtkImage" id="image28">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-bold</property>
</object>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="italic_togglebutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Italic</property>
<signal name="toggled" handler="on_italic_togglebutton_toggled" swapped="no"/>
<child>
<object class="GtkImage" id="image29">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-italic</property>
</object>
</child>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkDrawingArea" id="drawingarea12">
<property name="height_request">12</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkSeparator" id="vseparator7">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="theme_options_table">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="row_spacing">6</property>
<property name="column_spacing">6</property>
<child>
<object class="GtkSeparator" id="hseparator16">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">5</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label389">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Inactive</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label390">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Composing</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label391">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Paused</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label394">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Gone</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label392">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">MUC
Messages</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">6</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label395">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">MUC Directed
Messages</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">7</property>
</packing>
</child>
<child>
<object class="GtkColorButton" id="muc_directed_msg_colorbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<signal name="color-set" handler="on_muc_directed_msg_colorbutton_color_set" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">7</property>
</packing>
</child>
<child>
<object class="GtkColorButton" id="muc_msg_colorbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<signal name="color-set" handler="on_muc_msg_colorbutton_color_set" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">6</property>
</packing>
</child>
<child>
<object class="GtkColorButton" id="gone_colorbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<signal name="color-set" handler="on_gone_colorbutton_color_set" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkColorButton" id="paused_colorbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<signal name="color-set" handler="on_paused_colorbutton_color_set" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkColorButton" id="composing_colorbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<signal name="color-set" handler="on_composing_colorbutton_color_set" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkColorButton" id="inactive_colorbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<signal name="color-set" handler="on_inactive_colorbutton_color_set" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label393">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="ypad">5</property>
<property name="label" translatable="yes">&lt;b&gt;Chatstate Tab Colors&lt;/b&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</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">0</property>
</packing>
</child>
<child>
<object class="GtkButtonBox" id="hbuttonbox28">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">6</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="close_button">
<property name="label">gtk-close</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="receives_default">False</property>
<property name="use_stock">True</property>
<signal name="clicked" handler="on_close_button_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</interface>

View File

@ -159,6 +159,9 @@
</child>
</object>
</child>
<style>
<class name="gajim-banner"/>
</style>
</object>
<packing>
<property name="expand">False</property>

View File

@ -1509,86 +1509,6 @@ $T will be replaced by auto-not-available timeout</property>
<property name="orientation">vertical</property>
<property name="spacing">12</property>
<child>
<object class="GtkFrame" id="frame9">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkGrid" id="grid9">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">12</property>
<property name="margin_top">6</property>
<property name="row_spacing">6</property>
<property name="column_spacing">12</property>
<child>
<object class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="halign">end</property>
<property name="label" translatable="yes">Chat message</property>
<property name="justify">right</property>
<property name="ellipsize">start</property>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkFontButton" id="conversation_fontbutton">
<property name="width_request">200</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="font">Sans 12</property>
<signal name="font-set" handler="on_conversation_fontbutton_font_set" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="default_chat_font">
<property name="label" translatable="yes">Use system _default</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="halign">start</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_default_chat_font_toggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel" id="label23">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;b&gt;Font&lt;/b&gt;</property>
<property name="use_markup">True</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkFrame" id="frame3">
@ -1658,11 +1578,10 @@ $T will be replaced by auto-not-available timeout</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="theme_combobox">
<object class="GtkComboBoxText" id="theme_combobox">
<property name="width_request">200</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<signal name="changed" handler="on_theme_combobox_changed" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@ -1721,423 +1640,6 @@ $T will be replaced by auto-not-available timeout</property>
</packing>
</child>
<child>
<object class="GtkFrame" id="frame24">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkGrid" id="grid11">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="margin_left">12</property>
<property name="margin_top">6</property>
<property name="row_spacing">6</property>
<property name="column_spacing">12</property>
<child>
<object class="GtkLabel" id="label72">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="label" translatable="yes">Contact's nickname</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label13">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="label" translatable="yes">Contact's message</property>
<property name="justify">right</property>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label71">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="label" translatable="yes">_Status message</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label30">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="label" translatable="yes">Group chat highlight</property>
<property name="use_underline">True</property>
<property name="justify">right</property>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkBox" id="hbox8">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkCheckButton" id="incoming_msg_checkbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="halign">start</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_incoming_msg_checkbutton_toggled" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkColorButton" id="incoming_msg_colorbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">start</property>
<property name="rgba">rgb(0,0,0)</property>
<signal name="color-set" handler="on_incoming_msg_colorbutton_color_set" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkBox" id="hbox11">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkColorButton" id="status_msg_colorbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">start</property>
<property name="rgba">rgb(0,0,0)</property>
<signal name="color-set" handler="on_status_msg_colorbutton_color_set" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkBox" id="hbox10">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
<child>
<object class="GtkColorButton" id="incoming_nick_colorbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">start</property>
<property name="rgba">rgb(0,0,0)</property>
<signal name="color-set" handler="on_incoming_nick_colorbutton_color_set" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="hbox2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
<child>
<object class="GtkColorButton" id="muc_highlight_colorbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">start</property>
<property name="rgba">rgb(0,0,0)</property>
<signal name="color-set" handler="on_muc_highlight_colorbutton_color_set" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label70">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="label" translatable="yes">Your nickname</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="left_attach">3</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label14">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="label" translatable="yes">Your message</property>
<property name="justify">right</property>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="left_attach">3</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label371">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="label" translatable="yes">_URL highlight</property>
<property name="use_underline">True</property>
<property name="justify">right</property>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="left_attach">3</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkBox" id="hbox12">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
<child>
<object class="GtkColorButton" id="outgoing_nick_colorbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">start</property>
<property name="rgba">rgb(0,0,0)</property>
<signal name="color-set" handler="on_outgoing_nick_colorbutton_color_set" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">4</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="hbox9">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkCheckButton" id="outgoing_msg_checkbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="halign">start</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_outgoing_msg_checkbutton_toggled" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkColorButton" id="outgoing_msg_colorbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">start</property>
<property name="rgba">rgb(0,0,0)</property>
<signal name="color-set" handler="on_outgoing_msg_colorbutton_color_set" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">4</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkBox" id="hbox13">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
<child>
<object class="GtkColorButton" id="url_msg_colorbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">start</property>
<property name="rgba">rgb(0,0,0)</property>
<signal name="color-set" handler="on_url_msg_colorbutton_color_set" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">4</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="width_request">6</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="reset_colors_button">
<property name="label" translatable="yes">_Reset to Default Colors</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="focus_on_click">False</property>
<property name="receives_default">False</property>
<property name="halign">end</property>
<property name="margin_top">6</property>
<property name="use_underline">True</property>
<signal name="clicked" handler="on_reset_colors_button_clicked" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">4</property>
<property name="width">5</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel" id="label228">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;b&gt;Chat Line Colors&lt;/b&gt;</property>
<property name="use_markup">True</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>

View File

@ -65,6 +65,9 @@ Agent JID - node</property>
</child>
</object>
</child>
<style>
<class name="gajim-banner"/>
</style>
</object>
<packing>
<property name="expand">False</property>

View File

@ -0,0 +1,208 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.18"/>
<object class="GtkPopover" id="popover1">
<property name="can_focus">False</property>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">never</property>
<property name="shadow_type">in</property>
<property name="min_content_height">200</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkListBox" id="choose_option_listbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="selection_mode">none</property>
<signal name="row-activated" handler="_add_option" swapped="no"/>
<style>
<class name="theme_popover_listbox"/>
</style>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
<object class="GtkListStore" id="theme_store">
<columns>
<!-- column-name name -->
<column type="gchararray"/>
</columns>
</object>
<object class="GtkGrid" id="theme_grid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="row_spacing">6</property>
<property name="column_spacing">6</property>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">never</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeView" id="theme_treeview">
<property name="width_request">150</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="model">theme_store</property>
<child internal-child="selection">
<object class="GtkTreeSelection">
<signal name="changed" handler="_on_theme_selected" swapped="no"/>
</object>
</child>
<child>
<object class="GtkTreeViewColumn">
<property name="title" translatable="yes">Themes</property>
<child>
<object class="GtkCellRendererText">
<property name="editable">True</property>
<signal name="edited" handler="_on_theme_name_edit" swapped="no"/>
</object>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="_on_add_new_theme" swapped="no"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">list-add-symbolic</property>
<property name="icon_size">1</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="remove_theme_button">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="_on_remove_theme" swapped="no"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">list-remove-symbolic</property>
<property name="icon_size">1</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<style>
<class name="linked"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="vexpand">True</property>
<property name="hscrollbar_policy">never</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkListBox" id="option_listbox">
<property name="width_request">200</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="selection_mode">none</property>
<property name="activate_on_single_click">False</property>
<style>
<class name="theme_listbox"/>
</style>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuButton" id="add_option_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="popover">popover1</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">list-add-symbolic</property>
<property name="icon_size">1</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
<style>
<class name="linked"/>
</style>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
</object>
</interface>

View File

@ -0,0 +1,18 @@
.gajim-incoming-nickname {
color: rgb(164, 0, 0)
}
.gajim-outgoing-nickname {
color: rgb(52, 101, 164)
}
.gajim-url {
color: rgb(117, 80, 123)
}
.gajim-highlight-message {
color: rgb(245, 121, 0)
}
.gajim-msg-correcting text {
background: rgb(131, 148, 150)
}
.gajim-status-message {
color: rgb(115, 210, 22)
}

View File

@ -0,0 +1 @@
.gajim-incoming-nickname { color: rgb(164, 0, 0) } .gajim-outgoing-nickname { color: rgb(52, 101, 164) } .gajim-outgoing-message-text { color: rgb(85, 87, 83) } .gajim-status-message { color: rgb(78, 154, 6) } .gajim-url { color: rgb(32, 74, 135) } .gajim-highlight-message { color: rgb(255, 128, 128) } .gajim-restored-message { color: rgb(85, 87, 83) } .gajim-roster-disconnected { background: rgb(171 ,97 ,97) } .gajim-roster-connected { background: rgb(173, 195, 198) }

View File

@ -0,0 +1,2 @@
/* Gajim Dark Application CSS File */

View File

@ -1,5 +1,31 @@
/* Gajim Application CSS File */
.gajim-state-composing {
color: rgb(0, 139, 0)
}
.gajim-state-inactive {
color: rgb(158, 158, 158)
}
.gajim-state-gone {
color: rgb(128, 128, 128)
}
.gajim-state-paused {
color: rgb(0, 0, 205)
}
.gajim-msg-correcting text {
background: rgb(238, 232, 170)
}
.gajim-state-tab-muc-directed-msg {
color: rgb(238, 0, 0)
}
.gajim-state-tab-muc-msg {
color: rgb(0, 0, 205)
}
.gajim-banner {
font-size: 13pt;
font-weight: 700
}
.chatcontrol-actionbar-button {
padding: 0px 5px 0px 5px;
@ -136,6 +162,28 @@ list.settings > row > box {
opacity: 0.95;
}
/* ThemesWindow */
#ThemesWindow grid { padding: 12px; }
.theme_listbox row { border-bottom: 1px solid; border-color: @theme_unfocused_bg_color; padding: 5px 10px 5px 10px;}
.theme_listbox row:last-child { border-bottom: 0px}
.theme_listbox row.activatable:active { box-shadow: none; }
.theme_listbox row:not(.activatable) label { color: @insensitive_fg_color }
.theme_listbox row:focus { outline: none; }
.theme_listbox row:hover { background-color: @theme_base_color }
.theme_remove_button {
background-color: @theme_base_color;
border: none;
background-image: none; }
.theme_remove_button:focus {outline: none;}
.theme_popover_listbox { padding: 10px; }
.theme_popover_listbox row { padding: 5px; }
.theme_popover_listbox row:focus { outline: none; }
/* Text style */
.bold16 { font-size: 16px; font-weight: bold; }

View File

@ -1058,7 +1058,6 @@ class SynchroniseSelectContactsDialog:
iter_ = model.iter_next(iter_)
self.dialog.destroy()
#Action that can be done with an incoming list of contacts
TRANSLATED_ACTION = {'add': _('add'), 'modify': _('modify'),
'remove': _('remove')}

View File

@ -65,6 +65,7 @@ from gajim.common import app
import nbxmpp
from gajim.common import helpers
from gajim.common import ged
from gajim.common.const import StyleAttr
LABELS = {
1: _('This service has not yet responded with detailed information'),
@ -597,34 +598,10 @@ _('Without a connection, you can not browse available services'))
self.banner_icon.clear()
self.banner_icon.hide() # Just clearing it doesn't work
def _set_window_banner_text(self, text, text_after = None):
theme = app.config.get('roster_theme')
bannerfont = app.config.get_per('themes', theme, 'bannerfont')
bannerfontattrs = app.config.get_per('themes', theme,
'bannerfontattrs')
if bannerfont:
font = Pango.FontDescription(bannerfont)
else:
font = Pango.FontDescription('Normal')
if bannerfontattrs:
# B is attribute set by default
if 'B' in bannerfontattrs:
font.set_weight(Pango.Weight.HEAVY)
if 'I' in bannerfontattrs:
font.set_style(Pango.Style.ITALIC)
font_attrs = 'font_desc="%s"' % font.to_string()
font_size = font.get_size()
# in case there is no font specified we use x-large font size
if font_size == 0:
font_attrs = '%s size="large"' % font_attrs
markup = '<span %s>%s</span>' % (font_attrs, text)
if text_after:
font.set_weight(Pango.Weight.NORMAL)
markup = '%s\n<span font_desc="%s" size="small">%s</span>' % \
(markup, font.to_string(), text_after)
def _set_window_banner_text(self, text, text_after=None):
markup = '<span>%s</span>' % (text)
if text_after is not None:
markup += '\n<span size="x-small">%s</span>' % text_after
self.banner.set_markup(markup)
def destroy(self, chain = False):
@ -1195,8 +1172,7 @@ class ToplevelAgentBrowser(AgentBrowser):
# Normal/success
cell.set_property('foreground_set', False)
else:
theme = app.config.get('roster_theme')
bgcolor = app.config.get_per('themes', theme, 'groupbgcolor')
bgcolor = app.css_config.get_value('.gajim-group-row', StyleAttr.BACKGROUND)
if bgcolor:
cell.set_property('cell_background_set', True)
cell.set_property('foreground_set', False)
@ -1329,8 +1305,7 @@ class ToplevelAgentBrowser(AgentBrowser):
AgentBrowser.cleanup(self)
def update_theme(self):
theme = app.config.get('roster_theme')
bgcolor = app.config.get_per('themes', theme, 'groupbgcolor')
bgcolor = app.css_config.get_value('.gajim-group-row', StyleAttr.BACKGROUND)
if bgcolor:
self._renderer.set_property('cell-background', bgcolor)
self.window.services_treeview.queue_draw()

View File

@ -1,411 +0,0 @@
# -*- coding:utf-8 -*-
## src/gajim_themes_window.py
##
## Copyright (C) 2003-2014 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
## Nikos Kouremenos <kourem AT gmail.com>
## Copyright (C) 2006 Jean-Marie Traissard <jim AT lapin.org>
## Copyright (C) 2007 Stephan Erb <steve-e AT h3c.de>
##
## This file is part of Gajim.
##
## Gajim is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation; version 3 only.
##
## Gajim is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
##
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import Pango
from gajim.common import app
from gajim.gtk import ErrorDialog
from gajim.gtk.util import get_builder
class GajimThemesWindow:
def __init__(self, transient):
self.xml = get_builder('gajim_themes_window.ui')
self.window = self.xml.get_object('gajim_themes_window')
self.window.set_transient_for(transient)
self.options = ['account', 'group', 'contact', 'banner']
self.options_combobox = self.xml.get_object('options_combobox')
self.textcolor_checkbutton = self.xml.get_object('textcolor_checkbutton')
self.background_checkbutton = self.xml.get_object('background_checkbutton')
self.textfont_checkbutton = self.xml.get_object('textfont_checkbutton')
self.text_colorbutton = self.xml.get_object('text_colorbutton')
self.background_colorbutton = self.xml.get_object('background_colorbutton')
self.text_fontbutton = self.xml.get_object('text_fontbutton')
self.bold_togglebutton = self.xml.get_object('bold_togglebutton')
self.italic_togglebutton = self.xml.get_object('italic_togglebutton')
self.themes_tree = self.xml.get_object('themes_treeview')
self.theme_options_vbox = self.xml.get_object('theme_options_vbox')
self.theme_options_table = self.xml.get_object('theme_options_table')
self.colorbuttons = {}
for chatstate in ('inactive', 'composing', 'paused', 'gone',
'muc_msg', 'muc_directed_msg'):
self.colorbuttons[chatstate] = self.xml.get_object(chatstate + \
'_colorbutton')
model = Gtk.ListStore(str)
self.themes_tree.set_model(model)
col = Gtk.TreeViewColumn(_('Theme'))
self.themes_tree.append_column(col)
renderer = Gtk.CellRendererText()
col.pack_start(renderer, True)
col.add_attribute(renderer, 'text', 0)
renderer.connect('edited', self.on_theme_cell_edited)
renderer.set_property('editable', True)
self.current_theme = app.config.get('roster_theme')
self.no_update = False
self.fill_themes_treeview()
self.select_active_theme()
self.current_option = self.options[0]
self.set_theme_options(self.current_theme, self.current_option)
self.xml.connect_signals(self)
self.window.connect('delete-event', self.on_themese_window_delete_event)
self.themes_tree.get_selection().connect('changed',
self.selection_changed)
self.window.show_all()
def on_themese_window_delete_event(self, widget, event):
self.window.hide()
return True # do NOT destroy the window
def on_close_button_clicked(self, widget):
window = app.get_app_window('Preferences')
if window is not None:
window.update_theme_list()
self.window.hide()
def on_theme_cell_edited(self, cell, row, new_name):
model = self.themes_tree.get_model()
iter_ = model.get_iter_from_string(row)
old_name = model.get_value(iter_, 0)
if old_name == new_name:
return
if old_name == 'default':
ErrorDialog(
_('You cannot make changes to the default theme'),
_('Please create a new clean theme.'))
return
new_config_name = new_name.replace(' ', '_')
if new_config_name in app.config.get_per('themes'):
return
app.config.add_per('themes', new_config_name)
# Copy old theme values
old_config_name = old_name.replace(' ', '_')
properties = ['textcolor', 'bgcolor', 'font', 'fontattrs']
app.config.add_per('themes', new_config_name)
for option in self.options:
for property_ in properties:
option_name = option + property_
app.config.set_per('themes', new_config_name, option_name,
app.config.get_per('themes', old_config_name, option_name))
app.config.del_per('themes', old_config_name)
if old_config_name == app.config.get('roster_theme'):
app.config.set('roster_theme', new_config_name)
model.set_value(iter_, 0, new_name)
self.current_theme = new_name
def fill_themes_treeview(self):
model = self.themes_tree.get_model()
model.clear()
for config_theme in app.config.get_per('themes'):
theme = config_theme.replace('_', ' ')
model.append([theme])
def select_active_theme(self):
model = self.themes_tree.get_model()
iter_ = model.get_iter_first()
active_theme = app.config.get('roster_theme').replace('_', ' ')
while iter_:
theme = model[iter_][0]
if theme == active_theme:
self.themes_tree.get_selection().select_iter(iter_)
if active_theme == 'default':
self.xml.get_object('remove_button').set_sensitive(False)
self.theme_options_vbox.set_sensitive(False)
self.theme_options_table.set_sensitive(False)
else:
self.xml.get_object('remove_button').set_sensitive(True)
self.theme_options_vbox.set_sensitive(True)
self.theme_options_table.set_sensitive(True)
break
iter_ = model.iter_next(iter_)
def selection_changed(self, widget = None):
(model, iter_) = self.themes_tree.get_selection().get_selected()
selected = self.themes_tree.get_selection().get_selected_rows()
if not iter_ or selected[1] == []:
self.theme_options_vbox.set_sensitive(False)
self.theme_options_table.set_sensitive(False)
return
self.current_theme = model.get_value(iter_, 0)
self.current_theme = self.current_theme.replace(' ', '_')
self.set_theme_options(self.current_theme)
if self.current_theme == 'default':
self.xml.get_object('remove_button').set_sensitive(False)
self.theme_options_vbox.set_sensitive(False)
self.theme_options_table.set_sensitive(False)
else:
self.xml.get_object('remove_button').set_sensitive(True)
self.theme_options_vbox.set_sensitive(True)
self.theme_options_table.set_sensitive(True)
def on_add_button_clicked(self, widget):
model = self.themes_tree.get_model()
iter_ = model.append()
i = 0
# don't confuse translators
theme_name = _('theme name')
theme_name_ns = theme_name.replace(' ', '_')
while theme_name_ns + str(i) in app.config.get_per('themes'):
i += 1
model.set_value(iter_, 0, theme_name + str(i))
app.config.add_per('themes', theme_name_ns + str(i))
self.themes_tree.get_selection().select_iter(iter_)
col = self.themes_tree.get_column(0)
path = model.get_path(iter_)
self.themes_tree.set_cursor(path, col, True)
def on_remove_button_clicked(self, widget):
(model, iter_) = self.themes_tree.get_selection().get_selected()
if not iter_:
return
if self.current_theme == app.config.get('roster_theme'):
ErrorDialog(
_('You cannot delete your current theme'),
_('Pick another theme to use first.'))
return
self.theme_options_vbox.set_sensitive(False)
self.theme_options_table.set_sensitive(False)
self.xml.get_object('remove_button').set_sensitive(False)
app.config.del_per('themes', self.current_theme)
model.remove(iter_)
def set_theme_options(self, theme, option = 'account'):
self.no_update = True
self.options_combobox.set_active(self.options.index(option))
textcolor = app.config.get_per('themes', theme, option + 'textcolor')
if textcolor:
state = True
rgba = Gdk.RGBA()
rgba.parse(textcolor)
self.text_colorbutton.set_rgba(rgba)
else:
state = False
self.textcolor_checkbutton.set_active(state)
self.text_colorbutton.set_sensitive(state)
bgcolor = app.config.get_per('themes', theme, option + 'bgcolor')
if bgcolor:
state = True
rgba = Gdk.RGBA()
rgba.parse(bgcolor)
self.background_colorbutton.set_rgba(rgba)
else:
state = False
self.background_checkbutton.set_active(state)
self.background_colorbutton.set_sensitive(state)
# get the font name before we set widgets and it will not be overridden
font_name = app.config.get_per('themes', theme, option + 'font')
font_attrs = app.config.get_per('themes', theme, option + 'fontattrs')
self._set_font_widgets(font_attrs)
if font_name:
state = True
self.text_fontbutton.set_font_name(font_name)
else:
state = False
self.textfont_checkbutton.set_active(state)
self.text_fontbutton.set_sensitive(state)
self.no_update = False
app.interface.roster.change_roster_style(None)
for chatstate in ('inactive', 'composing', 'paused', 'gone',
'muc_msg', 'muc_directed_msg'):
color = app.config.get_per('themes', theme, 'state_' + chatstate + \
'_color')
rgba = Gdk.RGBA()
rgba.parse(color)
self.colorbuttons[chatstate].set_rgba(rgba)
def on_textcolor_checkbutton_toggled(self, widget):
state = widget.get_active()
self.text_colorbutton.set_sensitive(state)
self._set_color(state, self.text_colorbutton,
'textcolor')
def on_background_checkbutton_toggled(self, widget):
state = widget.get_active()
self.background_colorbutton.set_sensitive(state)
self._set_color(state, self.background_colorbutton,
'bgcolor')
def on_textfont_checkbutton_toggled(self, widget):
self.text_fontbutton.set_sensitive(widget.get_active())
self._set_font()
def on_text_colorbutton_color_set(self, widget):
self._set_color(True, widget, 'textcolor')
def on_background_colorbutton_color_set(self, widget):
self._set_color(True, widget, 'bgcolor')
def on_text_fontbutton_font_set(self, widget):
self._set_font()
def on_options_combobox_changed(self, widget):
index = self.options_combobox.get_active()
if index == -1:
return
self.current_option = self.options[index]
self.set_theme_options(self.current_theme,
self.current_option)
def on_bold_togglebutton_toggled(self, widget):
if not self.no_update:
self._set_font()
def on_italic_togglebutton_toggled(self, widget):
if not self.no_update:
self._set_font()
def _set_color(self, state, widget, option):
"""
Set color value in prefs and update the UI
"""
if state:
color = widget.get_rgba()
color_string = color.to_string()
else:
color_string = ''
begin_option = ''
if not option.startswith('state'):
begin_option = self.current_option
app.config.set_per('themes', self.current_theme,
begin_option + option, color_string)
# use faster functions for this
if self.current_option == 'banner':
app.interface.roster.repaint_themed_widgets()
return
if self.no_update:
return
app.interface.roster.change_roster_style(self.current_option)
def _set_font(self):
"""
Set font value in prefs and update the UI
"""
state = self.textfont_checkbutton.get_active()
if state:
font_string = self.text_fontbutton.get_font_name()
else:
font_string = ''
app.config.set_per('themes', self.current_theme,
self.current_option + 'font', font_string)
font_attrs = self._get_font_attrs()
app.config.set_per('themes', self.current_theme,
self.current_option + 'fontattrs', font_attrs)
# use faster functions for this
if self.current_option == 'banner':
app.interface.roster.repaint_themed_widgets()
if self.no_update:
return
app.interface.roster.change_roster_style(self.current_option)
def _toggle_font_widgets(self, font_props):
"""
Toggle font buttons with the bool values of font_props tuple
"""
self.bold_togglebutton.set_active(font_props[0])
self.italic_togglebutton.set_active(font_props[1])
def _get_font_description(self):
"""
Return a FontDescription from togglebuttons states
"""
fd = Pango.FontDescription()
if self.bold_togglebutton.get_active():
fd.set_weight(Pango.Weight.BOLD)
if self.italic_togglebutton.get_active():
fd.set_style(Pango.Style.ITALIC)
return fd
def _set_font_widgets(self, font_attrs):
"""
Set the correct toggle state of font style buttons by a font string of
type 'BI'
"""
font_props = [False, False, False]
if font_attrs:
if font_attrs.find('B') != -1:
font_props[0] = True
if font_attrs.find('I') != -1:
font_props[1] = True
self._toggle_font_widgets(font_props)
def _get_font_attrs(self):
"""
Get a string with letters of font attribures: 'BI'
"""
attrs = ''
if self.bold_togglebutton.get_active():
attrs += 'B'
if self.italic_togglebutton.get_active():
attrs += 'I'
return attrs
def _get_font_props(self, font_name):
"""
Get tuple of font properties: weight, style
"""
font_props = [False, False, False]
font_description = Pango.FontDescription(font_name)
if font_description.get_weight() != Pango.Weight.NORMAL:
font_props[0] = True
if font_description.get_style() != Pango.Style.ITALIC:
font_props[1] = True
return font_props
def on_inactive_colorbutton_color_set(self, widget):
self.no_update = True
self._set_color(True, widget, 'state_inactive_color')
self.no_update = False
def on_composing_colorbutton_color_set(self, widget):
self.no_update = True
self._set_color(True, widget, 'state_composing_color')
self.no_update = False
def on_paused_colorbutton_color_set(self, widget):
self.no_update = True
self._set_color(True, widget, 'state_paused_color')
self.no_update = False
def on_gone_colorbutton_color_set(self, widget):
self.no_update = True
self._set_color(True, widget, 'state_gone_color')
self.no_update = False
def on_muc_msg_colorbutton_color_set(self, widget):
self.no_update = True
self._set_color(True, widget, 'state_muc_msg_color')
self.no_update = False
def on_muc_directed_msg_colorbutton_color_set(self, widget):
self.no_update = True
self._set_color(True, widget, 'state_muc_directed_msg_color')
self.no_update = False

View File

@ -59,6 +59,7 @@ from gajim.common.modules import dataforms
from gajim.common import ged
from gajim.common import i18n
from gajim.common import contacts
from gajim.common.const import StyleAttr
from gajim.chat_control import ChatControl
from gajim.chat_control_base import ChatControlBase
@ -95,11 +96,11 @@ def cell_data_func(column, renderer, model, iter_, user_data):
theme = app.config.get('roster_theme')
has_parent = bool(model.iter_parent(iter_))
if has_parent:
bgcolor = app.config.get_per('themes', theme, 'contactbgcolor')
renderer.set_property('cell-background', bgcolor or None)
bgcolor = app.css_config.get_value('.gajim-contact-row', StyleAttr.BACKGROUND)
renderer.set_property('cell-background', bgcolor)
else:
bgcolor = app.config.get_per('themes', theme, 'groupbgcolor')
renderer.set_property('cell-background', bgcolor or None)
bgcolor = app.css_config.get_value('.gajim-group-row', StyleAttr.BACKGROUND)
renderer.set_property('cell-background', bgcolor)
if user_data == 'status':
status_cell_data_func(column, renderer, model, iter_, has_parent)
@ -133,18 +134,15 @@ def text_cell_data_func(column, renderer, model, iter_, has_parent, theme):
# cell data func is global, because we don't want it to keep
# reference to GroupchatControl instance (self)
if has_parent:
color = app.config.get_per('themes', theme, 'contacttextcolor')
if color:
renderer.set_property('foreground', color)
else:
renderer.set_property('foreground', None)
renderer.set_property('font',
gtkgui_helpers.get_theme_font_for_option(theme, 'contactfont'))
color = app.css_config.get_value('.gajim-contact-row', StyleAttr.COLOR)
renderer.set_property('foreground', color)
desc = app.css_config.get_font('.gajim-contact-row')
renderer.set_property('font-desc', desc)
else:
color = app.config.get_per('themes', theme, 'grouptextcolor')
renderer.set_property('foreground', color or None)
renderer.set_property('font',
gtkgui_helpers.get_theme_font_for_option(theme, 'groupfont'))
color = app.css_config.get_value('.gajim-group-row', StyleAttr.COLOR)
renderer.set_property('foreground', color)
desc = app.css_config.get_font('.gajim-group-row')
renderer.set_property('font-desc', desc)
class PrivateChatControl(ChatControl):
@ -1023,14 +1021,14 @@ class GroupchatControl(ChatControlBase):
color = None
if chatstate == 'attention' and (not has_focus or not current_tab):
self.attention_flag = True
color = 'state_muc_directed_msg_color'
color = 'tab-muc-directed-msg'
elif chatstate == 'active' or (current_tab and has_focus):
self.attention_flag = False
# get active color from gtk
color = 'active'
elif chatstate == 'newmsg' and (not has_focus or not current_tab) \
and not self.attention_flag:
color = 'state_muc_msg_color'
color = 'tab-muc-msg'
if self.is_continued:
# if this is a continued conversation
@ -1146,18 +1144,16 @@ class GroupchatControl(ChatControlBase):
room jid, subject
"""
self.name_label.set_ellipsize(Pango.EllipsizeMode.END)
font_attrs, font_attrs_small = self.get_font_attrs()
if self.is_continued:
name = self.get_continued_conversation_name()
else:
name = self.room_jid
text = '<span %s>%s</span>' % (font_attrs, name)
self.name_label.set_markup(text)
self.name_label.set_text(name)
if self.subject:
subject = GLib.markup_escape_text(self.subject)
subject_text = self.urlfinder.sub(self.make_href, subject)
subject_text = '<span>%s</span>' % subject_text
self.subject_button.get_popover().set_text(subject_text)
def _nec_vcard_published(self, obj):
@ -1719,13 +1715,8 @@ class GroupchatControl(ChatControlBase):
if status != '':
status = helpers.reduce_chars_newlines(status, max_lines=1)
# escape markup entities and make them small italic and fg color
color = gtkgui_helpers.get_fade_color(self.list_treeview,
selected, focus)
colorstring = "#%04x%04x%04x" % (int(color.red * 65535),
int(color.green * 65535), int(color.blue * 65535))
name += ('\n<span size="small" style="italic" foreground="%s">'
'%s</span>') % (colorstring, GLib.markup_escape_text(
status))
name += ('\n<span size="small" style="italic" alpha="70%">'
'{}</span>'.format(GLib.markup_escape_text(status)))
if (gc_contact.affiliation != 'none' and
app.config.get('show_affiliation_in_groupchat')):
@ -2112,7 +2103,7 @@ class GroupchatControl(ChatControlBase):
if self.correcting:
self.correcting = False
gtkgui_helpers.remove_css_class(
self.msg_textview, 'msgcorrectingcolor')
self.msg_textview, 'gajim-msg-correcting')
def send_message(self, message, xhtml=None, process_commands=True):
"""

View File

@ -52,6 +52,7 @@ from gajim.gtk.dialogs import AspellDictError
from gajim.gtk.dialogs import HigDialog
from gajim.gtk.dialogs import SSLErrorDialog
from gajim.gtk.dialogs import ChangePasswordDialog
from gajim.gtk.dialogs import NewConfirmationDialog
from gajim.gtk.about import AboutDialog
from gajim.gtk.join_groupchat import JoinGroupchatWindow

View File

@ -986,3 +986,42 @@ class ChangePasswordDialog(Gtk.Dialog):
self._error_label.set_text(error_text)
self._password1_entry.set_sensitive(True)
self._password2_entry.set_sensitive(True)
class NewConfirmationDialog(Gtk.MessageDialog):
def __init__(self, text, sec_text, buttons, transient_for=None):
Gtk.MessageDialog.__init__(self,
transient_for=transient_for,
message_type=Gtk.MessageType.QUESTION,
text=text)
self._buttons = buttons
for response, button in buttons.items():
self.add_button(button.text, response)
if button.action is not None:
widget = self.get_widget_for_response(response)
widget.get_style_context().add_class(button.action.value)
self.format_secondary_markup(sec_text)
self.connect('response', self._on_response)
self.run()
def _on_response(self, dialog, response):
if response == Gtk.ResponseType.DELETE_EVENT:
# Look if DELETE_EVENT is mapped to another response
response = self._buttons.get(response, None)
if response is None:
# If DELETE_EVENT was not mapped we assume CANCEL
response = Gtk.ResponseType.CANCEL
button = self._buttons.get(response, None)
if button is None:
self.destroy()
return
if button.callback is not None:
button.callback()
self.destroy()

View File

@ -25,7 +25,7 @@ from gajim.common import config as c_config
from gajim.common import idle
from gajim.gtk.util import get_builder
from gajim.gtk import AspellDictError
from gajim.gajim_themes_window import GajimThemesWindow
from gajim.gtk.themes import Themes
from gajim.advanced_configuration_window import AdvancedConfigurationWindow
from gajim.chat_control_base import ChatControlBase
from gajim.config import ManageProxiesWindow, ManageSoundsWindow
@ -160,9 +160,8 @@ class Preferences(Gtk.ApplicationWindow):
### Style tab ###
# Themes
theme_combobox = self.xml.get_object('theme_combobox')
cell = Gtk.CellRendererText()
theme_combobox.pack_start(cell, True)
theme_combobox.add_attribute(cell, 'text', 0)
self.changed_id = theme_combobox.connect(
'changed', self.on_theme_combobox_changed)
self.update_theme_list()
# iconset
@ -207,19 +206,6 @@ class Preferences(Gtk.ApplicationWindow):
st = app.config.get('use_transports_iconsets')
self.xml.get_object('transports_iconsets_checkbutton').set_active(st)
# Color widgets
self.draw_color_widgets()
# Font for messages
font = app.config.get('conversation_font')
# try to set default font for the current desktop env
fontbutton = self.xml.get_object('conversation_fontbutton')
if font == '':
fontbutton.set_sensitive(False)
self.xml.get_object('default_chat_font').set_active(True)
else:
fontbutton.set_font_name(font)
### Personal Events tab ###
# outgoing send chat state notifications
st = app.config.get('outgoing_chat_state_notifications')
@ -473,8 +459,8 @@ class Preferences(Gtk.ApplicationWindow):
self.default_msg_tree.get_model().connect('row-changed',
self.on_default_msg_treemodel_row_changed)
self.theme_preferences = None
self.sounds_preferences = None
self.theme_preferences = None
self.notebook.set_current_page(0)
@ -625,36 +611,32 @@ class Preferences(Gtk.ApplicationWindow):
def on_show_avatar_in_tabs_checkbutton_toggled(self, widget):
self.on_checkbutton_toggled(widget, 'show_avatar_in_tabs')
def on_theme_combobox_changed(self, widget):
model = widget.get_model()
active = widget.get_active()
config_theme = model[active][0].replace(' ', '_')
app.config.set('roster_theme', config_theme)
@staticmethod
def on_theme_combobox_changed(combobox):
theme = combobox.get_active_id()
app.config.set('roster_theme', theme)
app.css_config.change_theme(theme)
# begin repainting themed widgets throughout
app.interface.roster.repaint_themed_widgets()
app.interface.roster.change_roster_style(None)
gtkgui_helpers.load_css()
def update_theme_list(self):
theme_combobox = self.xml.get_object('theme_combobox')
model = Gtk.ListStore(str)
theme_combobox.set_model(model)
i = 0
for config_theme in app.config.get_per('themes'):
theme = config_theme.replace('_', ' ')
model.append([theme])
if app.config.get('roster_theme') == config_theme:
theme_combobox.set_active(i)
i += 1
with theme_combobox.handler_block(self.changed_id):
theme_combobox.remove_all()
theme_combobox.append('default', 'default')
for config_theme in app.css_config.themes:
theme_combobox.append(config_theme, config_theme)
theme_combobox.set_active_id(app.config.get('roster_theme'))
def on_manage_theme_button_clicked(self, widget):
if self.theme_preferences is None:
self.theme_preferences = GajimThemesWindow(self)
window = app.get_app_window(Themes)
if window is None:
Themes(self)
else:
self.theme_preferences.window.present()
self.theme_preferences.select_active_theme()
window.present()
def on_iconset_combobox_changed(self, widget):
model = widget.get_model()
@ -747,128 +729,6 @@ class Preferences(Gtk.ApplicationWindow):
else:
self.sounds_preferences.window.present()
def update_text_tags(self):
"""
Update color tags in opened chat windows
"""
for ctrl in self._get_all_controls():
ctrl.update_tags()
def on_preference_widget_color_set(self, widget, text):
color = widget.get_color()
color_string = color.to_string()
app.config.set(text, color_string)
self.update_text_tags()
def on_preference_widget_font_set(self, widget, text):
if widget:
font = widget.get_font_name()
else:
font = ''
app.config.set(text, font)
gtkgui_helpers.load_css()
def on_incoming_nick_colorbutton_color_set(self, widget):
self.on_preference_widget_color_set(widget, 'inmsgcolor')
def on_outgoing_nick_colorbutton_color_set(self, widget):
self.on_preference_widget_color_set(widget, 'outmsgcolor')
def on_incoming_msg_colorbutton_color_set(self, widget):
self.on_preference_widget_color_set(widget, 'inmsgtxtcolor')
def on_outgoing_msg_colorbutton_color_set(self, widget):
self.on_preference_widget_color_set(widget, 'outmsgtxtcolor')
def on_url_msg_colorbutton_color_set(self, widget):
self.on_preference_widget_color_set(widget, 'urlmsgcolor')
def on_status_msg_colorbutton_color_set(self, widget):
self.on_preference_widget_color_set(widget, 'statusmsgcolor')
def on_muc_highlight_colorbutton_color_set(self, widget):
self.on_preference_widget_color_set(widget, 'markedmsgcolor')
def on_conversation_fontbutton_font_set(self, widget):
self.on_preference_widget_font_set(widget, 'conversation_font')
def on_default_chat_font_toggled(self, widget):
font_widget = self.xml.get_object('conversation_fontbutton')
if widget.get_active():
font_widget.set_sensitive(False)
font_widget = None
else:
font_widget.set_sensitive(True)
self.on_preference_widget_font_set(font_widget, 'conversation_font')
def draw_color_widgets(self):
col_to_widget = {'inmsgcolor': 'incoming_nick_colorbutton',
'outmsgcolor': 'outgoing_nick_colorbutton',
'inmsgtxtcolor': ['incoming_msg_colorbutton',
'incoming_msg_checkbutton'],
'outmsgtxtcolor': ['outgoing_msg_colorbutton',
'outgoing_msg_checkbutton'],
'statusmsgcolor': 'status_msg_colorbutton',
'urlmsgcolor': 'url_msg_colorbutton',
'markedmsgcolor': 'muc_highlight_colorbutton'}
for c in col_to_widget:
col = app.config.get(c)
if col:
if isinstance(col_to_widget[c], list):
rgba = Gdk.RGBA()
rgba.parse(col)
self.xml.get_object(col_to_widget[c][0]).set_rgba(rgba)
self.xml.get_object(col_to_widget[c][0]).set_sensitive(True)
self.xml.get_object(col_to_widget[c][1]).set_active(True)
else:
rgba = Gdk.RGBA()
rgba.parse(col)
self.xml.get_object(col_to_widget[c]).set_rgba(rgba)
else:
rgba = Gdk.RGBA()
rgba.parse('#000000')
if isinstance(col_to_widget[c], list):
self.xml.get_object(col_to_widget[c][0]).set_rgba(rgba)
self.xml.get_object(col_to_widget[c][0]).set_sensitive(False)
self.xml.get_object(col_to_widget[c][1]).set_active(False)
else:
self.xml.get_object(col_to_widget[c]).set_rgba(rgba)
def on_reset_colors_button_clicked(self, widget):
col_to_widget = {'inmsgcolor': 'incoming_nick_colorbutton',
'outmsgcolor': 'outgoing_nick_colorbutton',
'inmsgtxtcolor': 'incoming_msg_colorbutton',
'outmsgtxtcolor': 'outgoing_msg_colorbutton',
'statusmsgcolor': 'status_msg_colorbutton',
'urlmsgcolor': 'url_msg_colorbutton',
'markedmsgcolor': 'muc_highlight_colorbutton'}
for c in col_to_widget:
app.config.set(c, app.interface.default_colors[c])
self.draw_color_widgets()
self.update_text_tags()
def _set_color(self, state, widget_name, option):
"""
Set color value in prefs and update the UI
"""
if state:
color = self.xml.get_object(widget_name).get_rgba()
color_string = color.to_string()
else:
color_string = ''
app.config.set(option, color_string)
def on_incoming_msg_checkbutton_toggled(self, widget):
state = widget.get_active()
self.xml.get_object('incoming_msg_colorbutton').set_sensitive(state)
self._set_color(state, 'incoming_msg_colorbutton', 'inmsgtxtcolor')
def on_outgoing_msg_checkbutton_toggled(self, widget):
state = widget.get_active()
self.xml.get_object('outgoing_msg_colorbutton').set_sensitive(state)
self._set_color(state, 'outgoing_msg_colorbutton', 'outmsgtxtcolor')
def on_auto_away_checkbutton_toggled(self, widget):
self.on_checkbutton_toggled(widget, 'autoaway',
[self.auto_away_time_spinbutton, self.auto_away_message_entry])

415
gajim/gtk/themes.py Normal file
View File

@ -0,0 +1,415 @@
# Copyright (C) 2018 Philipp Hörist <philipp AT hoerist.com>
#
# This file is part of Gajim.
#
# Gajim is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
# by the Free Software Foundation; version 3 only.
#
# Gajim is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
from collections import namedtuple
from enum import IntEnum
from gi.repository import Gtk
from gi.repository import Gdk
from gajim.common import app
from gajim.common.const import StyleAttr, DialogButton, ButtonAction
from gajim.common.connection_handlers_events import StyleChanged
from gajim.gtk import ErrorDialog
from gajim.gtk import NewConfirmationDialog
from gajim.gtk.util import get_builder
StyleOption = namedtuple('StyleOption', 'label selector attr')
CSS_STYLE_OPTIONS = [
StyleOption(_('Chatstate Composing'),
'.gajim-state-composing',
StyleAttr.COLOR),
StyleOption(_('Chatstate Inactive'),
'.gajim-state-inactive',
StyleAttr.COLOR),
StyleOption(_('Chatstate Gone'),
'.gajim-state-gone',
StyleAttr.COLOR),
StyleOption(_('Chatstate Paused'),
'.gajim-state-paused',
StyleAttr.COLOR),
StyleOption(_('MUC Tab New Directed Message'),
'.gajim-state-tab-muc-directed-msg',
StyleAttr.COLOR),
StyleOption(_('MUC Tab New Message'),
'.gajim-state-tab-muc-msg',
StyleAttr.COLOR),
StyleOption(_('Banner Foreground Color'),
'.gajim-banner',
StyleAttr.COLOR),
StyleOption(_('Banner Background Color'),
'.gajim-banner',
StyleAttr.BACKGROUND),
StyleOption(_('Banner Font'),
'.gajim-banner',
StyleAttr.FONT),
StyleOption(_('Account Row Foreground Color'),
'.gajim-account-row',
StyleAttr.COLOR),
StyleOption(_('Account Row Background Color'),
'.gajim-account-row',
StyleAttr.BACKGROUND),
StyleOption(_('Account Row Font Color'),
'.gajim-account-row',
StyleAttr.FONT),
StyleOption(_('Group Row Foreground Color'),
'.gajim-group-row',
StyleAttr.COLOR),
StyleOption(_('Group Row Background Color'),
'.gajim-group-row',
StyleAttr.BACKGROUND),
StyleOption(_('Group Row Font Color'),
'.gajim-group-row',
StyleAttr.FONT),
StyleOption(_('Contact Row Foreground Color'),
'.gajim-contact-row',
StyleAttr.COLOR),
StyleOption(_('Contact Row Background Color'),
'.gajim-contact-row',
StyleAttr.BACKGROUND),
StyleOption(_('Contact Row Font Color'),
'.gajim-contact-row',
StyleAttr.FONT),
StyleOption(_('Conversation Font'),
'.gajim-conversation-font',
StyleAttr.FONT),
StyleOption(_('Incoming Nickname Color'),
'.gajim-incoming-nickname',
StyleAttr.COLOR),
StyleOption(_('Outgoing Nickname Color'),
'.gajim-outgoing-nickname',
StyleAttr.COLOR),
StyleOption(_('Incoming Message Text Color'),
'.gajim-incoming-message-text',
StyleAttr.COLOR),
StyleOption(_('Incoming Message Text Font'),
'.gajim-incoming-message-text',
StyleAttr.FONT),
StyleOption(_('Outgoing Message Text Color'),
'.gajim-outgoing-message-text',
StyleAttr.COLOR),
StyleOption(_('Outgoing Message Text Font'),
'.gajim-outgoing-message-text',
StyleAttr.FONT),
StyleOption(_('Status Message Color'),
'.gajim-status-message',
StyleAttr.COLOR),
StyleOption(_('Status Message Font'),
'.gajim-status-message',
StyleAttr.FONT),
StyleOption(_('URL Color'),
'.gajim-url',
StyleAttr.COLOR),
StyleOption(_('Highlight Message Color'),
'.gajim-highlight-message',
StyleAttr.COLOR),
StyleOption(_('Message Correcting'),
'.gajim-msg-correcting text',
StyleAttr.BACKGROUND),
StyleOption(_('Restored Message Color'),
'.gajim-restored-message',
StyleAttr.COLOR),
StyleOption(_('Contact Disconnected Background'),
'.gajim-roster-disconnected',
StyleAttr.BACKGROUND),
StyleOption(_('Contact Connected Background '),
'.gajim-roster-connected',
StyleAttr.BACKGROUND),
]
class Column(IntEnum):
THEME = 0
class Themes(Gtk.ApplicationWindow):
def __init__(self, transient):
Gtk.Window.__init__(self)
self.set_application(app.app)
self.set_title(_('Gajim Themes'))
self.set_name('ThemesWindow')
self.set_show_menubar(False)
self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
self.set_transient_for(transient)
self.set_resizable(True)
self.set_default_size(600, 400)
self.builder = get_builder('themes_window.ui')
self.add(self.builder.get_object('theme_grid'))
widgets = ['option_listbox', 'remove_theme_button', 'theme_store',
'theme_treeview', 'choose_option_listbox',
'add_option_button']
for widget in widgets:
setattr(self, '_%s' % widget, self.builder.get_object(widget))
# Doesnt work if we set it in Glade
self._add_option_button.set_sensitive(False)
self._get_themes()
self.builder.connect_signals(self)
self.connect('destroy', self._on_destroy)
self.show_all()
self._fill_choose_listbox()
def _get_themes(self):
for theme in app.css_config.themes:
self._theme_store.append([theme])
def _on_theme_name_edit(self, renderer, path, new_name):
iter_ = self._theme_store.get_iter(path)
old_name = self._theme_store[iter_][Column.THEME]
if new_name == 'default':
ErrorDialog(
_('Invalid Name'),
_('Name <b>default</b> is not allowed'),
transient_for=self)
return
if ' ' in new_name:
ErrorDialog(
_('Invalid Name'),
_('Spaces are not allowed'),
transient_for=self)
return
if new_name == '':
return
result = app.css_config.rename_theme(old_name, new_name)
if result is False:
return
self._theme_store.set_value(iter_, Column.THEME, new_name)
def _select_theme_row(self, iter_):
self._theme_treeview.get_selection().select_iter(iter_)
def _on_theme_selected(self, tree_selection):
store, iter_ = tree_selection.get_selected()
if iter_ is None:
self._clear_options()
return
theme = store[iter_][Column.THEME]
app.css_config.change_preload_theme(theme)
self._add_option_button.set_sensitive(True)
self._remove_theme_button.set_sensitive(True)
self._load_options(theme)
def _load_options(self, name):
self._option_listbox.foreach(self._remove_option)
for option in CSS_STYLE_OPTIONS:
value = app.css_config.get_value(
option.selector, option.attr, pre=True)
if value is None:
continue
row = Option(option, value)
self._option_listbox.add(row)
def _add_option(self, listbox, row):
for option in self._option_listbox.get_children():
if option == row:
return
row = Option(row.option, None)
self._option_listbox.add(row)
def _clear_options(self):
self._option_listbox.foreach(self._remove_option)
def _fill_choose_listbox(self):
for option in CSS_STYLE_OPTIONS:
self._choose_option_listbox.add(ChooseOption(option))
def _remove_option(self, row):
self._option_listbox.remove(row)
row.destroy()
def _on_add_new_theme(self, *args):
name = self._create_theme_name()
if not app.css_config.add_new_theme(name):
return
self._remove_theme_button.set_sensitive(True)
iter_ = self._theme_store.append([name])
self._select_theme_row(iter_)
@staticmethod
def _create_theme_name():
i = 0
while 'newtheme%s' % i in app.css_config.themes:
i += 1
return 'newtheme%s' % i
def _on_remove_theme(self, *args):
store, iter_ = self._theme_treeview.get_selection().get_selected()
if iter_ is None:
return
theme = store[iter_][Column.THEME]
if theme == app.config.get('roster_theme'):
ErrorDialog(
_('Active Theme'),
_('You tried to delete the currently active theme. '
'Please switch to a different theme first.'),
transient_for=self)
return
def _remove_theme():
app.css_config.remove_theme(theme)
store.remove(iter_)
first = store.get_iter_first()
if first is None:
self._remove_theme_button.set_sensitive(False)
self._add_option_button.set_sensitive(False)
self._clear_options()
buttons = {
Gtk.ResponseType.CANCEL: DialogButton('Keep Theme'),
Gtk.ResponseType.OK: DialogButton('Delete',
_remove_theme,
ButtonAction.DESTRUCTIVE),
}
NewConfirmationDialog('Delete Theme',
'Do you want to permanently delete this theme?',
buttons,
transient_for=self)
@staticmethod
def _on_destroy(*args):
window = app.get_app_window('Preferences')
if window is not None:
window.update_theme_list()
class Option(Gtk.ListBoxRow):
def __init__(self, option, value):
Gtk.ListBoxRow.__init__(self)
self._option = option
self._box = Gtk.Box(spacing=12)
label = Gtk.Label()
label.set_text(option.label)
label.set_hexpand(True)
label.set_halign(Gtk.Align.START)
self._box.add(label)
if option.attr in (StyleAttr.COLOR, StyleAttr.BACKGROUND):
self._init_color(value)
elif option.attr == StyleAttr.FONT:
self._init_font(value)
remove_button = Gtk.Button.new_from_icon_name(
'list-remove-symbolic', Gtk.IconSize.MENU)
remove_button.get_style_context().add_class('theme_remove_button')
remove_button.connect('clicked', self._on_remove)
self._box.add(remove_button)
self.add(self._box)
self.show_all()
def _init_color(self, color):
color_button = Gtk.ColorButton()
if color is not None:
rgba = Gdk.RGBA()
rgba.parse(color)
color_button.set_rgba(rgba)
color_button.set_halign(Gtk.Align.END)
color_button.connect('color-set', self._on_color_set)
self._box.add(color_button)
def _init_font(self, desc):
font_button = Gtk.FontButton()
if desc is not None:
font_button.set_font_desc(desc)
font_button.set_halign(Gtk.Align.END)
font_button.connect('font-set', self._on_font_set)
self._box.add(font_button)
def _on_color_set(self, color_button):
color = color_button.get_rgba()
color_string = color.to_string()
app.css_config.set_value(
self._option.selector, self._option.attr, color_string, pre=True)
app.nec.push_incoming_event(StyleChanged(None))
def _on_font_set(self, font_button):
desc = font_button.get_font_desc()
app.css_config.set_font(self._option.selector, desc, pre=True)
app.nec.push_incoming_event(StyleChanged(None,))
def _on_remove(self, *args):
self.get_parent().remove(self)
app.css_config.remove_value(
self._option.selector, self._option.attr, pre=True)
app.nec.push_incoming_event(StyleChanged(None))
self.destroy()
def __eq__(self, other):
if isinstance(other, ChooseOption):
return other.option == self._option
else:
return other._option == self._option
class ChooseOption(Gtk.ListBoxRow):
def __init__(self, option):
Gtk.ListBoxRow.__init__(self)
self.option = option
label = Gtk.Label(option.label)
label.set_xalign(0)
self.add(label)
self.show_all()

View File

@ -207,3 +207,14 @@ def python_month(month):
def gtk_month(month):
return month - 1
def convert_rgb_to_hex(rgb_string):
rgb = Gdk.RGBA()
rgb.parse(rgb_string)
rgb.to_color()
red = int(rgb.red * 255)
green = int(rgb.green * 255)
blue = int(rgb.blue * 255)
return '#%02x%02x%02x' % (red, green, blue)

View File

@ -21,7 +21,7 @@ from gi.repository import GLib
from gajim.common import app
from gajim.common import ged
from gajim.common.const import Option, OptionKind, OptionType
from gajim.common.const import Option, OptionKind, OptionType, StyleAttr
from gajim.gtk import ErrorDialog
from gajim.gtk import util
from gajim.gtk.util import get_builder
@ -97,8 +97,10 @@ class XMLConsoleWindow(Gtk.Window):
def create_tags(self):
buffer_ = self.textview.get_buffer()
in_color = app.config.get('inmsgcolor')
out_color = app.config.get('outmsgcolor')
in_color = app.css_config.get_value(
'.gajim-incoming-nickname', StyleAttr.COLOR)
out_color = app.css_config.get_value(
'.gajim-outgoing-nickname', StyleAttr.COLOR)
tags = ['presence', 'message', 'stream', 'iq']

View File

@ -38,6 +38,7 @@ import os
import sys
import math
import xml.etree.ElementTree as ET
try:
from PIL import Image
except:
@ -172,22 +173,6 @@ def get_completion_liststore(entry):
entry.set_completion(completion)
return liststore
def get_theme_font_for_option(theme, option):
"""
Return string description of the font, stored in theme preferences
"""
font_name = app.config.get_per('themes', theme, option)
font_desc = Pango.FontDescription()
font_prop_str = app.config.get_per('themes', theme, option + 'attrs')
if font_prop_str:
if font_prop_str.find('B') != -1:
font_desc.set_weight(Pango.Weight.BOLD)
if font_prop_str.find('I') != -1:
font_desc.set_style(Pango.Style.ITALIC)
fd = Pango.FontDescription(font_name)
fd.merge(font_desc, True)
return fd.to_string()
def move_window(window, x, y):
"""
Move the window, but also check if out of screen
@ -812,71 +797,19 @@ def __label_size_allocate(widget, allocation):
def get_action(action):
return app.app.lookup_action(action)
def load_css():
path = os.path.join(configpaths.get('DATA'), 'style', 'gajim.css')
try:
with open(path, "r") as f:
css = f.read()
except Exception as exc:
print('Error loading css: %s', exc)
return
def add_css_class(widget, class_name, prefix=None):
if class_name and prefix:
class_name = prefix + class_name
provider = Gtk.CssProvider()
css = "\n".join((css, convert_config_to_css()))
provider.load_from_data(bytes(css.encode()))
Gtk.StyleContext.add_provider_for_screen(
Gdk.Screen.get_default(),
provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
def convert_config_to_css():
css = ''
themed_widgets = {
'ChatControl-BannerEventBox': ('bannerbgcolor', 'background'),
'ChatControl-BannerNameLabel': ('bannertextcolor', 'color'),
'ChatControl-BannerLabel': ('bannertextcolor', 'color'),
'GroupChatControl-BannerEventBox': ('bannerbgcolor', 'background'),
'GroupChatControl-BannerNameLabel': ('bannertextcolor', 'color'),
'GroupChatControl-BannerLabel': ('bannertextcolor', 'color'),
'Discovery-BannerEventBox': ('bannerbgcolor', 'background'),
'Discovery-BannerLabel': ('bannertextcolor', 'color')}
classes = {'state_composing_color': ('', 'color'),
'state_inactive_color': ('', 'color'),
'state_gone_color': ('', 'color'),
'state_paused_color': ('', 'color'),
'msgcorrectingcolor': ('text', 'background'),
'state_muc_directed_msg_color': ('', 'color'),
'state_muc_msg_color': ('', 'color')}
theme = app.config.get('roster_theme')
for key, values in themed_widgets.items():
config, attr = values
css += '#{} {{'.format(key)
value = app.config.get_per('themes', theme, config)
if value:
css += '{attr}: {color};\n'.format(attr=attr, color=value)
css += '}\n'
for key, values in classes.items():
node, attr = values
value = app.config.get_per('themes', theme, key)
if value:
css += '.theme_{cls} {node} {{ {attr}: {color}; }}\n'.format(
cls=key, node=node, attr=attr, color=value)
css += add_css_font()
return css
def add_css_class(widget, class_name):
style = widget.get_style_context()
for css_cls in style.list_classes():
if css_cls.startswith('theme_'):
style.remove_class(css_cls)
if class_name:
style.add_class('theme_' + class_name)
if prefix is not None:
# Remove all css classes with prefix
for css_cls in style.list_classes():
if css_cls.startswith(prefix):
style.remove_class(css_cls)
if class_name is not None:
style.add_class(class_name)
def add_css_to_widget(widget, css):
provider = Gtk.CssProvider()
@ -887,28 +820,7 @@ def add_css_to_widget(widget, css):
def remove_css_class(widget, class_name):
style = widget.get_style_context()
style.remove_class('theme_' + class_name)
def add_css_font():
conversation_font = app.config.get('conversation_font')
if not conversation_font:
return ''
font = Pango.FontDescription(conversation_font)
unit = "pt" if Gtk.check_version(3, 22, 0) is None else "px"
css = """
.font_custom {{
font-family: "{family}";
font-size: {size}{unit};
font-weight: {weight};
}}""".format(
family=font.get_family(),
size=int(round(font.get_size() / Pango.SCALE)),
unit=unit,
weight=pango_to_css_weight(font.get_weight()))
css = css.replace("font-size: 0{unit};".format(unit=unit), "")
css = css.replace("font-weight: 0;", "")
css = "\n".join(filter(lambda x: x.strip(), css.splitlines()))
return css
style.remove_class(class_name)
def draw_affiliation(surface, affiliation):
icon_size = 16

View File

@ -2655,15 +2655,6 @@ class Interface:
self.gpg_passphrase = {}
self.pass_dialog = {}
self.db_error_dialog = None
self.default_colors = {
'inmsgcolor': app.config.get('inmsgcolor'),
'outmsgcolor': app.config.get('outmsgcolor'),
'inmsgtxtcolor': app.config.get('inmsgtxtcolor'),
'outmsgtxtcolor': app.config.get('outmsgtxtcolor'),
'statusmsgcolor': app.config.get('statusmsgcolor'),
'urlmsgcolor': app.config.get('urlmsgcolor'),
'markedmsgcolor': app.config.get('markedmsgcolor'),
}
self.handlers = {}
self.roster = None
@ -2684,6 +2675,9 @@ class Interface:
# enable plugin_installer by default when creating config file
app.config.set_per('plugins', 'plugin_installer', 'active', True)
# Load CSS files
app.load_css_config()
app.logger.reset_shown_unread_messages()
# override logging settings from config (don't take care of '-q' option)
if app.config.get('verbose'):
@ -2721,24 +2715,7 @@ class Interface:
default[msg][4])
app.config.set_per('statusmsg', msg, 'mood_text',
default[msg][5])
#add default themes if there is not in the config file
theme = app.config.get('roster_theme')
if not theme in app.config.get_per('themes'):
app.config.set('roster_theme', _('default'))
if len(app.config.get_per('themes')) == 0:
d = ['accounttextcolor', 'accountbgcolor', 'accountfont',
'accountfontattrs', 'grouptextcolor', 'groupbgcolor',
'groupfont', 'groupfontattrs', 'contacttextcolor',
'contactbgcolor', 'contactfont', 'contactfontattrs',
'bannertextcolor', 'bannerbgcolor']
default = app.config.themes_default
for theme_name in default:
app.config.add_per('themes', theme_name)
theme = default[theme_name]
for o in d:
app.config.set_per('themes', theme_name, o,
theme[d.index(o)])
# Add Tor proxy if there is not in the config
if len(app.config.get_per('proxies')) == 0:
default = app.config.proxies_default

View File

@ -48,7 +48,7 @@ from gi.repository import Gio
from gajim.common import i18n
from gajim.common import configpaths
from gajim.common.const import StyleAttr
def is_standalone():
# Determine if we are in standalone mode
@ -395,14 +395,14 @@ class HistoryManager:
if kind in (KindConstant.SINGLE_MSG_RECV,
KindConstant.CHAT_MSG_RECV, KindConstant.GC_MSG):
# it is the other side
color = app.config.get('inmsgcolor') # so incoming color
color = app.css_config.get_value('.gajim-incoming-nickname', StyleAttr.COLOR) # so incoming color
elif kind in (KindConstant.SINGLE_MSG_SENT,
KindConstant.CHAT_MSG_SENT): # it is us
color = app.config.get('outmsgcolor') # so outgoing color
color = app.css_config.get_value('.gajim-outgoing-nickname', StyleAttr.COLOR) # so outgoing color
elif kind in (KindConstant.STATUS,
KindConstant.GCSTATUS): # is is statuses
# so status color
color = app.config.get('statusmsgcolor')
color = app.css_config.get_value('.gajim-status-message', StyleAttr.COLOR)
# include status into (status) message
if message is None:
message = ''

View File

@ -57,6 +57,8 @@ from gajim.gtk.util import get_builder
from gajim.common import helpers
from gajim.gtk import JoinGroupchatWindow
from gajim.gtk import AddNewContactWindow
from gajim.common.const import StyleAttr
import logging
log = logging.getLogger('gajim.htmlview')
@ -514,7 +516,7 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
tag.href = href
tag.type_ = type_ # to be used by the URL handler
tag.connect('event', self.textview.hyperlink_handler, 'url')
tag.set_property('foreground', app.config.get('urlmsgcolor'))
tag.set_property('foreground', app.css_config.get_value('.gajim-url', StyleAttr.COLOR))
tag.set_property('underline', Pango.Underline.SINGLE)
tag.is_anchor = True
if title:
@ -855,7 +857,7 @@ class HtmlTextView(Gtk.TextView):
buffer_ = self.get_buffer()
self.tagURL = buffer_.create_tag('url')
color = app.config.get('urlmsgcolor')
color = app.css_config.get_value('.gajim-url', StyleAttr.COLOR)
self.tagURL.set_property('foreground', color)
self.tagURL.set_property('underline', Pango.Underline.SINGLE)
self.tagURL.connect('event', self.hyperlink_handler, 'url')

View File

@ -55,7 +55,7 @@ class MessageTextView(Gtk.TextView):
self.set_right_margin(2)
self.set_pixels_above_lines(2)
self.set_pixels_below_lines(2)
self.get_style_context().add_class('font_custom')
self.get_style_context().add_class('gajim-conversation-font')
# set undo list
self.undo_list = []

View File

@ -643,15 +643,14 @@ class MessageWindow(object):
if isinstance(ctrl, ChatControl):
tab_label_str = ctrl.get_tab_label()
# Set Label Color
class_name = 'state_{}_color'.format(chatstate)
gtkgui_helpers.add_css_class(nick_label, class_name)
gtkgui_helpers.add_css_class(nick_label, chatstate, 'gajim-state-')
else:
tab_label_str, color = ctrl.get_tab_label(chatstate)
# Set Label Color
if color == 'active':
gtkgui_helpers.add_css_class(nick_label, None)
gtkgui_helpers.add_css_class(nick_label, None, 'gajim-state-')
elif color is not None:
gtkgui_helpers.add_css_class(nick_label, color)
gtkgui_helpers.add_css_class(nick_label, color, 'gajim-state-')
nick_label.set_markup(tab_label_str)

View File

@ -57,6 +57,7 @@ from gajim import tooltips
from gajim import message_control
from gajim import adhoc_commands
from gajim.accounts_window import AccountsWindow
from gajim.gtk import JoinGroupchatWindow
from gajim.gtk import ConfirmationDialogCheck
from gajim.gtk import ConfirmationDialog
@ -73,14 +74,12 @@ from gajim.gtk import AccountCreationWizard
from gajim.gtk import ServiceRegistration
from gajim.gtk import HistoryWindow
from gajim.common.const import AvatarSize
from gajim.common import app
from gajim.common import helpers
from gajim.common import idle
from gajim.common.exceptions import GajimGeneralException
from gajim.common import i18n
from gajim.common.const import PEPEventType
from gajim.common.const import PEPEventType, AvatarSize, StyleAttr
if app.is_installed('GEOCLUE'):
from gajim.common import location_listener
from gajim.common import ged
@ -1265,15 +1264,11 @@ class RosterWindow:
status = helpers.reduce_chars_newlines(status,
max_lines = 1)
# escape markup entities and make them small
# italic and fg color color is calculated to be
# always readable
color = gtkgui_helpers.get_fade_color(self.tree, selected,
focus)
colorstring = '#%04x%04x%04x' % (int(color.red * 65535),
int(color.green * 65535), int(color.blue * 65535))
name += '\n<span size="small" style="italic" ' \
'foreground="%s">%s</span>' % (colorstring,
GLib.markup_escape_text(status))
# italic
name += ('\n<span size="small" style="italic" '
'alpha="70%">{}</span>'.format(
GLib.markup_escape_text(status)))
icon_name = helpers.get_icon_name_to_show(contact, account)
# look if another resource has awaiting events
@ -4688,6 +4683,9 @@ class RosterWindow:
gtkgui_helpers.set_unset_urgency_hint(self.window, nb_unread)
def _style_changed(self, *args):
self.change_roster_style(None)
def _change_style(self, model, path, titer, option):
if option is None or model[titer][Column.TYPE] == option:
# We changed style for this type of row
@ -4751,18 +4749,18 @@ class RosterWindow:
return
theme = app.config.get('roster_theme')
if type_ == 'account':
color = app.config.get_per('themes', theme, 'accounttextcolor')
renderer.set_property('foreground', color or None)
renderer.set_property('font',
gtkgui_helpers.get_theme_font_for_option(theme, 'accountfont'))
color = app.css_config.get_value('.gajim-account-row', StyleAttr.COLOR)
renderer.set_property('foreground', color)
desc = app.css_config.get_font('.gajim-account-row')
renderer.set_property('font-desc', desc)
renderer.set_property('xpad', 0)
renderer.set_property('width', 3)
self._set_account_row_background_color(renderer)
elif type_ == 'group':
color = app.config.get_per('themes', theme, 'grouptextcolor')
renderer.set_property('foreground', color or None)
renderer.set_property('font',
gtkgui_helpers.get_theme_font_for_option(theme, 'groupfont'))
color = app.css_config.get_value('.gajim-group-row', StyleAttr.COLOR)
renderer.set_property('foreground', color)
desc = app.css_config.get_font('.gajim-group-row')
renderer.set_property('font-desc', desc)
parent_iter = model.iter_parent(titer)
if model[parent_iter][Column.TYPE] == 'group':
renderer.set_property('xpad', 8)
@ -4781,19 +4779,16 @@ class RosterWindow:
ctrl = app.interface.minimized_controls[account].get(jid,
None)
if ctrl and ctrl.attention_flag:
color = app.config.get_per('themes', theme,
'state_muc_directed_msg_color')
renderer.set_property('foreground', 'red')
color = app.css_config.get_value(
'.state_muc_directed_msg_color', StyleAttr.COLOR)
renderer.set_property('foreground', color)
if not color:
color = app.config.get_per('themes', theme,
'contacttextcolor')
if color:
color = app.css_config.get_value('.gajim-contact-row', StyleAttr.COLOR)
renderer.set_property('foreground', color)
else:
renderer.set_property('foreground', None)
self._set_contact_row_background_color(renderer, jid, account)
renderer.set_property('font',
gtkgui_helpers.get_theme_font_for_option(theme, 'contactfont'))
desc = app.css_config.get_font('.gajim-contact-row')
renderer.set_property('font-desc', desc)
parent_iter = model.iter_parent(titer)
if model[parent_iter][Column.TYPE] == 'contact':
renderer.set_property('xpad', 16)
@ -4886,25 +4881,25 @@ class RosterWindow:
def _set_account_row_background_color(self, renderer):
theme = app.config.get('roster_theme')
color = app.config.get_per('themes', theme, 'accountbgcolor')
renderer.set_property('cell-background', color or None)
color = app.css_config.get_value('.gajim-account-row', StyleAttr.BACKGROUND)
renderer.set_property('cell-background', color)
def _set_contact_row_background_color(self, renderer, jid, account):
theme = app.config.get('roster_theme')
if jid in app.newly_added[account]:
renderer.set_property('cell-background', app.config.get(
'just_connected_bg_color'))
renderer.set_property('cell-background', app.css_config.get_value(
'.gajim-roster-connected', StyleAttr.BACKGROUND))
elif jid in app.to_be_removed[account]:
renderer.set_property('cell-background', app.config.get(
'just_disconnected_bg_color'))
renderer.set_property('cell-background', app.css_config.get_value(
'.gajim-roster-disconnected', StyleAttr.BACKGROUND))
else:
color = app.config.get_per('themes', theme, 'contactbgcolor')
renderer.set_property('cell-background', color or None)
color = app.css_config.get_value('.gajim-contact-row', StyleAttr.BACKGROUND)
renderer.set_property('cell-background', color)
def _set_group_row_background_color(self, renderer):
theme = app.config.get('roster_theme')
color = app.config.get_per('themes', theme, 'groupbgcolor')
renderer.set_property('cell-background', color or None)
color = app.css_config.get_value('.gajim-group-row', 'background')
renderer.set_property('cell-background', color)
################################################################################
### Everything about building menus
@ -5945,3 +5940,5 @@ class RosterWindow:
self._nec_decrypted_message_received)
app.ged.register_event_handler('blocking', ged.GUI1,
self._nec_blocking)
app.ged.register_event_handler('style-changed', ged.GUI1,
self._style_changed)